DOCKER简介

Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。目前docker已经分为企业版EE与社区版CE。

目前版本(Docker-ce 18.09.0)有以下几个部分组成:

  1. Docker Client客户端(docker),cli客户端提供底层操作的命令封装,对外是用户操作docker的入口,对内与dockerd交互。
  2. Docker Daemon守护进程(dockerd),向上监听cli客户端的操作,向下与containerd交互,如果docker传入的命令是docker pull、docker build、docker cp等非涉及容器生命周期命令,则有dockerd模块进行处理,如果是docker run、docker start、docker stop、docker restart等涉及容器生命周期等命令则向下传递给containerd进行处理。
  3. containerd,容器技术标准化后的产物,从早期Docker Daemon剥离出来的专门用于管理容器生命周期的组件,跳过docker本身containerd可以独立运行和管理容器。上层也可以对接其他容器管理软件。containerd向上监听dockerd传入的管理命令,向下结合containerd-shim与runc对多个容器进行独立的生命周期管理。
  4. containerd-shim,是每一个容器的运行时载体,向上与containerd交互(一对多,即一个containerd可以包含多个containerd-shim,通过pstree  -g可以直观查看),向下与runc交互(一对一)。
  5. runc是一个命令行工具负责直接运行容器,与cgroup、linux kernel等进行交互,负责为容器配置namespace等启动容器所需的环境,创建启动容器的相关进程。
  6. ctr与Docker Client客户端类似,是containerd的一个cli客户端。
  7. docker-init,用于在容器初始化init进程,当使用docker run命令运行容器时加上--init时,容器中1号进程则与宿主机一样是init进程,如果不加--init,容器中1号进程则为run命令给定的ENTRYPOINT或CMD(一般为/bin/bash)
  8. docker-proxy,用于docker到宿主机的端口映射。
  9. Docker Image镜像,docker镜像的静态存放
  10. Docker Container容器,docker镜像的动态运行
  11. 如果从具体运行时环境考虑的话还应该包括linux系统的cgroups、namespace以及chroot

在containerd与runc之间为什么使用containerd-shim,而不是直接由containerd直接关联runc,相关解释如下:

  1. 允许runc在创建并运行容器之后退出,由于runc比较占内存,启动n个容器需要额外占用m*n的内存(m为runc占用的内存),而containerd-shim比runc占用内存少。
  2. 让containerd与容器进程解耦,为了防止当containerd挂掉时,shim仍然能运行,保证容器打开的文件描述符不被关掉。其方式也是类似于一般Pass平台,即管理端与客户端分离。

 下图由上至下是docker各层的调用关系

docker 完整版centos docker-ce-cli_Docker

 

下图说明了containerd、runc成为独立模块后与docker的关系。 

docker 完整版centos docker-ce-cli_docker 完整版centos_02

 

 

所谓docker容器本质上是一个linux系统的进程,只不过相比普通进程有些特殊性,每个docker容器(进程)拥有自己的独立的namespace命名空间、cgroups限制组、运行时会被chroot切换进程根目录。

虽然容器进程有其特殊性,但linux下所有容器还是共享操作系统内核的,当需要内核相关操作时,由容器进程与宿主机内核打交道。由此可以看出docker容器与虚拟机相比的一个弊端,隔离的不彻底,宿主机内核的全局变量对每个容器还是具有实际影响的。当然这也可以解读为docker容器的优势,由于不加载内核相关驱动等内容,docker容器的启动速度非常快 。

从开发者的角度考虑docker最大的好处应该是docker image的打包功能,免去了各种环境的配置工作,一次打包就可以在任何docker环境中使用,并保持一致性。如果从平台运维的角度考虑,个人觉得其最大好处应该是docker使用联合文件系统节省了存储空间(毕竟存储空间总是不够用)以及k8s平台提供的调度机制。

由于docker的特性(依托于k8s平台),天然适合微服务、无状态服务(如nginx、tomcat),部署简单一目了然,使用本地仓库拉取和启动都在秒级。当然也支持有状态服务(如mysql、redis),由于涉及持久化部署上步骤及考虑的问题会多一些。当然也有一些不建议或目前不支持部署在容器中的应用。

docker支持4种网络模式:bridge、host、overlay、macvlan

bridge为默认模式(也是k8s管理的模式),docker启动后如果不指定网络则生成默认的docker0网桥,每个容器有独立的network namespace,容器和网桥之间通过veth pair虚拟网卡对进行通信。

host模式最为简单,直接使用主机网络,由于所用容器共享宿主机端口,不需要经过转换,性能高,相对的容易引起端口冲突,使得容器直接暴露在宿主机之外。

overlay模式称为附加网络层,用于连接多台宿主机上的docker进行通信。例如k8s下常用的flannel是overlay网络的一种实现,其在所有docker0网桥之上建立一个上层网络用于转发所有docker0网桥上的数据包。

macvlan模式使用macvlan网络驱动程序为每个容器的虚拟网络接口分配MAC地址,使其看起来像是直接连接到物理网络的物理网络接口,在做端口流量监控时可以使用该模式。

docker镜像存储模式使用联合文件系统将镜像分层处理

当使用docker pull命令从镜像仓库拉取一个镜像是会产生一些静态只读的分层文件,这些静态层可由多个container共享,从而节省了存储空间。而使用docker run加载镜像后,基于这些静态层之外,会产生另外两个层文件,一个是init初始化层用于保存hostname、hosts、resolv.conf、挂载点信息。另一个是该容器的可读写层,用户的改动都体现在该层。各操作系统对于联合文件系统的实现也不尽相同,centos下为overlayfs,ubuntu下为aufs。

容器和虚拟机

容器本身具备很多虚拟机不具备的功能特性,如快速启动、方便迁移、高度一致性等,但目前并不是所有应用都能完美的兼容容器技术,仍然需要部署于虚拟机中,就容器技术本身而言与虚拟机可以协同工作,其可以直接部署与裸机之上,也可以部署于虚拟机中。所以容器并不是取代虚拟机的存在。