containerd 支持使用其定义的大多数接口来扩展其功能。这包括自定义运行时、快照器、内容存储,甚至添加gRPC接口。
智能客户端模型
containerd 采用智能客户端架构,这意味着守护进程不需要的任何功能都由客户端完成。这包括大多数高级交互,如创建容器规范、与映像注册主机交互或从 tar 中加载映像。
containerd 的 Go 客户端允许用户访问许多扩展点,从创建容器时创建自己的选项到解析镜像注册主机名称等许多扩展点。
外部插件
外部插件允许使用正式发布的版本来扩展 containerd的功能,而无需重新编译守护进程来添加插件。
containerd 允许通过两种方法进行扩展:
- 通过containerd PATH 中的二进制文件
- 通过配置containerd来代理另一个 gRPC 服务
V2 运行时
containerd 支持多个容器运行时。每个容器可以调用不同的运行时。
使用容器运行时接口 (CRI) 插件时,可以在未指定运行时的情况下运行容器时,将使用配置的默认运行时。另外,在创建容器时,也可以在通过选择CRI gRpc使用的镜像处理器来显示声明不同名字的运行时。
当 ctr
或 nerdctl
等客户端创建容器时,它可以选择性地指定要使用的运行时和选项。如果没有指定运行时,containerd 将使用其默认运行时。
containerd 调用 v2 运行时作为系统上的二进制文件、二进制文件用于启动containerd 的 shim 进程。这反过来又允许containerd 使用二进制文件返回的运行时 shim api 来启动和管理这些容器。
有关运行时和 shim 的更多详情,包括如何调用和配置它们、请参阅运行时 v2 文档
代理插件
代理插件使用 containerd 的配置文件进行配置,并在启动 containerd 时与内部插件一起加载。这些插件使用本地套接字连接到 containerd,该套接字作为 containerd 的 gRPCAPI 服务之一连接到 containerd。每个插件都配置了类型和名称,就像内部插件一样,每个插件都配置了类型和名称。
配置
更新 containerd 配置文件,默认配置文件位于/etc/containerd/config.toml
。添加一个 [proxy_plugins]
部分和一个 [proxy_plugins.myplugin]
部分。address
必须指向 containerd 进程可以访问的本地 socket 文件。目前支持的类型是 snapshot
、content
和 diff
。
version = 2
[proxy_plugins]
[proxy_plugins.customsnapshot]
type = "snapshot"
address = "/var/run/mysnapshotter.sock"
实现
实现代理插件就像实现服务的gRPC API一样简单。要在 Go 中实现代理插件,请查看以下 Go 文档内容存储服务、快照服务 和 diff 服务。
下面的示例创建了一个快照插件二进制文件,可用于的任何实现
containerd的快照器接口
package main
import (
"fmt"
"net"
"os"
"google.golang.org/grpc"
snapshotsapi "github.com/containerd/containerd/v2/api/services/snapshots/v1"
"github.com/containerd/containerd/v2/contrib/snapshotservice"
"github.com/containerd/containerd/v2/plugins/snapshots/native"
)
func main() {
// 提供要监听的 unix 地址,这将是 `proxy_plugin`
// 配置中的 `address配置中的地址。
// root 将用于存储快照。
if len(os.Args) < 3 {
fmt.Printf("invalid args: usage: %s <unix addr> <root>\n", os.Args[0])
os.Exit(1)
}
// 创建 gRPC 服务器
rpc := grpc.NewServer()
// 配置自定义快照器,本示例使用本地快照器和一个根目录
//自定义的快照器将比已包含的快照器有用得多。
// https://godoc.org/github.com/containerd/containerd/snapshots#Snapshotter
sn, err := native.NewSnapshotter(os.Args[2])
if err != nil {
fmt.Printf("error: %v\n", err)
os.Exit(1)
}
// 将快照器转换为 gRPC 服务、
// github.com/containerd/containerd/contrib/snapshotservice中的示例
service := snapshotservice.FromSnapshotter(sn)
// 向gRPC服务器注册服务
snapshotsapi.RegisterSnapshotsServer(rpc, service)
// 监听和提供服务
l, err := net.Listen("unix", os.Args[1])
if err != nil {
fmt.Printf("error: %v\n", err)
os.Exit(1)
}
if err := rpc.Serve(l); err != nil {
fmt.Printf("error: %v\n", err)
os.Exit(1)
}
}
使用前面的配置和示例,您可以运行快照插件
# 在终端中启动插件
$ go run ./main.go /var/run/mysnapshotter.sock /tmp/snapshots
# 在另一个终端中使用ctr
$ CONTAINERD_SNAPSHOTTER=customsnapshot ctr images pull docker.io/library/alpine:latest
$ tree -L 3 /tmp/snapshots
/tmp/snapshots
|-- metadata.db
`-- snapshots
`-- 1
|-- bin
|-- dev
|-- etc
|-- home
|-- lib
|-- media
|-- mnt
|-- proc
|-- root
|-- run
|-- sbin
|-- srv
|-- sys
|-- tmp
|-- usr
`-- var
18 directories, 1 file
内置插件
containerd 在内部使用插件,以确保内部实现与外部插件松耦合、稳定,并与外部插件一视同仁。要查看所有插件,请使用 ctr plugins ls
。
$ ctr plugins ls
TYPE ID PLATFORMS STATUS
io.containerd.content.v1 content - ok
io.containerd.snapshotter.v1 btrfs linux/amd64 ok
io.containerd.snapshotter.v1 aufs linux/amd64 error
io.containerd.snapshotter.v1 native linux/amd64 ok
io.containerd.snapshotter.v1 overlayfs linux/amd64 ok
io.containerd.snapshotter.v1 zfs linux/amd64 error
io.containerd.metadata.v1 bolt - ok
io.containerd.differ.v1 walking linux/amd64 ok
io.containerd.gc.v1 scheduler - ok
io.containerd.service.v1 containers-service - ok
io.containerd.service.v1 content-service - ok
io.containerd.service.v1 diff-service - ok
io.containerd.service.v1 images-service - ok
io.containerd.service.v1 leases-service - ok
io.containerd.service.v1 namespaces-service - ok
io.containerd.service.v1 snapshots-service - ok
io.containerd.runtime.v1 linux linux/amd64 ok
io.containerd.runtime.v2 task linux/amd64 ok
io.containerd.monitor.v1 cgroups linux/amd64 ok
io.containerd.service.v1 tasks-service - ok
io.containerd.internal.v1 restart - ok
io.containerd.grpc.v1 containers - ok
io.containerd.grpc.v1 content - ok
io.containerd.grpc.v1 diff - ok
io.containerd.grpc.v1 events - ok
io.containerd.grpc.v1 healthcheck - ok
io.containerd.grpc.v1 images - ok
io.containerd.grpc.v1 leases - ok
io.containerd.grpc.v1 namespaces - ok
io.containerd.grpc.v1 snapshots - ok
io.containerd.grpc.v1 tasks - ok
io.containerd.grpc.v1 version - ok
io.containerd.grpc.v1 cri linux/amd64 ok
从输出中可以看到所有插件以及没有成功加载的插件。在这种情况下,aufs
和 zfs
预计不会加载因为主机不支持它们。日志会显示失败的原因、但也可以使用 -d
选项获取更多详细信息。
$ ctr plugins ls -d id==aufs id==zfs
Type: io.containerd.snapshotter.v1
ID: aufs
Platforms: linux/amd64
Exports:
root /var/lib/containerd/io.containerd.snapshotter.v1.aufs
Error:
Code: Unknown
Message: modprobe aufs failed: "modprobe: FATAL: Module aufs not found in directory /lib/modules/4.17.2-1-ARCH\n": exit status 1
Type: io.containerd.snapshotter.v1
ID: zfs
Platforms: linux/amd64
Exports:
root /var/lib/containerd/io.containerd.snapshotter.v1.zfs
Error:
Code: Unknown
Message: path /var/lib/containerd/io.containerd.snapshotter.v1.zfs must be a zfs filesystem to be used with the zfs snapshotter
插件返回的错误信息解释了插件无法加载的原因。
配置
插件使用 containerd 配置中的 [plugins]
部分进行配置。每个插件都拥有自己的部分,它使用 [plugins."<plugin type>.<plugin id>"]
模式。
配置示例:
version = 2
[plugins]
[plugins."io.containerd.monitor.v1.cgroups"]
no_prometheus = false
要查看完整配置示例,请运行 containerd config default
。如果想获得与您的配置相结合的配置,请运行 containerd config dump
。
版本头
containerd 有两个配置版本:
- 版本 2(推荐): 在 containerd 1.3 中引入。
- 版本 1(默认): 在 containerd 1.0 中引入。在 containerd 2.0 中移除。
使用版本 2 的配置必须有 version = 2
标头,并且必须有
插件 ID:
version = 2
[plugins]
[plugins."io.containerd.monitor.v1.cgroups"]
no_prometheus = false
版本 1 的配置可能没有 version
标头,也不需要完全限定的插件 ID。
[plugins]
[plugins.cgroups]
no_prometheus = false