1 容器发展史

在2020 年底,Kubernetes 宣布不再推荐使⽤Docker作为容器运⾏时,并在后续的版本中停⽌⽀持Docker。这个决定肯定会对原有的Kubernetes⽤户产⽣⼀定的影响,同时也引发了⼀系列问题:如果不使⽤Docker,使⽤什么替代品?

Kubernetes为什么要停⽌⽀持Docker?

为了回答这些问题,我们需要简要回顾⼀下容器技术的发展史。

1.1 初始阶段

Docker 的发展经历了⼀系列的变⾰,Docker最初使⽤的架构是Container+libContainer :在这个阶段,Docker主要分为两个部分:Docker Daemon(守护进程)和 Docker Client(客户端)。

  • 1、Docker Daemon 负责处理 Docker Client 发送的请求,并对请求进⾏相应的操作(如:镜像、容器的管理)。
  • 2、在管理 Linux 容器时,Docker Daemon 依赖 libContainer 这个库来完成底层的容器操作。

libContainer 提供了⼀系列接⼝和功能,⽤于创建、启动、停⽌和销毁容器,以及配置容器的资源限制、⽹络、存储等。通过 libContainer,Docker Daemon 可以实现对容器的完整管理。

Docker Client <-------请求/响应------------> Docker Daemon <-------使用------------>libContainer

Docker 为了推动容器技术的标准化和通⽤性,将libContainer赠送给了Open Container Initiative(OCI)并将其重命名为 runc 。OCI 是⼀个专⻔制定容器技术标准的组织,⽬的是让容器技术变得更加开放、透明和互相兼容。

  • 镜像标准:定义了容器镜像的结构和内容,包括镜像⽂件的组织⽅式、⽂件和⽬录类型等,以及如何创建符合标准的镜像⽂件。
  • 运⾏时标准:规定了如何从运⾏时⽂件系统启动容器程序,包括容器接收的指令类型、指令⾏为、⽣命周期管理,以及如何配置名称空间、Cgroup 和⽂件系统挂载等。

runc 是⼀个独⽴的可执⾏程序,根据OCI 标准启动和管理容器进程。这样⼀来,Docker 容器运⾏时的核⼼组件(即容器的创建和管理)不再紧密依赖于 Docker守护进程。

意味着Docker 容器运⾏时的核⼼组件(即 runc)和 Docker 守护进程之间的关联变得更加松散。Docker 守护进程可以直接调⽤ runc 来管理容器,⽽不再需要依赖于内部的 libContainer 库。这使得其他容器运⾏时(如 containerd 或 CRIO)也可以采⽤相同的 OCI 标准,实现与 Docker 更好的兼容性。

1.2 发展阶段

从Docker 1.11 版本开始,Docker 容器的运⾏和管理不再仅依赖于 Docker Daemon。它通过整合 Containerd、runc 和 container-shim 等组件共同完成。 Docker Daemon 仍然负责与 Docker Client 交互,以及管理 Docker 镜像。⽽将容器的底层运⾏时操作交给了containerd和 runc。

容器的⽣命周期管理变得更加模块化,Docker Daemon 和 Containerd 通过gRPC 接⼝进⾏通信。Containerd 进⼀步调⽤ runc 来实际启动和管理容器进程。为了解决容器进程和 runc 之间的依赖,Docker 引⼊了 container-shim。container-shim 是⼀个轻量级的进程,⽤于在容器和 runc 之间进⾏通信,使容器在 runc 退出后能够继续运⾏。

Docker Client <---------请求/响应----------> Docker Daemon<---------gRPC 接⼝----------> Containerd <---------调用----------> container-shim <---------调用----------> runc

当我们要创建⼀个容器的时候,现在Docker Daemon并不能直接帮我们创建了

  • 1. Docker Daemon 请求 containerd 进⾏容器的创建。
  • 2. containerd 不直接创建容器,因为containerd如果故障,所有容器都会故障,因此引⼊了containerd-shim,来进⾏容器的创建
  • 3. containerd-shim 调⽤ runc(符合 OCI 标准)来创建容器。
  • 4. runc 创建容器后退出,containerd-shim 成为容器进程的⽗进程,负责状态收集和清理⼯作。

通过引⼊containerd 和 containerd-shim,Docker 架构变得更加模块化,各个组件之间的职责分⼯更加明确,从⽽有助于提⾼整体的稳定性和可维护性。

1.3 CRI诞⽣

在容器技术⻜速发展的背景下,Docker 将重⼼转向到了编排⼯具的开发上,进⽽将 containerd 捐赠给 CNCF。这⼀举措既推动了容器技术的发展,还吸引了更多社区成员的参与,使得 Docker 能够集中精⼒开发 Swarm 等⼯具。此举还能降低 Docker ⾯临的市场竞争⻛险。

然⽽,在Kubernetes 编排⼯具诞⽣之后,它为了保持中⽴性、同时⽀持多种容器运⾏时与Kubernetes 对接,以避免对特定技术的依赖和Docker 公司⼀家独⼤的局⾯,因此Kubernetes 在 1.5 版本引⼊了 CRI 。由Google 和红帽共同推动CRI标准的产⽣。

CRI(Container Runtime Interface,容器运⾏时接⼝)是 Kubernetes 设计的⼀组接⼝,⽤于与容器运⾏时互动。简单来说,只要容器运⾏时实现了 CRI 接⼝,就可以与 Kubernetes 平台集成。然⽽,并⾮所有的容器运⾏时都会直接实现 CRI 接⼝,因此出现了“shim(垫⽚)”这个概念。shim 的作⽤类似于适配器(USB转Type-c),将各种不同的容器运⾏时的接⼝适配到 Kubernetes 的 CRI接⼝上。如,dockershim 就是 Kubernetes 为了将 Docker 接⼝适配到 CRI 接⼝上,⽽设计的⼀种实现

Containerd_Docker

当时,由于Docker 在容器领域的地位举⾜轻重,Kubernetes 便直接将dockershim 嵌⼊到 kubelet 中。因此,对于使⽤Docker 容器运⾏时的⽤户⽽⾔,⽆需额外安装配置适配器,如下图所示:

Containerd_docker_02

1.4 弃⽤阶段

实际上,在使⽤Kubernetes 与 Docker 结合的情况下,整个调⽤链相当⻓。然⽽,对于真正的容器相关操作,Containerd 已经完全⾜够。尽管 Docker 的调⽤链很⻓,但这并未影响其受欢迎程度。但对于 Kubernetes 来说,这些功能并⾮必要,因为它仅通过接⼝操作容器。所以 Kubernetes 可以⾃然地将容器运⾏时切换到 Containerd。

其次为了⽀持Docker,Kubernetes 开发了⼀个名为 dockershim 的垫⽚。随着时间的推移,Kubernetes 受众不断扩⼤,同时 Containerd 也成功从 CNCF 毕业,其健壮性得到了显著提升。在这种情况下,Kubernetes 终于可以考虑摒弃dockershim 垫⽚。

因为在维护dockershim 时,Kubernetes 遇到了许多问题,⾸先 Kubernetes 需要处理 Docker 中很多与其核⼼功能⽆关的内容。此外,随着 Docker 版本的升级,dockershim 不得不进⾏相应的适配,并进⾏兼容性测试,这为维护⼯作带来了额外的复杂性。

Containerd_Docker_03

containerd 1.0 版本开始,通过⼀个独⽴的 CRI-Containerd 进程来适配 CRI,原因是 containerd 最初也需要适配其他系统(如 swarm),因此没有直接实现CRI。这个对接⼯作由 CRI-Containerd shim 完成。

随后,在containerd 1.1 版本中,移除了 CRI-Containerd shim,并将适配逻辑以插件形式集成到 containerd 主进程中。如今,调⽤过程变得更加简洁。

1.5 CRI-O

Kubernetes 社区也开发了⼀个专⻔⽤于 Kubernetes 的 CRI 运⾏时,名为 CRI-O,CRI-O 直接兼容 CRI 和 OCI 规范,为 Kubernetes 提供了更加⾼效的容器运⾏时⽀持。https://cri-o.io/

Containerd_Docker_04

2 Containerd

2.1 安装

1、安装 libseccomp 依赖,但默认的libseccomp版本为2.3⽆法版本

containerd1.6版本,需要安装2.4版本以上;

wget https://mirrors.aliyun.com/centos/8/BaseOS/x86_64/os/Packages/libseccomp-2.5.1-1.el8.x86_64.rpm

rpm -Uvh libseccomp-2.5.1-1.el8.x86_64.rpm

2、下载containerd1.6,⽽后解压

tar -xf cri-containerd-1.6.18-linux-amd64.tar.gz -C /

3、创建Containerd配置

mkdir -p /etc/containerd

containerd config default > /etc/containerd/config.toml

4、运⾏containerd

systemctl start containerd

systemctl status containerd

5、检查

Containerd_私有仓库_05

2.2 镜像管理

1、拉取镜像

ctr images pull hub.atomgit.com/amd64/alpine:3.15.10

Containerd_Docker_06

2、查看镜像

ctr images ls

ctr images list -q

Containerd_Docker_07

3、删除镜像

delete, del, remove, rm  remove one or more images by reference

ctr images rm hub.atomgit.com/amd64/alpine:3.15.10

4、由于containerd和docker存储数据⽬录不⼀致,因此互相之间的镜像⽆法查看,那么docker构建的镜像如何提供给containerd

  • 1、dockerfile构建镜像
  • 2、docker push 推送私有仓库
  • 3、containerd通过私有仓库获取镜像即可
  • 4、如果私有仓库存在⽤户密码认证  

ctr image pull --plain-http --user admin:Harbor12345 192.168.137.130/library/redis-photon:latest

--plain-http默认拉取使用https,此参数运行使用http

2.3 容器管理

1、启动容器

语法:ctr containers run <image_name> <container_name>

示例:

ctr run -d hub.atomgit.com/amd64/alpine:3.15.10 mytest01 /bin/sh# 后台运⾏

ctr run -t hub.atomgit.com/amd64/alpine:3.15.10 mytest02 /bin/sh 前台运⾏

3、进⼊容器(-exec-id 可以指定字符串)

ctr tasks exec -t --exec-id sh mytest01 /bin/sh

4、查看容器

Containerd_docker_08

5、删除容器

ctr containers delete mytest02

2.4 私有镜像

默认ctr拉取公开镜像⽆需输⼊⽤户名和密码,但如果是私有仓库镜像则⽆法正常获取;

1、⽅式⼀,需要使⽤--user指定明确指定⽤户名和密码

ctr image pull --plain-http --user admin:Harbor12345 192.168.137.130/library/redis-photon:latest

2、⽅式⼆,通过crictl下载镜像,在配置⽂件中定义对应的私有仓库信息,以及⽤户名称和密码

vim /etc/containerd/config.toml

2.1直接配置文件修改

Containerd_docker_09

2.2 配置文件引用配置文件

Containerd_docker_10

Containerd_docker_11

2.5 Nerdctl

containerd使⽤ctr管理ctr使⽤起来还是不太顺⼿,为了能够让⼤家更好的转到containerd 上⾯来,社区提供了⼀个新的命令⾏⼯具:nerdctl。nerdctl 是⼀个与 docker cli ⻛格兼容的 containerd 客户端⼯具,⽽且直接兼容 docker compose 的语法的,这就⼤⼤提⾼了直接将 containerd 作为本地开发、测试或者单机容器部署使⽤的效率。

1、下载并安装nerdctl

wget https://github.com/containerd/nerdctl/releases/download/v1.4.0/nerdctl-full-1.4.0-linux-amd64.tar.gz

tar xf nerdctl-full-1.4.0-linux-amd64.tar.gz

cp bin/nerdctl bin/buildctl bin/buildkitd /usr/local/bin/

# 添加命令补全

echo 'source <(nerdctl completion bash)' >> /etc/profile

source /etc/profile

2、测试nerdctl命令

Containerd_Docker_12

2.6 buildkit

1、编写dockerfile

cat dockerfile
FROM hub.atomgit.com/amd64/alpine:3.15.10
ENV LANG=en_US.UTF-8
ENV TZ="Asia/Shanghai"
RUN echo '/bin/sleep 315360000' > start.sh
CMD ["sh","start.sh"]
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

cp dockerfile Containerfile

Containerd_Docker_13

2、执⾏build

nerdctl build -t 192.168.137.130/library/alpine-test:latest .

Containerd_docker_14

3、拷⻉buildkitd启动脚本

cp lib/systemd/system/buildkit.service /usr/lib/systemd/system/

systemctl daemon-reload

systemctl enable buildkit --now

4、再次执⾏build构建命令

Containerd_docker_15

5、镜像推送

这个工具默认推送https,直接用ctr解决了

ctr image push --plain-http --user admin:Harbor12345 192.168.137.130/library/alpine-test:latest