containerd的前世今生,与docker的关系、如何部署containerd、如何接入k8s中

OCI

OCI(Open Container Initiative)开放容器倡议,OCI由docker以及其他容器行业领导者创建于2015年,目前主要有两个标准:容器运行时标准(runtime-spec)和容器镜像标准(image-spec)

这两个标准通过OCI runtime filesytem bundle的标准格式连接在一起,OCI镜像可以通过工具转换成bundle,然后 OCI 容器引擎能够识别这个bundle来运行容器

CRI

CRI(Container Runtime Interface)容器运行时接口,CRI是kubernetes推出的一个标准,推出标准可见其在容器编排领域的地位

runc、containerd 等运行时都去支持此接口。

runc

runC的前身是docker的libcontainer项目,在libcontainer的基础上做了封装,捐赠给OCI的一个符合标准的runtime实现,docker引擎内部也是基于runC构建的

runC只做一件事情就是运行容器,提供创建和运行容器的CLI(command-line interface)工具, runC直接与容器所依赖的cgroup/namespace linux kernel等进行交互,

负责为容器配置cgroup/namespace等启动容器所需的环境,创建启动容器的相关进程

shim

中文意思 : 楔子

例如: dockershim和containershim

containerd-shim

containerd-shim进程由containerd进程拉起,即containerd进程是containerd-shim的父进程,容器进程由containerd-shim进程拉起,

这样的优点比如升级,重启docker或者containerd 不会影响已经running的容器进程,而假如这个父进程就是containerd,那每次containerd挂掉或升级,整个宿主机上所有的容器都得退出了. 而引入了 containerd-shim 就规避了这个问题(当containerd 退出或重启时, shim 会 re-parent 到 systemd 这样的 1 号进程上)

部署containerd服务

安装runc

yum -y install runc

如果未安装则会ctr run 容器时会提示如下:

ctr: OCI runtime create failed: unable to retrieve OCI runtime error (open /run/containerd/io.containerd.runtime.v2.task/default/nginx/log.json: no such file or directory): exec: “runc”: executable file not found in $PATH: unknown

安装containerd

二进制分发

1
2
3
tar -zxf containerd-1.4.4-linux-amd64.tar.gz -C /usr/local/
mkdir -p /etc/containerd
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
cat <<EOF | sudo tee /usr/lib/systemd/system/containerd.service

[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target

[Service]
ExecStartPre=/sbin/modprobe overlay
ExecStart=/usr/local/bin/containerd
Delegate=yes
KillMode=process
LimitNOFILE=1048576
# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNPROC=infinity
LimitCORE=infinity

[Install]
WantedBy=multi-user.target

EOF

配置介绍:

Delegate : 这个选项允许 Containerd 以及运行时自己管理自己创建的容器的 cgroups。

如果不设置这个选项,systemd 就会将进程移到自己的 cgroups 中,从而导致 Containerd 无法正确获取容器的资源使用情况。

KillMode : 这个选项用来处理 Containerd 进程被杀死的方式。

默认情况下,systemd 会在进程的 cgroup 中查找并杀死 Containerd 的所有子进程,这肯定不是我们想要的。KillMode字段可以设置的值如下。

我们需要将 KillMode 的值设置为 process,这样可以确保升级或重启 Containerd时不杀死现有的容器。

control-group(默认值):当前控制组里面的所有子进程,都会被杀掉

process:只杀主进程

mixed:主进程将收到 SIGTERM 信号,子进程收到 SIGKILL 信号

none:没有进程会被杀掉,只是执行服务的 stop 命令。

启动服务

1
2
3
systemctl enable containerd
systemctl start containerd
systemctl status containerd

Container命令ctr,crictl的用法

containerd 相比于docker , 多了namespace概念, 每个image和container
都会在各自的namespace下可见, 目前k8s会使用k8s.io 作为命名空间

image

查看ctr image

1
2
3
ctr image list
ctr i list
ctr i ls

镜像标记tag

1
ctr -n k8s.io i tag registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.2 k8s.gcr.io/pause:3.2

注意: 若新镜像reference 已存在, 需要先删除新reference, 或者如下方式强制替换

1
ctr -n k8s.io i tag --force registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.2 k8s.gcr.io/pause:3.2

删除镜像

1
ctr -n k8s.io i rm k8s.gcr.io/pause:3.2

拉取镜像

1
ctr -n k8s.io i pull -k k8s.gcr.io/pause:3.2

推送镜像

1
ctr -n k8s.io i push -k k8s.gcr.io/pause:3.2

导出镜像

1
ctr -n k8s.io i export pause.tar k8s.gcr.io/pause:3.2

导入镜像

1
ctr -n k8s.io i import pause.tar

不支持 build,commit 镜像

运行容器

例子:

1
2
3
4
5
6
7
8
9
ctr -n k8s.io run --null-io --net-host -d \
–env PASSWORD=$drone_password \
–mount type=bind,src=/etc,dst=/host-etc,options=rbind:rw \
–mount type=bind,src=/root/.kube,dst=/root/.kube,options=rbind:rw \
$image sysreport bash /sysreport/run.sh


--null-io: 将容器内标准输出重定向到/dev/null
--net-host: 主机网络

-d:
当task执行后就进行下一步shell命令,如没有选项,则会等待用户输入,并定向到容器内建立容器,ctr run 必须提供容器ID

1
ctr c delete down

ctr run 镜像全名称 容器名称 命令 (相对于docker的建立:docker run –name=容器名称 镜像名 命令)

1
例如:ctr run -t --rm docker.io/huisebug/down:latest down ls

建立容器,同时运行了task,并后台运行-d

1
ctr run -d docker.io/library/nginx:latest nginx

注意点
ctr run 时,最后如果传递了命令,此命令会覆盖Dockerfile中 ENTRYPOINT 和 CMD; 最后传递命令不受影响,和docker run是有区别的
Dockerfile如下

1
2
3
4
FROM alpine
WORKDIR /etc
ENTRYPOINT ["ls"]
CMD ["/"]
1
ctr run --rm --net-host docker.io/huisebug/containerd-entrypoint:v1 containerd-entrypoint ls

上面执行结果为:列出/etc目录下所有文件

1
ctr run --rm --net-host docker.io/huisebug/containerd-entrypoint:v1 containerd-entrypoint pwd

上面执行结果为:获取当前工作目录,因在Dockerfile中声明了WORKDIR,所以结果为/etc

1
ctr run --rm --net-host docker.io/huisebug/containerd-entrypoint:v1 containerd-entrypoint

上面执行结果为:列出/根目录下所有文件

过程总结

容器container是分配和附加资源的元数据对象。其实就是相当于docker create,此时容器并未运行
任务task是系统上正在运行的实时过程。每次运行ctr t start会启动容器
(相当于docker start)后都应删除任务ctr t kill (相当于docker stop),而容器可以多次使用,更新和查询。
一般我们都是直接docker run 就是相当于执行了ctr run,就是执行了ctr c create 创建容器后马上执行ctr t start
如果ctr run (docker run)时没有加上-d参数后台运行,执行后会在终端进行输出。中断终端后task结束
命令为ctr t ls 相当于使用docker ps -a(会列出所有的容器)
docker rm就是相当于执行了ctr t rm和ctr c rm删除task和容器,一般删除容器,对应的task也会删除

进入容器

进入容器–exec-id 0必须填写,

1
ctr t exec --exec-id 0 -t nginx sh

停止容器,

需要先停止容器内的task, 再删除容器

1
2
ctr -n k8s.io tasks kill -a -s 9 {id}
ctr -n k8s.io c rm {id}

容器日志

注意: 容器默认使用fifo创建日志文件,如果不读取日志文件,会因为fifo容量导致业务运行阻塞

如要创建日志文件,建议如下方式创建:

1
ctr -n k8s.io run --log-uri file:///var/log/xx.log …

ctr命令与docker命令

1
2
ctr image ls
docker images
1
2
ctr image pull
docker pull
1
2
ctr image tag
docker tag
1
2
ctr image push
docker push
1
2
ctr image import 不支持压缩
docker load <
1
2
ctr run 镜像名 容器名称
docker run --name=容器名称 镜像名
1
2
ctr task ls 会显示stop的task和running的task
docker ps -a

crictl用法

crictl 工具 是为k8s使用containerd而制作的, 其他非k8s的创建的crictl是无法看到和调试的, 也就是说用ctr run 运行的容器无法使用crictl 看到

crictl 使用命名空间 k8s.io.

cri plugin区别对待pod和container

ps: 列出在k8s.io 命名空间下的业务容器

pods: 列出在k8s.io 命名空间下的sandbox容器,在k8s里,通常是pause容器

logs: 打印业务容器日志

create: 创建容器,这里需要先创建sandbox,获取sandbox容器的id后,再用此id创建业务容器

inspect: 列出业务容器状态

inspectp: 列出sandbox容器状态

crictl 是cri的控制命令
因为暂时还未完全舍弃docker,所以docker.sock是排在第一位的,指定使用containerd.sock

1
crictl -r unix:///run/containerd/containerd.sock  images

ctr指定命名空间必须跟在ctr命令后面

crictl所做的操作是在ctr 的 k8s.io命名空间下
例如镜像, crictl -r unix:///run/containerd/containerd.sock images 和 ctr -n k8s.io i ls
docker所做的操作是在ctr的moby命名空间下,例如docker正在运行的容器,docker ps 和 ctr -n moby t ls,镜像不是一致的
crictl使用镜像docker.io仓库的镜像不用写全名

containerd配置文件/etc/containerd/config.toml所做的修改是给k8s(或者说是crictl命令使用)使用,可以利用下面的命令看到当前containerd基于k8s的配置

1
crictl -r unix:///run/containerd/containerd.sock info | jq

清理k8s集群中未使用的镜像

1
crictl -r unix:///run/containerd/containerd.sock rmi  --prune 

docker在containerd的命名空间

除了k8s有命名空间以外,containerd也支持命名空间。docker创建的默认的都在moby空间,而k8s默认是k8s.io这个空间下面,不同空间的容器互相隔离。

1
ctr -n moby c ls

上面只会显示正在运行的docker容器,没有运行的不会显示

拉取dockerhub镜像

containerd要求必须先pull镜像,并且镜像需要写全镜像信息,包含仓库地址、用户、镜像tag

1
例如: ctr i pull docker.io/huisebug/down:latest

docker官方维护的镜像,用户名是library,比如nginx、redis

1
ctr i pull docker.io/library/nginx:latest

login自建仓库

ctr 不支持http,必须使用https, 所有需要添加 –plain-http 参数

1
2
ctr images pull --plain-http --user admin:123456 192.168.137.101:5000/debug/registry:latest
ctr images pull --plain-http 192.168.137.101:5000/debug/registry:latest