Dragonfly V2 分发集群的镜像

共 22393字,需浏览 45分钟

 ·

2024-03-24 18:00


1. Dragonfly 简介


Dragonfly 的相关文档在社区 https://d7y.io/zh/docs/ 已经有详细说明。这里只是简单介绍一下,V2 版本的主要组件:



  • Manager,提供 UI 界面、用户管理、集群监控、任务管理等功能

  • Scheduler,调度 Peer 之间的流量、提供预热等功能

  • Seed Peer,回源节点,用于从源站(Harbor、Docker.io 等)下载数据,也可以作为 Peer 节点

  • Peer,提供下载数据的终端节点


其中 Manager、Scheduler 是单独的容器镜像,Seed Peer 和 Peer 是同一个容器镜像。


Dragonfly 支持的镜像预热功能,可以和 Harbor 进行集成,但本文不会涉及。本文主要是介绍我们在支撑 AI 业务时,生产环境下的一些实践。值得注意的是 Dragonfly V2 实际上构建了一个 P2P 分发的网络,不仅可以分发镜像,还可以分发文件,这就打开了想象空间。


2. IDC 机房中的 Dragonfly 集群


我们 AI 模型的推理和训练都是基于 Kubernetes 集群,后端存储采用的是企业版 JuiceFS,在每个 Node 节点都挂载了几个 T 的 SSD 磁盘,用来挂载 JuiceFS 的缓存目录。


因此,Kubernetes 集群中的每个 Node 节点都具备作为 Dragonfly Peer 节点的条件。但 Peer 组网时,我们不希望有额外的负担,包括:



  • 跨 VPC 的 NAT 流量

  • 公网传输数据


下面是 Dragonflyv2 机房多 VPC 部署拓扑图:


3a9782d638e406554e5cf82c570c2d64.webp



  • LB 需要公网 IP,作为 Peer 的接入点

  • 一个 VPC 对应一个 Dragonfly 的 Cluster 抽象

  • 虽然 IDC 打通了 VPC 之间的网络,但一个 VPC 内的 Peer 才允许组网

  • 集群内每个 Node 节点部署一个 Peer


VPC 内,下面这张图给出了详细的高可用方案。


ead6456e236346b9e0f92fc3e368aaa9.webp



  • LB 只需要内网 IP 即可

  • 使用云厂的 MySQL 8.0、Redis 6 服务

  • 两台 VM 部署 Manager、Scheduler、Seed Peer

  • 每个 VM 部署的是一套完整的 Dragonfly 集群,包括 Manager、Scheduler、Seed Peer,不用经过 LB 也能用

  • 每个 Node 节点部署 Peer


Dragonfly 构建的 P2P 分发网络,不应该和 PaaS 层耦合太紧密,避免循环依赖。因此,这里采用双 VM 的方案,共享数据存储,保障可用性。在 Kubernetes 集群的 Master 节点上,我们也不会进行加速优化,保障 PaaS 层控制面的简洁和独立。


3. VM 上部署 Dragonfly 控制平面


需要提前安装好 Docker,分别在两台 VM 上进行独立部署。


3.1 安装 docker-compose



  • 下载 docker-compose


      
curl -L https://mirror.ghproxy.com/https://github.com/docker/compose/releases/download/v2.23.3/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose



  • 添加执行权限


      
chmod +x /usr/local/bin/docker-compose



  • 查看版本


      
docker-compose -v


3.2 安装 dragonfly


参考 https://d7y.io/zh/docs/getting-started/quick-start/docker-compose/



  • 下载 docker-compose 部署文件


      
cd /data
wget https://mirror.ghproxy.com/https://github.com/dragonflyoss/Dragonfly2/archive/refs/tags/v2.1.28.tar.gz
tar -zxvf v2.1.28.tar.gz
cp -r Dragonfly2-2.1.28/deploy/docker-compose ./



  • 清理不需要的文件


      
rm -rf *2.1.28*



  • 生成默认的配置文件


      
cd docker-compose


由于默认的发布包中,没有配置文件,这里先生成一份配置文件,然后再修改。


      
export IP=VM_IP
./run.sh


立即终止执行,然后继续修改配置文件。



  • 固定镜像版本


      
sed -i 's/latest/v2.1.28/g' docker-compose.yaml



  • 修改存储账号及其他配置


修改 Redis、MySQL 地址和密码


      
vim template/manager.template.yaml


修改 Redis 密码


      
vim template/scheduler.template.yaml


在这两个配置文件中,还有一些其他的配置项,可以根据实际情况进行修改。比如,manager 的 addr 指向当前主机的服务、将日志输出到控制台、开启 Metrics 等。



  • 修改 seed-peer 的缓存目录


      
vim docker-compose.yaml


      
volumes:
  - ./cache:/var/cache/dragonfly
  - ./data:/var/lib/dragonfly


如果关闭了 seed-peer 作为 peer 节点的功能,可以跳过这一步,同时,VM 的磁盘空间也可以不用太大。



  • 启动服务


      
docker-compose up -d



  • 查看服务


      
docker-compose ps

NAME        IMAGE                            COMMAND                  SERVICE     CREATED        STATUS                  PORTS
manager     dragonflyoss/manager:v2.1.28     "/opt/dragonfly/bin/…"   manager     14 hours ago   Up 14 hours (healthy)   0.0.0.0:8080->8080/tcp, 0.0.0.0:65003->65003/tcp
scheduler   dragonflyoss/scheduler:v2.1.28   "/opt/dragonfly/bin/…"   scheduler   14 hours ago   Up 14 hours (healthy)   0.0.0.0:8002->8002/tcp
seed-peer   dragonflyoss/dfdaemon:v2.1.28    "/opt/dragonfly/bin/…"   seed-peer   14 hours ago   Up 14 hours (healthy)   65001/tcp, 0.0.0.0:65006-65008->65006-65008/tcp



  • 打开管理页面看看


访问 http://${VM_IP}:8080 端口可以看到 Dragonfly 的管理界面,如果机器没有公网 IP,可以使用 socat 进行端口转发。找一台有公网 IP 的机器,执行以下命令,将 30000 端口转发到 8080 端口:


      
export IP=VM_IP
socat TCP-LISTEN:30000,fork TCP:$IP:8080


两台 VM 部署完成,在 Dashboard 中可以看到这样一个集群,两个 Scheduler、两个 Seed Peer。如下图:


a0dfe461174ea0580b0f84dc53db77a7.webp


4. 在集群部署 Peer 节点



  • 创建命名空间


      
kubectl create ns dragonfly-system



  • 创建配置文件


这里需要将 LB 的 IP 地址填入配置文件中,Peer 才能接入到 Dragonfly 集群中。


      
export MANAGER_IP=LB_IP


有很多参数,可以根据实际情况进行修改,这里提供了一份默认的配置文件。


      
kubectl apply -f - <<EOF
apiVersion: v1
data:
  dfget.yaml: |
    aliveTime: 0s
    gcInterval: 1m0s
    keepStorage: false
    workHome: /usr/local/dragonfly
    logDir: /var/log/dragonfly
    cacheDir: /var/cache/dragonfly
    pluginDir: /usr/local/dragonfly/plugins
    dataDir: /var/lib/dragonfly
    console: true
    health:
      path: /server/ping
      tcpListen:
        port: 40901
    verbose: true
    pprof-port: 18066
    metrics: ":8000"
    jaeger: ""
    scheduler:
      manager:
        enabletrue
        netAddrs:
          - type: tcp
            addr: $MANAGER_IP:65003
        refreshInterval: 10m
      netAddrs:
      scheduleTimeout: 30s
      disableAutoBackSource: false
      seedPeer:
        clusterID: 1
        enablefalse
        type: super
    host:
      idc: ""
      location: ""
    download:
      calculateDigest: true
      downloadGRPC:
        security:
          insecure: true
          tlsVerify: true
        unixListen:
          socket: ""
      peerGRPC:
        security:
          insecure: true
        tcpListen:
          port: 65000
      perPeerRateLimit: 512Mi
      prefetch: false
      totalRateLimit: 1024Mi
    upload:
      rateLimit: 1024Mi
      security:
        insecure: true
        tlsVerify: false
      tcpListen:
        port: 65002
    objectStorage:
      enablefalse
      filter: Expires&Signature&ns
      maxReplicas: 3
      security:
        insecure: true
        tlsVerify: true
      tcpListen:
        port: 65004
    storage:
      diskGCThreshold: 1000Gi
      multiplex: true
      strategy: io.d7y.storage.v2.simple
      taskExpireTime: 72h
    proxy:
      defaultFilter: Expires&Signature&ns
      defaultTag:
      tcpListen:
        port: 65001
      security:
        insecure: true
        tlsVerify: false
      registryMirror:
        dynamic: true
        insecure: false
        url: https://index.docker.io
      proxies:
        - regx: blobs/sha256.*
    security:
      autoIssueCert: false
      caCert: ""
      certSpec:
        dnsNames: null
        ipAddresses: null
        validityPeriod: 4320h
      tlsPolicy: prefer
      tlsVerify: false
    network:
      enableIPv6: false
    announcer:
      schedulerInterval: 30s
kind: ConfigMap
metadata:
  labels:
    app: dragonfly
  name: dragonfly-dfdaemon
  namespace: dragonfly-system
EOF



  • 创建 DaemonSet


我们从官方的 Helm Chart 中提取出来的 DaemonSet 文件。需要注意的是,Peer 使用的缓存目录是主机上的 /data/dfget 目录。最好提前清理主机上的 /data/dfget 目录,避免出现权限问题,也不用提前创建,DaemonSet 会自动创建。


      
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    app: dragonfly
  name: dragonfly-dfdaemon
  namespace: dragonfly-system
spec:
  selector:
    matchLabels:
      app: dragonfly
  template:
    metadata:
      labels:
        app: dragonfly
    spec:
      containers:
      - image: dragonflyoss/dfdaemon:v2.1.28
        livenessProbe:
          exec:
            command:
            - /bin/grpc_health_probe
            - -addr=:65000
        name: dfdaemon
        ports:
        - containerPort: 65001
          protocol: TCP
        - containerPort: 40901
          hostIP: 127.0.0.1
          hostPort: 40901
          protocol: TCP
        - containerPort: 8000
          protocol: TCP
        readinessProbe:
          exec:
            command:
            - /bin/grpc_health_probe
            - -addr=:65000
          failureThreshold: 3
          initialDelaySeconds: 5
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        resources:
          limits:
            cpu: "2"
            memory: 2Gi
        securityContext:
          capabilities:
            add:
            - SYS_ADMIN
        volumeMounts:
        - mountPath: /etc/dragonfly
          name: config
        - mountPath: /var/cache/dragonfly
          name: dfgetcache
        - mountPath: /var/lib/dragonfly
          name: dfgetdata
      hostNetwork: true
      hostPID: true
      volumes:
      - configMap:
          defaultMode: 420
          name: dragonfly-dfdaemon
        name: config
      - hostPath:
          path: /data/dfget/cache
          type: DirectoryOrCreate
        name: dfgetcache
      - hostPath:
          path: /data/dfget/data
          type: DirectoryOrCreate
        name: dfgetdata
EOF



  • 查看负载


      
kubectl -n dragonfly-system get pod

NAME                       READY   STATUS    RESTARTS   AGE
dragonfly-dfdaemon-79qkw   1/1     Running   0          14h
dragonfly-dfdaemon-8hhzb   1/1     Running   3          14h
dragonfly-dfdaemon-nnfc5   1/1     Running   0          14h
dragonfly-dfdaemon-w7lff   1/1     Running   0          14h
dragonfly-dfdaemon-wrmzw   1/1     Running   0          14h


5. 在 VM 上部署 Peer 节点



  • 创建目录


      
mkdir -p /data/dfget && cd /data/dfget



  • 设置 IP


      
wget https://mirror.ghproxy.com/https://raw.githubusercontent.com/shaowenchen/hubimage/main/nydus/dfget.template.yaml -O dfget.yaml


      
export MANAGER_IP=LB_IP
sed -i "s/__MANAGER_IP__/$MANAGER_IP/g" dfget.yaml



  • 启动 Peer


      
nerdctl run -d --name=peer --restart=always \
            -p 65000:65000 -p 65001:65001 -p 65002:65002 \
            -v $(pwd)/data:/var/lib/dragonfly \
            -v $(pwd)/cache:/var/cache/dragonfly \
            -v $(pwd)/dfget.yaml:/etc/dragonfly/dfget.yaml:ro \
            dragonflyoss/dfdaemon:v2.1.28


6. 使用节点配置


6.1 Docker


Docker 的 Mirror 方式只能加速 Docker.io 的镜像,这里采用 Proxy 的方式,代理全部 Dockerd 的流量。Proxy 与 Mirror 的区别在于,Mirror 挂了,Dockerd 会拉取源站,而 Proxy 挂了,Dockerd 直接拉取失败。



  • 添加代理


      
mkdir -p /etc/systemd/system/docker.service.d


      
cat > /etc/systemd/system/docker.service.d/http-proxy.conf <<EOF
[Service]
Environment="HTTP_PROXY=http://127.0.0.1:65001"
Environment="HTTPS_PROXY=http://127.0.0.1:65001"
EOF



  • 重启 Docker


      
systemctl daemon-reload
systemctl restart docker


注意,这里如果 /etc/docker/daemon.json 中没有配置 "live-restore": true ,会导致容器全部重启。



  • 查看环境变量


      
systemctl show --property=Environment docker

Environment=HTTP_PROXY=http://127.0.0.1:65001 HTTPS_PROXY=http://127.0.0.1:65001



  • 镜像拉取测试


      
docker pull nginx


此时,Dockerd 的流量会经过 Dragonfly Peer 节点。


6.2 Containerd


参考 https://github.com/containerd/containerd/blob/main/docs/cri/config.md#registry-configuration


/etc/containerd/config.toml[plugins."io.containerd.grpc.v1.cri".registry] 项的 config_path = "/etc/containerd/certs.d" 提供了类似于 mirror 的配置方式。



  • 配置 Docker.io


      
mkdir -p /etc/containerd/certs.d/docker.io


      
cat > /etc/containerd/certs.d/docker.io/hosts.toml <<EOF
server = "https://docker.io"

[host."http://127.0.0.1:65001"]
  capabilities = ["pull""resolve"]
  [host."http://127.0.0.1:65001".header]
    X-Dragonfly-Registry = ["https://registry-1.docker.io"]
  [host."https://registry-1.docker.io"]
    capabilities = ["pull""resolve"]
EOF



  • 配置其他、私有镜像仓库


其他镜像仓库的配置可以通过脚本生成,比如:


      
wget https://mirror.ghproxy.com/https://raw.githubusercontent.com/dragonflyoss/Dragonfly2/main/hack/gen-containerd-hosts.sh
bash gen-containerd-hosts.sh ghcr.io


这里没有用脚本生成 docker.io 的配置是因为,生成的配置文件中,X-Dragonfly-Registryhttps://docker.io,而不是 https://registry-1.docker.io


使用 X-Dragonfly-Registry = ["https://docker.io"] 会出现如下错误:


      
unknow type: text/html


以上添加的 mirror,不用重启 Containerd,直接能生效。



  • 镜像拉取测试


      
nerdctl pull nginx


此时,在本地 /data/dfget/data 目录下可以看到 Peer 节点缓存的镜像数据。


7. 集成 Nydus


如果 Nydus 已经配置好,这里其实已经能轻松配置好。



  • 给 Nydusd 添加 mirror


      
vim /etc/nydus/nydusd-config.fusedev.json


      
{
  "device": {
    "backend": {
      "type""registry",
      "config": {
        "mirrors": [
          {
            "host""http://127.0.0.1:65001",
            "auth_through"false,
            "headers": {
              "X-Dragonfly-Registry""https://index.docker.io"
            },
            "ping_url""http://127.0.0.1:40901/server/ping"
          }
        ]
      }
    }
  }
}



  • 重启 Nydusd


      
systemctl restart nydus-snapshotter



  • 镜像拉取测试


      
nerdctl pull shaowenchen/demo-ubuntu:latest-nydus


8. 总结


本篇记录了这周在生产环境中,测试并部署 Dragonfly V2 的部分过程,主要内容包括:



  • 机房中 Dragonfly 集群的部署拓扑

  • 集群和 VM 上 Peer 节点的部署

  • Docker、Containerd、Nydus 的集成


说下不足,没有指标监控,在做 Benchmark 时,我们发现 AZ 内和跨 AZ 的 Peer 之间的数据传输都受限,如果想构建高性能的 P2P 分发网络,Peer 与 Peer、Peer 与 Seed Peer 之间的网络是一个重要的考量因素。


bc8d4ff952a98acf524367176df89b7a.webp


浏览 203
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报