在上一篇文章中,分析了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)
}