containerd基础概念
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 | tar -zxf containerd-1.4.4-linux-amd64.tar.gz -C /usr/local/ |
创建系统服务
1 | cat <<EOF | sudo tee /usr/lib/systemd/system/containerd.service |
配置介绍:
Delegate : 这个选项允许 Containerd 以及运行时自己管理自己创建的容器的 cgroups。
如果不设置这个选项,systemd 就会将进程移到自己的 cgroups 中,从而导致 Containerd 无法正确获取容器的资源使用情况。
KillMode : 这个选项用来处理 Containerd 进程被杀死的方式。
默认情况下,systemd 会在进程的 cgroup 中查找并杀死 Containerd 的所有子进程,这肯定不是我们想要的。KillMode字段可以设置的值如下。
我们需要将 KillMode 的值设置为 process,这样可以确保升级或重启 Containerd时不杀死现有的容器。
control-group(默认值):当前控制组里面的所有子进程,都会被杀掉
process:只杀主进程
mixed:主进程将收到 SIGTERM 信号,子进程收到 SIGKILL 信号
none:没有进程会被杀掉,只是执行服务的 stop 命令。
启动服务
1 | systemctl enable containerd |
Container命令ctr,crictl的用法
containerd 相比于docker , 多了namespace概念, 每个image和container
都会在各自的namespace下可见, 目前k8s会使用k8s.io 作为命名空间
image
查看ctr image
1 | ctr image list |
镜像标记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 | ctr -n k8s.io run --null-io --net-host -d \ |
-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 | FROM alpine |
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 | ctr -n k8s.io tasks kill -a -s 9 {id} |
容器日志
注意: 容器默认使用fifo创建日志文件,如果不读取日志文件,会因为fifo容量导致业务运行阻塞
如要创建日志文件,建议如下方式创建:
1 | ctr -n k8s.io run --log-uri file:///var/log/xx.log … |
ctr命令与docker命令
1 | ctr image ls |
1 | ctr image pull |
1 | ctr image tag |
1 | ctr image push |
1 | ctr image import 不支持压缩 |
1 | ctr run 镜像名 容器名称 |
1 | ctr task ls 会显示stop的task和running的task |
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 | ctr images pull --plain-http --user admin:123456 192.168.137.101:5000/debug/registry:latest |