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 允许通过下面两种办法扩展

  1. 通过在 containerd 的 Path 中,使用可用的二进制文件
  2. 通过配置 containerd 来代理给其他 gRPC 服务

V2 Runtimes

V2 runtime 接口允许解析 runtimes 到二进制文件。这些二进制被用来启动 shim 进程,允许 containerd 来控制这些 containers 使用二进制提供的 runtime shim api。

详情 runtime v2 documentation

Proxy Plugins

代理插件使用 containerd 的配置文件进行配置,并在 containerd 启动时与内部插件一起加载。 这些插件使用本地套接字连接到 containerd,该套接字为 containerd 的 gRPC API 服务之一提供服务。 每个插件都配置有类型和名称,就像内部插件一样。

Configuration

containerd 配置默认在 /etc/containerd/config.toml,添加 [proxy_plugins] 和你的 Plugin [proxy_plugins.myplugin], address 必须引用 containerd 有权访问的本地 socket 文件。目前支持的类型是 snapshotcontent

  1. [proxy_plugins]
  2. [proxy_plugins.customsnapshot]
  3. type = "snapshot"
  4. address = "/var/run/mysnapshotter.sock"

Implementation

实现一个 proxy plugin 与实现一个 gRPC API 一样容易。如果要使用 Go 实现 proxy plugin,参考 content store servicesnapshotter service.

下面的例子是创建一个快照 plugin,可以被用于任何 containerd’s Snapshotter interface 实现。

  1. package main
  2. import (
  3. "fmt"
  4. "net"
  5. "os"
  6. "google.golang.org/grpc"
  7. snapshotsapi "github.com/containerd/containerd/api/services/snapshots/v1"
  8. "github.com/containerd/containerd/contrib/snapshotservice"
  9. "github.com/containerd/containerd/snapshots/native"
  10. )
  11. func main() {
  12. // Provide a unix address to listen to, this will be the `address`
  13. // in the `proxy_plugin` configuration.
  14. // The root will be used to store the snapshots.
  15. if len(os.Args) < 3 {
  16. fmt.Printf("invalid args: usage: %s <unix addr> <root>\n", os.Args[0])
  17. os.Exit(1)
  18. }
  19. // Create a gRPC server
  20. rpc := grpc.NewServer()
  21. // Configure your custom snapshotter, this example uses the native
  22. // snapshotter and a root directory. Your custom snapshotter will be
  23. // much more useful than using a snapshotter which is already included.
  24. // https://godoc.org/github.com/containerd/containerd/snapshots#Snapshotter
  25. sn, err := native.NewSnapshotter(os.Args[2])
  26. if err != nil {
  27. fmt.Printf("error: %v\n", err)
  28. os.Exit(1)
  29. }
  30. // Convert the snapshotter to a gRPC service,
  31. // example in github.com/containerd/containerd/contrib/snapshotservice
  32. service := snapshotservice.FromSnapshotter(sn)
  33. // Register the service with the gRPC server
  34. snapshotsapi.RegisterSnapshotsServer(rpc, service)
  35. // Listen and serve
  36. l, err := net.Listen("unix", os.Args[1])
  37. if err != nil {
  38. fmt.Printf("error: %v\n", err)
  39. os.Exit(1)
  40. }
  41. if err := rpc.Serve(l); err != nil {
  42. fmt.Printf("error: %v\n", err)
  43. os.Exit(1)
  44. }
  45. }

使用先前的配置和用例,运行一个快照 plugin

  1. # Start plugin in one terminal
  2. $ go run ./main.go /var/run/mysnapshotter.sock /tmp/snapshots
  3. # Use ctr in another
  4. $ CONTAINERD_SNAPSHOTTER=customsnapshot ctr images pull docker.io/library/alpine:latest
  5. $ tree -L 3 /tmp/snapshots
  6. /tmp/snapshots
  7. |-- metadata.db
  8. `-- snapshots
  9. `-- 1
  10. |-- bin
  11. |-- dev
  12. |-- etc
  13. |-- home
  14. |-- lib
  15. |-- media
  16. |-- mnt
  17. |-- proc
  18. |-- root
  19. |-- run
  20. |-- sbin
  21. |-- srv
  22. |-- sys
  23. |-- tmp
  24. |-- usr
  25. `-- var
  26. 18 directories, 1 file

Built-in Plugins

containerd 内部使用 Plugin 来确保内部实现解耦,稳定,与 external plugins 平等,查看 containerd 有的所有插件,使用 ctr plugins ls

  1. $ ctr plugins ls
  2. TYPE ID PLATFORMS STATUS
  3. io.containerd.content.v1 content - ok
  4. io.containerd.snapshotter.v1 btrfs linux/amd64 ok
  5. io.containerd.snapshotter.v1 aufs linux/amd64 error
  6. io.containerd.snapshotter.v1 native linux/amd64 ok
  7. io.containerd.snapshotter.v1 overlayfs linux/amd64 ok
  8. io.containerd.snapshotter.v1 zfs linux/amd64 error
  9. io.containerd.metadata.v1 bolt - ok
  10. io.containerd.differ.v1 walking linux/amd64 ok
  11. io.containerd.gc.v1 scheduler - ok
  12. io.containerd.service.v1 containers-service - ok
  13. io.containerd.service.v1 content-service - ok
  14. io.containerd.service.v1 diff-service - ok
  15. io.containerd.service.v1 images-service - ok
  16. io.containerd.service.v1 leases-service - ok
  17. io.containerd.service.v1 namespaces-service - ok
  18. io.containerd.service.v1 snapshots-service - ok
  19. io.containerd.runtime.v1 linux linux/amd64 ok
  20. io.containerd.runtime.v2 task linux/amd64 ok
  21. io.containerd.monitor.v1 cgroups linux/amd64 ok
  22. io.containerd.service.v1 tasks-service - ok
  23. io.containerd.internal.v1 restart - ok
  24. io.containerd.grpc.v1 containers - ok
  25. io.containerd.grpc.v1 content - ok
  26. io.containerd.grpc.v1 diff - ok
  27. io.containerd.grpc.v1 events - ok
  28. io.containerd.grpc.v1 healthcheck - ok
  29. io.containerd.grpc.v1 images - ok
  30. io.containerd.grpc.v1 leases - ok
  31. io.containerd.grpc.v1 namespaces - ok
  32. io.containerd.grpc.v1 snapshots - ok
  33. io.containerd.grpc.v1 tasks - ok
  34. io.containerd.grpc.v1 version - ok
  35. io.containerd.grpc.v1 cri linux/amd64 ok

从输出里可以看到所有的 plugins,包括那些没有成功加载的。 现在 aufszfs 没有像预期一样载入,因为他们不支持本机。日志显示了为什么他会失败,但是你能够使用 -d 来获取更多细节。

  1. $ ctr plugins ls -d id==aufs id==zfs
  2. Type: io.containerd.snapshotter.v1
  3. ID: aufs
  4. Platforms: linux/amd64
  5. Exports:
  6. root /var/lib/containerd/io.containerd.snapshotter.v1.aufs
  7. Error:
  8. Code: Unknown
  9. Message: modprobe aufs failed: "modprobe: FATAL: Module aufs not found in directory /lib/modules/4.17.2-1-ARCH\n": exit status 1
  10. Type: io.containerd.snapshotter.v1
  11. ID: zfs
  12. Platforms: linux/amd64
  13. Exports:
  14. root /var/lib/containerd/io.containerd.snapshotter.v1.zfs
  15. Error:
  16. Code: Unknown
  17. 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>]

样例配置

  1. [plugins]
  2. [plugins.cgroups]
  3. no_prometheus = false
  4. [plugins.cri]
  5. stream_server_address = ""
  6. stream_server_port = "10010"
  7. enable_selinux = false
  8. sandbox_image = "k8s.gcr.io/pause:3.5"
  9. stats_collect_period = 10
  10. systemd_cgroup = false
  11. [plugins.cri.containerd]
  12. snapshotter = "overlayfs"
  13. [plugins.cri.containerd.default_runtime]
  14. runtime_type = "io.containerd.runtime.v1.linux"
  15. runtime_engine = ""
  16. runtime_root = ""
  17. [plugins.cri.containerd.untrusted_workload_runtime]
  18. runtime_type = ""
  19. runtime_engine = ""
  20. runtime_root = ""
  21. [plugins.cri.cni]
  22. bin_dir = "/opt/cni/bin"
  23. conf_dir = "/etc/cni/net.d"
  24. [plugins.cri.registry]
  25. [plugins.cri.registry.mirrors]
  26. [plugins.cri.registry.mirrors."docker.io"]
  27. endpoint = ["https://registry-1.docker.io"]