### containerd package
![containerd架构.png](https://blog.zs-fighting.cn/upload/2022/04/containerd%E6%9E%B6%E6%9E%84-36e665c9146445ad817f2d534d65500b.png)
```golang
// containerd Service
// 创建containerd client
func New(address string, opts ...ClientOpt) (*Client, error)
// 拉取镜像,并创建Image对象
func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (_ Image, retErr error)
// 创建container
func (c *Client) NewContainer(ctx context.Context, id string, opts ...NewContainerOpts) (Container, error)
// 获取已存在container的元数据
func (c *Client) LoadContainer(ctx context.Context, id string) (Container, error)
------------------------------------------------------------------------------
container Service
// Container is a metadata object for container resources and task creation
type Container interface {
// ID identifies the container
ID() string
// 返回底层容器记录类型
Info(context.Context, ...InfoOpts) (containers.Container, error)
// 删除容器
Delete(context.Context, ...DeleteOpts) error
// 基础容器元数据创建task
NewTask(context.Context, cio.Creator, ...NewTaskOpts) (Task, error)
// 返回 OCI 运行时规范
Spec(context.Context) (*oci.Spec, error)
// 返回容器的当前任务
// 如果传递了 cio.Attach 选项,客户端将重新连接到 IO 以进行运行任务
// 客户端必须确保只有一个阅读器连接到任务并使用任务fifos的输出
Task(context.Context, cio.Attach) (Task, error)
// 返回容器所基于的镜像
Image(context.Context) (Image, error)
// 返回容器上设置的标签
Labels(context.Context) (map[string]string, error)
// 为容器设置提供的标签并返回最终的标签集
SetLabels(context.Context, map[string]string) (map[string]string, error)
// 返回容器上设置的扩展
Extensions(context.Context) (map[string]prototypes.Any, error)
// 更新容器
Update(context.Context, ...UpdateContainerOpts) error
// 创建当前容器的检查点镜像
Checkpoint(context.Context, string, ...CheckpointOpts) (Image, error)
}
------------------------------------------------------------------------------
namespaces Service
// 设置namespace
func WithNamespace(ctx context.Context, namespace string) context.Context
------------------------------------------------------------------------------
task Service
// Task is the executable object within containerd
type Task interface {
Process
// 暂停任务的执行
Pause(context.Context) error
// 恢复任务的执行
Resume(context.Context) error
// 在任务中创建一个新进程
Exec(context.Context, string, *specs.Process, cio.Creator) (Process, error)
// 返回任务内系统特定进程 PID 的列表
Pids(context.Context) ([]ProcessInfo, error)
// 将任务的运行时和内存信息序列化成一个可以从远程资源推送和拉取的 OCI 索引
Checkpoint(context.Context, ...CheckpointTaskOpts) (Image, error)
// 使用更新的设置修改执行任务
Update(context.Context, ...UpdateTaskOpts) error
// 加载先前创建的执行进程
LoadProcess(context.Context, string, cio.Attach) (Process, error)
// 返回运行时特定指标的任务指标
Metrics(context.Context) (*types.Metric, error)
// 返回任务的当前 OCI 规范
Spec(context.Context) (*oci.Spec, error)
}
```
### 示例
**可以做成K8S登陆终端**
```golang
package main
import (
"context"
"fmt"
"io"
"log"
"syscall"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cio"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/oci"
"github.com/google/uuid"
"github.com/opencontainers/runtime-spec/specs-go"
)
var (
_dockerNamespace = "moby"
_containerdNamespace = "k8s.io"
_image = "docker.io/library/busybox:latest"
_containerdEndpoint = "/run/containerd/containerd.sock"
_containerId = "9d238888faff3ebf3e55f33cd98848902c7594a9fc6aab4f62bfbd9e5c8929b6"
_defaultCommand = []string{"sh", "-l"}
_containerUuid = fmt.Sprintf("container-demo-" + uuid.New().String())
)
func main() {
ctx := namespaces.WithNamespace(context.Background(), _dockerNamespace)
stdin, stdout := io.Pipe()
// 1、获取正在运行container的pid以及mount信息
client, _ := containerd.New(_containerdEndpoint)
container, _ := client.LoadContainer(ctx, _containerId)
task, _ := container.Task(ctx, nil)
pids, _ := task.Pids(ctx)
pid := int64(pids[0].Pid)
log.Printf("get containerID: %s,pid: %d\n", _containerId, pid)
image, _ := client.Pull(ctx, _image, containerd.WithPullUnpack)
// 2、生成默认的OCI标准
var ops []oci.SpecOpts
// 生成 oci 标准的 image 回调函数
ops = append(ops, oci.WithImageConfig(image))
// 生成 oci 标准的 特权 回调函数
ops = append(ops, oci.WithPrivileged)
// 生成 oci 标准 tty 回调函数
ops = append(ops, oci.WithTTY)
// 生成 oci 标准的 command 回调函数
ops = append(ops, oci.WithProcessArgs(_defaultCommand...))
// 生成 oci 标准的 数据卷挂载 结构 回调函数
//ops = append(ops, nil)
// 3、把正在运行container的Namespace加到OCI标准中
ops = append(ops, oci.WithLinuxNamespace(specs.LinuxNamespace{
Type: specs.UTSNamespace,
Path: fmt.Sprintf("/proc/%v/ns/uts", pid),
}))
ops = append(ops, oci.WithLinuxNamespace(specs.LinuxNamespace{
Type: specs.NetworkNamespace,
Path: fmt.Sprintf("/proc/%v/ns/net", pid),
}))
ops = append(ops, oci.WithLinuxNamespace(specs.LinuxNamespace{
Type: specs.PIDNamespace,
Path: fmt.Sprintf("/proc/%v/ns/pid", pid),
}))
//ops = append(ops, oci.WithLinuxNamespace(specs.LinuxNamespace{
// Type: specs.CgroupNamespace,
// Path: fmt.Sprintf("/proc/%v/ns/uts", pid),
//}))
//ops = append(ops, oci.WithLinuxNamespace(specs.LinuxNamespace{
// Type: specs.UserNamespace,
// Path: fmt.Sprintf("/proc/%v/ns/user", pid),
//}))
//ops = append(ops, oci.WithLinuxNamespace(specs.LinuxNamespace{
// Type: specs.MountNamespace,
// Path: fmt.Sprintf("/proc/%v/ns/mnt", pid),
//}))
// 4、根据OCI标准生成container元数据,运行task
// 初始化 container
newContainer, _ := client.NewContainer(ctx,
_containerUuid,
containerd.WithNewSnapshot(_containerUuid, image),
containerd.WithNewSpec(ops...))
defer func() {
newContainer.Delete(ctx, containerd.WithSnapshotCleanup)
}()
log.Printf("new container is successful %s", newContainer.ID())
// 设置 cio 标准的 streams
streamIO := cio.WithStreams(stdin, stdout, stdout)
// 初始化 containerd task
newTask, err := newContainer.NewTask(ctx, cio.NewCreator(streamIO, cio.WithTerminal))
defer func() {
newTask.Kill(ctx, syscall.SIGTERM|syscall.SIGKILL)
newTask.Delete(ctx, containerd.WithProcessKill)
}()
if newTask == nil || err != nil {
log.Printf("new container task is failed %s: %v\n", _containerId, err)
}
log.Printf("new container task is successful %s", newTask.ID())
exitStatusCh, _ := newTask.Wait(ctx)
newTask.Start(ctx)
status := <-exitStatusCh
code, _, _ := status.Result()
log.Printf("task exited with status %d\n", code)
}
```
### ctr 教程
```shell
NAME:
ctr -
__
_____/ /______
/ ___/ __/ ___/
/ /__/ /_/ /
\___/\__/_/
containerd CLI
USAGE:
ctr [global options] command [command options] [arguments...]
VERSION:
1.4.12
DESCRIPTION:
ctr is an unsupported debug and administrative client for interacting
with the containerd daemon. Because it is unsupported, the commands,
options, and operations are not guaranteed to be backward compatible or
stable from release to release of the containerd project.
COMMANDS:
plugins, plugin provides information about containerd plugins
version print the client and server versions
containers, c, container manage containers
content manage content
events, event display containerd events
images, image, i manage images
leases manage leases
namespaces, namespace, ns manage namespaces
pprof provide golang pprof outputs for containerd
run run a container
snapshots, snapshot manage snapshots
tasks, t, task manage tasks
install install a new package
oci OCI tools
shim interact with a shim directly
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--debug enable debug output in logs
--address value, -a value address for containerd's GRPC server (default: "/run/containerd/containerd.sock") [$CONTAINERD_ADDRESS]
--timeout value total timeout for ctr commands (default: 0s)
--connect-timeout value timeout for connecting to containerd (default: 0s)
--namespace value, -n value namespace to use with commands (default: "default") [$CONTAINERD_NAMESPACE]
--help, -h show help
--version, -v print the version
```
#### 镜像操作
```shell
ctr image ls
ctr image pull docker.io/library/nginx:alpine
ctr image tag docker.io/library/nginx:alpine harbor.k8s.local/course/nginx:alpine
ctr image rm harbor.k8s.local/course/nginx:alpine
// 将镜像挂载到主机目录
ctr image mount docker.io/library/nginx:alpine /mnt
// 将镜像从主机目录上卸载
ctr image unmount /mnt
// 导出
ctr image export nginx.tar.gz docker.io/library/nginx:alpine
// 导入
ctr image import nginx.tar.gz
```
#### 容器操作
```shell
ctr container ls
// 类似于 docker inspect 功能
ctr container info nginx
ctr container rm nginx
```
#### 任务操作
**上面我们通过 container create 命令创建的容器,并没有处于运行状态,只是一个静态的容器。一个 container 对象只是包含了运行一个容器所需的资源及相关配置数据,表示 namespaces、rootfs 和容器的配置都已经初始化成功了,只是用户进程还没有启动。**
**一个容器真正运行起来是由 Task 任务实现的,Task 可以为容器设置网卡,还可以配置工具来对容器进行监控等。**
```shell
ctr task ls
// 使用 exec 命令进入容器进行操作
ctr task exec --exec-id 0 -t nginx sh
// 暂停容器
ctr task pause nginx
// 恢复容器
ctr task resume nginx
ctr task kill nginx
ctr task rm nginx
ctr task metrics nginx
```
Containerd 入门实战