Containerd 是一个工业级标准的容器运行时,它强调简单性、健壮性和可移植性。Containerd 可以在宿主机中管理完整的容器生命周期:容器镜像的传输和存储、容器的执行和管理、存储和网络等。详细点说,Containerd 负责干下面这些事情:
- 调用 runC 运行容器(与 runC 等容器运行时交互)
Containerd 被设计成嵌入到一个更大的系统中,而不是直接由开发人员或终端用户使用。
Containerd 被设计成嵌入到一个更大的系统中,而不是直接由开发人员或终端用户使用。所以 containerd 具有宏大的愿景(此图来自互联网):
支持 containerd 和 runC 的组合了,相信接下来会有更多类似的容器平台出现。
- 支持(runtime 和 image spec)
- 性能的定义良好的容器核心功能
- 插件式的扩展和重用
里提到了通过,他们将 containerd 设计成了 snapshotter 的模式,这也使得 containerd 对于 overlay 文件系、snapshot 文件系统的支持比较好。Metadata 和 runtime 的三大块划分非常清晰,通过抽象出 events 的设计,添加和配置 API。这样做的好处无疑是巨大的,保留最小功能集合的纯粹和高效,而将更多的复杂性及灵活性交给了插件及上层系统。
调用 runC,所以在安装 containerd 之前请先安装 runC。RunC 的安装请参考笔者博文《》。
,当前的最新版本为 v1.1.0。
文件,通过上面的命令它们被安装到了 /usr/local/bin 目录中:
- 调用 containerd 启动的 docker 容器。
生成 containerd 配置文件配置文件默认为 /etc/containerd/config.toml。这里我们可以通过命令来生成一个默认的配置文件:
文件 containerd.service:
内容如下:
[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
用法与 runC 非常相似,所以这里不再赘述。Containerd 还提供了 client package 用于在代码中集成 containerd 客户端,下面的 demo 就采用 golang 和 client package 在代码中访问 containerd 服务来创建并运行容器!
文件,内容如下:
<span style="color: #800000">"<span style="color: #800000">log<span style="color: #800000">"
<span style="color: #800000">"<span style="color: #800000">github.com/containerd/containerd<span style="color: #800000">"<span style="color: #000000">
)
func main() {
<span style="color: #0000ff">if err := redisExample(); err !=<span style="color: #000000"> nil {
log.Fatal(err)
}
}
func redisExample() error {
client,err := containerd.New(<span style="color: #800000">"<span style="color: #800000">/run/containerd/containerd.sock<span style="color: #800000">"<span style="color: #000000">)
<span style="color: #0000ff">if err !=<span style="color: #000000"> nil {
<span style="color: #0000ff">return<span style="color: #000000"> err
}
defer client.Close()
<span style="color: #0000ff">return<span style="color: #000000"> nil
}
代码中使用默认的 containerd 套接字创建了一个客户端对象。因为 containerd daemon 通过 gRPC 协议提供服务,所以我们需要创建一个用于调用客户端方法的上下文。在创建上下文之后,我们还应该为我们的 demo 设置一个 namespace,创建单独的 namespace 可以与用户的资源进行隔离以免发生冲突:
方法从 dockerhub 上拉取 redis 镜像,这个方法支持 Opts 模式,所以我们可以指定 containerd.WithPullUnpackso 让下载完成后直接把镜像解压缩为一个 snapshotter 作为即将运行的容器的 rootfs。
方法可以使用默认的 OCI runtime spec 直接创建容器对象。当然,也可以通过 Opts 模式的参数修改默认值:
添加了 defer container.Delete 调用来删除容器以及它的快照。
》一文中介绍的 "created"。这意味着 namespaces、rootfs 和容器的配置都已经初始化成功了,只是用户进程(这里是 redis-server)还没有启动。在这个时机,我们可以为容器设置网卡,还可以配置工具来对容器进行监控等。
<span style="color: #0000ff">return<span style="color: #000000"> err
}
status := <-<span style="color: #000000">exitStatusC
code,exitedAt,err :=<span style="color: #000000"> status.Result()
<span style="color: #0000ff">if err !=<span style="color: #000000"> nil {
<span style="color: #0000ff">return<span style="color: #000000"> err
}
fmt.Printf(<span style="color: #800000">"<span style="color: #800000">redis-server exited with status: %d\n<span style="color: #800000">",code)
。下面编译 demo 代码并运行: