containerd 支持使用它定义的接口,扩展功能。这包括使用自定义的运行时,快照,content 存储,甚至增加 gRPC 接口。
Smart Client Model
containerd 有一个智能的 client 架构,意思是着 deamon 不需要的任何功能都是被 client 完成的。这包括最高级的交互,例如创建一个容器规范,与镜像源交互或者从 tar 中载入一个镜像。 containerd 的 Go client 允许用户访问很多扩展点,从创建他们自己的容器选项到解析镜像源名称。
详情 containerd’s Go documentation。
External Plugins
External Plugins 允许使用官方的 containerd 发行版本扩展功能,不需要从新编译守护进程来添加 Plugin。
containerd 允许通过下面两种办法扩展
- 通过在 containerd 的 Path 中,使用可用的二进制文件
- 通过配置 containerd 来代理给其他 gRPC 服务
V2 Runtimes
V2 runtime 接口允许解析 runtimes 到二进制文件。这些二进制被用来启动 shim 进程,允许 containerd 来控制这些 containers 使用二进制提供的 runtime shim api。
Proxy Plugins
代理插件使用 containerd 的配置文件进行配置,并在 containerd 启动时与内部插件一起加载。 这些插件使用本地套接字连接到 containerd,该套接字为 containerd 的 gRPC API 服务之一提供服务。 每个插件都配置有类型和名称,就像内部插件一样。
Configuration
containerd 配置默认在 /etc/containerd/config.toml
,添加 [proxy_plugins]
和你的 Plugin [proxy_plugins.myplugin]
, address
必须引用 containerd 有权访问的本地 socket 文件。目前支持的类型是 snapshot
和 content
[proxy_plugins]
[proxy_plugins.customsnapshot]
type = "snapshot"
address = "/var/run/mysnapshotter.sock"
Implementation
实现一个 proxy plugin 与实现一个 gRPC API 一样容易。如果要使用 Go 实现 proxy plugin,参考 content store service 和 snapshotter service.
下面的例子是创建一个快照 plugin,可以被用于任何 containerd’s Snapshotter interface 实现。
package main
import (
"fmt"
"net"
"os"
"google.golang.org/grpc"
snapshotsapi "github.com/containerd/containerd/api/services/snapshots/v1"
"github.com/containerd/containerd/contrib/snapshotservice"
"github.com/containerd/containerd/snapshots/native"
)
func main() {
// Provide a unix address to listen to, this will be the `address`
// in the `proxy_plugin` configuration.
// The root will be used to store the snapshots.
if len(os.Args) < 3 {
fmt.Printf("invalid args: usage: %s <unix addr> <root>\n", os.Args[0])
os.Exit(1)
}
// Create a gRPC server
rpc := grpc.NewServer()
// Configure your custom snapshotter, this example uses the native
// snapshotter and a root directory. Your custom snapshotter will be
// much more useful than using a snapshotter which is already included.
// 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)
}
// Convert the snapshotter to a gRPC service,
// example in github.com/containerd/containerd/contrib/snapshotservice
service := snapshotservice.FromSnapshotter(sn)
// Register the service with the gRPC server
snapshotsapi.RegisterSnapshotsServer(rpc, service)
// Listen and serve
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)
}
}
使用先前的配置和用例,运行一个快照 plugin
# Start plugin in one terminal
$ go run ./main.go /var/run/mysnapshotter.sock /tmp/snapshots
# Use ctr in another
$ 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
Built-in Plugins
containerd 内部使用 Plugin 来确保内部实现解耦,稳定,与 external plugins 平等,查看 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
从输出里可以看到所有的 plugins,包括那些没有成功加载的。 现在 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
plugin 返回的错误消息解释了它为什么没能被加载
configuration
Plugins 通过使用 containerd 配置文件中的 [plugin]
配置,每一个插件能够拥有他自己的段,通过使用 [plugins.<plugin id>]
样例配置
[plugins]
[plugins.cgroups]
no_prometheus = false
[plugins.cri]
stream_server_address = ""
stream_server_port = "10010"
enable_selinux = false
sandbox_image = "k8s.gcr.io/pause:3.5"
stats_collect_period = 10
systemd_cgroup = false
[plugins.cri.containerd]
snapshotter = "overlayfs"
[plugins.cri.containerd.default_runtime]
runtime_type = "io.containerd.runtime.v1.linux"
runtime_engine = ""
runtime_root = ""
[plugins.cri.containerd.untrusted_workload_runtime]
runtime_type = ""
runtime_engine = ""
runtime_root = ""
[plugins.cri.cni]
bin_dir = "/opt/cni/bin"
conf_dir = "/etc/cni/net.d"
[plugins.cri.registry]
[plugins.cri.registry.mirrors]
[plugins.cri.registry.mirrors."docker.io"]
endpoint = ["https://registry-1.docker.io"]