介绍
Dragonfly 是一款基于 P2P 的智能镜像和文件分发工具。它旨在提高大规模文件传输的效率和速率,最大限度地利用网络带宽。在应用分发、缓存分发、日志分发和镜像分发等领域被大规模使用。
官方文档地址:https://d7y.io/zh/docs/
github地址:https://github.com/dragonflyoss/Dragonfly2
架构
Manager
1、存储动态配置供 Seed Peer 集群、Scheduler 集群以及 Dfdaemon 消费。
2、维护 Seed Peer 集群和 Scheduler 集群之间关联关系。
3、提供统一异步任务管理,用作预热等功能。
4、监听各模块是否健康运行。
5、为 Dfdaemon 筛选最优 Scheduler 集群调度使用。
6、提供可视化控制台,方便用户操作管理 P2P 集群。
Scheduler
1、基于机器学习的多场景自适应智能 P2P 节点调度, 为当前下载节点选择最优父节点。
2、构建 P2P 下载网络的有向无环图。
3、根据不同特征值评估节点下载能力, 剔除异常节点。
4、当下载失败情况,主动通知 Dfdaemon 进行回源下载。
Dfdaemon
1、基于 gRPC 提供下载功能, 并提供多源适配能力。
2、开启 Seed Peer 模式可以作为 P2P 集群中回源下载节点, 也就是整个集群中下载的根节点。
3、为镜像仓库或者其他 HTTP 下载任务提供代理服务。
4、下载任务基于 HTTP 或 HTTPS 或其他自定义协议。
环境
- k8s版本:1.24.16
- containerd版本:1.6.9
- harbor版本:v2.8.2,开启了https,参考这里
- dragonfly版本:v2.1.0
部署
添加helm repo
我是部署在k8s集群内的,所以这里使用helm chart来部署。
helm repo add dragonfly https://dragonflyoss.github.io/helm-charts/
helm repo list
helm install --wait --create-namespace --namespace dragonfly-system dragonfly dragonfly/dragonfly -f charts-config.yaml
我的机器拉github超时。所以去github上下载压缩包。
helm pull dragonfly/dragonfly
wget https://github.com/dragonflyoss/helm-charts/releases/download/dragonfly-1.1.3/dragonfly-1.1.3.tgz
tar xvf dragonfly-1.1.3.tgz
修改values.yaml
containerRuntime:
containerd:
enable: true
scheduler:
replicas: 1
metrics:
enable: true
config:
verbose: true
pprofPort: 18066
seedPeer:
replicas: 1
metrics:
enable: true
config:
verbose: true
pprofPort: 18066
dfdaemon:
metrics:
enable: true
config:
verbose: true
pprofPort: 18066
manager:
replicas: 1
metrics:
enable: true
config:
verbose: true
pprofPort: 18066
jaeger:
enable: true
部署
helm install --create-namespace --namespace dragonfly-system dragonfly .
这里mysql和redis会卡在pending状态,因为pvc没有配置storageCLass
。导出pvc-yaml后删除pvc,添加storageCLassName
,再apply即可。下面是mysql的pvc-yaml。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
labels:
app.kubernetes.io/component: primary
app.kubernetes.io/instance: dragonfly
app.kubernetes.io/name: mysql
name: data-dragonfly-mysql-0
namespace: dragonfly-system
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 8Gi
volumeMode: Filesystem
storageClassName: fast
k delete pvc data-dragonfly-mysql-0 redis-data-dragonfly-redis-master-0 redis-data-dragonfly-redis-replicas-0 -n dragonfly-system
redis-replicas会有3副本,所以redis-data-dragonfly-redis-replicas-1,redis-data-dragonfly-redis-replicas-2
也得删除重建。
values.yaml
中的global选项中没有配置storageCLassName
的地方,mysql和redis的配置中也没有,所以只能这样删除重建了。
查看服务情况
k get po,pvc -n dragonfly-system
dragonfly-jaeger-query
是headless服务,这里再创建一个nodePort服务。
cat svc.yaml
apiVersion: v1
kind: Service
metadata:
annotations:
meta.helm.sh/release-name: dragonfly
meta.helm.sh/release-namespace: dragonfly-system
labels:
app.kubernetes.io/component: service-query
app.kubernetes.io/instance: dragonfly
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: jaeger
app.kubernetes.io/version: 1.39.0
helm.sh/chart: jaeger-0.66.1
name: dragonfly-jaeger-query-nodeport
namespace: dragonfly-system
spec:
ports:
- name: http-query
port: 16686
protocol: TCP
targetPort: 16686
nodePort: 30686
- name: grpc-query
port: 16685
protocol: TCP
targetPort: 16685
nodePort: 30685
selector:
app.kubernetes.io/component: all-in-one
app.kubernetes.io/instance: dragonfly
app.kubernetes.io/name: jaeger
sessionAffinity: None
type: NodePort
k get svc -n dragonfly-system
启用containerd的Mirror模式
配置dfdaemon
修改values.yaml
。
helm upgrade dragonfly -n dragonfly-system .
配置containerd
前面helm安装dragonfly时配置了containerd,只启用了enable: true
。如果添加了injectConfigPath: true
的配置,就意味着允许Charts注入config_path
并重新启动 containerd。此选项启用多个镜像仓库支持。我这里只加速一个镜像仓库,故不启用此选项。下图是启用该选项的chart配置:
修改config.toml
添加配置文件路径:config_path = "/etc/containerd/certs.d"
,下面不能有其他配置,否则containerd会无法正常启动。状态是正常的,但是会无法执行crictl ps
。
containerd配置参考:https://github.com/containerd/containerd/blob/main/docs/cri/config.md#registry-configuration
# 创建certs.d目录
mkdir /etc/containerd/certs.d
# 拷贝config.toml文件
ansible all -m copy -a "src=/etc/containerd/config.toml dest=/etc/containerd/"
生成hosts.toml
可以手动生成也可以自动生成。路径: /etc/containerd/certs.d/example.com/hosts.toml
。自动生成 hosts.toml 脚本为 https://github.com/dragonflyoss/Dragonfly2/blob/main/hack/gen-containerd-hosts.sh
bash gen-containerd-hosts.sh harbor.harbor.com
下面是手动生成的配置文件示例。
server = "https://harbor.harbor.com"
[host."http://127.0.0.1:65001"]
capabilities = ["pull", "resolve"]
[host."http://127.0.0.1:65001".header]
X-Dragonfly-Registry = ["https://harbor.harbor.com"]
[host."https://harbor.harbor.com"]
capabilities = ["pull", "resolve"]
重启containerd
systemctl daemon-reload
systemctl restart containerd
containerd对接harbor
添加harbor域名解析
首先添加主机hosts解析。
ansible all -m shell -a "echo '192.168.1.1 `harbor.harbor.com`' >> /etc/hosts"
尝试拉取镜像:
crictl pull harbor.harbor.com/xxx/nginx:1.24.0
会报错500:
这是因为dragonfly的pod内无法解析harbor.harbor.com
,需要手动添加hosts解析。
修改dragonfly-dfdaemon(ds),dragonfly-manager(deployment),dragonfly-scheduler(sts),dragonfly-seed-peer(sts)
的yaml文件,添加harbor域名解析。
k edit ds dragonfly-dfdaemon -n dragonfly-system -o yaml
hostAliases:
- hostnames:
- harbor.harbor.com
ip: 192.168.1.1
添加证书
等待pod重启完成后,再次尝试拉取镜像,报错x509:certificate signed by unknown authority
。
这个是因为harbor启用了https,containerd认证失败了。如果是http就没这个问题了。拷贝harbor的证书目录到/etc/containerd/certs.d/harbor.harbor.com
目录。
修改hosts.toml
,手动添加证书文件如下:
server = "https://harbor.harbor.com"
[host."http://127.0.0.1:65001"]
capabilities = ["pull", "resolve"]
[host."http://127.0.0.1:65001".header]
X-Dragonfly-Registry = ["https://harbor.harbor.com"]
[host."https://harbor.harbor.com"]
capabilities = ["pull", "push", "resolve"]
ca = "/etc/containerd/certs.d/harbor.harbor.com/ca.crt"
client = ["/etc/containerd/certs.d/harbor.harbor.com/harbor.harbor.com.cert", "/etc/containerd/certs.d/harbor.harbor.com/harbor.harbor.com.key"]
重启containerd,再次拉取报错:found a certificate rather than a key in the PEM for the private key
,我没找到正确添加证书认证的方法。所以采用了跳过证书认证的方法…
server = "https://harbor.harbor.com"
[host."http://127.0.0.1:65001"]
capabilities = ["pull", "resolve"]
[host."http://127.0.0.1:65001".header]
X-Dragonfly-Registry = ["https://harbor.harbor.com"]
[host."https://harbor.harbor.com"]
capabilities = ["pull", "resolve"]
skip_verify = true
重启containerd后,可以登录harbor了。忽略这个登录时的报错,截图时还没重启dragonfly的pod。重启后就不会报错了。
但是仍然无法拉取。还是报错x509。
添加ca证书信任
cat /etc/containerd/certs.d/harbor.harbor.com/ca.crt
# 把ca证书添加到本地信任
cat ca.crt >> /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
再次尝试拉取,拉取成功。
验证dragonfly
containerd通过dragonfly首次回源拉镜像
前面已经加了jaeger的的nodePort,访问jaeger的ui。
比如镜像为harbor.harbor.com/xxx/redis:5.0.6
,tag的格式为:http.url="/v2/xxx/redis/blobs/sha256:e1285fcc6a46ea12a10f75a49278f3c82937cc174e5cdf052ef5e31bdaf93509?ns=harbor.harbor.com"
,sha256那串为镜像的某一层。
Tracing详细内容:
集群内首次回源时,下载 e1285fcc6a46ea12a10f75a49278f3c82937cc174e5cdf052ef5e31bdaf93509
层需要消耗时间为 215.86ms。
containerd下载镜像命中dragonfly本地peer的缓存
删除下载的redis镜像,在该节点上再次下载redis镜像。
nerdctl rmi harbor.harbor.com/xxx/redis:5.0.6
nerdctl images
jaeger上搜索上面的tag。
命中本地peer缓存时,下载 e1285fcc6a46ea12a10f75a49278f3c82937cc174e5cdf052ef5e31bdaf93509
层需要消耗时间为 409us。
containerd下载镜像命中dragonfly远程peer的缓存
在其他节点上下载redis镜像。jaeger上搜索上面的tag。
Tracing详细内容:
命中远程peer缓存时,下载 e1285fcc6a46ea12a10f75a49278f3c82937cc174e5cdf052ef5e31bdaf93509
层需要消耗时间为 74.31ms。
综上,可以看出dragonfly可以一定程度上提升镜像拉取的速度。由于我这里只启用了一个manager,scheduler,peer节点,所以看着不是太明显,增加pod数量可以提升下载速度。
登录控制台
修改manager的svc为nodePort。
k edit svc dragonfly-manager -n dragonfly-system
访问 http://ip:30088 。
默认账号为 root, 密码为 dragonfly。可以注册账号。
查看集群信息。
查看调度信息。
查看peer信息。
预热
预热功能在对接有公网域名的harbor时非常有用,因为一般公网域名的harbor都配置了CDN,而预热功能会把镜像缓存在CDN。这样再次拉取的时候CDN直接返回,就不用再去源镜像仓库拉取了。我这里harbor是离线的,所以预热功能开不开都没区别。这里介绍一下如何配置:
首先containerd得是Mirror模式。上文中已经配置。下面就是配置harbor。
harbor官方文档地址:https://goharbor.io/docs/2.8.0/administration/p2p-preheat/manage-preheat-providers/
登录harbor,点击分布式分发,创建实例。
注意:这里的endpoint为dragonfly-manager
的地址。我按照官方文档的8002地址,以为是dragonfly-scheduler
,一直调不通…
预热镜像
执行命令:
curl --location --request POST 'http://192.168.1.1:30088/api/v1/jobs' \
--header 'Content-Type: application/json' \
--data '{
"type": "preheat",
"args": {
"type": "image",
"url": "https://harbor.harbor.com/xxx/jenkins/manifests/2.332.3"
}
}'
这里不能按照官方文档的命令格式,会报错--data-raw is unknown
,得改成--data
。
查看任务执行情况:
curl --request GET 'http://192.168.1.1:30088/api/v1/jobs/1'
如果返回预热任务状态为 SUCCESS,表示预热成功:
在两台node上都下载镜像。jaeger上搜索tag:http.url="/v2/xxx/jenkins/blobs/sha256:xxxxxxxx?ns=harbor.harbor.com
可以看到第一次下载层耗时2.08s,第二次耗时527.33ms。
查看tracing信息。
预热功能已实现。
和kraken的对比
kraken GitHub地址:https://github.com/uber/kraken
两者的主要差异是:
- Dragonfly 集群有一个或几个“超级节点”,用于协调集群中每个 4MB 大小的数据块的传输。虽然超级节点能够做出最佳决策,但整个集群的吞吐量会受到一台或数台主机的处理能力限制,并且随着 blob 大小或集群大小的增加,整体性能会线性下降。
- Kraken的架构中,中心部件Tracker只负责搭建起一个随机正则图的网络拓扑结构,但是具体的数据传输的协商是由每台机器上的peer进程负责,所以Kraken的性能不会太受集群和镜像大小的影响。此外,Kraken是一个高可用性的系统,并且支持跨集群异地无损复制,适合使用于混合云环境。
我的环境都是本地或者云上环境,没有混合云场景。所以具体内容没有测试。