所学的所有技术都为业务服务

Docker技术架构

Docker的进程模型

运行docker之后执行。虽然运行了5个docker容器,但却有10个进程
image.png
查看进程树,docker创建了很多进程
image.png
查看详细,共为这10个进程
image.png
image.png
查看bin目录下的docker相关shell
image.png
其中

  • docker根进程
    /usr/bin/dockerd-current 是docker的启动进程,是核心
  • docker服务端server进程 docker-containerd-current
    docker服务端的核心进程。负责与Docker客户端client、容器container进行通信
    执行docker run命令,fork容器进程
    其中 -l unix:///var/run/docker/libcontainerd/docker-containerd.sock 表示打开一个sock,实现所有的Docker容器和客户端通信
  • docker运行具体镜像的容器进程 docker-containerd-shim-current

容器进程启动2种模式

  • Docker容器内进程是宿主机独立进程
  • 构建shell启动方式镜像
    1.创建redisshell文件
FROM centos:latest
RUN  yum -y install redis
EXPOSE 6379
CMD "/usr/bin/redis-server"

2.打包构建redisshell:1.0镜像
docker build -t redisshell:1.0 -f dockershell .
image.png
image.png
3.通过镜像创建容器并进入
image.png
其1号进程为/bredis进程,没有其他进程存在

  • 构建利用exec启动方式镜像
    1.创建redisexec文件
FROM centos:latest
RUN  yum -y install redis
EXPOSE 6379
CMD ["/usr/bin/redis-server"]

2.打包构建redisexec:1.0镜像
docker build -t redisexec:1.0 -f dockerexec .
image.png
3.通过镜像创建容器并进入
image.png

image.png

因为docker stop只对docker容器内的1号进程发送退出信号。所以,为了优雅停机,官方建议采用exec模式启动线程并且每个docker只部署一个服务在1号线程

容器线程隔离

image.png
每个容器之间的线程都是隔离的如上图

容器的重启

docker run命令可以加入重启策略参数,具体可使用man docker run命令查阅。
重启策略有4种,为保证系统负载不过大,每次重启间隔时间从100ms开始以2的指数倍增大

  • no 不自动重启,默认
  • on-failure 1号进程退出非0时重启,可指定最大重启次数
  • always:永远重启,当docker Daemon启动时就启动容器
  • unless-stop 永远重启,当docker Daemon启动时,如果容器之前不为stop状态就启动容器

容器中的用户权限的隔离和传送

Docker安装的时候会自动创建Docker用户组,在启动Docker的时候需要较大的权限。
Docker用户组内执行命令的时候默认会添加sudo
Docker守护线程允许root和DOcker用户组访问docker。在使用相关容器的时候应谨慎,避免下载恶意镜像

Docker守护进程宕机的处理机制

由于Docker容器是Docker守护线程的子进程,所以守护线程挂掉的时候,所有Docker容器也会挂掉。
Docker 1.12版本增加live-restore宣传,去掉守护线程依赖。即使dockerd进程关闭,容器也可以运行。等待dockerd进程恢复后,重新管理容器。
可以在
/etc/docker/daemon.json中添加"live-restore":true 配置
/usr/lib/systemd/system/docker.service 添加 KillMode=process 配置
image.png

容器container的本质

container=CGroups+Namespace+Rootfs

Namespace

命名空间,很多中间件都有这种思想。其是内核级别的环境隔离的方法
在Linux下PID为1时超级父进程,对每个子进程而言,如果超级父进程PID为1即可以达到资源隔离。每个容器都有自己的1号进程
其实现基于

  • clone
    创建新的进程,实现进程的系统调用
  • unshare
    把进程脱离某个Namespace
  • setns
    把进程加入某个Namespace

Rootfs

Rootfs即Docker容器启动时内部进程文件系统,即Docker的根目录
image.png
在/var/run/docker/libcontainerd 目录下的文件目录为每一个docker容器的根目录
image.png

CGroup

Control Group 把进程放到Group中统一管理控制,根据特定的行为把一系列任务及其子任务整合或分离到不同组类。实现不同用户层面的资源管理

  • 资源限制
    可对总资源进行限制,例如内存上线,超过则OOM
  • 优先级分配
    对不同进程分配CPU时间片及I/O带宽
  • 资源统计
    统计系统资源使用量,CPU时长、内存用量
  • 进程控制
    对进程进行挂起、恢复
    Docker是通过CGroup对容器资源限制和监控
    例如docker run -m 512m 镜像:tag
    即对镜像进行512m内存大小限制。如果此时在容器内设置JVM内存参数,则可能会和容器的内存参数有冲突

Docker启动总结

docker启动时利用fork命令从Docker-containerd进程中创建fork一个子进程,然后exec执行。
容器进程被fork之后创建Namespaec,再执行初始化

  • dockerinit初始化网络栈
  • ENTRYPOINT完成用户动态配置
  • CMD负责启动入口
  • Docker容器和Docker守护线程Daemon通过sock文件描述符进行痛惜

Docker体系架构

#官方说明
官方文档
https://docs.docker.com/get-started/overview/
image.png
Docker 使用客户端-服务器体系结构。

  • Docker客户端与 Docker守护程序通信,守护程序负责构建、运行和分发 Docker 容器。Docker 客户端和守护程序可以在同一系统上运行,也可以将 Docker 客户端连接到远程 Docker 守护程序。
  • Docker 客户端和守护程序使用 REST API、UNIX 套接字或网络接口进行通信。
  • 另一个 Docker 客户端是 Docker Compose,它允许您使用由一组容器组成的应用程序。

Docker网络架构

Docker的网络模式较复杂的、默认有4种模式,可以通过docker run --network=="bridge" 来设置,默认为bridge桥接模式
Linux网桥时用一组代码模拟网络协议栈,类似交换机
image.png

Bridge模式

Docker默认的网络模式使用eth0虚拟网桥通信,在iptables上遵循了DNAT规则,实现端口转发功能

  • 安装Docker之后会创建虚拟网桥
    ip addr
    image.png
    网段为172.17.0.1/16
    route
    172.17.0.0网段的报文全部给docker0虚拟网桥
    image.png

  • 验证
    1.启动一个centos镜像
    image.png
    2.查看容器内部ip
    image.png
    3.宿主机ping容器IP,可以ping通
    image.png

Host模式

容器不会获得独立的Namespace,和宿主机共用Network Namespace。同时也不会虚拟出自己的网卡、IP。也是和宿主机共用
--privileged=true命令,容器会被允许直接配置主机的网络堆栈
如下图所示,所有的网络配置IP等和主机相同。
此时操作容器中的某些配置可能会影响到宿主机本身
image.png

Container模式

将Docker新建的容器进程放入一个已存在的网络栈中,虽然进程隔离,但是会和已经存在的容器共享IP、端口等网络信息

  • 例如
    再重新从镜像中启动一个容器和已有容器共享IP为172.17.0.2
    如图所示ip及端口完全相同
    image.png

None模式

新建隔离网络栈,不进行任何网络配置,可以自己手动配置
image.png

Docker集群网络模式

分布式环境下的集群网络通信系统,保证网络易管理、高容错

Bridge端口转发

集群环境下,Docker容器依然可以通过Bridge模式进行通信。

  • 容器和宿主机之间通信
    通过docker0网桥进行通信
  • 宿主机和宿主机之间通信
    通过物理网卡和网络进行通信

实现较简单,但是由于本机会维护路由表,性能不如直连。容器和宿主机暴露的IP、端口绑定(端口如果有冲突叫麻烦)。维护麻烦

交换机打通Docker网络和物理网络

  • 实现
    将物理网卡eth0插拔关联每个Docker容器上,为eth0
    分配和物理网络同源IP地址。容器的IP地址就是物理网络同网段的IP地址。可直接给物理交换机转发
  • 优势
    性能优于NAT(Network Address Translation指网络地址转换)
  • 劣势
    需要提前规划,IP地址可能会冲突,需要做好分配和回收。

新建虚拟网桥

新建一个虚拟网桥,将物理网卡插入该网桥中,将容器也插入虚拟网桥作为出口

Docker安全架构

Docker安全问题

  • Docker和宿主机共内核
    某个容器滥用宿主机资源导致宿主机崩溃会导致所有宿主机崩溃
  • Docker自身漏洞
    问题:Docker自身漏洞主要在代码执行、权限提升、信息泄露、绕过隔离
    解决:将Docker升级最新版本
  • Docker源问题
    Docker Hub可以上传、下载镜像。也会有安全问题:
    1.下载镜像被植入恶意代码(上传恶意镜像)、使用有漏洞软件(下载镜像后需要检查软件版本信息)
    2.传输过程镜像被篡改(中间人攻击篡改镜像,MD5校验)
    3.镜像搭建环境本身漏洞
  • DDoS攻击资源耗尽
    CGroups可以防止此类攻击,不为单一的容器分配过多的资源
  • 共享root
    如果以root权限运行容器,则容器内的root用户也拥有宿主机的root权限
  • 未隔离文件系统
    Docker有一些文件系统暂时未被隔离如/sys、/proc/sys、/proc/bus

Docker安全措施

  • 内核级别
    1.更新内核
    2.将宿主机和Docker界限划分清晰(Namespace、CGroups)
  • 网络级别
    1.禁止在容器上映射特权端口,容器只开放所需要的端口
  • 镜像级别
    1.创建本地镜像仓库服务
    2.使用镜像扫描及签名
  • 容器级别
    1.容器以单一主线程方式进行
    2.禁止容器运行ssh服务
    3.限制容器中可用的进程数量,防止fork炸弹
  • 其他设置
    1.定期对容器进行安全审计
    2.避免在同一宿主机上部署大量容器,维持一个能够管理的数量
  • 容器最小化
  • Docker Remote API
    1.Docker远程调用API接口存在未授权访问漏洞
  • 限制流量流向
    1.使用iptables过滤器限制Docker容器的源IP地址泛微和流量流向
  • 使用普通用户启动Docker服务
  • 文件系统限制
    挂载的容器根目录是绝对只读的,而且不同容器对应的文件目录权限分离,最好每个容器在宿主机上都有自己单独的分区
  • 保证镜像安全
    在镜像仓库客户端使用证书认证,对下载的镜像进行检查,并且不使用--insecure-registry= 参数
  • 保证Docker Client与Daemon通信安全
    防止链路劫持、会话劫持,通信的两端应该通过加密方式进行通信
  • 资源限制
  • 宿主机升级内核
  • 避免Docker信息泄露
    使用Dockerfile或者docker-compose文件创建容器时会有宿主机上的敏感信息在文件上
  • 安全安全加固
  • 限制系统命令调用
  • SUID和GUID限制
  • 能力限制
  • 多租户环境
    由于Docker容器内核共享,无法在多租户环境中安全实现责任分离。应将容器运行在没有其他目的且不用于敏感操作的宿主机上
  • 完全虚拟化
    使用完全虚拟化的解决方案来容纳Docker。防止漏洞从容器中扩大到宿主机上
  • 日志分析
    收集、归档Docker相关的安全日志。rsyslog或stdout+ELK进行日志收集、存储、分析
    1.使用docker run -v 宿主机日志目录:容器日志目录 container /bin/bash
    2.使用docker export导出为一个压缩包格式
  • 漏洞扫描
    Docker漏洞扫描的工具有Docker-slim、Docker Bench for Security
  • 端口扫描
    Nmap和Masscan工具扫描端口

这个家伙很懒,啥也没有留下😋