docker distritubion源码分析之二:storage driver详解(基于2.6.0版本)

在上一篇文章中,分析了docker distribution的启动过程,本篇着重分析distribution使用storage driver的原理。


在NewApp函数中,会初始化app.driver:

app.driver, err = factory.Create(config.Storage.Type(), storageParams)
if err != nil {
	// TODO(stevvooe): Move the creation of a service into a protected
	// method, where this is created lazily. Its status can be queried via
	// a health check.
	panic(err)
}

Create函数在registry/storage/factory/factory.go中,它通过config.Storage的Type从driverFactories这个数组缓存中取出相对应的driverFactory,然后调用该driverFactory的Create函数,创建对应的StorageDriver:

func Create(name string, parameters map[string]interface{}) (storagedriver.StorageDriver, error) {
	driverFactory, ok := driverFactories[name]
	if !ok {
		return nil, InvalidStorageDriverError{name}
	}
	return driverFactory.Create(parameters)
}

而driverFactories数组是通过各个storage driver在启动时自动执行的init()函数中调用factory.Registry函数来初始化的:

func Register(name string, factory StorageDriverFactory) {
	if factory == nil {
		panic("Must not provide nil StorageDriverFactory")
	}
	_, registered := driverFactories[name]
	if registered {
		panic(fmt.Sprintf("StorageDriverFactory named %s already registered", name))
	}

	driverFactories[name] = factory
}

以s3 driver为例,在main函数执行之前,就会先执行registry/storage/s3-aws/s3.go中的init函数,把自己注册到driverFactories数组中:

func init() {
	
	……

	// Register this as the default s3 driver in addition to s3aws
	factory.Register("s3", &s3DriverFactory{})
	factory.Register(driverName, &s3DriverFactory{})
}

这样,如果在配置文件中配置了s3 driver的话,调用driverFactory.Create函数就会调用到s3-aws/s3.go中的Create函数,进而调用到FromParameters函数:

func (factory *s3DriverFactory) Create(parameters map[string]interface{}) (storagedriver.StorageDriver, error) {
	return FromParameters(parameters)
}

在FromParameters函数中,会根据配置文件中配置的s3的accessKey、regionName、bucket等一系列参数来完成初始化工作,最后调用New(params)函数来创建并返回最终的s3 storage driver:

// New constructs a new Driver with the given AWS credentials, region, encryption flag, and
// bucketName
func New(params DriverParameters) (*Driver, error) {
	
	……

	d := &driver{
		S3:        s3obj,
		Bucket:    params.Bucket,
		ChunkSize: params.ChunkSize,
		Encrypt:   params.Encrypt,
		KeyID:     params.KeyID,
		MultipartCopyChunkSize:      params.MultipartCopyChunkSize,
		MultipartCopyMaxConcurrency: params.MultipartCopyMaxConcurrency,
		MultipartCopyThresholdSize:  params.MultipartCopyThresholdSize,
		RootDirectory:               params.RootDirectory,
		StorageClass:                params.StorageClass,
		ObjectACL:                   params.ObjectACL,
	}

	return &Driver{
		baseEmbed: baseEmbed{
			Base: base.Base{
				StorageDriver: d,
			},
		},
	}, nil
}

这样,就完成了storage driver的初化化工作,driver最终保存在了app.driver变量中。

下面,以manifest的put为例,讲述registry镜像至storage的过程。

通过NewApp函数中的app.register(v2.RouteNameManifest, manifestDispatcher)这段代码,所有manifest的API请求都会registry/handlers/manifests.go中的manifestDispatcher这个函数来处理。在这个函数中,定义了http router的处理handler:

mhandler := handlers.MethodHandler{
	"GET":  http.HandlerFunc(manifestHandler.GetManifest),
	"HEAD": http.HandlerFunc(manifestHandler.GetManifest),
}

if !ctx.readOnly {
	mhandler["PUT"] = http.HandlerFunc(manifestHandler.PutManifest)
	mhandler["DELETE"] = http.HandlerFunc(manifestHandler.DeleteManifest)
}

manifest的PUT请求由manifestHandler.PutManifest来处理。

在PutManifest函数中,首先会调用imh.Repository.Manifests函数生成manifestService:

manifests, err := imh.Repository.Manifests(imh)
if err != nil {
	imh.Errors = append(imh.Errors, err)
	return
}

其中,imh.Repository变量是由registry启动时构建在context中的,这段代码会调用到registry/storage/registry.go中的Manifests函数,返回manifestStore结构体变量,manifestStore实现了manifests.go中的ManifestService接口。

之后,通过调用manifests.Put,调用到registry/storage/manifeststore.go中的Put函数。

_, err = manifests.Put(imh, manifest, options...)
if err != nil {

在该Put函数中,会根据manifest的type是schemal1还是schema2,调用其相应的Put函数。以schema2为例,调用到registry/storage/schema2manifesthandler.go中的Put函数。

在这个Put函数中,进一步会调用到ms.blobStore.Put函数:

revision, err := ms.blobStore.Put(ctx, mt, payload)
if err != nil {
	context.GetLogger(ctx).Errorf("error putting payload into blobstore: %v", err)
	return "", err
}

接下来,在registry/storage/blobstore.go中最终会调用到bs.driver.PutContent函数:

return distribution.Descriptor{
		Size: int64(len(p)),

		// NOTE(stevvooe): The central blob store firewalls media types from
		// other users. The caller should look this up and override the value
		// for the specific repository.
		MediaType: "application/octet-stream",
		Digest:    dgst,
	}, bs.driver.PutContent(ctx, bp, p)

如果storage driver是s3,那么会调用到registry/storage/driver/s3-aws/s3.go中的PutContent函数,在这个函数中,最终会调用s3 sdk的PutObject接口,完成数据的上传工作:

// PutContent stores the []byte content at a location designated by "path".
func (d *driver) PutContent(ctx context.Context, path string, contents []byte) error {
	_, err := d.S3.PutObject(&s3.PutObjectInput{
		Bucket:               aws.String(d.Bucket),
		Key:                  aws.String(d.s3Path(path)),
		ContentType:          d.getContentType(),
		ACL:                  d.getACL(),
		ServerSideEncryption: d.getEncryptionMode(),
		SSEKMSKeyId:          d.getSSEKMSKeyID(),
		StorageClass:         d.getStorageClass(),
		Body:                 bytes.NewReader(contents),
	})
	return parseError(path, err)
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值