3

Kubernetes runtime从Docker迁移到containerd探索 - DockOne.io

 3 years ago
source link: http://dockone.io/article/2434301
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Kubernetes runtime从Docker迁移到containerd探索


Kubernetes宣布在1.20版本之后将弃用Docker作为容器运行时,在2021年末发布的1.23版本中将彻底移除dockershim组件。Dockershim是kubelet内置的一个组件,功能是使Kubernetes能够通过CRI(Container Runtime Interface)操作Docker。一旦Docker有任何的功能特性变更,dockershim 代码必须加以改动来保证能够继续和Docker通信。另外,Docker的底层运行时是containerd,而containerd自身是可以支持CRI的,也就是说Kubernetes可以绕过Docker通过CRI直接与containerd通信,这也是Kubernetes社区希望弃用dockershim的原因。

Containerd在1.0版本中虽然考虑了CRI,但是它将CRI-Containerd作为一个独立组件存在的,即:Kubernetes需要先通过CRI接口调用CRI-Containerd,再由这个组件去调用containerd。在Containerd 1.1版本之后对该特性做了重新的设计,它将CRI-plugin内嵌在containerd中,以此来达到与containerd通信的目的,调用链路更短了。Containerd 1.1支持Kubernetes 1.10及以上版本作为容器运行时,并且支持Kubernetes的全部特性。

下图说明了Docker和containerd作为容器运行时的工作原理。由此可以看出,如果之前使用Docker作为容器运行时,那么迁移到containerd是一个相对容易的选择,而且containerd具有更好的性能和更低的成本。

接下来,主要介绍如何将Kubernetes的运行时从Docker迁移到containerd,并且迁移之后使用上的一些变化。

Kubernetes运行时从Docker迁移到containerd

操作系统:SUSE 12 SP5

Kernel版本:4.12.14-120

Kubernetes版本:v1.14.0

Docker版本:docker-ee-18.09.9

Containerd版本:1.4.4

查看当前节点运行时信息

kubectl get node -o wide


可以看到,当前所有节点使用的运行时都是Docker,通过systemctl status containerd可以看到containerd服务默认也是启动的。使用如下命令列出containerd的命名空间。
ctr namespaces list

可以看到有一个moby命名空间,这也是Docker服务默认使用的命名空间。
ctr -namespace moby container list

使用如上命令列出Moby命名空间下运行的所有容器,结果如下图,可以看到跟docker ps输出的容器个数相同。

驱逐节点并停止节点上的Docker和kubelet服务

下面以节点spk8mgr03为例说明Docker到containerd的迁移流程。
kubectl drain spk8mgr03 --ignore-daemonsets --delete-local-data --force

systemctl stop kubelet

systemctl stop docker

卸载Docker(该步骤是可选的,为了排除测试过程中Docker的干扰,这里选择卸载):
zypper rm -y docker-ee docker-ee-cli containerd.io

安装配置containerd

下载containerd并解压安装:
wget https://github.com/containerd/containerd/releases/download/v1.4.4/cri-containerd-cni-1.4.4-linux-amd64.tar.gz

tar -C / -xzvf cri-containerd-cni-1.4.4-linux-amd64.tar.gz

解压后的文件包括如下内容:
/

/etc/

/etc/systemd/

/etc/systemd/system/

/etc/systemd/system/containerd.service

/etc/crictl.yaml

/etc/cni/

/etc/cni/net.d/

/etc/cni/net.d/10-containerd-net.conflist

/usr/

/usr/local/

/usr/local/bin/

/usr/local/bin/containerd

/usr/local/bin/containerd-shim

/usr/local/bin/crictl

/usr/local/bin/containerd-shim-runc-v2

/usr/local/bin/critest

/usr/local/bin/containerd-shim-runc-v1

/usr/local/bin/ctr

/usr/local/sbin/

/usr/local/sbin/runc

/opt/

/opt/containerd/

/opt/containerd/cluster/

/opt/containerd/cluster/gce/

/opt/containerd/cluster/gce/env

/opt/containerd/cluster/gce/cni.template

/opt/containerd/cluster/gce/configure.sh

/opt/containerd/cluster/gce/cloud-init/

/opt/containerd/cluster/gce/cloud-init/node.yaml

/opt/containerd/cluster/gce/cloud-init/master.yaml

/opt/containerd/cluster/version

/opt/cni/

/opt/cni/bin/

/opt/cni/bin/bandwidth

/opt/cni/bin/host-device

/opt/cni/bin/flannel

/opt/cni/bin/static

/opt/cni/bin/loopback

/opt/cni/bin/dhcp

/opt/cni/bin/ptp

/opt/cni/bin/ipvlan

/opt/cni/bin/vlan

/opt/cni/bin/host-local

/opt/cni/bin/firewall

/opt/cni/bin/tuning

/opt/cni/bin/sbr

/opt/cni/bin/bridge

/opt/cni/bin/portmap

/opt/cni/bin/macvlan

启动并配置containerd:
systemctl start containerd

systemctl enable containerd

mkdir -p /etc/containerd

containerd config default > /etc/containerd/config.toml

config.toml文件内容如下,注意修改sandbox_image参数:
version = 2

root = "/var/lib/containerd"

state = "/run/containerd"

plugin_dir = ""

disabled_plugins = []

required_plugins = []

oom_score = 0

[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 = "k8s.gc.io/pause:3.1"

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]

[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

修改完配置后,重启containerd服务:
systemctl restart containerd

测试containerd:
ctr images pull docker.io/library/nginx:alpine

看到输出done,说明containerd运行正常。

配置crictl

crictl默认与Docker进行通信,如果希望crictl直接与containerd通信,需要修改crictl的配置文件,在/etc/crictl.yaml加入如下内容:
runtime-endpoint: unix:///run/containerd/containerd.sock

注:安装containerd时解压好的文件默认已经添加了该配置。

测试一下cri插件是否可用:
crictl pull docker.io/library/nginx:alpine

crictl images

配置kubelet

kubelet默认使用docker作为容器运行时,如果希望使用containerd,需要修改kubelet的配置文件。编辑/etc/systemd/system/kubelet.service.d/10-kubeadm.conf文件,添加如下内容:
[Service]

Environment="KUBELET_EXTRA_ARGS=--container-runtime=remote --runtime-request-timeout=15m --container-runtime-endpoint=unix:///run/containerd/containerd.sock"

重启kubelet服务:
systemctl daemon-reload

systemctl restart kubelet
kubectl get node -o wide


可以看到spk8mgr03节点的容器运行时已经变成了containerd,这时节点还是不可调度状态,执行如下命令将其改为可调度状态。
kubectl uncordon spk8mgr03

此时再查看containerd的命名空间,会发现多了一个k8s.io的命名空间,而且所有的容器都会运行在该命名空间中,而Moby命名空间中没有任何容器运行了。

至此,我们成功完成了容器运行时从Docker到containerd的迁移,集群中的其他节点可以重复上述步骤完成全部迁移。

Containerd和Docker使用对比

当使用Docker作为容器运行时,系统管理员有时会登录Kubernetes节点执行Docker命令来收集系统或者应用信息,这些命令都是通过docker CLI实现的。而迁移到containerd之后,可以通过containerd CLI工具ctr来实现与containerd的交互,但是从使用便捷性和功能性上考虑,更推荐使用crictl作为troubleshooting的工具。Crictl是类似于docker CLI的客户端调试工具,并且适用于所有与CRI兼容的容器运行时,包括Docker。下面将围绕镜像、容器、Pod方面比较一下Docker、ctr、crictl常用命令的使用区别。

镜像相关功能

容器相关功能

这里要特别说明一下,通过ctr containers create创建容器后,只是一个静态的容器,容器中的用户进程并没有启动,所以还需要通过ctr task start来启动容器进程。当然,也可以用ctr run的命令直接创建并运行容器。在进入容器操作时,与docker不同的是,必须在ctr task exec命令后指定--exec-id参数,这个id可以随便写,只要唯一就行。另外,ctr没有stop容器的功能,只能暂停(ctr task pause)或者杀死(ctr task kill)容器。

Pod相关功能

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

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

Kubernetes弃用Docker这一决定可能对从事相关工作的人员来说有些措手不及,但其实无需特别担心。对于Kubernetes的终端用户来说,这仅仅是一个后端容器运行时的更改,从使用方面来说几乎感觉不到任何区别;对于应用开发/运维人员来说,依旧可以继续使用Docker来构建镜像,以相同的方式将镜像推送到registry,并将这些镜像部署到Kubernetes环境中;对于Kubernetes集群管理员来说,只需要将Docker切换成其它的容器运行时(比如containerd),并将节点troubleshooting工具从docker CLI切换到crictl即可。

原文链接:https://mp.weixin.qq.com/s/0F-Gj0pAGqXinr-sg0vKYg

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK