传输服务是一种简单灵活的服务,可用于在源和目的地之间传输人工制品对象。灵活的应用程序接口(API)允许传输接口的每个实施方案决定是否可以在源和目的地之间进行传输。这样,实现者就可以直接添加新功能,而无需对应用程序接口进行版本控制或要求其他实现者处理接口更改。
传输服务建立在 libchan 项目提出的核心理念基础之上:将二进制流和数据通道作为一级对象的应用程序接口更加灵活,可以在不需要不断更新协议和应用程序接口的情况下提供更广泛的用例。为了实现这一目标,传输服务使用了流服务,允许传输对象访问二进制流和对象流,即使在使用 grpc 和 ttrpc 时也是如此。
传输 API
传输 API 包含一个单独的操作,根据不同的操作对象,可以调用不同的操作。
在 Go 语言中,该应用程序接口看起来像:
            
            
              go
              
              
            
          
          type Transferrer interface {
	Transfer(ctx context.Context, source interface{}, destination interface{}, opts ...Opt) error
}proto API看起来像:
            
            
              proto
              
              
            
          
          service Transfer {
	rpc Transfer(TransferRequest) returns (google.protobuf.Empty);
}
message TransferRequest {
	google.protobuf.Any source = 1;
	google.protobuf.Any destination = 2;
	TransferOptions options = 3;
}
message TransferOptions {
 string progress_stream = 1;
 // 进展最小间隔时间
}转移对象(来源和目的地)
传输操作
|    源    |   目的地   |   描述    | 本地执行版本 |
|---------|---------|---------|--------|---|--------|
| 注册主机    | 镜像存储    | "pull"  | 1.7    |   | 本地实现版本 |
| 镜像存储库   | 注册主机    | "推"     | 1.7    |   | 对象流(存档 |
| 对象流(存档) | 镜像存储    | "导入"    | 1.7    |
| 镜像存储    | 对象流(存档) | "导出"    | 1.7    |
| 对象流(图层) | 装载/快照   | "解压缩"   | 未实施    |
| 安装/快照   | 对象流(层)  | "diff"  | 未实现    |
| 镜像存储    | 镜像存储    | "标签"    | 1.7    |   | 图像存储   |
| 注册主机    | 注册主机    | 镜像注册表映像 | 未实现    |
本地 containerd 守护进程支持
containerd 有一个内置传输插件,可实现大多数基本传输操作。本地插件的配置方式与其他 containerd 插件相同
            
            
              csharp
              
              
            
          
          [plugins]
[plugins."io.containerd.transfer.v1"]图表
拖动部件
数据流
作为操作的一部分,传输服务使用流来发送或接收数据流,以及处理回调(同步或异步)。流协议对于客户 Go 接口来说是不可见的。在通过 RPC 传输时,诸如函数、读取器和写入器等对象类型可以透明地转换为流协议。客户端和服务器接口可以保持不变,而proto的 marshaling 和 unmarshaling 需要了解流协议并访问流管理器。流由客户端使用客户端流管理器创建,并以字符串流标识符的形式通过proto RPC 发送。服务的服务器实现可以使用服务器端流管理器,通过流标识符查找流。
进度
Progress 是服务器发送给客户端的异步回调。它通常在 Go 接口中表示为一个简单的回调函数,由客户端实现并由服务器调用。
在 Go 类型中,progress 使用以下类型:
            
            
              go
              
              
            
          
          type ProgressFunc func(Progress)
type Progress struct {
	Event    string
	Name     string
	Parents  []string
	Progress int64
	Total    int64
}通过数据流发送的proto信息类型是:
            
            
              proto
              
              
            
          
          message Progress {
	string event = 1;
	string name = 2;
	repeated string parents = 3;
	int64 progress = 4;
	int64 total = 5;
}进度可作为传输选项传递,以获取任何传输操作的进度。进度事件可能因传输操作而异。
二进制流
传输对象也可以直接使用io.Reader和io.WriteCloser。
字节通过流传输时使用两种简单的proto信息类型:
            
            
              proto
              
              
            
          
          message Data {
	bytes data = 1;
}
message WindowUpdate {
	int32 update = 1;
}发送方发送 Data信息,接收方发送 WindowUpdate信息。当客户端发送 io.Reader 时,客户端是发送方,服务器是接收方。当客户端发送 io.WriteCloser时,服务器是发送方,客户端是接收方。
二进制流用于导入(发送 io.Reader)和导出(发送 io.WriteCloser)。
凭证
证书是作为服务器到客户端的同步回调来处理的。当服务器遇到来自注册表的授权请求时,就会进行回调。
在传输对象中使用凭证助手的 Go 接口如下所示:
            
            
              go
              
              
            
          
          type CredentialHelper interface {
	GetCredentials(ctx context.Context, ref, host string) (Credentials, error)
}
type Credentials struct {
	Host     string
	Username string
	Secret   string
	Header   string
}它使用proto信息通过数据流发送
            
            
              proto
              
              
            
          
          // AuthRequest 作为流的回调发送
message AuthRequest {
	// host 是注册表主机
	string host = 1;
	// 引用是从注册表请求的命名空间和存储库名称
	string reference = 2;
	// wwwauthenticate 是注册表返回的 HTTP WWW-Authenticate 头值
	repeated string wwwauthenticate = 3;
}
enum AuthType {
	NONE = 0;
	// CREDENTIALS 用于交换访问令牌的用户名/密码
  // 使用 oauth 或 `Docker 注册令牌服务器`
	CREDENTIALS = 1;
	// REFRESH 用于使用 oauth 或 "Docker 注册令牌 "服务器将secret与访问令牌交换。
	REFRESH = 2;
	// HEADER 用于将 HTTP 授权头直接设置为注册表的secret。
  // 其值应为 `<auth-scheme> <authorization-parameters>` 。
	HEADER = 3;
}
message AuthResponse {
	AuthType authType = 1;
	string secret = 2;
	string username = 3;
	google.protobuf.Timestamp expire_at = 4;
}