Containerd 入门到尝试
Kubernetes v1.24 之前的版本直接集成了 Docker Engine 的一个组件,名为 dockershim [用于调用Docker]。 这种特殊的直接整合不再是 Kubernetes 的一部分 (这次删除被作为 v1.20 发行版本的一部分宣布)。 这意味Kubernetes从版本1.24开始就弃用Docker作为容器运行时,取而代之的是更加轻量级的Containerd。
containerd可用作 Linux 和 Windows 的守护进程。它管理其主机系统的完整容器生命周期,从图像传输和存储到容器执行和监督,再到低级存储到网络附件等等。
一、Containerd介绍
1、Containerd的由来
【Docker名噪一时,捐出runC】2013年docker公司在推出docker产品后,由于其对全球技术产生了一定的影响力,Google公司明显感觉到自己公司内部所使用的Brog系统江湖地位受到的威胁,希望Docker公司能够与自己联合打造一款开源的容器运行时作为Docker核心依赖,但Docker公司拒绝了;接着Google公司联合RedHat、IBM等公司说服Docker公司把其容器核心技术libcontainer
捐给OCI(Open Container Intiative),并更名为runC
。
【CNCF成立,kubernetes被迫开源】为了进一步遏制Docker在未来技术市场影响力,避免在容器市场上Docker一家独大,Google公司带领RedHat、IBM等成立了CNCF(Cloud Native Computing Fundation)基金会,即云原生计算基金会。CNCF的目标很明确,既然在容器应用领域无法与Docker相抗衡,那就做Google更有经验的技术市场——大规模容器编排应用场景。Google公司把自己内部使用的Brog系统开源——Kubernetes,也就是我们今天所说的云原生技术生态。
【Docker妥协,贡献出Containerd】2016年Docker公司推出了Docker Swarm,意在一统Docker生态,让Docker既可以实现容器应用管理,也可以实现大规模容器编排,经过近1年左右时间的市场验证后,发现在容器编排方面无法独立抗衡kubernetes,所以Docker公司于2017年正式宣布原生支持Kubernetes。至此,Docker在大规模容器编排应用市场败下阵来,但是Docker依然不甘心失败,把Docker核心依赖Containerd捐给了CNCF,依此说明Docker依旧是一个PaaS平台。
【k8s宣布不支持Docker,Containerd成为CRI主角】2020年CNCF基金会宣布Kubernetes 1.20版本将不再仅支持Docker容器管理工具,此事的起因主要也与Docker捐给CNCF基金会的Containerd有关,早期为了实现Kubernetes能够使用Docker实现容器管理,专门在Kubernetes组件中集成一个shim
技术,用来将Kubernetes 容器运行时接口(CRI,Container Runntime Interface)调用翻译成Docker的API,这样就可以很好地使用Docker了。但是随着Kubernetes在全球技术市场的广泛应用,有更多的容器管理工具的出现,它们都想能够借助于Kubernetes被用户所使用,所以就提出标准化容器运行时接口,只要适配了这个接口就可以集成到Kubernetes生态当中,所以Kubernetes取消了对shim的维护,并且由于Containerd技术的成功,可以实现无缝对接Kubernetes,所以接下来Kubernetes容器运行时的主角是Containerd。
2、Containerd概念
早在2016年3月,Docker 1.11的Docker Engine里就包含了containerd,而现在则是把containerd从Docker Engine里彻底剥离出来,作为一个独立的开源项目独立发展,目标是提供一个更加开放、稳定的容器运行基础设施。
和原先包含在Docker Engine里containerd相比,独立的containerd将具有更多的功能,可以涵盖整个容器运行时管理的所有需求。另外独立之后containerd的特性演进可以和Docker Engine分开,专注容器运行时管理,可以更稳定。
Containerd是一个工业标准的容器运行时,重点是它简洁,健壮,便携,在Linux和window上可以作为一个守护进程运行,它可以管理主机系统上容器的完整的生命周期:镜像传输和存储,容器的执行和监控,低级别的存储和网络。
每个containerd只负责一台机器,Pull镜像,对容器的操作(启动、停止等),网络,存储都是由containerd完成。具体运行容器由runC负责,实际上只要是符合OCI规范的容器都可以支持。
Containerd和docker不同,containerd重点是集成在大规模的系统中,例如kubernetes、Swarm、Mesos等【对于容器编排服务来说,运行时只需要使用containerd+runC,更加轻量,容易管理。】。Containerd 被设计成嵌入到一个更大的系统中,而不是直接由开发人员或终端用户使用。
Containerd的特点:
- 简洁的基于 gRPC 的 API 和 client library。
- 完整的 OCI 支持(runtime 和 image spec)。
- 同时具备稳定性和高性能的定义良好的容器核心功能。
- 一个解耦的系统(让 image、filesystem、runtime 解耦合),实现插件式的扩展和重用。
Containerd的作用:
- 管理容器的生命周期(从创建容器到销毁容器)。
- 拉取/推送容器镜像。
- 存储管理(管理镜像及容器数据的存储)。
- 调用 runC 运行容器(与 runC 等容器运行时交互)。
- 管理容器网络接口及网络。
使用 bucketbench 对 Docker、crio 和 Containerd 的性能测试结果,包括启动、停止和删除容器,以比较它们所耗的时间,可以发现Containerd 在各个方面都表现良好,总体性能优于 Docker 和 crio。
3、Containerd架构
Containerd 采用标准的 C/S 架构:服务端通过 GRPC 协议提供稳定的 API;客户端通过调用服务端的 API 进行高级的操作。
为了实现解耦,Containerd 将不同的职责划分给不同的组件,每个组件就相当于一个子系统(subsystem)。连接不同子系统的组件被称为模块。
Containerd 被分为三个大块: Storage 、 Metadata 和 Runtime。
Containerd 两大子系统为:
Bundle : 在 Containerd 中,Bundle 包含了配置、元数据和根文件系统数据,你可以理解为 容器的文件系统。而 Bundle 子系统允许用户从镜像中提取和打包 Bundles。
Runtime : Runtime 子系统用来执行 Bundles,比如创建容器。其中,每一个子系统的行为都由一个或多个模块协作完成(架构图中的 Core 部分)。
4、几个概念区分
containerd
是一个高级容器运行时,又名容器管理器。简单来说,它是一个守护进程,在单个主机上管理完整的容器生命周期:创建、启动、停止容器、拉取和存储镜像、配置挂载、网络等。
ctr
是作为 containerd 项目的一部分提供的命令行客户端。该ctr
界面 与 Docker CLI不兼容,乍一看,可能看起来不太用户友好。因为它的主要受众是测试守护进程的容器开发人员。ctr + containerd比docker + dockerd更接近实际的容器。
nerdctl
是一个相对较新的containerd命令行客户端。与ctr不同,nerdctl的目标是用户友好和docker兼容。在某种程度上,nerdctl + containerd可以无缝地替代docker + dockerd。
crictl
是一个命令行客户端,用于 [kubernetes] CRI兼容的容器运行时。引入 Kubernetes 容器运行时接口 (CRI)以使 Kubernetes 容器运行时不可知。Kubernetes节点代理kubelet实现了 CRI客户端 API,可以使用任何实现 CRI 服务器 API的容器运行时来管理其节点上的容器和 Pod。
二、Containerd安装
containerd官网:https://containerd.io/
containerd官方安装步骤:https://github.com/containerd/containerd/blob/main/docs/getting-started.md
step1:安装Containerd
因后续版本Kubernetes 对containerd运行时有版本要求这里安装1.6.x版本的. Ubuntu源是1.5.9版本. 这里 直接下载deb安装.
1 | # 下载Deb |
将containerd服务设置卫开机启动。
1 | $ systemctl enable --now containerd |
至此,containerd安装完成。
step2:修改配置
1 | # 创建目录 |
step3:安装cni插件和cni工具包
1 | $ mkdir -p /home/cni-plugins |
完成。
三、ctr命令行操作
ctr是作为 containerd 项目的一部分提供的命令行客户端。
该ctr界面 [显然] 与 Docker CLI不兼容,乍一看,可能看起来不太用户友好。显然,它的主要受众是测试守护进程的容器开发人员。但是,由于它最接近实际的 containerd API,因此它可以作为一种很好的探索手段——通过检查可用命令,可以大致了解 containerd可以做什么和不可以做什么。
1 | $ ctr --help |
1、Containerd镜像管理
1 | # 拉取镜像 拉取镜像,完全合格的参考似乎是必需的,所以不能忽略镜像仓库或标签部分。 |
2、Containerd容器管理
1 | # 创建【静态】容器 |
3、Containerd命名空间管理
containerd中namespace的作用为:隔离运行的容器,可以实现运行多个容器。
1 | # 拉取镜像 |
4、Containerd网络管理
默认Containerd管理的容器仅有lo网络,无法访问容器之外的网络,可以为其添加网络插件,使用容器可以连接外网,CNI(Container Network Interface)
cni插件和cni工具包已经在前面第二章节已经部署完毕。
1 | # cni插件目录 |
下面先开始准备容器网络配置文件,用于为容器提供网关、IP地址等。
bash1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26$ vim /etc/cni/net.d/10-mynet.conf
$ vim /etc/cni/net.d/99-loopback.conf
$ cat /etc/cni/net.d/10-mynet.conf
{
"cniVersion": "1.0.0",
"name": "mynet",
"type": "bridge",
"bridge": "cni0",
"isGateway": true,
"ipMasq": true,
"ipam": {
"type": "host-local",
"subnet": "10.66.0.0/16",
"routes": [
{
"dst": "0.0.0.0/0"
}
]
}
}
$ cat /etc/cni/net.d/99-loopback.conf
{
"cniVerion": "1.0.0",
"name": "lo",
"type": "loopback"
}生成cni网络
bash1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32# 获取epel源
$ wget -O /etc/yum.repos.d/epel.repo [http://mirrors.aliyun.com/repo/epel-7.repo](http://mirrors.aliyun.com/repo/epel-7.repo "http://mirrors.aliyun.com/repo/epel-7.repo")
#安装jq【jq是一个'出色'的'针对-->JSON处理器'的命令行】
$ yum -y install jq
# 进入cni工具目录
$ cd /home/cni-tools/cni-1.1.2
# 执行脚本文件,基于/etc/cni/net.d/目录中的 \*.conf配置文件生成容器网络
# CNI\_PATH是cni插件安装的目录
$ CNI\_PATH=/home/cni-plugins ./priv-net-run.sh echo "Hello World"
# cni插件和cni工具包版本要兼容,否则可能会报错如下:
mynet : error executing ADD: {
"code": 1,
"msg": "incompatible CNI versions",
"details": "config is "1.0.0", plugin supports \["0.1.0" "0.2.0" "0.3.0" "0.3.1" "0.4.0"]"
}
#安装成功后,在宿主机上查看是否生成容器网络名为cni0的网桥
$ ip a s
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
...
7: cni0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
link/ether 6a:32:bc:eb:0f:23 brd ff:ff:ff:ff:ff:ff
inet 10.66.0.1/16 brd 10.66.255.255 scope global cni0
valid_lft forever preferred_lft forever
inet6 fe80::6832:bcff:feeb:f23/64 scope link
valid_lft forever preferred_lft forever创建容器
bash1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17$ ctr images pull docker.io/library/busybox:latest
$ ctr run -d docker.io/library/busybox:latest busybox
$ ctr tasks exec --exec-id$RANDOM -t busybox sh ip a s
1: lo: \<LOOPBACK,UP,LOWER\_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid\_lft forever preferred\_lft forever
inet6 ::1/128 scope host
valid\_lft forever preferred\_lft forever
2: tunl0\@NONE: mtu 1480 qdisc noop qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
#获取容器进程ID及其网络命名空间
$ pid= $(ctr tasks ls | grep busybox | awk '{print $ 2}') && echo $pid
39287
$ netnspath=/proc/ $pid/ns/net && echo $netnspath/proc/39287/ns/net
# 进入目录为指定容器添加网络配置
$cd /home/cni-tools/cni-1.1.2/scripts/$ CNI\_PATH=/home/cni-plugins ./exec-plugins.sh add $ pid $netnspath验证容器网络与宿主机网络的互访功能
bash1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57# 验证
# 进入容器确认是否添加网卡信息
$ctr tasks exec --exec-id$RANDOM -t busybox sh
$ ip a s
1: lo: \<LOOPBACK,UP,LOWER\_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid\_lft forever preferred\_lft forever
inet6 ::1/128 scope host
valid\_lft forever preferred\_lft forever
2: tunl0\@NONE: 
 mtu 1480 qdisc noop qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
3: eth0\@if9: \<BROADCAST,MULTICAST,UP,LOWER\_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 5a:36:90:c1:33:b1 brd ff:ff:ff:ff:ff:ff
inet 10.66.0.4/16 brd 10.66.255.255 scope global eth0
valid\_lft forever preferred\_lft forever
inet6 fe80::5836:90ff:fec1:33b1/64 scope link
valid\_lft forever preferred\_lft forever
# 在容器中ping容器宿主机IP地址
$ ping -c 2 192.168.168.201
PING 192.168.168.201 (192.168.168.201): 56 data bytes
64 bytes from 192.168.168.201: seq=0 ttl=64 time=0.067 ms
64 bytes from 192.168.168.201: seq=1 ttl=64 time=0.070 ms
\--- 192.168.168.201 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.067/0.068/0.070 ms
# 在容器中ping容器宿主机IP地址
$ ping -c 2 192.168.168.2
PING 192.168.168.2 (192.168.168.2): 56 data bytes
64 bytes from 192.168.168.2: seq=0 ttl=127 time=0.141 ms
64 bytes from 192.168.168.2: seq=1 ttl=127 time=0.324 ms
\--- 192.168.168.2 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.141/0.232/0.324 ms
# 在容器中ping容器宿主机IP地址
$ ping -c 2 192.168.168.202
PING 192.168.168.201 (192.168.168.202): 56 data bytes
64 bytes from 192.168.168.202: seq=0 ttl=64 time=0.067 ms
64 bytes from 192.168.168.202: seq=1 ttl=64 time=0.070 ms
\--- 192.168.168.202 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.067/0.068/0.070 ms
# 在容器中开启httpd服务
$ echo "containerd net web test" > /tmp/index.html
$ httpd -h /tmp
$ wget -O - -q 127.0.0.1
containerd net web test
$ exit
\#在宿主机访问容器提供的httpd服务
\$ curl 10.66.0.4
containerd net web test
5、Containerd数据持久化
实现把宿主机目录挂载至Containerd容器中,实现容器数据持久化存储。
1 | # 创建一个静态容器,实现宿主机目录与容器挂载,src=/tmp 为宿主机目录 dst=/hostdir 为容器中目录 |
6、Docker集成Container容器管理
目前Containerd主要任务还在于解决容器运行时的问题,对于其周边生态还不完善,所以有时需要借助:Docker结合Containerd来实现Docker完整的功能应用。
Docker安装与使用教程:https://blog.csdn.net/qq_41822345/article/details/107123094
docker运行的容器默认在moby命名空间下。
1 | # 安装docker |