Containerd 中的 Snapshot 到底是个什么鬼?

更多奇技淫巧欢迎订阅博客:https://fuckcloudnative.io

先问大家问题,containerd 的 snapshot 是什么鬼?难道是给容器做快照的吗?

答案是:是也不是

为什么说是呢?它的确可以实现部分快照的功能,但他和常规意义的虚拟机快照又不是一回事。

我们先看一下 containerd 是干嘛的?譬如我们拉镜像

可以看到 snapshot 的服务作用是准备 rootfs 的,就是通过 mount 各个层,提供容器的 rootfs。所有。content 只是保存 tar 和 mainfest 和 config 文件的,是 OCI 那套东西,我们需要将他们逐一解压、mount 后提供给容器。

所以 push 操作就和 snapshot 没有毛关系了。如下所示:

当我们理解 snapshot 是干嘛的以后,可以通过源码看一下 containerd 目前的 snapshot 有哪些实现:

那么大家可能又有疑惑了,为啥他们可以加入 snapshot,因为这些文件系统都有一个共同的特性就是:支持快照。这就是这个模块的名称叫做 snapshot 了。每个文件系统的接入就需要实现下面的接口。

type Snapshotter interface {
// Stat returns the info for an active or committed snapshot by name or
// key.
//
// Should be used for parent resolution, existence checks and to discern
// the kind of snapshot.
Stat(ctx context.Context, key string) (Info, error)

// Update updates the info for a snapshot.
//
// Only mutable properties of a snapshot may be updated.
Update(ctx context.Context, info Info, fieldpaths ...string) (Info, error)

// Usage returns the resource usage of an active or committed snapshot
// excluding the usage of parent snapshots.
//
// The running time of this call for active snapshots is dependent on
// implementation, but may be proportional to the size of the resource.
// Callers should take this into consideration. Implementations should
// attempt to honer context cancellation and avoid taking locks when making
// the calculation.
Usage(ctx context.Context, key string) (Usage, error)

// Mounts returns the mounts for the active snapshot transaction identified
// by key. Can be called on an read-write or readonly transaction. This is
// available only for active snapshots.
//
// This can be used to recover mounts after calling View or Prepare.
Mounts(ctx context.Context, key string) ([]mount.Mount, error)

// Prepare creates an active snapshot identified by key descending from the
// provided parent. The returned mounts can be used to mount the snapshot
// to capture changes.
//
// If a parent is provided, after performing the mounts, the destination
// will start with the content of the parent. The parent must be a
// committed snapshot. Changes to the mounted destination will be captured
// in relation to the parent. The default parent, "", is an empty
// directory.
//
// The changes may be saved to a committed snapshot by calling Commit. When
// one is done with the transaction, Remove should be called on the key.
//
// Multiple calls to Prepare or View with the same key should fail.
Prepare(ctx context.Context, key, parent string, opts ...Opt) ([]mount.Mount, error)

// View behaves identically to Prepare except the result may not be
// committed back to the snapshot snapshotter. View returns a readonly view on
// the parent, with the active snapshot being tracked by the given key.
//
// This method operates identically to Prepare, except that Mounts returned
// may have the readonly flag set. Any modifications to the underlying
// filesystem will be ignored. Implementations may perform this in a more
// efficient manner that differs from what would be attempted with
// `Prepare`.
//
// Commit may not be called on the provided key and will return an error.
// To collect the resources associated with key, Remove must be called with
// key as the argument.
View(ctx context.Context, key, parent string, opts ...Opt) ([]mount.Mount, error)

// Commit captures the changes between key and its parent into a snapshot
// identified by name. The name can then be used with the snapshotter's other
// methods to create subsequent snapshots.
//
// A committed snapshot will be created under name with the parent of the
// active snapshot.
//
// After commit, the snapshot identified by key is removed.
Commit(ctx context.Context, name, key string, opts ...Opt) error

// Remove the committed or active snapshot by the provided key.
//
// All resources associated with the key will be removed.
//
// If the snapshot is a parent of another snapshot, its children must be
// removed before proceeding.
Remove(ctx context.Context, key string) error

// Walk will call the provided function for each snapshot in the
// snapshotter which match the provided filters. If no filters are
// given all items will be walked.
// Filters:
// name
// parent
// kind (active,view,committed)
// labels.(label)
Walk(ctx context.Context, fn WalkFunc, filters ...string) error

// Close releases the internal resources.
//
// Close is expected to be called on the end of the lifecycle of the snapshotter,
// but not mandatory.
//
// Close returns nil when it is already closed.
Close() error
}

当我们通过 pull 下载镜像的时候就会 apply 每一层:

func applyLayers(ctx context.Context, layers []Layer, chain []digest.Digest, sn snapshots.Snapshotter, a diff.Applier, opts []snapshots.Opt, applyOpts []diff.ApplyOpt) error {

mounts, err = sn.Prepare(ctx, key, parent.String(), opts...)

diff, err = a.Apply(ctx, layer.Blob, mounts, applyOpts...)


if err = sn.Commit(ctx, chainID.String(), key, opts...); err != nil {
err = errors.Wrapf(err, "failed to commit snapshot %s", key)
return err
}

return nil
}

通过 prepare –> apply –> commit 三步完成 layer 的构建。其中 prepare 只返回了具体 mount 列表,并不做具体的 mount 擦着,具体实现在 apply 这个方法:

func apply(ctx context.Context, mounts []mount.Mount, r io.Reader) error {
return mount.WithTempMount(ctx, mounts, func(root string) error {
_, err := archive.Apply(ctx, root, r)
return err
})
}

其中 archive.Apply 就是解压 tar 包写层的”archive/tar.go“

func applyNaive(ctx context.Context, root string, tr *tar.Reader, options ApplyOptions) (size int64, err error) {
. . .

总结一下,snapshot 目的是为了给容器的提供 rootfs,通过实现 snapshot 的接口,就可以接入不同的具有快照功能的文件系统。

原文链接:
https://blog.csdn.net/u010278923/article/details/111721116

你可能还喜欢

点击下方图片即可阅读

WireGuard 真的很香吗?香个屁!

云原生是一种信仰 




码关注公众号

后台回复◉k8s◉获取史上最方便快捷的 Kubernetes 高可用部署工具,只需一条命令,连 ssh 都不需要!

点击
 “阅读原文” 

获取

更好的阅读体验!

:heart:
给个
「在看」
,是对我最大的支持:heart:

云原生实验室
我还没有学会写个人说明!
下一篇

23种设计模式 - 单例模式​

你也可能喜欢

评论已经被关闭。

插入图片