Containerd

前面知道了 Docker Engine 中就有 Containerd,只不过现在是将 Containerd 从 Docker Engine 里分离出来,作为一个独立的开源项目,目标是提供一个更加开放、稳定的容器运行基础设施。分离出来的 Containerd 将具有更多的功能,涵盖整个容器运行时管理的所有需求,提供更强大的支持。

Containerd 是一个工业级标准的容器运行时,它强调简单性、健壮性和可移植性,Containerd 可以负责干下面这些事情:

  • 管理容器的生命周期(从创建容器到销毁容器)
  • 拉取/推送容器镜像
  • 存储管理(管理镜像及容器数据的存储)
  • 调用 runc 运行容器(与 runc 等容器运行时交互)
  • 管理容器网络接口及网络

架构

Containerd 可用作 Linux 和 Windows 的守护程序,它管理其主机系统完整的容器生命周期,从镜像传输和存储到容器执行和监测,再到底层存储到网络附件等等。

上图是 Containerd 官方提供的架构图,可以看出 Containerd 采用的也是 C/S 架构,服务端通过 unix domain socket 暴露低层的 gRPC API 接口出去,客户端通过这些 API 管理节点上的容器,每个 Containerd 只负责一台机器。Pull 镜像,对容器的操作(启动、停止等),网络,存储都是由 Containerd 完成。具体运行容器则是由 runc 负责,实际上只要是符合 OCI 规范的容器都可以支持。

为了解耦,Containerd 将系统划分成了不同的组件,每个组件都由一个或多个模块协作完成,每一种类型的模块都以插件的形式集成到 Containerd 中,而且插件之间是相互依赖的。

总体来看,Containerd 可以分为三个大块:Storage、Metadata 和 Runtime。

安装

由于 Containerd 需要调用 runc,所以需要先安装 runc,不过 Containerd 提供了一个包含相关依赖的压缩包 cri-containerd-cni-${VERSION}.${OS}-${ARCH}.tar.gz,可以直接使用这个包来进行安装。

下载最新版 Containerd:

https://github.com/containerd/containerd/releases

我这里有 cri-containerd-cni 安装包的最新版本是 1.6.14,系统是 CentOS 7.9。

# 下载安装包
yum -y install wget
wget https://github.com/containerd/containerd/releases/download/v1.6.14/cri-containerd-cni-1.6.14-linux-amd64.tar.gz

# 直接解压到系统目录
tar -C / -xzf cri-containerd-cni-1.6.14-linux-amd64.tar.gz

Containerd 的默认配置文件为 /etc/containerd/config.toml,可以通过命令生成一个默认的配置:

mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml

启动 Containerd:

systemctl enable containerd --now

此时执行目录就能查看到 Containerd 的版本信息,和 docker 的用法类似:

ctr version

如果 Containerd 没有启动成功是无法看到 Server 端的版本信息的。

镜像操作

Docker CLI 工具提供了增强用户体验的功能,Containerd 同样也提供一个对应的 CLI 工具:ctr,不过 ctr 的功能没有 docker 完善,但是关于镜像和容器的基本功能都是有的。

拉取镜像

# 拉取镜像
ctr image pull docker.io/library/nginx:alpine

和直接使用 docker 不同,Containerd 拉取 Docker Hub 官方镜像 nginx:alpine,需要在镜像地址前面加上域名 docker.io

列出本地镜像

# 查看本地已有的镜像
ctr image ls

由于显示的内容比较多,可以使用 -q 只显示镜像名称。

查看本地镜像状态

# 查看本地已有的镜像状态
ctr image check

主要查看其中的 STATUS 字段,complete 表示镜像是完整可用的状态。

给镜像打标签

# 给镜像指定新的标签
ctr image tag docker.io/library/nginx:alpine hub.ezops.cn/nginx:alpine

这样做的用处在于一个镜像相当于有了多个名称,便于规范命名和使用。

删除镜像

# 删除现有镜像
ctr image rm docker.io/library/nginx:alpine

如果一个镜像有多个 Tag,删除互不影响。同时加上 --sync 选项则可以同步删除镜像和所有相关的资源。

将镜像挂载到目录

# 将镜像挂载到目录
ctr image mount hub.ezops.cn/nginx:alpine /data

此时就可以在宿主机上进入挂载目录查看镜像内部的文件信息。

卸载挂载的镜像

# 卸载挂载的镜像
ctr image unmount /data

需要注意的是不能在挂载的目录中执行该命令,会提示:device or resource busy

将镜像导出为压缩包

# 将镜像导出为压缩包
ctr image export nginx.tar.gz hub.ezops.cn/nginx:alpine

从压缩包导入镜像

# 从压缩包导入镜像
ctr image import nginx.tar.gz

直接导入可能会出现类似于 ctr: content digest sha256:xxxxxx not found 的错误,要解决这个办法需要 pull 所有平台镜像。例如:

ctr image pull --all-platforms docker.io/library/nginx:alpine
ctr image export --all-platforms nginx.tar.gz docker.io/library/nginx:alpine
ctr image rm docker.io/library/nginx:alpine
ctr image import nginx.tar.gz 

容器操作

容器相关操作可以通过 ctr container 获取。

创建容器

# 创建容器
ctr container create docker.io/library/nginx:alpine nginx-demo

列出容器

# 列出容器
ctr image ls

查看容器详细信息

# 查看容器详细信息
ctr container info nginx-demo

删除容器

# 删除容器
ctr container rm nginx-demo

除了使用 rm 子命令之外,也可以使用 delete 或者 del 删除容器。

任务

通过 container create 命令创建的容器并没有处于运行状态,只是一个静态的容器。

一个 container 对象只是包含了运行一个容器所需的资源及相关配置数据,表示 namespaces、rootfs 和容器的配置都已经初始化成功了,只是用户进程还没有启动。

一个容器真正运行起来是由 Task 任务实现的,Task 可以为容器设置网卡,还可以配置工具来对容器进行监控等。

对于 CentOS 系统如果直接启动容器可能会出现报错:

ctr: failed to create shim task: OCI runtime create failed: unable to retrieve OCI runtime error (open /run/containerd/io.containerd.runtime.v2.task/defaule or directory): runc did not terminate successfully: exit status 127: unknown

原因是系统自带的 libseccomp 包版本低了,需要卸载升级一下:

rpm -e libseccomp-2.3.1-4.el7.x86_64 --nodeps
wget https://vault.centos.org/centos/8/BaseOS/x86_64/os/Packages/libseccomp-2.5.1-1.el8.x86_64.rpm
rpm -ivh libseccomp-2.5.1-1.el8.x86_64.rpm 
rpm -qa | grep libseccomp

启动容器

# 启动容器
ctr task start -d nginx-demo

查看运行中的容器

# 查看运行中的容器
ctr task ls

进入容器

# 进入容器
ctr task exec --exec-id 0 -t nginx-demo sh

不过这里需要注意必须要指定 --exec-id 参数,这个 id 可以随便写,只要唯一就行。

暂停和恢复容器

# 暂停容器
ctr task pause nginx-demo

# 恢复容器
ctr task resume nginx-demo

杀掉和删除容器

# 杀掉容器
ctr task kill nginx-demo

# 删除容器
ctr task rm nginx-demo

默认没有 stop 容器,kill 之后就是 STOPED 状态。

查看容器 PID 信息

# 查看容器使用的 PID 信息
ctr task ps nginx-demo

命名空间

Containerd 是支持命名空间的概念的,比如查看命名空间:

ctr ns ls

如果不指定,ctr 默认使用的是 default 空间。同样也可以使用 ns create 命令创建一个命名空间:

ctr ns create demo

使用 remove 或者 rm 可以删除 namespace:

ctr ns rm demo

有了命名空间后就可以在操作资源的时候指定 namespace,比如查看 demo 命名空间的镜像,可以在操作命令后面加上 -n test 选项,注意需要放在前面:

ctr -n demo image ls

Docker 其实也是默认调用的 containerd,事实上 Docker 使用的 containerd 下面的命名空间默认是 moby,而不是 default,所以假如我们有用 docker 启动容器,那么我们也可以通过 ctr -n moby 来定位下面的容器:

ctr -n moby container ls

同样 Kubernetes 下使用的 containerd 默认命名空间是 k8s.io

ctr -n k8s.io container ls

结语

ctr 工具相对于用习惯了 Docker 的朋友还是有些不习惯,没关系,下一章节会聊聊 nerdctl,有了它就能够让我们操作 Containerd 也能像操作 Docker 一样变得简单。