侧边栏壁纸
博主头像
汪洋

即使慢,驰而不息,纵会落后,纵会失败,但一定可以达到他所向的目标。 - 鲁迅

  • 累计撰写 204 篇文章
  • 累计创建 79 个标签
  • 累计收到 130 条评论

Containerd 上手

汪洋
2024-08-30 / 2 评论 / 1 点赞 / 436 阅读 / 8,473 字

很多同学都在询问,老师是不是 Docker 都不用了,Containerd 成为了主流。其实,真的是各有优势罢了。如果在资源较为充沛的私有云环境中,Docker 仅仅多一点点的资源占用是非常能够接受的,毕竟更熟悉的"配方"和镜像仓库管理是 Containerd 所不具备的。如果,在资源使用要求十分紧凑的公有云,并且不在乎易用性问题,Containerd 当然也是一个不错的选择。话虽如此,但是大家的要求还是要满足的,上干货!!!

一、容器发展史

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

如果不使⽤ Docker,使⽤什么替代品?Kubernetes 为什么要停⽌⽀持 Docker?

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

1.1 初始阶段

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

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

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

1724989657001.png

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

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

runc 是⼀个独⽴的可执⾏程序,根据 OCI 标准启动和管理容器进程。这样⼀来,Docker 容器运⾏时的核⼼组件(即容器的创建和管理)不再紧密依赖于 Docker 守护进程。意味着 Docker 容器运⾏时的核⼼组件(即 runc)和 Docker 守护进程之间的关联变得更加松散。Docker 守护进程可以直接调⽤ runc 来管理容器,⽽不再需要依赖于内部的 libContainer 库。这使得其他容器运⾏时(如 containerd 或 CRI-O)也可以采⽤相同的 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 退出后能够继续运⾏。

1724997294343.png

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

  • Docker Daemon 请求 containerd 进⾏容器的创建

  • containerd 不直接创建容器,因为containerd如果故障,所有容器都会故障,因此引⼊了containerd-shim,来进⾏容器的创建

  • containerd-shim 调⽤ runc(符合 OCI 标准)来创建容器

  • runc 创建容器后退出,containerd-shim 成为容器进程的⽗进程,负责状态收集和清理⼯作

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

为了提高稳定性引入 containerd-shim ,containerd-shim 也会出现故障有什么本质区别呢?

一、故障影响范围不同

  1. 如果 containerd 直接创建容器且出现故障,由于它是中心的容器运行时管理组件,负责与 Kubernetes 等容器编排系统交互以及管理众多容器的生命周期,其故障很可能会导致所有容器的管理出现问题,因为它是容器管理的核心枢纽。
  2. 而当 containerd-shim 出现故障时,通常只会影响它所管理的单个容器实例或一小部分容器。containerd-shim 是为每个容器或一组容器创建独立的进程,即使一个 shim 进程出现故障,其他容器的 shim 进程仍然可以正常运行,从而降低了故障的影响范围。

二、恢复机制不同

  1. 如果 containerd 出现故障,恢复过程可能比较复杂,需要重新启动整个容器运行时管理系统,这可能会导致所有容器的状态丢失或需要较长时间的恢复过程。
  2. 对于 containerd-shim 的故障,通常可以更快速地进行恢复。由于 shim 是轻量级的进程,并且可以独立地进行监控和恢复。例如,可以通过重新启动故障的 shim 进程来恢复受影响的容器,而不会影响其他正常运行的容器。

综上所述,引入 containerd-shim 虽然不能完全消除容器故障的风险,但可以在一定程度上降低故障的影响范围,并提供更灵活的恢复机制。

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 接⼝上,⽽设计的⼀种实现。

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

1724998227018.png

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

1724998273138.png

1.4 弃⽤阶段

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

其次为了⽀持 Docker,Kubernetes 开发了⼀个名为 dockershim 的垫⽚。随着时间的推移,Kubernetes 受众不断扩⼤,同时 Containerd 也成功从 CNCF 毕业,其健壮性得到了显著提升。在这种情况下,Kubernetes 终于可以考虑摒弃 dockershim 垫⽚。因为在维护 dockershim 时,Kubernetes 遇到了许多问题,⾸先 Kubernetes 需要处理 Docker 中很多与其核⼼功能⽆关的内容。此外,随着 Docker 版本的升级,dockershim 不得不进⾏相应的适配,并进⾏兼容性测试,这为维护⼯作带来了额外的复杂性。

1724998997368.png

containerd 1.0 版本开始,通过⼀个独⽴的 CRI-Containerd 进程来适配 CRI,原因是 containerd 最初也需要适配其他系统(如 swarm),因此没有直接实现 CRI。这个对接⼯作由 CRI-Containerd shim 完成。随后,在 containerd 1.1 版本中,移除了 CRI-Containerd shim,并将适配逻辑以插件形式集成到 containerd 主进程中。如今,调⽤过程变得更加简洁。

1724998771645.png

1.5 CRI-O

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

1724999495451.png

二、Containerd

2.1 安装

1、安装 libseccomp 依赖,但默认的 libseccomp 版本为 2.3 ⽆法版本 containerd1.6 版本,需要安装 2.4 版本以上;(如果是 RockyLinux9.3 不需要更新,rpm -qa | grep libseccomp = libseccomp-2.5.2-2.el9.x86_64)

[root@wangyang ~]# wget https://mirrors.aliyun.com/centos/8/BaseOS/x86_
64/os/Packages/libseccomp-2.5.1-1.el8.x86_64.rpm
[root@wangyang ~]# rpm -ivh libseccomp-2.5.1-1.el8.x86_64.rpm --force

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

[root@wangyang ~]# wget http://file.oldxu.net/cri-containerd-1.6.18-linux-amd64.tar.gz
[root@wangyang ~]# tar tf cri-containerd-1.6.18-linux-amd64.tar.gz
[root@wangyang ~]# tar xf cri-containerd-1.6.18-linux-amd64.tar.gz -C /

3、创建 Containerd 配置

[root@wangyang ~]# mkdir -p /etc/containerd
[root@wangyang ~]# containerd config default > /etc/containerd/config.toml

4、运⾏ containerd

[root@wangyang ~]# systemctl start containerd
[root@wangyang ~]# systemctl status containerd

5、检查

[root@wangyang ~]# ctr version
Client:
 Version: v1.6.18
 Revision: 2456e983eb9e37e47538f59ea18f2043c9a73640
 Go version: go1.19.6
Server:
 Version: 1.6.21
 Revision: 3dce8eb055cbb6872793272b4f20ed16117344f8
 UUID: b4734440-e529-4598-a06f-2615ad6d96c3
[root@docker-node1 ~]# runc --version
runc version 1.1.4
commit: v1.1.4-0-g5fd4c4d1
spec: 1.0.2-dev
go: go1.19.6
libseccomp: 2.5.1

2.2 镜像管理

1、拉取镜像

# 语法:ctr images pull <image_name>
# 示例:ctr images pull docker.io/library/alpine:latest

2、查看镜像

[root@wangyang ~]# ctr images list -q
	docker.io/library/alpine:latest

3、删除镜像

# 语法:ctr images remove <image_name>
# 示例:ctr images remove docker.io/library/alpine:latest

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

# 1、dockerfile构建镜像
# 2、docker push 推送私有仓库
# 3、containerd通过私有仓库获取镜像即可
# 4、如果私有仓库存在⽤户密码认证 ctr images pull --user admin:Harbor12345 harbor.oldxu.net/nginx:1.20

2.3 容器管理

1、启动容器

# 语法:ctr containers run <image_name> <container_name>
示例:
[root@wangyang ~]# ctr run -d docker.io/library/alpine:latest mytest1 /bin/sh # 后台运⾏
[root@wangyang ~]# ctr run -t docker.io/library/alpine:latest mytest2 /bin/sh # 前台运⾏

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

[root@wangyang ~]# ctr task exec -t --exec-id sh mytest2 /bin/sh

3、查看容器

[root@wangyang ~]# ctr c list
    CONTAINER IMAGE RUNTIME 
    mytest docker.io/library/alpine:latest io.containerd.runc.v2 
    mytest2 docker.io/library/alpine:latest io.containerd.runc.v2 
    mytest3 docker.io/library/alpine:latest io.containerd.runc.v2 
[root@wangyang ~]# ctr task list
    TASK PID STATUS 
    mytest2 20153 RUNNING
    mytest3 20268 RUNNING

4、删除容器

# 语法:ctr containers delete <container_name>
# 示例:ctr containers delete mytest2

2.4 私有镜像

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

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

[root@wangyang ~]# ctr images pull --user admin:Harbor12345 harbor.oldxu.net/infra/nginx:1.20
[root@wangyang ~]# ctr images list -q
    docker.io/library/alpine:latest
    docker.io/library/nginx:1.18
    harbor.oldxu.net/infra/nginx:1.20

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

# vim /etc/containerd/config.toml
[plugins]
...
 [plugins."io.containerd.grpc.v1.cri".registry]
 ...
 # 私有镜像仓库认证信息
 [plugins."io.containerd.grpc.v1.cri".registry.configs]
 # 4、如果https不受信任,可以通过如下参数跳过
 [plugins."io.containerd.grpc.v1.cri".registry.configs."harbor.oldx
u.net".tls]
 insecure_skip_verify = true
 # 3、私有仓库认证的⽤户名及密码
 [plugins."io.containerd.grpc.v1.cri".registry.configs."harbor.oldx
u.net".auth]
 username = "admin"
 password = "Harbor12345"
 [plugins."io.containerd.grpc.v1.cri".registry.headers]
 
 # 定制镜像仓库
 [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
 # 1、镜像加速
 [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
 endpoint = ["https://q2gr04ke.mirror.aliyuncs.com","https://dock
er.mirrors.ustc.edu.cn"]
 # 2、私有仓库(需要继续添加私有仓库的⽤户名和密码,否则通过crictl⽆法正常拉取
镜像)
 [plugins."io.containerd.grpc.v1.cri".registry.mirrors."harbor.oldx
u.net"]
 endpoint = ["https://harbor.oldxu.net"]

2.5 Nerdctl

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

1、下载并安装nerdctl

[root@wangyang ~]# wget https://github.com/containerd/nerdctl/releases/download/v1.4.0/nerdctl-full-1.4.0-linux-amd64.tar.gz
[root@wangyang ~]# tar xf nerdctl-full-1.4.0-linux-amd64.tar.gz
[root@wangyang ~]# cp bin/nerdctl bin/buildctl bin/buildkitd /usr/loca
l/bin/

# 添加命令补全
echo 'source <(nerdctl completion bash)' >> /etc/profile
source /etc/profile

2、测试 nerdctl 命令

# nerdctl pull nginx:1.18
# nerdctl images
    REPOSITORY TAG IMAGE ID CREATED PLATFORM SIZ
    E BLOB SIZE
    nginx 1.18 e90ac5331fe0 37 seconds ago linux/amd64 139.
    4 MiB 51.1 MiB
0

评论区