S_lion's Studio

k8s替换容器运行时为containerd

字数统计: 3.2k阅读时长: 15 min
2021/12/16 Share

环境描述

主机名 ip地址 系统环境 系统版本 集群角色
slions_pc1 192.168.100.10 cpu:x4 mem:8GB filesystem:40GB CentOS 7.6.1810 k8s master & node

我本地之前已经安装过kubernetes v1.19.0,使用的运行时是docker,本次测试下如何将k8s的容器运行时换为containerd。

变更前的工作

因为这是我的本地测试环境,如果是真实环境需要考虑如何将业务影响最小化,以下是整体的一个思路。

维护节点

首先标记需要切换的节点为维护模式,强制驱逐节点上正在运行的 Pod,这样可以最大程度降低切换过程中影响应用的正常运行。

1
kubectl cordon  $<NODE_NAME> 

执行完上面的命令后,slions_pc1节点变成了一个 SchedulingDisabled 状态,表示不可调度,这样新创建的 Pod 就不会调度到当前节点上来了。

接下来维护 slions_pc1节点,使用 kubectl drain 命令来维护节点并驱逐节点上的 Pod:

1
kubectl drain  $<NODE_NAME>  --ignore-daemonsets

上面的命令会强制将 node1 节点上的 Pod 进行驱逐,加了一个 --ignore-daemonsets 的参数可以用来忽略 DaemonSet 控制器管理的 Pods,因为这些 Pods 不用驱逐到其他节点去,当节点驱逐完成后接下来我们就可以来对节点进行维护操作了,除了切换容器运行时可以这样操作,比如我们需要变更节点配置、升级内核等操作的时候都可以先将节点进行驱逐,然后再进行维护。

停止服务

为了减小后期产生问题时的干扰因素,将docker、containerd 和 kubelet手动停止。

1
2
3
systemctl stop docker
systemctl stop containerd
systemctl stop kubelet

安装containerd

以下是官方文档中指出的安装和配置的先决条件,之前已经在安装k8s环境时完成了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

# Setup required sysctl params, these persist across reboots.
cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF

# Apply sysctl params without reboot
sudo sysctl --system

之前环境安装过docker,docker 默认使用了 containerd 作为后端的容器运行时,所以不需要单独安装 containerd 了。

配置containerd

因为 containerd 中默认已经实现了 CRI,但是是以 plugin 的形式配置的,以前 Docker 中自带的 containerd 默认是将 CRI 这个插件禁用掉了的(使用配置 disabled_plugins = ["cri"]),所以这里我们重新生成默认的配置文件来覆盖掉:

1
[root@slions-pc1 ~]# containerd config default > /etc/containerd/config.toml

配置文件内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
version = 2
root = "/var/lib/containerd"
state = "/run/containerd"
plugin_dir = ""
disabled_plugins = []
required_plugins = []
oom_score = -999

[grpc]
address = "/run/containerd/containerd.sock"
tcp_address = ""
tcp_tls_cert = ""
tcp_tls_key = ""
uid = 0
gid = 0
max_recv_message_size = 16777216
max_send_message_size = 16777216

[ttrpc]
address = ""
uid = 0
gid = 0

[debug]
address = ""
uid = 0
gid = 0
level = ""

[metrics]
address = ""
grpc_histogram = false

[cgroup]
path = ""

[timeouts]
"io.containerd.timeout.shim.cleanup" = "5s"
"io.containerd.timeout.shim.load" = "5s"
"io.containerd.timeout.shim.shutdown" = "3s"
"io.containerd.timeout.task.state" = "2s"

[plugins]
[plugins."io.containerd.gc.v1.scheduler"]
pause_threshold = 0.02
deletion_threshold = 0
mutation_threshold = 100
schedule_delay = "0s"
startup_delay = "100ms"
[plugins."io.containerd.grpc.v1.cri"]
disable_tcp_service = true
stream_server_address = "127.0.0.1"
stream_server_port = "0"
stream_idle_timeout = "4h0m0s"
enable_selinux = false
selinux_category_range = 1024
sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.2"
stats_collect_period = 10
systemd_cgroup = false
enable_tls_streaming = false
max_container_log_line_size = 16384
disable_cgroup = false
disable_apparmor = false
restrict_oom_score_adj = false
max_concurrent_downloads = 3
disable_proc_mount = false
unset_seccomp_profile = ""
tolerate_missing_hugetlb_controller = true
disable_hugetlb_controller = true
ignore_image_defined_volumes = false
[plugins."io.containerd.grpc.v1.cri".containerd]
snapshotter = "overlayfs"
default_runtime_name = "runc"
no_pivot = false
disable_snapshot_annotations = true
discard_unpacked_layers = false
[plugins."io.containerd.grpc.v1.cri".containerd.default_runtime]
runtime_type = ""
runtime_engine = ""
runtime_root = ""
privileged_without_host_devices = false
base_runtime_spec = ""
[plugins."io.containerd.grpc.v1.cri".containerd.untrusted_workload_runtime]
runtime_type = ""
runtime_engine = ""
runtime_root = ""
privileged_without_host_devices = false
base_runtime_spec = ""
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
runtime_engine = ""
runtime_root = ""
privileged_without_host_devices = false
base_runtime_spec = ""
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
[plugins."io.containerd.grpc.v1.cri".cni]
bin_dir = "/opt/cni/bin"
conf_dir = "/etc/cni/net.d"
max_conf_num = 1
conf_template = ""
[plugins."io.containerd.grpc.v1.cri".registry]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
endpoint = ["https://registry-1.docker.io"]
[plugins."io.containerd.grpc.v1.cri".image_decryption]
key_model = ""
[plugins."io.containerd.grpc.v1.cri".x509_key_pair_streaming]
tls_cert_file = ""
tls_key_file = ""
[plugins."io.containerd.internal.v1.opt"]
path = "/opt/containerd"
[plugins."io.containerd.internal.v1.restart"]
interval = "10s"
[plugins."io.containerd.metadata.v1.bolt"]
content_sharing_policy = "shared"
[plugins."io.containerd.monitor.v1.cgroups"]
no_prometheus = false
[plugins."io.containerd.runtime.v1.linux"]
shim = "containerd-shim"
runtime = "runc"
runtime_root = ""
no_shim = false
shim_debug = false
[plugins."io.containerd.runtime.v2.task"]
platforms = ["linux/amd64"]
[plugins."io.containerd.service.v1.diff-service"]
default = ["walking"]
[plugins."io.containerd.snapshotter.v1.devmapper"]
root_path = ""
pool_name = ""
base_image_size = ""
async_remove = false

需要注意的有几点:

  1. 修改默认的 pause 镜像为我之前所用的地址。
1
2
3
[plugins]
[plugins."io.containerd.grpc.v1.cri"]
sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.2"
  1. 如果使用systemd 作为cgroup driver的话,需要修改下面字段。
1
2
3
4
5
6
[plugins]
[plugins."io.containerd.grpc.v1.cri".containerd]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true

修改 kubelet 配置

将容器运行时配置为 containerd,打开 /etc/sysconfig/kubelet 文件,在该文件中可以添加一些额外的 kubelet 启动参数,配置如下所示:

1
KUBELET_EXTRA_ARGS='--container-runtime=remote --container-runtime-endpoint=unix:///run/containerd/containerd.sock'
  • --container-runtime 参数是用来指定使用的容器运行时的,可选值为 docker 或者 remote,默认是 docker,由于这里使用的是 containerd 这种容器运行时,所以配置为 remote 值(也就是除 docker 之外的容器运行时都应该指定为 remote
  • --container-runtime-endpoint 是用来指定远程的运行时服务的 endpiont 地址的,这里指定连接 containerd 的套接字地址 unix:///run/containerd/containerd.sock

恢复服务

配置完成后重启 containerd 和 kubelet 。

1
2
3
systemctl daemon-reload
systemctl restart containerd
systemctl restart kubelet

查看节点状态是否正常:

1
2
3
[root@slions-pc1 ~]# kubectl get node -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
slions-pc1 Ready master 2d v1.19.0 192.168.100.10 <none> CentOS Linux 7 (Core) 3.10.0-957.el7.x86_64 containerd://1.4.12

slions_pc1节点的容器运行时已经切换到 containerd://1.4.12 了。

恢复调度

最后把节点重新加回到集群中来允许调度 Pod。

1
kubectl uncordon $<NODE_NAME> 

crictl

可以使用 ctr 命令来管理 containerd,查看多了一个名为 k8s.io 的命名空间:

1
2
3
4
[root@slions-pc1 ~]# ctr ns ls
NAME LABELS
k8s.io
moby

kubernetes集群对接的 containerd 所有资源都在 k8s.io 的命名空间下面,而 docker 的则默认在 moby 下面,当然现在 moby 下面没有任何的数据了,但是在 k8s.io 命名空间下面就有很多镜像和容器资源了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
[root@slions-pc1 ~]# ctr -n k8s.io t ls
TASK PID STATUS
17ae102739d906d0accba9c1e585084b85f8ce663373f15784d6ba68ace74b1d 35543 RUNNING
71da443d2b4005a37e879ed1c6d563ed775ae0d2291d10ea41f82b9ec9fa593d 51711 RUNNING
9545145b2d9854fc80ee15a3bfe1f0b57fd8a0bd4630b571947c350adfe897b0 53337 RUNNING
3fb571cc7679101e630b9fabc7159346fcf2004110844bf74850710bd182cd36 53459 RUNNING
71890ef89c6b61f3a5d118c050d37bd796b01700d3449735ad6ca14fa3c9b40f 35169 RUNNING
6e688d8e857237919e7a81c186502d10b9dc8bb15bfbfbd8e400419039d2cacb 35447 RUNNING
cad990849d28051b75df2fd855d12ffad81065a49091c19f20480ecbc72f7c56 35116 RUNNING
b5869243675c19aade230142e7a15e564e9b031b5cd32a901475291b2f1f59c2 53685 RUNNING
51e15fd5f4961283c32fc63bfa7a166ee3e6289aef19f14ffd3a95410640ea12 35351 RUNNING
484541d3b39bbe199b7c16a5049b53a6f0950435b1123dca23064b7a72901917 35604 RUNNING
2f5c0484bb866d79bfdd1d2f3a6100a4559404bdb903b71a4af879468fc5bc7a 35393 RUNNING
31e6e8d32619247f01b21f76c73a5ab1a3b72da12142f6280dfca1824b3c051d 53743 RUNNING
d6fe5b981c93ab1b9870adaa14599710d26a58d2eb8604d5b08c2a30eb3aa11e 53846 RUNNING
ec086f05f1063ab3197e5d1342b5febeaa978535baae0f5533616b79b78023fa 53878 RUNNING
ea80243d5a9e662b141dda43d229b8e68e95d1a6077d7837f0a88bb2d1e4dbbd 51743 RUNNING
468ae24960d0cc200573b8609c03944e45d202ed37fe9148e678f178e8112f05 35015 RUNNING
[root@slions-pc1 ~]# ctr -n moby t ls
TASK PID STATUS
[root@slions-pc1 ~]# ctr -n k8s.io i ls -q
docker.io/rancher/mirrored-flannelcni-flannel-cni-plugin:v1.0.0
docker.io/rancher/mirrored-flannelcni-flannel-cni-plugin@sha256:bfe8f30c74bc6f31eba0cc6659e396dbdd5ab171314ed542cc238ae046660ede
quay.io/coreos/flannel:v0.15.1
quay.io/coreos/flannel@sha256:9a296fbb67790659adc3701e287adde3c59803b7fcefe354f1fc482840cdb3d9
registry.aliyuncs.com/google_containers/coredns:1.7.0
registry.aliyuncs.com/google_containers/coredns@sha256:73ca82b4ce829766d4f1f10947c3a338888f876fbed0540dc849c89ff256e90c
registry.aliyuncs.com/google_containers/etcd:3.4.9-1
registry.aliyuncs.com/google_containers/etcd@sha256:735f090b15d5efc576da1602d8c678bf39a7605c0718ed915daec8f2297db2ff
registry.aliyuncs.com/google_containers/kube-apiserver:v1.19.0
registry.aliyuncs.com/google_containers/kube-apiserver@sha256:522d17d35a8994637d27d1232bebd35cfae8e3e21ab359431403f2b8023e332c
registry.aliyuncs.com/google_containers/kube-controller-manager:v1.19.0
registry.aliyuncs.com/google_containers/kube-controller-manager@sha256:6c11a3d4d06385f7547a5ea0c3f0d5e7b12baa558111d01406ac1f778cb3f00b
registry.aliyuncs.com/google_containers/kube-proxy:v1.19.0
registry.aliyuncs.com/google_containers/kube-proxy@sha256:c752ecbd04bc4517168a19323bb60fb45324eee1e480b2b97d3fd6ea0a54f42d
registry.aliyuncs.com/google_containers/kube-scheduler:v1.19.0
registry.aliyuncs.com/google_containers/kube-scheduler@sha256:529a1566960a5b3024f2c94128e1cbd882ca1804f222ec5de99b25567858ecb9
registry.aliyuncs.com/google_containers/pause:3.2
registry.aliyuncs.com/google_containers/pause@sha256:927d98197ec1141a368550822d18fa1c60bdae27b78b0c004f705f548c07814f
...

ctr工具比起docker cli工具太难用了,crictl 为 CRI 兼容的容器运行时提供 CLI,后续可以使用crictl工具来进行运维。

安装K8s时默认是装了cri-tools软件包的,所以可以直接上手。

需要修改下默认的配置文件,默认为 /etc/crictl.yaml,在文件中指定容器运行时和镜像的 endpoint 地址,内容如下所示:

1
2
3
4
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
debug: false
pull-image-on-create: true

配置完成后就可以使用 crictl 命令了。

获取 Pod 列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@slions-pc1 home]# crictl pods
POD ID CREATED STATE NAME NAMESPACE ATTEMPT RUNTIME
d6fe5b981c93a About an hour ago Ready coredns-6d56c8448f-mlj4r kube-system 0 (default)
b5869243675c1 About an hour ago Ready coredns-6d56c8448f-vtx97 kube-system 0 (default)
9545145b2d985 About an hour ago Ready kube-flannel-ds-4qwnd kube-system 0 (default)
71da443d2b400 About an hour ago Ready kube-controller-manager-slions-pc1 kube-system 0 (default)
17ae102739d90 3 hours ago Ready kube-proxy-dd4dg kube-system 0 (default)
71890ef89c6b6 3 hours ago Ready kube-apiserver-slions-pc1 kube-system 0 (default)
cad990849d280 3 hours ago Ready etcd-slions-pc1 kube-system 0 (default)
468ae24960d0c 3 hours ago Ready kube-scheduler-slions-pc1 kube-system 0 (default)
[root@slions-pc1 home]# crictl pods --name kube-apiserver-slions-pc1
POD ID CREATED STATE NAME NAMESPACE ATTEMPT RUNTIME
71890ef89c6b6 3 hours ago Ready kube-apiserver-slions-pc1 kube-system 0 (default)
[root@slions-pc1 home]# crictl pods --label app=flannel
POD ID CREATED STATE NAME NAMESPACE ATTEMPT RUNTIME
9545145b2d985 About an hour ago Ready kube-flannel-ds-4qwnd kube-system 0 (default)

获取镜像列表

1
2
3
4
5
6
7
8
9
10
11
[root@slions-pc1 home]# crictl images
IMAGE TAG IMAGE ID SIZE
docker.io/rancher/mirrored-flannelcni-flannel-cni-plugin v1.0.0 cd5235cd7dc26 4.68MB
quay.io/coreos/flannel v0.15.1 e6ea68648f0cd 21.7MB
registry.aliyuncs.com/google_containers/coredns 1.7.0 bfe3a36ebd252 14MB
registry.aliyuncs.com/google_containers/etcd 3.4.9-1 d4ca8726196cb 86.7MB
registry.aliyuncs.com/google_containers/kube-apiserver v1.19.0 1b74e93ece2f5 29.7MB
registry.aliyuncs.com/google_containers/kube-controller-manager v1.19.0 09d665d529d07 28MB
registry.aliyuncs.com/google_containers/kube-proxy v1.19.0 bc9c328f379ce 49.3MB
registry.aliyuncs.com/google_containers/kube-scheduler v1.19.0 cbdc8369d8b15 13.8MB
registry.aliyuncs.com/google_containers/pause 3.2 80d28bedfe5de 300kB

获取容器列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[root@slions-pc1 home]# crictl ps
CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID
ec086f05f1063 bfe3a36ebd252 About an hour ago Running coredns 0 d6fe5b981c93a
31e6e8d326192 bfe3a36ebd252 About an hour ago Running coredns 0 b5869243675c1
3fb571cc76791 e6ea68648f0cd About an hour ago Running kube-flannel 0 9545145b2d985
ea80243d5a9e6 09d665d529d07 About an hour ago Running kube-controller-manager 0 71da443d2b400
484541d3b39bb bc9c328f379ce 3 hours ago Running kube-proxy 0 17ae102739d90
6e688d8e85723 1b74e93ece2f5 3 hours ago Running kube-apiserver 0 71890ef89c6b6
2f5c0484bb866 d4ca8726196cb 3 hours ago Running etcd 0 cad990849d280
51e15fd5f4961 cbdc8369d8b15 3 hours ago Running kube-scheduler 0 468ae24960d0c

[root@slions-pc1 home]# crictl ps -s Running
CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID
ec086f05f1063 bfe3a36ebd252 About an hour ago Running coredns 0 d6fe5b981c93a
31e6e8d326192 bfe3a36ebd252 About an hour ago Running coredns 0 b5869243675c1
3fb571cc76791 e6ea68648f0cd About an hour ago Running kube-flannel 0 9545145b2d985
ea80243d5a9e6 09d665d529d07 About an hour ago Running kube-controller-manager 0 71da443d2b400
484541d3b39bb bc9c328f379ce 3 hours ago Running kube-proxy 0 17ae102739d90
6e688d8e85723 1b74e93ece2f5 3 hours ago Running kube-apiserver 0 71890ef89c6b6
2f5c0484bb866 d4ca8726196cb 3 hours ago Running etcd 0 cad990849d280
51e15fd5f4961 cbdc8369d8b15 3 hours ago Running kube-scheduler 0 468ae24960d0c

[root@slions-pc1 home]# crictl exec -it 3fb571cc76791 date
Thu Dec 16 12:45:35 UTC 2021

CLI对比

命令 Docker Containerd
docker crictl(推荐) ctr
查看容器列表 docker ps crictl ps ctr -n k8s.io c ls
查看容器详情 docker inspect crictl inspect ctr -n k8s.io c info
查看容器日志 docker logs crictl logs
容器内执行命令 docker exec crictl exec
挂载容器 docker attach crictl attach
显示容器资源使用情况 docker stats crictl stats
创建容器 docker create crictl create ctr -n k8s.io c create
启动容器 docker start crictl start ctr -n k8s.io run
停止容器 docker stop crictl stop
删除容器 docker rm crictl rm ctr -n k8s.io c del
查看镜像列表 docker images crictl images ctr -n k8s.io i ls
查看镜像详情 docker inspect crictl inspecti
拉取镜像 docker pull crictl pull ctr -n k8s.io i pull
推送镜像 docker push ctr -n k8s.io i push
删除镜像 docker rmi crictl rmi ctr -n k8s.io i rm
查看Pod列表 crictl pods
查看Pod详情 crictl inspectp
启动Pod crictl runp
停止Pod crictl stopp

crictl pods 列出的是 Pod 的信息,包括 Pod 所在的命名空间以及状态。crictl ps 列出的是应用容器的信息,而 docker ps 列出的是初始化容器(pause 容器)和应用容器的信息,初始化容器在每个 Pod 启动时都会创建,通常不会关注,所以 crictl 使用起来更简洁明了一些。

日志配置

docker 和 containerd 除了在常用命令上有些区别外,在容器日志及相关参数配置方面也存在一些差异。

当使用 Docker 作为 Kubernetes 容器运行时的时候,容器日志的落盘是由 Docker 来完成的,日志被保存在类似 /var/lib/docker/containers/<CONTAINER> 的目录下面,kubelet 会在 /var/log/pods/var/log/containers 下面创建软链接,指向容器日志目录下的容器日志文件。对应的日志相关配置可以通过配置文件进行指定,如下所示:

1
2
3
4
5
6
7
8
[root@k8s-master ~]# cat /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "3"
}
}

而当使用 containerd 作为 Kubernetes 容器运行时的时候,容器日志的落盘则由 kubelet 来完成了,被直接保存在 /var/log/pods/<CONTAINER> 目录下面,同时在 /var/log/containers 目录下创建软链接指向日志文件。同样日志配置则是通过 kubelet 参数中进行指定的,如下所示:

1
--container-log-max-files=10 --container-log-max-size="100Mi"
CATALOG
  1. 1. 环境描述
  2. 2. 变更前的工作
    1. 2.1. 维护节点
    2. 2.2. 停止服务
  3. 3. 安装containerd
  4. 4. 配置containerd
  5. 5. 修改 kubelet 配置
  6. 6. 恢复服务
  7. 7. 恢复调度
  8. 8. crictl
    1. 8.1. 获取 Pod 列表
    2. 8.2. 获取镜像列表
    3. 8.3. 获取容器列表
  9. 9. CLI对比
    1. 9.1. 日志配置