注意:Kubernetes 1.24通过 Dockershim 对 Docker 的支持已移除,新建节点的容器运行时请使用 Containerd,通过 Docker 构建的镜像可以继续使用。
Containerd 是更为稳定的运行时组件,支持 OCI 标准,但不支持 Docker API。
容器运行时(Container Runtime)是 Kubernetes(K8S) 最重要的组件之一,负责管理镜像和容器的生命周期。Kubelet 通过 Container Runtime Interface (CRI)
与容器运行时交互,以管理镜像和容器。
TKE 支持用户选择 Containerd 和 Docker 作为运行时组件:
说明:修改运行时组件及版本,只对集群内无节点池归属的增量节点生效,不会影响存量节点。
Containerd 不支持 docker API 和 docker CLI,但是可以通过 cri-tool 命令实现类似的功能。
镜像相关功能 | Docker | Containerd |
---|---|---|
显示本地镜像列表 | docker images | crictl images |
下载镜像 | docker pull | crictl pull |
上传镜像 | docker push | 无 |
删除本地镜像 | docker rmi | crictl rmi |
查看镜像详情 | docker inspect IMAGE-ID | crictl inspect IMAGE-ID |
容器相关功能 | Docker | Containerd |
---|---|---|
显示容器列表 | docker ps | crictl ps |
创建容器 | docker create | crictl create |
启动容器 | docker start | crictl start |
停止容器 | docker stop | crictl stop |
删除容器 | docker rm | crictl rm |
查看容器详情 | docker inspect | crictl inspect |
attach | docker attach | crictl attach |
exec | docker exec | crictl exec |
logs | docker logs | crictl logs |
stats | docker stats | crictl stats |
POD 相关功能 | Docker | Containerd |
---|---|---|
显示 POD 列表 | 无 | crictl pods |
查看 POD 详情 | 无 | crictl inspectp |
运行 POD | 无 | crictl runp |
停止 POD | 无 | crictl stopp |
kubelet --> docker shim (在 kubelet 进程中) --> dockerd --> containerd
kubelet --> cri plugin(在 containerd 进程中) --> containerd
其中 dockerd 虽增加了 swarm cluster、docker build 、docker API 等功能,但也会引入一些 bug,而与 Containerd 相比,多了一层调用。
包括 exec,preStop,ipv6,日志 stdout 的格式等。
kubectl exec/logs
等命令需要 kubelet 在 apiserver 跟容器运行时之间建立流转发通道。
可以通过了解kubectl exec
命令的原理来了解 CRI 的 Stream Service 是如何工作的:
kubectl exec
等命令时,请求经过 kube-apiserver 找到 Pod 对应的节点,转发到 kubelet。Docker API 本身提供 stream 服务,Dockershim 位于 kubelet 内部有默认的配置 "127.0.0.1:0" 。
Containerd 的 stream 服务需要单独配置:
[plugins.cri]
stream_server_address = "127.0.0.1"
stream_server_port = "0"
enable_tls_streaming = false
Containerd 的 stream 服务在 K8S 不同版本运行时场景下配置不同。
Docker 和 Containerd 在 Exec 的实现上略有区别,区别主要在 Execsync 的实现上,也即执行单条命令的情况。
因此 kubectl exec
命令在不指定参数-it
时 和 pod lifecycle 中的 ExecProbe 在 Runtime 类型不同的节点上表现可能稍有不同。
如 kubectl exec <pod-id> -- bash -c "nohup sleep 10 &"
命令,在 Runtime 是 docker 的节点上会在两秒左右结束;而在 containerd 的节点上需要等到 sleep 进程退出后才能结束。
kubelet 在实现 exec probe 时使用了 CRI Runtime 的 ExecSync 接口,因此 exec probe 和 kubectl exec # no -t -i
的表现一致,也即:
区别导致的影响主要出现在 pod lifecycle 的 postStartHook 和 preStopHook 中,如果在 hook 中使用 exec probe 并且出现残留子进程的情况,在 containerd 的节点上可能会遇到 Pod 长期卡在 containerCreating 状态。原因是 kubelet 在 syncPod 时会逐个容器拉起,并执行 probe,如果某个 probe 因上述原因阻塞住,会导致后续容器无法启动。
在 ExecProbe 中拉起子进程并退出父进程属于 K8S 中未定义的行为,具体表现可能会和运行时版本、种类相关,因此建议尽量不要在 probe 执行过于复杂的操作。
在正常情况下,Pod 中的容器会共享同一个 Network Namespace,因此 Pod 需要在创建 Sandbox 容器时将网络准备好。为了更好的说明区别,我们简单介绍下 Pod 网络初始化的流程:
Docker 和 containerd 在创建 pause 容器并初始化 Network Namespace和 调用 CNI 初始化 veth 这两步有区别。
Containerd 是为了 kubernetes 设计的 CRI Runtime,没有独立的网络模块;但 Docker 在设计时带有自己的网络功能的,因此 docker 在创建 Pause 容器时,会进行 Docker 特有的网络设置。该设置导致和 Containerd 最大的区别是在不使用 IPv6 的情况下,Docker 会将容器 Network Namespace 中内核参数net.ipv6.conf.all.disable_ipv6
设置为1,也即关闭容器内的 ipv6 选项。
同样的 Pod 在 Docker 的节点上只开启了 IPv4,而在 Containerd 的节点上会同时开启 IPv4 和 IPv6。
同时开启 IPv4 和 IPv6 的情况中,DNS 解析可能会同时发出v4、v6两个版本的包。在某些情况,业务如果需要频繁进行 DNS 解析,可能会触发 DNS 解析库的 Bug(取决于 Pod 业务的实现时的依赖)。在 Containerd 节点上,可以通过给 Pod 添加 init container 来针对 Pod 关闭 IPv6 设置。代码如下:
apiVersion: v1
kind: Pod
...
spec:
initContainers:
- image: busybox
name: sysctl
command: ["sysctl", "-w", "net.ipv6.conf.all.disable_ipv6=1"]
securityContext:
privileged: true
...
两者在调用 CNI 上没有实质区别。
对比项 | Docker | Containerd |
---|---|---|
谁负责调用 CNI | Kubelet 内部的 docker-shim | Containerd 内置的 cri-plugin |
如何配置 CNI | Kubelet 参数 --cni-bin-dir 和 --cni-conf-dir |
Containerd 配置文件(toml):[plugins.cri.cni] bin_dir = "/opt/cni/bin" conf_dir = "/etc/cni/net.d" |
对比项 | Docker | Containerd |
---|---|---|
存储路径 |
如果 Docker 作为 K8S 容器运行时,容器日志的落盘将由 docker 来完成,保存在类似/var/lib/docker/containers/$CONTAINERID 目录下。Kubelet 会在 /var/log/pods 和 /var/log/containers 下面建立软链接,指向 /var/lib/docker/containers/$CONTAINERID 该目录下的容器日志文件。
|
如果 Containerd 作为 K8S 容器运行时, 容器日志的落盘由 Kubelet 来完成,保存至 /var/log/pods/$CONTAINER_NAME 目录下,同时在 /var/log/containers 目录下创建软链接,指向日志文件。
|
存储大小 | Pod中的每个容器,docker默认会保留 100MB*10 = 1G 日志 | Pod中的每个容器,containerd默认会保留 10MB*5 = 50MB 日志 |
配置参数 |
在 docker 配置文件中指定:
"log-driver": "json-file",
"log-opts": {"max-size": "100m","max-file": "5"}
|
|
把容器日志保存到数据盘 | 把数据盘挂载到 “data-root”(缺省是 /var/lib/docker )即可。 |
创建一个软链接 /var/log/pods 指向数据盘挂载点下的某个目录。 在 TKE 中选择“将容器和镜像存储在数据盘”,会自动创建软链接 /var/log/pods 。
|
本页内容是否解决了您的问题?