> For the complete documentation index, see [llms.txt](https://gitbook.cdxiaodong.life/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://gitbook.cdxiaodong.life/rong-qi-an-quan/gai-shu/docker-engine.md).

# Docker引擎

Docker Engine是容器的核心，是容器运行的基础。Docker具有模块化设计，这意味着您可以交换/自定义其某些组件。

![docker-engine-arch](https://2241975275-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F7lonY2zgW7DNoIQMqYtw%2Fuploads%2Fgit-blob-e3a74418ac900169f422293a8551fa9032af591d%2Fdocker-engine-arch.png?alt=media)

Docker架构有五个组件：

* docker CLI
* dockerd
* containerd
* containerd-shim
* runc

## runc

[runc](https://github.com/opencontainers/runc)是`libcontainer`的轻量级CLI包装器，用于生成和运行容器。

您可以仅使用CLI运行容器而无需Docker。为此，您必须拥有符合OCI标准的捆绑包。容器的捆绑包是一个包含名为`config.json`的规范文件和根文件系统的目录。`config.json`用于在特定状态下创建容器。

`config.json`示例

```json
{  
  "ociVersion": "0.6.0-dev",  
  "platform": {     
    "os": "linux",     
    "arch": "amd64"   
  },   
  "process": {   
    "terminal": true,   
    "args": ["sh"],   
    "env": ["PATH=/usr/local/sbin:/usr/local/bin:/bin", ...]
  ...
}
```

给定捆绑包，您可以使用`runc`简单运行容器

```bash
# 创建一个空文件夹
mkdir ~/mycontainer
cd ~/mycontainer
# 创建rootfs，一种方法是复用docker导出
mkdir rootfs
docker export $(docker create busybox) | tar -C rootfs -xvf -
# 为捆绑包创建名为"config.json"的新规范文件
runc spec
# 运行容器
runc run mycontainerid
```

Docker是`runc`的更高级抽象，`runc`是`libcontainer`的CLI。此外，您还可以将docker设置为您自己的运行时`dockerd`守护进程[--add-runtime](https://docs.docker.com/engine/reference/commandline/dockerd/)。

## containerd

`containerd`是Docker模块化架构中的一层。当您向`dockerd`发出请求时，`containerd`管理与镜像分发相关的进程，例如将镜像推送/拉取到/从注册表。使用镜像，`containerd`为容器生成OCI捆绑包。

`containerd`管理容器生命周期：启动、停止、暂停或删除它。它还管理镜像分发：将镜像推送/拉取到/从注册表。

> 那么使用containerd实际上能得到什么呢？ 您获得推送和拉取功能以及镜像管理。您获得容器生命周期API来创建、执行和管理容器及其任务。一个专门用于快照管理的完整API。基本上，您需要构建容器平台的一切，而无需处理底层操作系统细节。 更多信息见[Docker containerd integration](https://www.docker.com/blog/docker-containerd-integration/)

## containerd-shim

`containerd-shim`允许您拥有无守护进程的容器。这是如何实现的：

* `containerd`为每个新容器分叉一个`runc`实例
* `runc`进程在容器创建后退出
* 一旦容器由`runc`创建，`containerd-shim`就成为容器进程的新父进程

您可以通过创建docker容器并检查进程来看到这一点。

```bash
$ docker run -d alpine sleep 30
$ ps fxa | grep dockerd -A 3  
2239 ?        Ssl    0:28 /usr/bin/dockerd -H fd://
2397 ?        Ssl    0:19  \_ docker-containerd -l unix:///var/run/docker/libcontainerd/docker-containerd.sock ...
15476 ?        Sl     0:00      \_ docker-containerd-shim 3de8... /var/run/docker/libcontainerd/3da7.. docker-runc  
15494 ?        Ss     0:00          \_ sleep 30
```

那里没有使用`runc`，因为它被`containerd-shim`替代了。

`containerd-shim`负责`STDIN`/`STDOUT`以及向Docker守护进程报告退出状态。

## dockerd

`dockerd`是一个守护进程和服务器，处理Docker API请求，然后利用`containerd`功能管理容器的生命周期。

## docker CLI

Docker CLI是与Docker服务器`dockerd`通信的方式之一。您可以使用它运行docker命令，如`docker run`或`docker build`。

## 运行容器

![running-container](https://2241975275-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F7lonY2zgW7DNoIQMqYtw%2Fuploads%2Fgit-blob-1f4e057340f38c2051b316b13a80b757c352bc6e%2Frunning-container.png?alt=media)

1. 用户使用docker CLI执行命令：

   ```bash
   docker container run -it --name <NAME> <IMAGE>:<TAG>
   ```
2. Docker客户端向守护进程的API发送POST请求
3. Docker守护进程接收指令并调用`containerd`启动新容器
4. `containerd`从Docker镜像创建OCI捆绑包
5. `containerd`告诉`runc`使用OCI捆绑包创建容器
6. `runc`与操作系统内核接口创建容器
7. 容器进程作为子进程启动
8. `runc`在启动容器后退出
9. `containerd-shim`接管子进程并成为其父进程
10. 容器正在运行！

## 参考资料

* [Docker引擎架构底层原理](https://medium.com/@yeldos/docker-engine-architecture-under-the-hood-741512b340d5)
