Dragonfly GitHub Repo:
https://github.com/dragonflyoss/Dragonfly2
本文将帮助您将 Dragonfly 与 Hugging Face 一起使用。当在 Hugging Face 下载数据集以及模型的时候,文件相对较大且会有并发下载文件的场景。这样很容易导致 Hugging Face 的 Git LFS 存储带宽被打满,从而引起下载过慢的情况,影响训练以及推理服务的使用。

这种方式比较好的解决方案是使用 Dragonfly 的 P2P 技术利用每个节点的闲置带宽缓解 Git LFS 存储的带宽压力,从而达到加速效果。在最理想的情况下 Dragonfly 可以让整个 P2P 集群中只有一个节点回源 Hugging Face 下载数据集或模型,其他节点流量均使用集群内 P2P 内网带宽。

Part.1
集成方案
1.1 依赖
| 所需软件 | 版本要求 | 文档 | 
| Kubernetes cluster | 1.20+ | kubernetes.io | 
| Helm | 3.8.0+ | helm.sh | 
注意: 如果没有可用的 Kubernetes 集群进行测试,推荐使用 Kind。
1.2 安装 Dragonfly
基于 Kubernetes cluster 详细安装文档可以参考 quick-start-kubernetes。
- 使用 Kind 安装 Kubernetes 集群 
创建 Kind 多节点集群配置文件 kind-config.yaml,配置如下:
kind: ClusterapiVersion: kind.x-k8s.io/v1alpha4nodes:role: control-planerole: workerextraPortMappings:containerPort: 30950hostPort: 65001role: worker
使用配置文件创建 Kind 集群:
kind create cluster --config kind-config.yaml切换 Kubectl 的 context 到 Kind 集群:
kubectl config use-context kind-kind- Kind 加载 Dragonfly 镜像 
下载 Dragonfly latest 镜像:
docker pull dragonflyoss/scheduler:latestdocker pull dragonflyoss/manager:latestdocker pull dragonflyoss/dfdaemon:latest
Kind 集群加载 Dragonfly latest 镜像:
kind load docker-image dragonflyoss/scheduler:latestkind load docker-image dragonflyoss/manager:latestkind load docker-image dragonflyoss/dfdaemon:latest
- 基于 Helm Charts 创建 Dragonfly P2P 集群 
创建 Helm Charts 配置文件 charts-config.yaml 并且设置 dfdaemon.config.proxy.registryMirror.url 为 Hugging Face 的 LFS 服务的地址, 配置如下:
scheduler:replicas: 1metrics:enable: trueconfig:verbose: truepprofPort: 18066seedPeer:replicas: 1metrics:enable: trueconfig:verbose: truepprofPort: 18066dfdaemon:metrics:enable: truehostNetwork: trueconfig:verbose: truepprofPort: 18066proxy:defaultFilter: 'Expires&Key-Pair-Id&Policy&Signature'security:insecure: truetcpListen:listen: 0.0.0.0port: 65001registryMirror:# When enable, using header "X-Dragonfly-Registry" for remote instead of url.dynamic: true# URL for the registry mirror.url: https://cdn-lfs.huggingface.co# Whether to ignore https certificate errors.insecure: true# Optional certificates if the remote server uses self-signed certificates.certs: []# Whether to request the remote registry directly.direct: false# Whether to use proxies to decide if dragonfly should be used.useProxies: trueproxies:regx: repos.*useHTTPS: truemanager:replicas: 1metrics:enable: trueconfig:verbose: truepprofPort: 18066
使用配置文件部署 Dragonfly Helm Charts:
$ helm repo add dragonfly https://dragonflyoss.github.io/helm-charts/$ helm install --wait --create-namespace --namespace dragonfly-system dragonfly dragonfly/dragonfly -f charts-config.yamlNAME: dragonflyLAST DEPLOYED: Wed Oct 19 04:23:22 2022NAMESPACE: dragonfly-systemSTATUS: deployedREVISION: 1TEST SUITE: NoneNOTES:1. Get the scheduler address by running these commands:export SCHEDULER_POD_NAME=$(kubectl get pods --namespace dragonfly-system -l "app=dragonfly,release=dragonfly,component=scheduler" -o jsonpath={.items[0].metadata.name})export SCHEDULER_CONTAINER_PORT=$(kubectl get pod --namespace dragonfly-system $SCHEDULER_POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")kubectl --namespace dragonfly-system port-forward $SCHEDULER_POD_NAME 8002:$SCHEDULER_CONTAINER_PORTecho "Visit http://127.0.0.1:8002 to use your scheduler"2. Get the dfdaemon port by running these commands:export DFDAEMON_POD_NAME=$(kubectl get pods --namespace dragonfly-system -l "app=dragonfly,release=dragonfly,component=dfdaemon" -o jsonpath={.items[0].metadata.name})export DFDAEMON_CONTAINER_PORT=$(kubectl get pod --namespace dragonfly-system $DFDAEMON_POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")You can use $DFDAEMON_CONTAINER_PORT as a proxy port in Node.3. Configure runtime to use dragonfly:https://d7y.io/docs/getting-started/quick-start/kubernetes/
检查 Dragonfly 是否部署成功:
$ kubectl get po -n dragonfly-systemNAME READY STATUS RESTARTS AGEdragonfly-dfdaemon-rhnr6 1/1 Running 4 (101s ago) 3m27sdragonfly-dfdaemon-s6sv5 1/1 Running 5 (111s ago) 3m27sdragonfly-manager-67f97d7986-8dgn8 1/1 Running 0 3m27sdragonfly-mysql-0 1/1 Running 0 3m27sdragonfly-redis-master-0 1/1 Running 0 3m27sdragonfly-redis-replicas-0 1/1 Running 1 (115s ago) 3m27sdragonfly-redis-replicas-1 1/1 Running 0 95sdragonfly-redis-replicas-2 1/1 Running 0 70sdragonfly-scheduler-0 1/1 Running 0 3m27sdragonfly-seed-peer-0 1/1 Running 2 (95s ago) 3m27s
创建 Peer Service 配置文件 peer-service-config.yaml 配置如下:
apiVersion: v1kind: Servicemetadata:name: peernamespace: dragonfly-systemspec:type: NodePortports:name: http-65001nodePort: 30950port: 65001selector:app: dragonflycomponent: dfdaemonrelease: dragonfly
使用配置文件部署 Peer Service:
kubectl apply -f peer-service-config.yaml1.3 通过 Dragonfly 分发 Hub Python Library 的下载文件流量
任何 Hub Python Library 的 API 使用 Requests 库下载文件,都可以通过设置 DragonflyAdapter 将流量使用 Dragonfly 分发。
- 使用 Dragonfly 下载单个文件 
下载单个文件可以使用 hf_hub_download, 并且通过 Dragonfly 分发流量。
创建 hf_hub_download_dragonfly.py 文件,使用 DragonflyAdapter 将下载流量转发至 Dragonfly HTTP Proxy。这样可以通过 P2P 网络分发流量,内容如下:
import requestsfrom requests.adapters import HTTPAdapterfrom urllib.parse import urlparsefrom huggingface_hub import hf_hub_downloadfrom huggingface_hub import configure_http_backendclass DragonflyAdapter(HTTPAdapter):def get_connection(self, url, proxies=None):# Change the schema of the LFS request to download large files from https:// to http://,# so that Dragonfly HTTP proxy can be used.if url.startswith('https://cdn-lfs.huggingface.co'):url = url.replace('https://', 'http://')return super().get_connection(url, proxies)def add_headers(self, request, **kwargs):super().add_headers(request, **kwargs)# If there are multiple different LFS repositories, you can override the# default repository address by adding X-Dragonfly-Registry header.if request.url.find('example.com') != -1:request.headers["X-Dragonfly-Registry"] = 'https://example.com'# Create a factory function that returns a new Session.def backend_factory() -> requests.Session:session = requests.Session()session.mount('http://', DragonflyAdapter())session.mount('https://', DragonflyAdapter())session.proxies = {'http': 'http://127.0.0.1:65001'}return session# Set it as the default session factoryconfigure_http_backend(backend_factory=backend_factory)hf_hub_download(repo_id="tiiuae/falcon-rw-1b", filename="pytorch_model.bin")
通过 Dragonfly 基于 LFS 协议下载单个文件:
$ python3 hf_hub_download_dragonfly.py(…)YkNX13a46FCg__&Key-Pair-Id=KVTP0A1DKRTAX: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2.62G/2.62G [00:52<00:00, 49.8MB/s]
- 验证基于 Dragonfly 下载单个文件 
执行命令:
# find podskubectl -n dragonfly-system get pod -l component=dfdaemon# find logspod_name=dfdaemon-xxxxxkubectl -n dragonfly-system exec -it ${pod_name} -- grep "peer task done" /var/log/dragonfly/daemon/core.log
日志输出:
peer task done, cost: 28349ms   {"peer": "89.116.64.101-77008-a95a6918-a52b-47f5-9b18-cec6ada03daf", "task": "2fe93348699e07ab67823170925f6be579a3fbc803ff3d33bf9278a60b08d901", "component": "PeerTask", "trace": "b34ed802b7afc0f4acd94b2cedf3fa2a"}- 使用 Dragonfly 下载仓库快照 
下载仓库快照可以使用 snapshot_download, 并且通过 Dragonfly 分发流量。
创建 snapshot_download_dragonfly.py 文件,使用 DragonflyAdapter 将下载流量转发至 Dragonfly HTTP Proxy。只有 Git LFS 协议的大文件流量会通过 P2P 网络分发,内容如下:
import requestsfrom requests.adapters import HTTPAdapterfrom urllib.parse import urlparsefrom huggingface_hub import snapshot_downloadfrom huggingface_hub import configure_http_backendclass DragonflyAdapter(HTTPAdapter):def get_connection(self, url, proxies=None):# Change the schema of the LFS request to download large files from https:// to http://,# so that Dragonfly HTTP proxy can be used.if url.startswith('https://cdn-lfs.huggingface.co'):url = url.replace('https://', 'http://')return super().get_connection(url, proxies)def add_headers(self, request, **kwargs):super().add_headers(request, **kwargs)# If there are multiple different LFS repositories, you can override the# default repository address by adding X-Dragonfly-Registry header.if request.url.find('example.com') != -1:request.headers["X-Dragonfly-Registry"] = 'https://example.com'# Create a factory function that returns a new Session.def backend_factory() -> requests.Session:session = requests.Session()session.mount('http://', DragonflyAdapter())session.mount('https://', DragonflyAdapter())session.proxies = {'http': 'http://127.0.0.1:65001'}return session# Set it as the default session factoryconfigure_http_backend(backend_factory=backend_factory)snapshot_download(repo_id="tiiuae/falcon-rw-1b")
通过 Dragonfly 基于 LFS 协议下载仓库快照:
$ python3 snapshot_download_dragonfly.py(…)03165eb22f0a867d4e6a64d34fce19/README.md: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 7.60k/7.60k [00:00<00:00, 374kB/s](…)7d4e6a64d34fce19/configuration_falcon.py: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 6.70k/6.70k [00:00<00:00, 762kB/s](…)f0a867d4e6a64d34fce19/modeling_falcon.py: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 56.9k/56.9k [00:00<00:00, 5.35MB/s](…)3165eb22f0a867d4e6a64d34fce19/merges.txt: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 456k/456k [00:00<00:00, 9.07MB/s](…)867d4e6a64d34fce19/tokenizer_config.json: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 234/234 [00:00<00:00, 106kB/s](…)eb22f0a867d4e6a64d34fce19/tokenizer.json: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2.11M/2.11M [00:00<00:00, 27.7MB/s](…)3165eb22f0a867d4e6a64d34fce19/vocab.json: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 798k/798k [00:00<00:00, 19.7MB/s](…)7d4e6a64d34fce19/special_tokens_map.json: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 99.0/99.0 [00:00<00:00, 45.3kB/s](…)67d4e6a64d34fce19/generation_config.json: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 115/115 [00:00<00:00, 5.02kB/s](…)165eb22f0a867d4e6a64d34fce19/config.json: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1.05k/1.05k [00:00<00:00, 75.9kB/s](…)eb22f0a867d4e6a64d34fce19/.gitattributes: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1.48k/1.48k [00:00<00:00, 171kB/s](…)t-oSSW23tawg__&Key-Pair-Id=KVTP0A1DKRTAX: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2.62G/2.62G [00:50<00:00, 52.1MB/s]Fetching 12 files: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 12/12 [00:50<00:00, 4.23s/it]
- 验证基于 Dragonfly 下载仓库快照 
执行命令:
# find podskubectl -n dragonfly-system get pod -l component=dfdaemon# find logspod_name=dfdaemon-xxxxxkubectl -n dragonfly-system exec -it ${pod_name} -- grep "peer task done" /var/log/dragonfly/daemon/core.log
日志输出:
peer task done, cost: 28349ms   {"peer": "89.116.64.101-77008-a95a6918-a52b-47f5-9b18-cec6ada03daf", "task": "2fe93348699e07ab67823170925f6be579a3fbc803ff3d33bf9278a60b08d901", "component": "PeerTask", "trace": "b34ed802b7afc0f4acd94b2cedf3fa2a"}Part.2
性能测试
测试 Hugging Face Python Library 的 hf_hub_download API 与 Dragonfly 集成后的单机模型文件下载的性能。
由于机器本身网络环境、配置等影响,实际下载时间不具有参考价值, 但是不同场景下载时间所提升的比率是有重要意义的。

- Hugging Face Python Library: 使用 hf_hub_download API 直接下载模型文件。 
- Hugging Face Python Library & Dragonfly Cold Boot: 使用 hf_hub_download API 直接下载模型文件,没有命中任何缓存。 
- Hit Dragonfly Remote Peer Cache: 使用 hf_hub_download API 直接下载模型文件,在命中 Dragonfly 的远端 Peer 缓存。 
- Hit Dragonfly Remote Local Cache: 使用 hf_hub_download API 直接下载模型文件,在命中 Dragonfly 的本地 Peer 缓存。 
- Hit Hugging Face Cache: 使用 hf_hub_download API 直接下载模型文件,在命中 Hugging Face 的缓存。 
测试结果表明 Hugging Face Python Library 和 Dragonfly 集成,能够有效减少模型文件下载时间。测试是在单机情况下基本在缓存命中情况下, 性能瓶颈在于磁盘。如果在多节点并发下载数据集或者模型的情况下, Dragonfly 效果会更加明显。
Part.3
相关链接
Dragonfly 社区
- Website: - https://d7y.io/ 
- Github Repo: - https://github.com/dragonflyoss/Dragonfly2 
- Slack Channel: - #dragonfly on CNCF Slack 
- Discussion Group: - dragonfly-discuss@googlegroups.com 
- Twitter: - @dragonfly_oss 
Hugging Face
- Website: - https://huggingface.co/ 
- Github Repo: - https://github.com/huggingface/huggingface_hub 
- Document: - https://huggingface.co/docs 
- Hub Python Library: - https://huggingface.co/docs/huggingface_hub/index 
本文由 Hugging Face 中文社区内容共建项目提供,稿件由社区成员投稿,经授权发布于 Hugging Face 公众号。文章内容不代表官方立场,文中介绍的产品和服务等均不构成投资建议。了解更多请关注公众号:
如果你有与开源 AI、Hugging Face 相关的技术和实践分享内容,以及最新的开源 AI 项目发布,希望通过我们分享给更多 AI 从业者和开发者们,请通过下面的链接投稿与我们取得联系:
内容中包含的图片若涉及版权问题,请及时与我们联系删除


评论
沙发等你来抢