作者 | 刘启伟,广东公司网络管理中心网管系统室平台团队核心专家。近年来,网管系统室一方面大力推进OSS应用建设,为“三零三自”的自智网络赋能;另一方面,积极推动微服务、容器化、PaaS、DevOps等云原生技术的实践落地。在团队中负责DevOps平台和容器云的建设运营工作,拥有丰富的Kubernetes、Istio、DevOps工具链落地实践经验,致力于克服技术落地难题,用云原生技术赋能应用。
Labs 导读
Kubernetes是一个可移植、可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化。Kubernetes拥有一个庞大且快速增长的生态系统,其服务、支持和工具的使用范围广泛。
1前言
前段时间,kubernetes推出了1.24版本,曾经轰动一时的docker弃用也正式实装了,这意味着1.24版本之后,docker将不能作为k8s的容器运行时。docker作为云原生的基础技术底座,如果kubernetes不再支持docker,这在互联网IT业界都会引发不大不小的恐慌,这到底该怎么办?是不是docker完全不能使用了?
2技术的真相
其实kubernetes只是弃用了dockershim,并不是弃用了docker的全部。docker体系中的containerd是符合CRI标准的,可以继续作为kubernetes的容器运行时。而OCI标准的实现者runC也是docker体系的。
另一方面,docker构建的镜像符合OCI标准,可以运行在kubernetes集群中,所以仍然可以在本地使用docker进行开发和测试。
2.1 OCI和CRI标准分别是什么?
OCI(Open Container Initiative)是一组围绕容器技术的开放标准和规范,主要定义了容器的生命周期管理规范。OCI的实现者通常被称为“低级容器运行时”,例如runC。低级运行时的主要功能是按照给定的容器文件系统和JSON配置文件,创建容器,并管理容器的生命周期。
CRI(Container Runtime Interface)是一组插件接口,定义了kubernetes(kubelet)与容器运行时的接口规范,实现两者之间的解耦。通过CRI与kubernetes交互的运行时通常被称为“高级容器运行时”。高级运行时的功能是为容器准备必要的运行环境,比如拉取镜像、解压镜像并创建容器文件系统、创建容器网络等,然后调用低级容器运行时,创建和运行容器。
2.2 kubernetes支持哪些容器运行时?
kubernetes支持任何符合CRI标准的容器运行时。在1.23版本之前,常用的容器运行时有三种:docker、containerd、cri-o.
docker
docker守护进程是不符合CRI标准的。为了支持docker作为容器运行时,kubelet内置了一个dockershim模块,kubelet通过CRI调用dockershim,再由它转换请求,调用docker守护进程,而1.24版本将要移除的就是这个模块。此模式下创建容器时的调用过程如下:
- kubelet通过CRI调用dockershim;
- dockershim转换请求,调用docker守护进程;
- docker调用containerd;
- containerd创建containerd-shim进程,再由containerd-shim调用runC完成容器创建。最终容器由containerd-shim管理,容器内所有进程都是containerd-shim的子进程。
containerd
containerd是从docker守护进程中独立出来的容器运行时,最终也要通过runC运行容器。
在CRI标准被提出后,为了兼容CRI,减少调用开销,containerd开发了一个守护进程,叫CRI-containerd。原先调用链kubelet -> dockershim -> dockerd -> containerd 被简化成为 kubelet -> CRI-containerd -> containerd。后来,containerd干脆将CRI-containerd以CRI插件形式内建在项目中,直接通过方法调用,进一步将调用链简化为 kubelet -> containerd。
cri-o
CRI标准被提出后,红帽按照CRI开发的一个轻量级容器运行时,是CRI标准的最小实现。此模式下kubelet直接调用cri-o,再由cri-o调用runC完成容器创建和管理,调用链比较简洁。
广东公司网络管理中心网管系统室负责建设和维护O域容器云,近期刚好启动kubernetes 版本升级工作,借此机会,我们决定在测试环境上将容器运行时从docker迁移至cri-o,并验证下kubernetes 1.23 -> 1.24版本升级方案,以下是迁移的部分注意事项及详细步骤。
3迁移注意事项和详细步骤
注意事项:
- 对于使用docker in docker的pod,如果是挂载宿主机的docker.sock守护进程,迁移后将不能运行,如果是在容器中安装独立的docker守护进程,迁移后仍然可以正常运行。
- /etc/docker/daemon.json中的配置需要同步到新的运行时,比如仓库的镜像站点。
- 检查各种运维脚本,如果包含docker命令需要修改。
- 容器stdout/stderr日志形式变更,如果使用Fluentd或者Filebeat收集日志,需要修改配置。
① 日志目录:使用docker时,日志通过/var/log/containers链接到/var/log/pods/目录,最后链接到/var/lib/docker/containers/xxx/目录,如果使用其他运行时,一般是通过/var/log/containers链接到/var/log/pods/目录,由kubelet管理。
② 日志格式:使用docker时,很多人习惯设置json格式,而切换到其他运行时,默认格式是text,格式为“time stream log-info”。日志解析配置需要修改。
③ 日志回滚:使用docker时,在daemon.json配置,切换运行时后,通过kubelet的配置项containerLogMaxSize、containerLogMaxFiles设置。
怎么将kubernetes的容器运行时从docker迁移至cri-o?
- 操作系统:centOS 7.9
- 内核版本:5.4.178
- kubernetes版本:1.23.3
- cri-o:1.22.3
1. 迁移按节点进行,先驱逐pod并隔离节点
kubectl drain --delete-emptydir-data --force --ignore-daemonsets <NODE_NAME>
- 1.
2. 卸载docker
systemctl stop kubelet
systemctl stop docker
systemctl disable docker
yum remove -y docker-ce
# docker数据目录先保留一段时间,运行没异常再删除
rm -rf /var/lib/docker
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
3. 内核设置
这些设置一般在k8s安装前都会设置,这里再确认一次,已经设置好的忽略这一步。
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
sysctl --system
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
modprobe overlay
modprobe br_netfilter
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
4. 安装cri-o
# 设置yum源
export OS=CentOS_7
export VERSION=1.22
curl -L -o
/etc/yum.repos.d/devel:kubic:libcontainers:stable.repo https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/$OS/devel:kubic:libcontainers:stable.repo
curl -L -o /etc/yum.repos.d/devel:kubic:libcontainers:stable:cri-o:$VERSION.repo https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable:cri-o:$VERSION/$OS/devel:kubic:libcontainers:stable:cri-o:$VERSION.repo
# 安装cri-o
yum install -y cri-o
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
5. 修改cri-o配置
# 查看conmon路径
which conmon
# 修改cri-o配置文件
vi /etc/crio/crio.conf
# 修改crio.runtime表,加上conmon路径配置
[crio.runtime]
conmon = "/usr/bin/conmon"
# 修改crio.image表,加上pause镜像设置。xxx需要换成你的私有镜像库
[crio.image]
insecure_registries = ["xxx"]
pause_image = "xxx/k8s/pause:3.6"
# 修改registry配置
vi /etc/containers/registries.conf
# 添加私有镜像库,xxx需要替换成你的私有镜像库,这里设置了insecure,可按实现情况修改
# 因为我用的是私有仓库,不需要设置镜像站点
[[registry]]
prefix = "xxx"
insecure = true
blocked = false
location = "xxx"
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
6. 启动cri-o服务
systemctl enable crio
systemctl start crio
systemctl status crio
- 1.
- 2.
- 3.
7. 修改kubelet配置
设置kubelet命令行启动参数,指定使用cri-o运行时。
vi /etc/sysconfig/kubelet
# 修改内容,加上以下两个参数
KUBELET_EXTRA_ARGS=--container-runtime=remote --container-runtime-endpoint='unix:///var/run/crio/crio.sock'
- 1.
- 2.
- 3.
修改 /var/lib/kubelet/kubeadm-flags.env 文件,文件中如果有以下3个参数,请删除。
- --cgroup-driver k8s建议在配置文件设置,不要在命令行。
- --cni-plugin 1.24版本后会和docker-shim一起被移除。
- --pod-infra-container-image 当使用cri-o运行时,kubelet忽略这个参数,需要在cri-o配置中指定。
修改kubelet的配置文件 /var/lib/kubelet/config.yaml,修改以下4个参数,如果参数不存在则添加上去。
设置kubelet的cgroup驱动为systemd,因为cri-o默认驱动是systemd,必须保持一致。旧版本kubelet默认驱动是cgroupfs,1.22以上才是默认systemd。
cgroupDriver: systemd
- 1.
设置运行时请求超时:
runtimeRequestTimeout: 5m
- 1.
容器stdout/stderr日志文件的回滚设置,按实际需求修改。
containerLogMaxSize: 100Mi
containerLogMaxFiles: 3
- 1.
- 2.
修改了 /var/lib/kubelet/config.yaml 文件后,建议同步修改内容到kubelet-config-1.xx configmap,1.xx是kubernetes的版本。因为集群扩容时,新节点使用这个configmap生成配置文件,这样可以保证新旧节点配置文件一致。
kubectl edit cm -n kube-system kubelet-config-1.23
- 1.
8. 启动kubelet,查看kubelet状态、节点状态、pod状态是否正常。
systemctl start kubelet
systemctl status kubelet
- 1.
- 2.
9. 更新kubeadm使用的cri运行时
kubeadm使用的cri运行时在node annotations中定义,需要单独修改,否则下次使用kubeadm时会出错,比如升级k8s版本的时候。
# 查看当前节点的kubeadm使用的cri运行时
kubectl get node <NODE_NAME> -o jsonpath='{.metadata.annotations.kubeadm\.alpha\.kubernetes\.io/cri-socket}'
# 将dokcershim修改为cri-o
kubectl annotate node <NODE_NAME> --overwrite kubeadm.alpha.kubernetes.io/cri-socket=/var/run/crio/crio.sock
- 1.
- 2.
- 3.
- 4.
10. 安装podman
podman是一个开源的容器管理工具,命令几乎与docker一致,可以用于替换docker。相较于docker,它不存在守护进程,因此podman避免了docker daemon引入的问题。另一方面,cri-o专注于CRI实现,没有提供build、tag镜像等功能,而podman和cri-o的镜像是共享的,可以为cri-o补充镜像管理功能。
yum install -y podman
podman info
- 1.
- 2.
11. 重启服务器
docker卸载后可能还有一些配置遗留,例如iptables规则,建议重启服务器,防止被影响。
12. 将节点重新加入集群调度。
kubectl uncordon <NODE_NAME>
- 1.
到这里,第一个节点的容器运行时迁移就完成了,可以按照相同的方法再迁移其他节点。
迁移完成后就能愉快地把k8s版本升到1.24.0了。
4后记
虽然k8s已经正式移除了dockershim,但是docker+kubernetes的方案经过多年发展已经成熟,被广泛地应用,短期内地位仍然不可撼动。开发、测试环境可以按照需求折腾,迁移容器运行时,积累实践经验。生产环境的话建议保持稳定,等时机成熟再迁移。