【Docker—基础】核心概念和架构

核心概念

镜像

镜像是什么呢?通俗地讲,它是一个只读的文件和文件夹组合。它包含了容器运行时所需要的所有基础文件和配置信息,是容器启动的基础。所以你想启动一个容器,那首先必须要有一个镜像。镜像是 Docker 容器启动的先决条件。

容器

容器是 Docker 的另一个核心概念。通俗地讲,容器是镜像的运行实体。镜像是静态的只读文件,而容器带有运行时需要的可写文件层,并且容器中的进程属于运行状态。即容器运行着真正的应用进程。容器有创建、运行、停止、暂停和删除五种状态。

虽然容器的本质是主机上运行的一个进程,但是容器有自己独立的命名空间隔离和资源限制。也就是说,在容器内部,无法看到主机上的进程、环境变量、网络等信息,这是容器与直接运行在主机上进程的本质区别。

仓库

Docker 的镜像仓库类似于代码仓库,用来存储和分发 Docker 镜像。镜像仓库分为公共镜像仓库和私有镜像仓库。

目前, Docker Hub 是 Docker 官方的公开镜像仓库,它不仅有很多应用或者操作系统的官方镜像,还有很多组织或者个人开发的镜像供我们免费存放、下载、研究和使用。除了公开镜像仓库,你也可以构建自己的私有镜像仓库。

架构

基于开放容器计划(OCI)相关标准的要求,Docker 引擎采用了模块化的设计原则,其组件是可替换的。 Docker引擎由如下主要的组件构成:Docker 客户端(Docker Client)、Docker 守护进程(Docker daemon)、containerd 以及 runc。 它们共同负责容器的创建和运行。

目前Docker引擎的架构示意图如图所示:

Docker客户端

Docker 客户端其实是一种泛称。其中 docker 命令是 Docker 用户与 Docker 服务端交互的主要方式。除了使用 docker 命令的方式,还可以使用直接请求 REST API 的方式与 Docker 服务端交互,甚至还可以使用各种语言的 SDK 与 Docker 服务端交互。

runc

runc 是 OCI 容器运行时规范的参考实现,实质上是一个轻量级的、针对 Libcontainer 进行了包装的命令行交互工具(Libcontainer 取代了早期 Docker 架构中的 LXC)。通俗地讲,runc 是一个用来运行容器的轻量级工具,是真正用来运行容器的。

containerd

containerd 是 Docker 服务端的一个核心组件,它是从 dockerd 中剥离出来的 ,它的主要任务是容器的生命周期管理——start | stop | pause | rm….。随着时间的推移,它被赋予了更多的功能,比如镜像管理。

containerd 位于 dockerd 和 runc 所在的 OCI 层之间,通过 containerd-shim 启动并管理 runc。Kubernetes 也可以通过 cri-containerd 使用 containerd,在Kubernetes中,containerd 是一个很受欢迎的容器运行时。

shim

shim 是实现无 daemon 的容器(用于将运行中的容器与 containerd 解耦,以便进行 dockerd 升级等操作)不可或缺的工具。

containerd 通过指挥 runc 来创建新容器。事实上,每次创建容器时它都会 fork 一个新的 runc 实例。不过,一旦容器创建完毕,对应的 runc 进程就会退出。因此,即使运行上百个容器,也无须保持上百个运行中的 runc 实例。一旦容器进程的父进程 runc 退出,相关联的 containerd-shim 进程就会成为容器的父进程。

作为容器的父进程,shim 的部分职责如下:

  • 保持所有 STDIN 和 STDOUT 流是开启状态,从而当 dockerd 重启的时候,容器不会因为管道(pipe)的关闭而终止。
  • 将容器的退出状态反馈给 dockerd。

延伸阅读: 从 docker 到 runC

各组件之间的关系

首先通过以下命令来启动一个 busybox 容器:

$ docker run -d busybox sleep 3600

容器启动后,通过以下命令查看一下 dockerd 的 PID:

$ sudo ps aux |grep dockerd


root      4147  0.3  0.2 1447892 83236 ?       Ssl  Jul09 245:59 /usr/bin/dockerd

通过上面的输出结果可以得知 dockerd 的 PID 为 4147。为了 Docker 各组件之间的调用关系,下面使用 pstree 命令查看一下进程父子关系:

$ sudo pstree -l -a -A 4147


dockerd


  |-containerd --config /var/run/docker/containerd/containerd.toml --log-level info


  |   |-containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/d14d20507073e5743e607efd616571c834f1a914f903db6279b8de4b5ba3a45a -address /var/run/docker/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc


  |   |   |-sleep 3600

事实上,dockerd 启动的时候, containerd 就随之启动了,dockerd 与 containerd 一直存在。当执行 docker run 命令(通过 busybox 镜像创建并启动容器)时,containerd 会创建 containerd-shim 充当 “垫片” 进程,然后启动容器的真正进程 sleep 3600 。这个过程和架构图是完全一致的。