简介
Dragonfly中文名"蜻蜓”,是一个基于P2P的智能文件分发系统。解决了应用部署,大规模缓存文件分发,数据文件分发,图像分发等大规模文件分发场景中低效率,低成功率,浪费网络带宽等问题。在阿里巴巴,系统每月转移20亿次,分配3.4PB数据,成为阿里巴巴最重要的基础设施之一。可靠性高达99.9999%。
DevOps从容器技术中获得很多好处。但同时也带来了很多挑战:图像分发的效率,特别是当你有很多的应用程序,同时需要图像分发。蜻蜓对于Docker和Pouch都非常适用,实际上我们可以兼容任何其他容器技术,而不需要对容器引擎进行任何修改。
Dragonfly具备镜像预热等功能,Docker原生的下载镜像速度慢,而采用P2P技术的蜻蜓很好地解决了这个问题。它提供高达57倍的native docker吞吐量,并节省高达99.5%的镜像带宽。
特性
- 基于P2P文件分发
- 支持各种容器化技术
- 主机级别限速策略
- 利用CDN机制避免远程重复下载
- 强一致性
- 磁盘保护,高效的IO处理
- 高性能
- 异常自动隔离
- 降低文件来源服务器压力
- 支持标准的Http Header
- 使用简单
Dragonfly使设置,操作和缩放任何类型的文件/图像/数据分布变得简单而经济。

蜻蜓P2P容器镜像分发示意图
架构设计介绍Architecture Introduction

分发普通文件
集群管理器也被称为超级节点,它负责CDN并调度每个对等体在它们之间传输块。 dfget是P2P的客户端,也被称为"peer”,主要用于下载和共享块。

分发镜像
镜像仓库与上面的文件服务器类似。 dfget代理也被称为df-daemon,它拦截来自docker pull或docker push的http请求,然后确定哪些请求需要使用dfget来处理。

块文件如何被下载
每个文件被分成多个块,这些块在对等体之间传输,一个对等体是一个P2P客户机。集群管理器将判断本地磁盘中是否存在相应的文件,如果不存在,则从文件服务器下载到集群管理器。
项目链接
https://github.com/alibaba/Dragonfly
https://www.jianshu.com/p/4e9d1e1be74d
Features特性
- 基于P2P的文件分发:利用P2P技术进行文件传输,可以充分利用每个对等体的带宽资源,提高下载效率。节省了大量的跨IDC带宽,尤其是较高的跨板带宽成本
- 无缝支持各种容器技术:蜻蜓可以无缝支持各种容器分发图像。
- 主机级别的速度限制:许多下载工具(wget / curl)只对当前下载任务有速率限制,但蜻蜓仍然为整个主机提供速率限制。
- 拥抱CDN:CDN机制可以避免重复的远程下载。
- 强一致性:即使用户没有提供任何校验码(MD5),蜻蜓也可以保证所有下载的文件必须一致。
- 磁盘保护和高效IO:预先检查磁盘空间,延迟同步,按最佳顺序写入文件块,拆分网络读取/磁盘写入等。
- 高性能:集群管理器是完全闭环的,即不依赖任何数据库和分布式缓存,处理性能极高的请求。
- 异常自动隔离:Dragonfly将自动隔离异常节点(对等或集群管理器),以提高下载稳定性。
- 对于文件来源来说没有压力:一般来说,只需要少数几个Cluster Managers就能从源文件下载文件。
- 支持标准的http头:支持http头,通过http头提交认证信息。
- 有效的注册表验证的并发控制:降低注册表验证服务的压力。
- 简单易用:极少需要配置。
测试案例
| Test Environment | 配置 |
|---|---|
| Dragonfly server | 2 * (24core 64GB 2000Mb/s) |
| File Source server | 2 * (24core 64GB 2000Mb/s) |
| Client | 4core 8GB 200Mb/s |
| Target file size | 200MB |
| Executed Date | 2016-04-20 |

对于Dragonfly而言,无论有多少客户端下载文件,平均下载时间大约是12秒。而当你有更多的客户时,wget时间就会增加。由1200个客户端,文件源崩溃,它不能服务于任何客户端。
Installation安装部署
服务端部署
推荐部署集群管理器的是至少两台至少具有8核16G的机器,最好提供千兆以太网。\
Step 1: 各方式所需依赖
方式一:部署在docker容器中
| Software Required | Version |
|---|---|
| Git | 1.9.1 + |
| Docker | 1.12.0 + |
方式二:部署在物理主机
| Software Required | Version |
|---|---|
| Git | 1.9.1 + |
| Jdk | 1.7 + |
| Maven | 3.0.3 + |
| Tomcat | 7.0 + |
| Nginx | 0.8 + |
获取源代码
git clone https://github.com/alibaba/Dragonfly.git
构建&&运行
- 方式一:部署在docker容器中
#Enter the project directory
cd Dragonfly
#Build Docker image
docker image build -t "dragonfly:supernode" . -f ./build/supernode/Dockerfile
#Show Docker image
docker image ls
#Get cluster manager (supernode) Docker imageId
docker image ls|grep -E 'dragonfly.*supernode'|awk '{print $3}'
#Start Docker container
docker run -d -p 8001:8001 -p 8002:8002 ${superNodeDockerImageId}
- 方式二:部署在物理主机
#Enter the project directory
#Build the source code
mvn clean -U install -DskipTests=true
#在tomcat上部署
#Copy package to tomcat deployment directory
copy target/supernode.war {CATALINA_HOME}/webapps/supernode.war
#Change context config of tomcat
#Add below config to server.xml of tomcat
<Context path="/" docBase="${CATALINA_HOME}/webapps/supernode" debug="0" reloadable="true" crossContext="true" />
``` nginx
#Start tomcat
./${CATALINA_HOME}/bin/catalina.sh run
#Start nginx
#Add nginx config
server {
listen 8001;
location / {
root /home/admin/supernode/repo;
}
}
server {
listen 8002;
location /peer {
proxy_pass http://127.0.0.1:8080;
}
}
#保存 #Example of nginx config less dragonfly/build/supernode/docker/nginx/nginx.conf #Start nginx sudo nginx
Step4: 验证安装 检查nginx和tomcat的8001和8002端口是否打开
ps aux|grep nginx
ps aux|grep tomcat
telnet 127.0.0.1 8001
telent 127.0.0.1 8002
安装蜻蜓客户端,使用蜻蜓客户端通过蜻蜓下载资源。
dfget --url "http://${resourceUrl}" --output ./resource.png --node "127.0.0.1"
客户端部署
-
从软件包安装
1.从这里下载软件包 df-client.linux-amd64.tar.gz
wget https://raw.githubusercontent.com/alibaba/Dragonfly/master/package/df-client.linux-amd64.tar.gz
2.解压tar xzvf df-client.linux-amd64.tar.gz -C xxx, “xxx” is installation directory.
3.设置环境变量PATH:PATH=$PATH:xxx/df-client -
源码自行编译安装
#source_dir是源代码所在的目录
cd source_dir/build/client
#--prefix = xxx指定安装目录,这个cmd参数是可选的,如果不指定--prefix,将使用当前目录。
./configure --prefix=xxx,
make
#你可以执行make package生成安装包。
make install
make clean
#设置环境变量PATH:
PATH=$PATH:xxx/df-client
下面以docker方式安装演示:
#首先部署在服务器上,记得包含docker&&git
[root@dockcon ~]# git clone https://github.com/alibaba/Dragonfly.git
正克隆到 'Dragonfly'...
remote: Counting objects: 627, done.
^C收对象中: 9% (61/627), 1.71 MiB | 33.00 KiB/s
[root@dockcon ~]# git clone https://github.com/alibaba/Dragonfly.git
正克隆到 'Dragonfly'...
remote: Counting objects: 627, done.
remote: Total 627 (delta 0), reused 0 (delta 0), pack-reused 627
接收对象中: 100% (627/627), 5.11 MiB | 25.00 KiB/s, done.
处理 delta 中: 100% (205/205), done.
[root@dockcon ~]# cd Dragonfly/
[root@dockcon Dragonfly]# ls
AUTHORS CHANGELOG.md docs LICENSE README.md VERSION
build CONTRIBUTING.md FAQ.md package src
[root@dockcon Dragonfly]# docker image build -t "dragonfly:supernode" . -f ./build/supernode/Dockerfile
Sending build context to Docker daemon 16.14MB
Step 1/19 : FROM centos:centos7
centos7: Pulling from library/centos
85432449fd0f: Pull complete
Digest: sha256:3b1a65e9a05f0a77b5e8a698d3359459904c2a354dc3b25ae2e2f5c95f0b3667
Status: Downloaded newer image for centos:centos7
---> 3fa822599e10
Step 2/19 : COPY ./build/supernode/docker/repos/aliyun-centos-7.repo /etc/yum.repos.d/CentOS-Base.repo
---> 469e9924e974
Step 3/19 : COPY ./build/supernode/docker/repos/aliyun-epel-7.repo /etc/yum.repos.d/epel.repo
---> debde0f2e73b
Step 4/19 : COPY ./build/supernode/docker/repos/nginx.repo /etc/yum.repos.d/nginx.repo
---> 18c6a384377f
Step 5/19 : RUN yum install -y nginx wget tar
..............................................................
Step 16/19 : COPY ./build/supernode/docker/tomcat/server.xml /usr/local/tomcat/conf/server.xml
---> 262c97d5829f
Step 17/19 : COPY ./build/supernode/docker/nginx/nginx.conf /etc/nginx/nginx.conf
---> 4700ac1066f8
Step 18/19 : EXPOSE 8001 8002
---> Running in d832dceabaa2
Removing intermediate container d832dceabaa2
---> 70059bedc42f
Step 19/19 : CMD nginx && catalina.sh run
---> Running in aa3e67997630
Removing intermediate container aa3e67997630
---> f5aa0459859a
Successfully built f5aa0459859a
Successfully tagged dragonfly:supernode
[root@dockcon Dragonfly]# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
dragonfly supernode f5aa0459859a 18 minutes ago 1.11GB
[root@dockcon ~]# docker image ls|grep -E 'dragonfly.*supernode'|awk '{print $3}'
f5aa0459859a
[root@dockcon ~]# docker run -d -p 8001:8001 -p 8002:8002 f5aa0459859a
22897bd31f69ea1d981eea18ce7a0552b8c1fb9fe44c628d5ca13a87e9baa074
[root@dockcon ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
22897bd31f69 f5aa0459859a "/bin/sh -c 'nginx &..." 8 seconds ago Up 7 seconds 0.0.0.0:8001-8002->8001-8002/tcp kind_villani
[root@dockcon ~]#
#服务端部署至此
#部署客户端
[root@dockcon ~]# wget https://raw.githubusercontent.com/alibaba/Dragonfly/master/package/df-client.linux-amd64.tar.gz
--2018-01-06 20:17:32-- https://raw.githubusercontent.com/alibaba/Dragonfly/master/package/df-client.linux-amd64.tar.gz
正在解析主机 raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.228.133
正在连接 raw.githubusercontent.com (raw.githubusercontent.com)|151.101.228.133|:443... 已连接。
已发出 HTTP 请求,正在等待回应... 200 OK
长度:3783061 (3.6M) [application/octet-stream]
正在保存至: "df-client.linux-amd64.tar.gz"
100%[=====================================================>] 3,783,061 194KB/s 用时 22s
2018-01-06 20:17:55 (172 KB/s) - 已保存 "df-client.linux-amd64.tar.gz" [3783061/3783061])
[root@dockcon ~]# tar xzvf df-client.linux-amd64.tar.gz -C /usr/bin/
./df-client/
./df-client/core/
./df-client/core/__init__.py
................................
./df-client/vendor/urllib3-1.22-py2.7.egg/urllib3/packages/ordered_dict.py
./df-client/vendor/idna-2.6-py2.7.egg
./df-client/vendor/chardet-3.0.4-py2.7.egg
./df-client/df-daemon
[root@dockcon ~]# vim /etc/profile
#df-client
PATH=$PATH:/usr/bin/df-client
[root@dockcon ~]# source /etc/profile
#尝试一下~
[root@dockcon ~]# dfget --url "http://caiyuyuan.top" --output ./resource.png --node "127.0.0.1"
--2018-01-06 20:25:19-- http://caiyuyuan.top
current user[root] output path[/root/resource.png]
/usr/lib/python2.7/site-packages/requests/__init__.py:80: RequestsDependencyWarning: urllib3 (1.22) or chardet (2.2.1) doesn't match a supported version!
RequestsDependencyWarning)
dfget version:0.0.1
workspace:/root/.small-dragonfly/ sign:5444-1515241519.470
client:127.0.0.1 connected to node:127.0.0.1
init success...
start download by dragonfly
download SUCCESS(0) cost(0.091s) length:121813 reason:0
[root@dockcon ~]# ls
resource.png
[root@dockcon ~]# cat resource.png | head -n 10
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge" >
<link rel="dns-prefetch" href="http://caiyuyuan.top">
<title>Taro balls's Blog</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
[root@dockcon ~]#
流程解析:
1. 当执行docker pull操作时,dfget-proxy会拦截docker pull请求。将请求转发给CM(cluster manager)。
cm的地址已经在client主机的/etc/dragonfly.conf文件中配置好了。另外上文中提到的dfget-proxy其实就是df-daemon。Dragonfly中有三个项目,client端:getter(python)、daemon(golang),docker pull时,df-daemon拦截到请求并通过dfget进行文件拉取,server端:supernode(java)。
2. df-daemon启动的时候带了registry参数,并且通过dfget传给服务端supernode。supernode解析参数到对应的镜像仓库获取镜像并以block的形式返回给客户端。如果再次拉取镜像时,supernode就会检测哪一个client存在和镜像文件对应的block,如果存在直接从该client下载,如果不存在就通过server端到镜像仓库拉取镜像。
supernode
可选:给supernode增加docker加速器,可以参考 cr.console.aliyun.com/cn-hangzhou… ,如果不需要,可以去掉。
$ cat <<EOD >/etc/docker/daemon.json{"registry-mirrors": ["https://xxxx.mirror.aliyuncs.com"] }EOD$ systemctl restart docker 复制代码
$ docker run --name dragonfly-supernode --restart=always -d -p 8001:8001 -p 8002:8002 -v /data/dragonfly/supernode:/home/admin/supernode registry.cn-hangzhou.aliyuncs.com/dragonflyoss/supernode:0.3.0 -Dsupernode.advertiseIp=192.168.0.44复制代码
说明:
- –restart=always 在容器退出时,自动重启容器,防止异常kill或者oom导致的异常退出
- registry.cn-hangzhou.aliyuncs.com/dragonflyoss/supernode:0.3.0 dragonfly的supernode目前没有docker hub镜像,只能用阿里云的
- -v /data/dragonfly/supernode:/home/admin/supernode 将supernode的data dir挂载到宿主机上
- -Dsupernode.advertiseIp=192.168.0.44 设置clinet可以访问的supernode ip,这是一个大坑。如果不设置,有可能会导致client无法连接supernode,届时,docker pull会走clinet的网络,从真实的registry直接下载镜像
dfdaemon
$ cat <<EOD >/etc/dragonfly.conf[node]address=192.168.0.44EOD$ docker run --name dragonfly-dfclient --restart=always -d -p 65001:65001 -v /root/.small-dragonfly:/root/.small-dragonfly -v /etc/dragonfly.conf:/etc/dragonfly.conf dragonflyoss/dfclient:v0.3.0 --registry=https://xxx.mirror.aliyuncs.com --ratelimit 100M$ cat <<EOD >/etc/docker/daemon.json{"registry-mirrors": ["http://127.0.0.1:65001"]}EOD$ systemctl restart docker 复制代码
说明:
- 在 /etc/dragonfly.conf 中配置client可以访问的supernode的ip地址,但是,目前官方没有做HA,supernode没法组集群,撑死算是联邦,不能共享文件信息,而且最坑的是,快速开始里,中英文均未提供需要配置此文件,而是在 Downloading Files with Dragonfly 等有所提及(我都是被坑完后,用关键词在d7y的org里搜索,类似知道答案后,找出处 手动[捂脸])
- -v /root/.small-dragonfly:/root/.small-dragonfly ,将容器中的关键目录挂载到宿主机上,防止重启或者镜像升级时,数据丢失
- –registry=https://xxx.mirror.aliyuncs.com 从何处下载镜像,可以写harbor地址,也可以写加速器地址。默认是 index.docker.io ,但是,因为国内网络原因,会导致大概率性失败。很灵异。而官方文档是写的
--registry https://xxx.xx.x不能算是坑,但是,对于docker不熟悉的,往往会不知能不能用加速器。 - –ratelimit 100M 是限速,默认是20M ,这肯定不算坑哈,这是正常特性,在 dfdaemon#Options 有说明,但是,文档是有误的
-ratelimit而实际是--ratelimit,如果不改此参数,会发现,下载很慢。 - 修改/etc/docker/daemon.json 是为了让docker engine走 dfdaemon
- systemctl restart docker 是为了让daemon生效