容器发展史
在2020年底,kubernetes宣布不再推荐使用docker作为容器运行时,并在后续的版本中停止支持docker。
这个决定肯定会对原有的kubernetes用户产生一定的影响,同时也引发了一系列问题:如果不使用docker,使用什么替代品?
kubernetes为什么要停止支持docker?
为了回答这些问题,我们需要简要回顾一下容器技术的发展史。
初始阶段
docker的发展经历了一系列的改革,docker最初使用的架构是container+libcontainer:
在这个阶段,docker主要分为两个部分:docker daemon(守护进程)和docker client(客户端)
- 1.
docker daemon负责处理docker client发送的请求,并对请求进行相应的操作(如:镜像、容器的管理)。 - 2.而
docker daemon依赖libcontainer这个库来完成底层的容器操作。例如:容器创建启动、停止和销毁容器等;
通过libcontainer,docker daemon可以实现对容器的完整管理。

docker为了推动容器技术的标准化和通用性,docker将核心库libcontainer赠送给了open container initiative(OCI)并重命名为runc。
OCI是一个致力于指定容器技术标准的组织,旨在使容器技术更加开放、透明和互相兼容。
OCI定义了两个主要标准:
- 镜像标准:规定了容器镜像的解构和内容,包括镜像文件的组织方式、文件和目录的类型,以及如何创建符合标准的镜像文件。
- 运行时标准:指定了从运行时文件系统启动容器程序的方法,包括接收的指令类型、行为、生命周期管理,以及名称空间、
Cgroup和文件系统挂载的配置。
runc是一个独立的可执行程序,按照OCI标准启动和管理容器进程。
这样的设计使得docker容器运行时的核心组件,即容器的创建和管理不再依赖于docker守护进程。
而是直接调用runc来管理容器,从而与内部的libcontainer库解耦。

发展阶段
从docker1.11版本开始,docker容器的运行和管理不再完全依赖于docker daemon。
它整合container、runc和container-shim等组件共同完成。
docker daemon仍然负责与docker client交互,以及docker镜像的管理,但将容器的底层运行时操作交给了containerd和runc。
- 1.
docker daemon通过grpc请求containerd进行容器的创建。 - 2.
containerd不直接创建容器,因为containerd如果故障,所有容器都会故障,因此引入了containerd=shim,来进行容器的创建。 - 3.
containerd-shim调用runc(符合OCI标准)来创建容器 - 4.
runc创建容器后退出,contained-shim成为容器进程的父进程,负责对容器进行监控和清理。
通过引入containerd和containerd-shim,docker架构变得更加模块化,各个组件之间的职责分工更加明确,从而有助于提高整体的稳定性和可维护性。

CRI诞生
随着容器技术的快速发展,docker逐渐将重心转移至编排工具的开发,并将containerd捐赠给了云原生计算基金会(CNCF)。
这一策略不仅促进了容器技术的进一步发展,也吸引了更多社区成员的参与,使得docker能够专注于开发如swarm这样的编排工具。
然而,随着kubernetes编排工具出现,它为了保持中立性、支持多种容器运行时与kubernetes对接,以避免对特定技术的依赖和docker公司一家独大的局面,因此kubernetes在1.5版本引入了CRI。
CRI(container runtime interface,容器运行时接口)是kubernetes设计的一组接口,用于与容器运行时接口对接。
简单来说,只要容器运行时实现了CRI接口,就可以与kubernetes平台集成。
然而,并非所有的容器运行时都会直接实现CRI接口,因此出现了shim(垫片)这个概念。
shim的作用类似于适配器(usb转type-c),将各种不同的容器运行时的接口适配到kuburnetes的CRI接口上。
由于docker在容器领域的地位举足轻重,kubenetes便直接将dockershim嵌入到kubelet中。
因此,对于使用docker容器运行时的用户而言,无需额外安装配置适配器。
弃用阶段
在使用kubernetes和docker的场景中,为了实现docker的无缝对接,kubernetes开发了dockershim垫片。
虽然docker可以与kuburnetes无缝集成,但也导致了整个调用链变得更长。
尽管docker的调用链较长,但其受欢迎程度未受影响。
然而,对于kubernetes来说,这些额外的调用并不是必需的。
实际上,对于运行一个容器而言,直接使用containerd已经足够了。

随着时间的推移,kubernetes的使用群体持续增长,同时containerd也在CNCF顺利毕业,并显著增强了其健壮性。
在这种情况下,kubernetes终于可以考虑移出dockershim垫片。
因为在维护dockershim时,kubernetes遇到了许多问题,首先kubernetes需要处理docker中很多与其核心功能无关的内容。
此外,随着docker版本的升级,dockershim不得不进行相应的适配,并进行兼容性测试,这为kubernetes的维护工作带来了额外的复杂性。
从containerd 1.0版本开始,它通过一个独立的进程CRI-containerd,来适配kubernetes的容器运行时接口(CRI)。
这是因为containerd最初需要兼容多个系统,如swarm,所以并未直接实现CRI。这个适配过程最初由CRI-containerd shim组件完成。

在containerd 1.1版本中,CRI-containerd shim被移除,并且将适配器以插件形式集成到containerd主进程中。
整个调用过程变得更加简介和高效。

CRI-O
kubernetes社区也开发了一个专门用于kubernetes的CRI运行时,名为CRI-O,CRI-O直接兼容CRI和OCI规范,为kubernetes提供了更加高效的容器运行时支持。
containerd
-
安装
yum install containerd -
生成
containerd配置
containerd config default > /etc/containerd/config.toml -
运行
containerd
systemctl start containerd
systemctl status containerd
- 检查
containerd和runc的版本
ctr version
Client:
Version: 1.6.33
Revision: d2d58213f83a351ca8f528a95fbd145f5654e957
Go version: go1.21.11
Server:
Version: 1.6.33
Revision: d2d58213f83a351ca8f528a95fbd145f5654e957
UUID: 8e2e5be1-ced2-41c6-8678-3ed2107c662b
runc --version
runc version 1.1.12
commit: v1.1.12-0-g51d5e94
spec: 1.0.2-dev
go: go1.21.11
libseccomp: 2.3.1
镜像管理
- 拉取镜像:
ctr images pull <image_name>
# 公开镜像
ctr images pull docker.io/library/alpine:latest
# 私有镜像
Bash
ctr images pull --user admin:Harbor12345 harbor.oldxu.net/infra/nginx:1.20
- 查看镜像:
ctr images list -q
ctr images ls -q
docker.io/library/alpine:latest
-
删除镜像:
ctr images remove <image_name>
ctr images remove docker.io/library/alpine:latest -
由于
containerd和docker存储数据目录不一致,因此互相之间的镜像无法查看,那么docker构建的镜像如何提供给containerd
# 1、dockerfile构建镜像
# 2、docker push 推送私有仓库
# 3、containerd通过私有仓库获取镜像即可
容器管理
- 启动容器,
ctr containers run <options> <image_name> <container_name>
ctr run -d docker.io/library/alpine:latest mytest1 /bin/sh # 后台运⾏
ctr run -t docker.io/library/alpine:latest mytest2 /bin/sh # 前台运行
- 查看容器
ctr c list
CONTAINER IMAGE RUNTIME
mytest1 docker.io/library/alpine:latest io.containerd.runc.v2
mytest2 docker.io/library/alpine:latest io.containerd.runc.v2
ctr task list
TASK PID STATUS
mytest1 122141 RUNNING
-
进入容器(
--exec-id sh mytest1 /bin/sh)
ctr task exec -t --exec-id sh mytest1 /bin/sh -
删除容器,
ctr containers delete <containers_name>
ctr task kill --signal 9 mytest1
ctr containers delete mytest1
nerdctl
前面使用ctr工具来管理containerd的镜像与容器,不是特别直观,特别是已经习惯使用docker命令行的用户
为了让这部分用户更容易过渡到containerd,社区开发了一个新的命令行工具:nerdctl。
这个工具模仿了docker的命令行界面,使得用户能够更顺畅地从docker迁移到containerd。
- 下载nerdctl
wget https://github.com/containerd/nerdctl/releases/download/v1.7.6/nerdctl-full-1.7.6-linux-amd64.tar.gz
# 加速地址
wget https://mirror.ghproxy.com/https://github.com/containerd/nerdctl/releases/download/v1.7.6/nerdctl-full-1.7.6-linux-amd64.tar.gz
- 安装
nerdctl
tar xf nerdctl-full-1.7.6-linux-amd64.tar.gz -C nerdctl-full-1.7.6/
cp nerdctl-full-1.7.6/bin/nerdctl /usr/local/bin/
- 配置
nerdctl命令补全功能
echo 'source <(nerdctl completion bash)' >> /etc/profile
source /etc/profile
- 测试
nerdctl命令
nerdctl pull nginx:1.26
nerdctl images
nerdctl运行容器
wget https://mirror.ghproxy.com/https://github.com/containernetworking/plugins/releases/download/v1.5.0/cni-plugins-linux-amd64-v1.5.0.tgz
mkdir /opt/cni/bin -p
tar xf cni-plugins-linux-amd64-v1.4.1.tgz -C /opt/cni/bin/
nerdctl run -d -p8888:80 nginx:1.26
buildkitd
buildkit是用于构建容器镜像的工具,它包含了两个主要组件:buildctl和buildkitd
buildctl是一个命令行工具(客户端),负责与buildkitd守护进程进行通信。开发者使用buildctl提交镜像构建请求。buildkitd是buildkit的守护进程(服务端),这是一个后台进程,专门处理来自客户端的构建请求。
-
编写
dockerfile -
使用
nerdctl执行build
nerdctl build -t harbor.oldxu.net/app/nginx:1.26 .会报错。 -
拷贝
buildctl、buildkitd以及buildkitd启动脚本
cp nerdctl-full-1.7.6/bin/buildctl nerdctl-full-1.7.6/bin/buildkitd /usr/local/bin/
cp nerdctl-full-1.7.6/lib/systemd/system/buildkit.service /usr/lib/systemd/system/
systemctl daemon-reload
systemctl start buildkit.service
systemctl status buildkit.service
- 再次执行
build构建命令
nerdctl build -t harbor.oldxu.net/app/nginx:1.26 .
nerdctl images
容器内使用nerdctl
nerdctl run -d --name jenkins \
--hostname "jenkins.oldxu.net" \
--restart=always \
-p 8080:8080 \
-p 50000:50000 \
-u root \
--privileged=true \
-e TZ=Asia/Shanghai \
-v /etc/localtime:/etc/localtime:ro \
-v jenkins_home:/var/jenkins_home \
-v /usr/local/bin/nerdctl:/usr/local/bin/nerdctl \
-v /run/containerd/containerd.sock:/run/containerd/containerd.sock \
-v /usr/local/bin/buildctl:/usr/local/bin/buildctl \
-v /run/buildkit/buildkitd.sock:/run/buildkit/buildkitd.sock \
--add-host gitlab.oldxu.net:10.0.0.110 \
--add-host nexus.oldxu.net:10.0.0.121 \
--add-host sonar.oldxu.net:10.0.0.130 \
--add-host harbor.oldxu.net:10.0.0.185 \
uhub.service.ucloud.cn/oldxu/jenkins:2.452.1-lts-jdk17
留言