容器开发
概述
参考:
go-containerregistry 是一个用于控制容器镜像的 Go 库。这个库的整体设计理念是将容器镜像抽象为 3 个接口:
- Image{} # 定义了与 OCI 标准的 Image 交互的接口
- Layer{} # 定义了访问 OCI Image 标准的 Layer 的交互接口
- ImageIndex{} # 定义了与 OCI Image 标准的 Index 交互的接口
这三个被抽象出来的接口可以通过多种 Medium(手段) 实现:
- Registry(注册中心) # 控制各种容器镜像的 Registry 中的镜像。比如 docker.io、gcr.io 等等
- 由 remote 包实现
- Tarball(压缩文件) # 控制
docker save
之类命令生成的 tarball 的镜像。- 由 tarball 包实现
- Daemon(守护进程) # 控制 Docker 守护进程中的镜像。这个包还不够完善
- 由 daemon 包实现
- …… 等等。随着库的扩展,可以实现三个接口的 Medium 将会越来越多
用人话说:
我们可以通过多个途径获取到容器镜像,比如容器镜像的注册中心,本地的压缩包(docker save
命令生成的文件等)、容器管理程序(Docker、Containerd 等)。这些可以获取到镜像的地方,称为 Medium(手段、介质)。
go-containerregisty 中编写了很多处理镜像的逻辑(也就是函数),这些处理镜像的逻辑符合 OCI 标准,但是很多逻辑都需要将“镜像”作为参数传递以便处理它们,但是通过这些 Medium 获取到的镜像格式可能不太一样,那么就需要对这些“镜像”建模,以便进行统一管理,所以就将“镜像”抽象为 Image{}
、Layer{}
、ImageIndex{}
这三个接口。
此时,每个 Medium 就可以自己实现想要的镜像管理逻辑了,只要其定义的结构体可以实现上述三个接口,那么就可以通过 go-containerregistry 中的函数处理 OCI 标准的镜像了。
说白了,go-containerregsitry 本质就是处理容器镜像的通用逻辑,那些 Medium 相关的代码其实并不真正属于该库的一部分,而是对该库的一种调用。
简单示例
比如我们用 remote 这个 Medium 作为示例。
首先我们需要实例化一个 Image{}
img, _ := remote.Image(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain))
想要实例化一个 Image{}
需要知道镜像的 tag、digest 中任意一个
ref, _ := name.ParseReference("nginx")
然后就可以通过实例化的 Image{}
处理镜像了,比如这里是获取镜像占用空间的大小
imageSize, _ := img.Size()
这几步合在一起
package main
import (
"fmt"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
)
func main() {
// 通过镜像的 tag 或者 digest 实例化一个镜像的引用。其实就是告诉 Medium 需要操作的镜像
// 这里之所以不在 remote.Image() 的第一个参数中只写填写镜像的 tag 或 digest,是为了可以在实例化 Image{} 之前对镜像名称进行一些操作,
// 比如通过 Reference{},我们可以获取镜像的 注册中心、名称 等等信息,
// 假如 ParseReference() 的参数是通过外部变量传递进来的,那么在实例化 Image{} 之前,我们可以先分析一下镜像的名称,对其进行过滤。
ref, _ := name.ParseReference("nginx")
// 通过镜像的引用实例化 Image{}
img, _ := remote.Image(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain))
// 通过实例化的镜像控制镜像,这里是获取镜像所占容量的大小
imageSize, _ := img.Size()
fmt.Println(imageSize)
}
v1.Image
实现了该接口的结构体:
// Image 定义了与 OCI Image 交互的接口
type Image interface {
// 返回了当前镜像的所有层级, 最老/最基础的层在数组的前面,最上面/最新的层在数组的后面
Layers() ([]Layer, error)
// 返回当前 image 的 MediaType
MediaType() (types.MediaType, error)
// 返回这个 Image manifest 的大小
Size() (int64, error)
// 返回这个镜像 ConfigFile 的hash值,也是这个镜像的 ImageID
ConfigName() (Hash, error)
// 返回这个镜像的 ConfigFile
ConfigFile() (*ConfigFile, error)
// 返回这个镜像的 ConfigFile 的字节数组
RawConfigFile() ([]byte, error)
// 返回这个Image Manifest 的sha256 值
Digest() (Hash, error)
// 返回这个Image Manifest
Manifest() (*Manifest, error)
// 返回 ImageManifest 的bytes数组
RawManifest() ([]byte, error)
// 返回这个镜像中的某一层layer, 根据 digest(压缩后的hash值) 来查找
LayerByDigest(Hash) (Layer, error)
// 返回这个镜像中的某一层layer, 根据 diffid (未压缩的hash值) 来查找
LayerByDiffID(Hash) (Layer, error)
}
v1.ImageIndex
实现了该接口的结构体:
// ImageIndex 定义与 OCI ImageIndex 交互的接口
type ImageIndex interface {
// 返回当前 imageIndex 的 MediaType
MediaType() (types.MediaType, error)
// 返回这个 ImageIndex manifest 的 sha256值。
Digest() (Hash, error)
// 返回这个 ImageIndex manifest 的大小
Size() (int64, error)
// 返回这个 ImageIndex 的 manifest 结构
IndexManifest() (*IndexManifest, error)
// 返回这个 ImageIndex 的 manifest 字节数组
RawManifest() ([]byte, error)
// 返回这个 ImageIndex 引用的 Image
Image(Hash) (Image, error)
// 返回这个 ImageIndex 引用的 ImageIndex
ImageIndex(Hash) (ImageIndex, error)
}
v1.Layer
实现了该接口的结构体:
// Layer 定义了访问 OCI Image 特定 Layer 的接口
type Layer interface {
// 返回了压缩后的layer的sha256 值
Digest() (Hash, error)
// 返回了 未压缩的layer 的sha256值.
DiffID() (Hash, error)
// 返回了压缩后的镜像层
Compressed() (io.ReadCloser, error)
// 返回了未压缩的镜像层
Uncompressed() (io.ReadCloser, error)
// 返回了压缩后镜像层的大小
Size() (int64, error)
// 返回当前 layer 的 MediaType
MediaType() (types.MediaType, error)
}
反馈
此页是否对你有帮助?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.