容器发展史
在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
留言