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

云原生实验室

共 5895字,需浏览 12分钟

 ·

2020-12-27 21:08


更多奇技淫巧欢迎订阅博客: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 都不需要!



点击 "阅读原文" 获取更好的阅读体验!

❤️给个「在看」,是对我最大的支持❤️
浏览 38
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报