远程快照器
Containerd 允许快照器重复使用其管理的某处已有的快照。
远程快照器(Remote Snapshotter)是一种快照器,它可以利用这一功能,重用存储在远程共享位置的快照。
这些远程共享的快照称为_远程快照_。
远程快照器允许 containerd 准备这些远程快照,而无需从注册表中提取层,从而有望缩短镜像提取所需的时间。
远程快照器实现之一是 Stargz Snapshotter。
它能让 containerd 利用远程快照器功能和 google/crfs 的 stargz 镜像,从符合标准的注册表中轻松提取镜像。
containerd 客户端 API
containerd 客户端的 Pull
API(带有解压缩模式)允许底层快照器在获取内容之前查询远程快照。
远程快照器需要以与普通快照器相同的方式插入containerd。
import (
containerd "github.com/containerd/containerd/v2/client"
)
image, err := client.Pull(ctx, ref,
containerd.WithPullUnpack,
containerd.WithPullSnapshotter("my-remote-snapshotter"),
)
传递快照器特定信息
某些远程快照程序需要通过 Pull
API 获取快照程序的特定信息。
这些信息将以各种方式使用,包括从远程存储中搜索快照内容。
其中一个需要快照器特定信息的快照器示例是 stargz snapshotter。
它需要镜像引用名称和层摘要等信息,以便从注册表中搜索层内容。
快照器通过以 containerd.io/snapshot/
为前缀的用户定义标签接收信息。
containerd 客户端支持两种将这些标签传递给底层快照器的方式。
使用快照器的 WithLabels
选项
用户定义的标签可以通过 snapshotter 选项 WithLabels
传递给底层快照器。
每次 containerd 客户端查询远程快照时,都会向下传递指定的标签。
如果无论快照如何,这些标签的值都是静态确定的,那么这个选项就非常有用。
这些用户定义的标签必须以 containerd.io/snapshot/
为前缀。
import (
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/core/snapshots"
)
image, err := client.Pull(ctx, ref,
containerd.WithPullUnpack,
containerd.WithPullSnapshotter(
"my-remote-snapshotter",
snapshots.WithLabels(map[string]string{
"containerd.io/snapshot/reference": ref,
}),
),
)
使用 containerd 客户端的 WithImageHandlerWrapper
选项
用户定义的标签也可以使用镜像处理器包装器传递。
当标签根据快照的不同而变化时,这个选项非常有用。
每次 containerd 客户端查询远程快照时,它都会向底层快照器传递附加到目标层描述符(指为准备该快照而提取和解压缩的层描述符)的 "注释"。
这些注释作为用户定义的标签传递给快照器。
注释的值可以在处理程序包装器中动态添加和修改。
请注意,注释的前缀必须是 containerd.io/snapshot/
。
github.com/containerd/containerd/v2/pkg/snapshotters
是 CRI 软件包、nerdctl 和 moby 使用的处理程序实现。
import (
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/pkg/snapshotters"
)
if _, err := client.Pull(ctx, ref,
containerd.WithPullUnpack,
containerd.WithPullSnapshotter("my-remote-snapshotter"),
containerd.WithImageHandlerWrapper(snapshotters.AppendInfoHandlerWrapper(ref)),
)
用于查询远程快照的快照器 API
containerd 客户端使用快照器 API 向底层远程快照器查询远程快照。
本节从高层次概述了如何使用快照器 API 来实现远程快照功能,并用一些伪代码描述了 containerd 客户端中实现的简化逻辑。
更多详情,请参阅实现这一逻辑的 unpacker.go
。
在镜像拉取过程中,containerd 客户端会调用带有标签 containerd.io/snapshot.ref
的 Prepare
API。
这是一个 containerd 定义的标签,其中包含 ChainID,目标是客户端正在尝试准备的已提交快照。
目前,用户定义的标签(前缀为 containerd.io/snapshot/
)也将被合并到标签选项中。
// 获取附加到目标定位层的注释,该注释将包含
// 用户传递的快照器特定信息。
labels := snapshots.FilterInheritedLabels(desc.Annotations)
if labels == nil {
labels = make(map[string]string)
}
// 指定目标提交快照的 ChainID。
labels["containerd.io/snapshot.ref"] = chainID
// 合并用户指定的快照器选项,这些选项将包含
// 用户传递的快照器特定信息。
opts := append(rCtx.SnapshotterOpts, snapshots.WithLabels(labels))
// 使用目标标识符和快照器特定信息调用 `Prepare` API信息。
mounts, err = sn.Prepare(ctx, key, parent.String(), opts...)
如果该快照器是一个远程快照器,那么提交的快照就有希望存在,例如存在于共享的远程存储中。
远程快照器必须定义并执行有关是否使用现有快照的策略。
当远程快照器允许用户使用该快照时,它必须返回 ErrAlreadyExists
。
如果containerd 客户端通过 Prepare
得到 ErrAlreadyExists
,它就会通过调用带有 ChainID 的 Stat
来确保已提交快照的存在。
如果该快照可用,容器客户机就会跳过准备和提交该快照所需的提取和解压缩层。
mounts, err = sn.Prepare(ctx, key, parent.String(), opts...)
if err != nil {
if errdefs.IsAlreadyExists(err) {
//确保层存在
if _, err := sn.Stat(ctx, chainID); err != nil {
//处理错误
} else {
// 发现带有 ChainID 的快照将跳过提取/解包
continue
}
} else {
return err
}
}