基于dragonfly p2p技术加速容器镜像分发实践
概述
首先p2p网络是一种分布式的去中心化的网络,在网络中每个节点的地位都是对等的,每个节点既能充当服务器,也同时能为其他节点提供服务,同时也享有其他节点提供的服务。
为什么需要引入p2p技术来加速镜像分发?
在大规模的容器集群内,镜像分发,往往需要消耗大量时间,并且会给镜像仓库带来很大的压力负担,通过p2p技术将流量分担到集群的每个节点上,这样可以大大缩短下载镜像的时间,并且能非常有效的减轻镜像仓库的压力。
Dragonfly介绍
这里我们使用的是阿里巴巴开源的基于P2P技术的PB级文件分发系统蜻蜓(dragonfly)。
引用阿里巴巴双11技术解密内容:
蜻蜓整体架构分三层:第一层是 Config Service,他管理所有的 Cluster Manager,Cluster Manager 又管理所有的 Host,Host 就是终端,dfget 就是类似 wget 的一个客户端程序。
Config Service 主要负责 Cluster Manager 的管理、客户端节点路由、系统配 置管理以及预热服务等等。简单的说,就是负责告诉 Host,离他最近的一组 Cluster Manager 的地址列表,并定期维护和更新这份列表,使 Host 总能找到离他最近的 Cluster Manager。
Cluster Manager 主要的职责有两个:
- 以被动 CDN 方式从文件源下载文件并生成一组种子分块数据;
- 构造 P2P 网络并调度每个 peer 之间互传指定的分块数据。Host 上就存放着 dfget,dfget 的语法跟 wget 非常类似。
主要功能包括文件下 载和 P2P 共享等。
原理
两个 Host 和 CM 会组成一个 P2P 网络,首先 CM 会查看本地是否有缓存,如果没有,就会回源下载,文件当然会被分片,CM 会多线程下载这些分片,同时会将 下载的分片提供给 Host 们下载,Host 下载完一个分片后,同时会提供出来给 peer 下载,如此类推,直到所有的 Host 全部下载完。
本地下载的时候会将下载分片的情况记录在 metadata 里,如果突然中断了下 载,再次执行 dfget 命令,会断点续传。
下载结束后,还会比对 MD5,以确保下载的文件和源文件是完全一致的。蜻蜓通过 HTTP cache 协议来控制 CM 端对文件的缓存时长,CM 端当然也有自己定期 清理磁盘的能力,确保有足够的空间支撑长久的服务。
需要注意的是开源版的dragonfly目前没有开源config-Service。
使用Dragonfly做docker镜像分发
技术原理类似我们用的BT下载技术的bitorrent协议
cluster-manager就类似于Tracker服务器。
.meta类似于torrent文件
通过torrent文件,获取其他正在下载该文件的网址名单,根据torrent文件的网址然后连接tracker服务器,从tracker服务器获取正在下载该文件的网址名单,然后与它们取得联系,从他们那里获取文件的片端,直到整个下载完成。
原理(来源官方github)
首先,docker pull 命令,会被 dfget proxy 截获。然后,由 dfget proxy 向 CM 发送调度请求,CM 在收到请求后会检查对应的下载文件是否已经被缓存到本地,如果没有被缓存,则会从Registry 中下载对应的文件,并生成种子分块数据(种子分块数据一旦生成就可以立即被使用);如果已经被缓存,则直接生成分块任务,请求者解析相应的分块任务,并从其他 peer 或者 supernode 中下载分块数据,当某个Layer的所有分块下载完成后,一个Layer也就下载完毕了,同样,当所有的Layer下载完成后,整个镜像也就下载完成了。
文件分块的下载
注: 其中cluster manager即超级节点(supernode)
每个文件会被分成多个块在对等者(peer)间进行传输。一个peer就是一个P2P客户端。
超级节点会判断文件是否存在本地,如果不存在,则会将其从文件服务器下载到本地。
配置方法 主要参考官方的服务端和客户端的安装和使用 https://github.com/alibaba/Dragonfly/tree/master/docs/zh 软件版本: docker:17.03-2 os:ubuntu16.04 Dragonfly:0.2.0 Harbor:1.4.0
环境信息
rke-node1:172.31.164.57 rke-node2:172.31.164.58 rke-node3:172.31.164.59 Harbor:172.31.164.66 cluster-manger:172.31.164.113
安装cluster-manger(https://github.com/alibaba/Dragonfly/blob/master/docs/zh/install_server.md)
分为两种方式
1、通过docker镜像方式安装,适于用快速部署测试的环境。
2、源码编译安装,适用于生产环境部署
我们这里主要介绍docker镜像方式安装,源码方式安装参考上述链接。
clone 代码
git clone https://github.com/alibaba/Dragonfly.git
进入项目目录
cd Dragonfly
编译代码,打包镜像需要本地安装java环境和maven
./build/build.sh supernode
获取镜像ID
${superNodeDockerImageId}=`docker image ls|grep 'supernode' |awk '{print $3}' | head -n1`
启动cluster-manger
docker run -d -p 8001:8001 -p 8002:8002 ${superNodeDockerImageId}
测试验证 telnet 127.0.0.1 8001 telent 127.0.0.1 8002
安装client端(https://github.com/alibaba/Dragonfly/blob/master/docs/zh/install_client.md)
安装客户端
wget https://github.com/alibaba/Dragonfly/raw/master/package/df-client.linux-amd64.tar.gz #创建文件夹存放 mkdir /root/install tar xzvf df-client.linux-amd64.tar.gz -C /root/install #设置环境变量 vim ~/.bashrc PATH=$PATH:/root/install/df-client source ~/.bashrc
验证执行命令
df-daemon -h Usage of df-daemon: -callsystem string caller name (default "com_ops_dragonfly") -certpem string cert.pem file path -dfpath string dfget path (default "/root/install/df-client/dfget") -h help -keypem string key.pem file path -localrepo string temp output dir of daemon (default "/root/.small-dragonfly/dfdaemon/data/") -notbs not try back source to download if throw exception (default true) -port uint daemon will listen the port (default 65001) -ratelimit string net speed limit,format:xxxM/K -registry string registry addr(https://abc.xx.x or http://abc.xx.x) and must exist if df-daemon is used to mirror mode -rule string download the url by P2P if url matches the specified pattern,format:reg1,reg2,reg3 -urlfilter string filter specified url fields (default "Signature&Expires&OSSAccessKeyId") -v version -verbose verbose
配置客户端连接超级节点
vi /etc/dragonfly.conf [node] address=172.31.164.113 #多台cluster-manger用逗号分隔
df-daemon --registry 172.31.164.66 &
配置docker-mirror和docker http-proxy
vim /etc/docker/daemon.json { "registry-mirrors": [ "http://127.0.0.1:65001" ], "insecure-registries" : [ "http://127.0.0.1:65001", "172.31.164.66" ] } vim /lib/systemd/system/docker.service Environment="HTTP_PROXY=http://127.0.0.1:65001" 重启docker systemctl daemon-reload systemctl restart docker
验证
直接将Harbor的地址改成 127.0.0.1:65001就可以拉取镜像,看看是否能拉取成功
docker pull 127.0.0.1:65001/library/front-end:30
Harbor内公开的项目镜像拉取,不用输入镜像仓库地址拉取
docker pull library/front-end:30
注意点:
1、private registry不能提供mirror的方式将流量转发到df-daemon,只能通过给docker配置http proxy的方式,原因在于docker pull project_name/image_name:tag方式下载镜像时不会在请求头里面携带docker login时输入的账号和密码而通过docker pull registry_address/project_name/image_name:tag方式会在请求头内通过authorization传递login时输入的账号和密码 。
2、蜻蜓默认是限速20M的,取消限速的方法https://github.com/alibaba/Dragonfly/issues/38
抓包分析
tcpdump -i lo port 65001 #确实有大量数据包经过
数据查看
查看到大量数据分片
ls ~/.small-dragonfly/data/
查看下载日志
less ~/.small-dragonfly/logs/dfclient.log
因为性能测试需要大并发,大流量环境下才能对比出差异性,所以这里性能测试结果使用阿里巴巴官方测试数据
上图可以看出,随着下载规模的扩大,蜻蜓与 Native 模式耗时差异显著扩 大,最高可提速可以达 20 倍。在测试环境中源的带宽也至关重要,如果源的带宽是 2Gbps,提速可达 57 倍。
向 200 个节点分发 500M 的镜像,比 docker 原生模式使用更低的网络流量, 实验数据表明采用蜻蜓后,Registry 的出流量降低了 99.5% 以上;而在 1000 并发 规模下,Registry 的出流量更可以降低到 99.9% 左右。
项目地址: https://github.com/alibaba/Dragonfly