实现流程可控的镜像下载和存储(二)
基于containers/storage的镜像存储
镜像结构
完整镜像Image主要由Layer、Blob和Manifest组成
Layer:通过链式结构组成,存储镜像的数据,每个Layer依赖其Parent Layer,即使两个Layer的内容完全相同,Parent Layer不同也会导致Layer不同
Blob:压缩后的Layer数据,解压后即是Layer,通过Digest区分不同Blob,Digest即Blob文件的sha256sum值
Manifest:存有镜像信息以及每个Layer的信息,通过Manifest可以将Blob组成Layer并合成Image
镜像存储
增加additional image store用于存储需要管理的镜像
/etc/containers/storage.conf
[storage]
driver = "overlay"
runroot = "/run/containers/storage"
graphroot = "/var/lib/containers/storage"
[storage.options]
additionalimagestores=["/var/cache/image"]
mount_program = "/usr/bin/fuse-overlayfs"
初始化store并获取镜像列表
import (
"github.com/containers/storage"
"github.com/containers/storage/types"
)
func Images() error {
store, err := storage.GetStore(
types.StoreOptions{
GraphDriverName: "overlay",
GraphRoot: "/var/cache/image",
})
if err != nil {
return err
}
images, err := store.Images()
if err != nil {
return err
}
for _, image := range images {
// fmt.Println(image.Names)
}
return nil
}
计算当前blob文件的解压后生成layer的ID和Diff ID
func GenerateDiffID(blobPath, parentID string) (string, digest.Digest, error) {
// ID: layer 's ID
// ParentID: ID of current layer 's parent layer
// Diff ID: Used to create layer
file, err := os.OpenFile(blobPath, os.O_RDONLY, 0666)
if err != nil {
return "", "", err
}
decompressed, err := archive.DecompressStream(file)
if err != nil {
return "", "", err
}
diff := digest.Canonical.Digester()
_, err = io.Copy(diff.Hash(), decompressed)
decompressed.Close()
if err != nil {
return "", "", err
}
diffID := diff.Digest()
id := ""
if parentID == "" {
id = diffID.Hex()
} else {
id = digest.Canonical.FromBytes([]byte(parentID + "+" + diffID.Hex())).Hex()
}
return id, diffID, nil
}
基于blob文件生成layer
其中digest为blob文件的sha256sum值,可在manifest中获取,也可以直接计算获取,其余参数GenerateDiffID可计算得到
import (
"os"
"github.com/containers/storage"
"github.com/containers/storage/types"
"github.com/opencontainers/go-digest"
)
func CreateLayer(id, parent, path string, digest, diffID digest.Digest) error {
store, err := storage.GetStore(
types.StoreOptions{
GraphDriverName: "overlay",
GraphRoot: "/var/cache/image",
})
if err != nil {
return err
}
// Layer exist
if store.Exists(id) {
return nil
}
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
_, _, err = store.PutLayer(id, parent, nil, "", false,
&storage.LayerOptions{
OriginalDigest: digest,
UncompressedDigest: diffID,
}, file)
return err
}
解析Manifest,获取镜像的部分参数
import (
"encoding/json"
"time"
"github.com/containers/image/v5/manifest"
"github.com/containers/storage"
"github.com/opencontainers/go-digest"
)
func ParseManifest(manifestData []byte) error {
// Find the list of layer blobs
man, err := manifest.FromBlob(manifestData, manifest.GuessMIMEType(manifestData))
if err != nil {
return err
}
// Get image info
imageInfoDigest := man.ConfigInfo().Digest
imageInfo, err := GetMetaData(imageInfoDigest.String())
if err != nil {
return err
}
imageOptions := storage.ImageOptions{}
imageInfoData := make(map[string]interface{})
// Get image create time
err = json.Unmarshal(imageInfo, &imageInfoData)
if err != nil {
imageOptions.CreationDate = time.Now()
} else {
created := imageInfoData["created"].(string)
t, err := time.Parse(time.RFC3339, created)
if err != nil {
imageOptions.CreationDate = time.Now()
} else {
imageOptions.CreationDate = t
}
}
intendedID := imageInfoDigest.String()[7:]
imageOptions.BigData = append(imageOptions.BigData, storage.ImageBigDataOption{
Key: "manifest",
Data: manifestData,
Digest: digest.Digest(imageInfoDigest.String()),
})
imageOptions.BigData = append(imageOptions.BigData, storage.ImageBigDataOption{
Key: "manifest-" + imageInfoDigest.String(),
Data: manifestData,
Digest: digest.Digest(imageInfoDigest.String()),
})
imageOptions.BigData = append(imageOptions.BigData, storage.ImageBigDataOption{
Key: imageInfoDigest.String(),
Data: imageInfo,
Digest: imageInfoDigest,
})
return nil
}
创建镜像
其中intendedID为镜像Mainfest的Digest,options在解析Manifest时可生成,lastLayer为镜像最后一个Layer的ID
import (
"github.com/containers/storage"
"github.com/containers/storage/types"
)
func CreateImage(intendedID, lastLayer string, options *storage.ImageOptions) (*storage.Image, error) {
store, err := storage.GetStore(
types.StoreOptions{
GraphDriverName: "overlay",
GraphRoot: "/var/cache/image",
})
if err != nil {
return nil, err
}
return store.CreateImage(intendedID, nil, lastLayer, "", options)
}
浙公网安备 33010602011771号