使用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 主要的职责有两个:

  1. 以被动 CDN 方式从文件源下载文件并生成一组种子分块数据;
  2. 构造 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 代码

1
git clone https://github.com/alibaba/Dragonfly.git

进入项目目录

1
cd Dragonfly

编译代码,打包镜像需要本地安装java环境和maven

1
./build/build.sh supernode

获取镜像ID

1
${superNodeDockerImageId}=`docker image ls|grep 'supernode' |awk '{print $3}' | head -n1`

启动cluster-manger

1
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)
安装客户端

1
wget https://github.com/alibaba/Dragonfly/raw/master/package/df-client.linux-amd64.tar.gz

创建文件夹存放

1
mkdir /root/install
1
tar xzvf df-client.linux-amd64.tar.gz -C /root/install

设置环境变量

1
vim ~/.bashrc

PATH=$PATH:/root/install/df-client

1
source ~/.bashrc

验证
执行命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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

使用方法
配置客户端连接超级节点

1
vi /etc/dragonfly.conf
1
2
3
[node]
address=172.31.164.113 #多台cluster-manger用逗号分隔

启动dfdaemon,指定镜像仓库地址,默认端口为65001

1
df-daemon --registry 172.31.164.66 &

配置docker-mirror和docker http-proxy

1
vim /etc/docker/daemon.json
1
2
3
4
5
6
7
8
9
10
{
"registry-mirrors": [
"http://127.0.0.1:65001"
],
"insecure-registries" : [
"http://127.0.0.1:65001",
"172.31.164.66"
]
}

1
vim /lib/systemd/system/docker.service
1
Environment="HTTP_PROXY=http://127.0.0.1:65001"

重启docker

1
systemctl daemon-reload
1
systemctl restart docker

验证查看

1
2
3
4
5
6
7
8
docker info

Insecure Registries:
http://127.0.0.1:65001
172.31.164.66
127.0.0.0/8
Registry Mirrors:
http://127.0.0.1:65001

验证
直接将Harbor的地址改成 127.0.0.1:65001就可以拉取镜像,看看是否能拉取成功

1
docker pull 127.0.0.1:65001/library/front-end:30

Harbor内公开的项目镜像拉取,不用输入镜像仓库地址拉取

1
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

抓包分析

1
tcpdump -i lo port 65001  #确实有大量数据包经过

数据查看
查看到大量数据分片

1
ls ~/.small-dragonfly/data/

查看下载日志

1
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