Kraken P2P 镜像分发系统的生产实践

2021-07-08

1. 基本介绍

Kraken (https://github.com/uber/kraken) 是一个由P2P(peer-to-peer)驱动的专注于提升扩展性和可靠性的docker镜像仓库。它被设计用来在混合云环境中进行docker镜像管理、复制和分发。通过可插拔式的后端支持,可以方便和已有的镜像仓库(如Harbor)协作(甚至可替代Harbor的核心功能:支持镜像拉取也支持镜像下载,将在下文介绍),作为镜像分发层来运行。

其可做到每天分发100w个镜像层,其中包含10w个超过1GB大小的块。峰值性能下,30s内可分发2w个100MB - 1G的镜像层

2. 功能特点

  • 高扩展性。Kraken镜像分开速度 > 主机下载速度的50%。而且,集群规模和镜像大小对下载速度没有明显影响。
    • 每个集群至少可支持15k台主机
    • 支持任意大镜像层。默认限制为20G,以获得最佳性能
  • 高可用性。所有组件都不是单点部署
  • 安全性。支持TLS上传验证和数据完整性保护
  • 兼容多种后端存储。将数据放在可靠存储服务,如S3, GCS, ECR, HDFS等,同时接入其他存储非常容易
  • 可实现跨集群镜像同步复制
  • 依赖极少。依赖后端存储之外,只依赖DNS

3. 设计理念

Kraken的高级思想是让少量专用主机(调用镜像存储服务拉取镜像)将内容播种到运行在集群中每个主机上的Agent上(即一个deamonset服务)

一个中心组件,跟踪器(tracker,类似Zookeeper的组件),将协调网络中的所有参与者,形成一个伪随机规则图(各个Agent之前随机同步)

这样的图连通度高,直径小。结果是,就算只有一个播种者,一秒内加入了成千上万的端点(peer),所有参与者理论上可以达到至少80%的最大上传/下载速度(当前实现下可达到60%),性能在镜像size变大和集群规模变大时不受影响

4. 架构说明

4.1 核心流程图

img

  • Agent
    • 部署在每台Node上
    • 实现docker镜像仓库接口(即可以接受docker pull请求)
    • 向tracker申明自己拥有的内容
    • 连接请求tracker后返回peer列表,用于下载内容
  • Origin
    • 专用播种机
    • 将镜像层存储在可靠存储上 (e.g. S3, GCS, ECR)
    • 形成一个自修复散列环来分配负载
  • Tracker
    • 记录哪些peer上有哪些内容 (包含下载中和下载完成的)
    • 提供拉取某些镜像层对应的peer列表
  • Proxy
    • 实现docker镜像仓库接口(即可以接受docker push请求)
    • 上传镜像层到origin,进而到后端存储
    • 上传Tag到Build Index,进而到后端存储
  • Build-Index
    • 将 tag 映射到 digest,如 v1.0.0 → sha256:6a8b53655bcda24a2a44131e249f70eec0251d2b8e6d75c901ebe7d3f34c641d (存储镜像层的后端存储只认识Digest)
    • 无一致性保证。镜像覆盖场景,如某个组件多次打镜像,一直用latest作为tag,因为ngnix缓存关系,Build-Index可能返回的不是最新的digest
    • 支持多集群间镜像复制
    • 将Tag存储在可靠存储上 (e.g. S3, GCS, ECR)

4.2 镜像拉取流程图

img

4.3 镜像复制流程图

img

5. 基准测试

3GB 镜像 每个镜像有两个镜像层 2600个节点同时下载(要进行5200次下载) 节点下载限速300MB/s (使用 5个trackers实例和 5个origins实例):

  • p50 = 10s
  • p99 = 18s
  • p99.9 = 22s

结论:2600个镜像层,在10s内下载完成,几乎所有的镜像层,在22s内全部下载完成

6. 与其他项目对比

6.1 Gragonfly from Alibaba

Dragonfly集群有一个或几个“超级节点”(类似Kraken中tracker的角色),它们协调集群中每4MB数据块的传输。

虽然超级节点可能会做出最优选择,但整个集群的吞吐量由几个节点的性能决定,随着镜像层大小和集群规模的增长,镜像分发性能将线性降低

Kraken的tracker只负责协调各个端点形成一个随机连接图,让数据传输的细节由各个端点自行决定,所以面对大镜像层分发有更好的扩展性。此外,Kraken支持HA并支持跨集群的镜像复制,对混合云环境而言非常重要

6.2 BitTorrent

Kraken最初基于BitTorrent创建,最终因考虑与存储方案集成和方便做性能优化,实现了自己的P2P驱动

7. 使用限制

  • Kraken对单次docker pull的速度并没有提升,只有在镜像分发吞吐量成了你的瓶颈时才能实现威力。如果想加速docker pull,可以用Makisu,实现在构建时镜像层更好的复用,或者调整镜像压缩比,因为拉取镜像时大部分时间都花在了解压缩上面
  • Build-Index无一致性保证。镜像覆盖场景,如某个组件多次打镜像,一直用latest作为tag,因为ngnix缓存关系,Build-Index可能返回的不是最新的digest,作者正在优化这块的功能逻辑。当前可以通过减少缓存失效时间来部分解决
  • 理论上,Kraken可分发任意大小的镜像层,而性能几乎不受影响。不过当前强制执行20G的限制,不能支持超大镜像层的使用。如果你有超大镜像层的需求,建议可以先把他们分割成小于10GB的层

8. 与Harbor集成

8.1 架构图

img

好处:

  • 保留harbor这一层,有利于利用harbor的项目管理、镜像扫描等kraken不具备的能力,且如果kraken出现问题,也可切换到harbor正常拉取镜像
  • 结合harbor-registry的通知机制,可同时实现镜像预热+P2P镜像分发,极大加快发布的速度

8.2 镜像预热

8.2.1 修改harbor-registry的配置文件,增加另一个通知项

notifications:
  endpoints:
    - name: harbor
      disabled: false
      url: http://harbor-harbor-core/service/notifications
      timeout: 3000ms
      threshold: 5
      backoff: 1s
      ignoredmediatypes:
        - application/vnd.docker.image.rootfs.diff.tar.gzip
        - application/vnd.docker.image.rootfs.foreign.diff.tar.gzip
        - application/vnd.oci.image.layer.v1.tar
        - application/vnd.oci.image.layer.v1.tar+gzip
        - application/vnd.oci.image.layer.v1.tar+zstd
        - application/vnd.oci.image.layer.nondistributable.v1.tar
        - application/vnd.oci.image.layer.nondistributable.v1.tar+gzip
        - application/vnd.oci.image.layer.nondistributable.v1.tar+zstd
        - application/octet-stream
    - name: kraken
      disabled: false
      url: http://kraken-proxy.kraken.svc.cluster.local:10050/registry/notifications
      timeout: 3000ms
      threshold: 5
      backoff: 1s

8.2.2 修改kraken-origin、kraken-buildindex的配置文件,将harbor配置为存储服务

build_index:
  replicas: 3
  extraBackends: |-
    - namespace: .*
      backend:
        registry_tag:
          address: xxx
          security:
            basic:
              username: admin
              password: ***

origin:
  replicas: 3
  extraBackends: |-
    - namespace: .*
      backend:
        registry_blob:
          address: xxx
          security:
            basic:
              username: admin
              password: ***

9. 核心配置 (代码块里均为默认配置)

9.1 Peer之间上传下载带宽 (node节点之间)

scheduler:
  conn:
    bandwidth:
      enable: true
      egress_bits_per_sec: 1677721600  # 200*8 Mbit
      ingress_bits_per_sec: 2516582400 # 300*8 Mbit

9.2 后端服务上传下载带宽 (与nos交互)

backends:
 - namespace: .*
   backend:
     s3: <omitted>
   bandwidth:
     enabled: true
     egress_bits_per_sec: 8589934592   # 8 Gbit
     ingress_bits_per_sec: 85899345920 # 10*8 Gbit

9.3 磁盘缓存过期时间和缓存文件数量

store:
  cache_cleanup:
    tti: 6h
  download_cleanup:
    tti: 6h

origin 另外可限制缓存Blob数

store:
  capacity: 1000000

9.4 内存缓存过期时间

agent / origin 配置

scheduler:
   seeder_tti: 5m

9.5 origin / build-index 配置多后端

backends:
- namespace: library/.*
   backend:
     registry_blob:
       address: index.docker.io
       timeout: 60s
       security:
         basic:
           username: ""
           password: ""
- namespace: test-domain/.*
   backend:
     http:
       download_url: http://test-domain:9000/download?sha256=%s
       download_backoff:
         enabled: true
- namespace: ecr-images/.*
  backend:
    registry_tag:
      address: 123456789012.dkr.ecr.<region>.amazonaws.com
      security:
        credsStore: 'ecr-login'
- namespace: s3-images/.*
  backend:
    s3:
      region: us-west-1
      bucket: test-bucket
      root_directory: /test-bucket/kraken/default/
      name_path: sharded_docker_blob
      username: kraken-user
- namespace: minio-images/.*
  backend:
    s3:
      region: us-east-1
      bucket: self-hosted-bucket
      root_directory: /kraken/default/
      name_path: sharded_docker_blob
      username: minio-user
      endpoint: http://172.17.0.1:9000
      disable_ssl: true
      force_path_style: true
  bandwidth:
    enable: true
- namespace: gcs-images/.*
  backend:
    gcs:
      username: kraken-user
      bucket: test-bucket
      root_directory: /test-bucket/kraken/default/
      name_path: sharded_docker_blob
  bandwidth:
    enable: true

auth:
 s3:
   kraken-user:
     s3:
       aws: kraken-user
       aws_access_key_id: <keyid>
       aws_secret_access_key: <key>
   minio-user:
     s3:
       aws: minio-user
       aws_access_key_id: <keyid>
       aws_secret_access_key: <key>
 gcs:
   kraken-user:
     gcs:
       access_blob: <service_account_key>

9.6 torrent并发下载数

 scheduler:
    connstate:
      max_open_conn: 10

10. 部署

10.1 通过Helm

# 添加 Helm repo
helm repo add iamyeka https://iamyeka.github.io/helm-charts

# 一键安装
helm install kraken -n kraken --create-namespace iamyeka/kraken

内置了 Grafana dashboard大盘,可导入使用,路径:https://github.com/iamyeka/helm-charts/tree/main/charts/kraken/dashboard

大盘的指标来源于二次开发添加的一些指标,具体修改见:https://github.com/iamyeka/kraken/tree/enhance-observability

生产实测,总体镜像平均拉取耗时下降 66.6%,最大拉取耗时下降 33.6%,加速效果较明显