Containerd 中的 Snapshot 到底是個(gè)什么鬼?

更多奇技淫巧歡迎訂閱博客:https://fuckcloudnative.io
先問大家問題,containerd 的 snapshot 是什么鬼?難道是給容器做快照的嗎?

答案是:是也不是
為什么說是呢?它的確可以實(shí)現(xiàn)部分快照的功能,但他和常規(guī)意義的虛擬機(jī)快照又不是一回事。
我們先看一下 containerd 是干嘛的?譬如我們拉鏡像

可以看到 snapshot 的服務(wù)作用是準(zhǔn)備 rootfs 的,就是通過 mount 各個(gè)層,提供容器的 rootfs。所有。content 只是保存 tar 和 mainfest 和 config 文件的,是 OCI 那套東西,我們需要將他們逐一解壓、mount 后提供給容器。
所以 push 操作就和 snapshot 沒有毛關(guān)系了。如下所示:

當(dāng)我們理解 snapshot 是干嘛的以后,可以通過源碼看一下 containerd 目前的 snapshot 有哪些實(shí)現(xiàn):

那么大家可能又有疑惑了,為啥他們可以加入 snapshot,因?yàn)檫@些文件系統(tǒng)都有一個(gè)共同的特性就是:支持快照。這就是這個(gè)模塊的名稱叫做 snapshot 了。每個(gè)文件系統(tǒng)的接入就需要實(shí)現(xiàn)下面的接口。
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
}
當(dāng)我們通過 pull 下載鏡像的時(shí)候就會(huì) 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 的構(gòu)建。其中 prepare 只返回了具體 mount 列表,并不做具體的 mount 擦著,具體實(shí)現(xiàn)在 apply 這個(gè)方法:
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)?{
.?.?.
總結(jié)一下,snapshot 目的是為了給容器的提供 rootfs,通過實(shí)現(xiàn) snapshot 的接口,就可以接入不同的具有快照功能的文件系統(tǒng)。
原文鏈接:https://blog.csdn.net/u010278923/article/details/111721116


你可能還喜歡
點(diǎn)擊下方圖片即可閱讀

云原生是一種信仰??
掃碼關(guān)注公眾號(hào)
后臺(tái)回復(fù)?k8s?獲取史上最方便快捷的 Kubernetes 高可用部署工具,只需一條命令,連 ssh 都不需要!


點(diǎn)擊?"閱讀原文"?獲取更好的閱讀體驗(yàn)!
??給個(gè)「在看」,是對(duì)我最大的支持??

