k8s搭配containerd
一直在关注Kubernetes动态,眼看着1.20 版本要发布了,抱着好奇的心理去Github看看这个版本更新了啥?结果在弃用位置发现了一个焦点,说要“弃用对Docker支持”
Kubelet 中对 Docker 支持被弃用,并将在以后的版本中删除。
目前Kubelet 使用一个名为 dockershim
的模块,该模块实现了对Docker的 CRI
支持,后续版本将删除dockershim
每一个容器运行时都需要自己实现一个 CRI shim,即完成对 CRI 这个抽象接口的具体实现。这样容器运行时就可以接收来自 Kubelet 的请求。
从上图可以看出,新增的 CRI shim 是 Kubelet 和容器运行时之间的交互纽带,Kubelet 只需要跟 CRI shim 进行交互。Kubelet 调用 CRI shim 的接口,CRI shim 响应请求后会调用底层的运行容器时,完成对容器的相关操作。
这里我们需要将 Kubelet、CRI shim 以及容器运行时都部署在同一个节点上。一般来说,大多数的容器运行时都默认实现了 CRI 的接口,比如containerd。
名词的解释:
https://blog.csdn.net/weixin_40864891/article/details/86655846
CRI
:容器运行时接口 container runtime interface
,CRI 中定义了容器和镜像两个接口
实现了这两个接口目前主流的是:
CRI-O: 相关介绍: https://linux.cn/article-9015-1.html
Containerd: 相关介绍 :https://segmentfault.com/a/1190000023618092
其主要的作用:
- 针对容器操作的接口,包括容器的创建、启动和停止等
- 针对镜像的操作,拉去、删除镜像等
- 针对 podsandbox(容器沙箱环境)
- 以上全是接口
OCI 中定义了两个标准:
容器运行时标准
和 容器镜像标准
,实现了这一标准的主流是:
runc(也即我们日常说的 Docker,因为是docker 开源出来的)
Kata-Container 介绍文章 https://segmentfault.com/a/1190000021845002?utm_medium=referral&utm_source=tuicool
主要作用,制作容器:
- 容器镜像制作内容,即
ImageSpec
- 容器需要接收哪些指令,即
runtimeSpec
Dockershim 作用
:把外部收到的请求转化成 Docker Daemon
能听懂的请求,让 Docker Daemon 执行创建、删除等容器操作。
组件介绍:
-
docker:命令行管理工具
-
dockerd:Docker守护进程,负责与docker client交互;
- containerd:负责镜像管理和容器管理的守护进程,containerd是一个标准的容器运行时,可以独立管理容器生命周期,也就是即使不运行dockerd,容器也能正常工作;
- containerd-shim:是一个真实运行的容器的载体,每启动一个容器都会起一个新的shim的一个进程;
- runC:一个命令行工具,根据OCI标准来创建和运行容器。
当我们执行docker run创建一个容器时,大致流程:
1.docker工具向dockerd守护进程发送创建容器请求;
2.dockerd收到请求后再向containerd请求创建一个容器;
3.containerd收到请求后并不会直接创建容器,而让shim创建容器;
4.docker-shim又调用runC创建容器(准备容器所需的namespace和cgroups就退出了),docker-shim就作为了该容器进程的父进程,负责收集容器状态并上报给containerd。
通过上面来看,Docker Daemon
和 dockershim
看上去就是两个不干活的东西,Kubelet 为啥不直接调用 containerd
呢?其实和容器历程有关,这里不在阐述。
有兴趣可以看张磊老师的博客https://time.geekbang.org/column/intro/100015201
尽管现在已经有 CRI-O
,containerd-plugin
这样更精简轻量的 Runtime
架构,但 dockershim
这一套作为经受了最多生产环境考验的方案,迄今为止仍是 Kubernetes 默认的 Runtime 实现
可以看出,调用链还是很复杂的,多层封装和调用,导致性能降低、提升故障率、不易排查,我想这也是弃用对Docker支持的主要原因吧!
如果把容器运行时换成containerd,如图所示
可见,Containerd 调用链更短,组件更少,占用节点资源也比较少
containerd 1.1 版本已经内置了对 CRI 的实现,比直接使用 Docker 的性能要高很多。
如果使用containerd我们就需要配置
kubelet 的 --container-runtime 参数为 remote,
设置 --container-runtime-endpoint 为对应的容器运行时的监听地址
比如:
--container-runtime=remote
--container-runtime-endpoint=unix:///run/containerd/containerd.sock
我这边用的是centos8的系统,所以需要安装containerd 和runc 相关的软件包
dnf install runc -y
https://github.com/containerd/containerd
wget https://github.com/containerd/containerd/releases/download/v1.3.9/containerd-1.3.9-linux-amd64.tar.gz
tar -zxvf containerd-1.3.9-linux-amd64.tar.gz
cd bin/
cp * /usr/bin/
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml
这里重点修改一下 Cgroup
说明一下Cgroup drivers:systemd cgroupfs 区别
那么 systemd 和 cgroupfs 这两种驱动有什么区别呢?
1. systemd cgroup driver 是 systemd 本身提供了一个 cgroup 的管理方式,使用systemd 做 cgroup 驱动的话,所有的 cgroup 操作都必须通过 systemd 的接口来完成,不能手动更改 cgroup 的文件
2. cgroupfs 驱动就比较直接,比如说要限制内存是多少、要用 CPU share 为多少?直接把 pid 写入对应的一个 cgroup 文件,然后把对应需要限制的资源也写入相应的 memory cgroup 文件和 CPU 的 cgroup 文件就可以了
所以可以看出来 systemd 更加安全,因为不能手动去更改 cgroup 文件,当然我们也推荐使用 systemd 驱动来管理 cgroup。
同时未必保证修改kubelet
cgroupDriver: systemd
通过systemd-cgls命令我们可以看到systemd工作的进程PID是1,而目录/sys/fs/cgroup/systemd是systemd维护的自己使用的非subsystem的cgroups层级结构
查考文档:
https://blog.csdn.net/avatar_2009/article/details/109603870
https://github.com/containerd/containerd/issues/4900
https://www.pianshen.com/article/89821412857/
cat <<EOF > /etc/systemd/system/containerd.service
[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target
[Service]
ExecStartPre=/sbin/modprobe overlay
ExecStartPre=/sbin/modprobe br_netfilter
ExecStart=/usr/bin/containerd
Restart=always
RestartSec=5
Delegate=yes
KillMode=process
OOMScoreAdjust=-999
LimitNOFILE=1048576
LimitNPROC=infinity
LimitCORE=infinity
[Install]
WantedBy=multi-user.target
EOF
systemctl enable containerd
systemctl restart containerd
systemctl status containerd
安装完成,就可以使用ctr客户端工具测试
containerd 相比于docker , 多了namespace概念, 每个image和container 都会在各自的namespace下可见
默认k8s使用的镜像都放置到 k8s.io 这个名称空间,使用离线镜像尽量导入到这个名词空间
https://github.com/containerd/containerd/blob/master/docs/namespaces.md
私用仓库的调用用户名和密码认证方式是:
ctr i pull -u "devops:xxxx" harbor.xxx.net/devops/pause:3.2
如果不想一直输入密码,可以增加配置文件
vim /etc/containerd/config.toml
ctr -n k8s.io images ls
ctr --namespace=k8s.io i tag harbor.xxx.net/devops/pause:3.2 k8s.gcr.io/pause:3.2
crictl
是一个命令行接口,用于与CRI兼容的容器运行时
https://kubernetes.io/zh/docs/tasks/debug-application-cluster/crictl/
需要安装相应的工具包
cat <<EOF > /etc/crictl.yaml
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
debug: false
EOF
使用K8S 离线包:
crictl pull --creds devops:xxxx harbor.xxx.net/devops/pause:3.2
看到的镜像一个和ctr -n k8s.io i ls 看到的镜像是一样的
目前官网支持的几种容器运行时的安装方式:
https://kubernetes.io/zh/docs/setup/production-environment/container-runtimes/#containerd
虽然未来 Kubelet 删除 dockershim 支持,但并不说明 Docker 马上就不能在 Kubernetes 中使用,目前容器市场 Docker 还是占用很大的比例。这中间会有一个过渡期,大家可以关注 Containerd
或者 Podman
。Centos8 开始,仓库源默认容器已经从 Docker
切换为 Podman
。
参考文章:
https://blog.51cto.com/juestnow/2440775
https://www.infoq.cn/article/odslclsjvo8bnx*mbrbk
https://www.likecs.com/show-305973752.html#sc=400
https://cloud.tencent.com/developer/article/1895808
https://www.likecs.com/show-132453.html