博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
结合P2P软件使用Ansible分发大文件
阅读量:5945 次
发布时间:2019-06-19

本文共 9262 字,大约阅读时间需要 30 分钟。

一 应用场景描述

 现在我需要向50+数量的服务器分发Logstash新版本的rpm包,大概220MB左右,直接使用Ansible的copy命令进行传输,命令如下:

 

1
ansible all  -m copy -a 
"src=/opt/software/logstash/logstash-agent-2.3.3-fb.centos6.x86_64.rpm dest=/opt/software/logstash"

在执行的过程中,很快就收到Zabbix网络监控的报警,报警项目就是瞬间流量变化大于5Mbps。同时,有的服务器很快执行完成,有很多出现ssh连接错误,Ansible卡死。

 开启Ansible的pipelining功能依然卡在ssh连接上。分发文件失败!!

所以,使用Ansible来分发执行命令很快速,但是如果直接使用Ansible来处理稍微大一点的文件分发就是一个很大的问题,即使耗费点时间可以忍受,但是单个分发点的带宽也会直接影响分发效率。

对于大文件分发,首先想到的就是BitTorrent,利用P2P协议实现快速分发,节省带宽,提高效率。

二 P2P软件介绍

 这里我们使用Twitter开源的murder。Twitter用它来分发大文件完成代码更新。在早期,Twitter为每天向上万太台的服务器发布代码而头疼,从中央代码服务器向其他成千上万的节点分发代码存在很大瓶颈,因为分发代码的执行时间与需要更新代码的节点成线性关系,节点越多,分发时间越长。为了解决这个问题,Twitter抛弃了以往的集中式架构,转向分布式架构,取名叫murder。使用murder后,他们以前需要40~60分钟的代码发布任务,现在12秒以内就可以完成。

Screen-Shot-2013-04-05-at-2.05.01-PM-10o     Screen-Shot-2013-04-05-at-2.06.03-PM-2f8

images-scm7r0.jpeg

1
2
3
wget https:
//github
.com
/lg/murder/archive/master
.zip -O murder.zip
unzip murder.zip
cd 
murder-master

muder是基于BitTornado来实现的。主要有以下几个组件:

 torrent tracker

        tracker使用murder_tracker.py运行,tracker实际上就是运行在一台服务器上的单个服务,其他任何成员都要依赖这个tracker。tracker-less disctribution(DHT)目前不支持。tracker存放BitTorrent客户端需要更新状态的路径。

  seeder  

        seeder就是存放需要向其他主机分发的文件的服务器。这些文件存放在seeder的一个目录,torrent根据这个目录创建。Murder会将这个目录打包成tgz格式,然后创建一个.torrent文件,这个文件很小,只存放关于这个tgz文件的基本哈希信息。这个.torrent文件让各个peers节点知道他们下载的是什么文件。同时,tracker会保持跟踪有哪些 .torrent文件正在被分发。一旦Murder开始传输文件,seeder服务器是众多主机首先获取文件碎片的地方。

  peers   

         peers就是成百上千需要接收文件的服务器,并且在它们之间可以相互传输文件。一旦一个peer节点下载整个tgz文件完成,它将继续seeding一段时间防止蜜罐效应。

命令行使用murder

1.开启tracker

1
python murder_tracker.py

muder_tracker.py实际上调用的这个文件BitTornado/BT1/track.py

track.py有很多参数,如果需要添加参数可以修改muder_tracker.py

几个重要的参数

--port tracker监听的端口,默认是8998

--dfile  存储近期下载信息的文件

--logfile  tracker日志文件,默认是标准输出

为tracker添加启动脚本/etc/init.d/murder-tracker

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#! /bin/sh
#
# Start/Stop murder-tracker
#
# chkconfig: 345 99 99
# description: murder-tracker
# processname: murder-tracker
 
if 
[ -f 
/etc/rc
.d
/init
.d
/functions 
]; 
then
    
/etc/rc
.d
/init
.d
/functions
fi
 
 
 
 
name=
"murder-tracker"
murder_tracker_bin=
"/opt/app/murder/dist/murder_tracker.py"
murder_tracker_log=
"/opt/logs/murder/murder_tracker.log"
murder_tracker_data=
"/opt/data/murder/tracker_data"
murder_user=murder
 
 
find_tracker_process () {
    
PID=`
ps 
-ef | 
grep 
murder_tracker | 
grep 
python |
grep 
-
v 
$0|
grep 
-
v 
grep
|
grep 
-
v 
sh|
grep 
-
v 
root| 
awk 
'{ print $2 }'
`
}
 
start () {
    
getent 
passwd 
$murder_user  >
/dev/null 
|| 
useradd 
-r  -s 
/sbin/nologin 
$murder_user
    
LOG_DIR=`
dirname 
${murder_tracker_log}`
    
DATA_DIR=`
dirname 
${murder_tracker_data}`
    
if 
[ ! -d $LOG_DIR ]; 
then
      
echo 
-e  
"\e[35mLog dir ${LOG_DIR} doesn't exist. Creating\e[0m"
      
mkdir 
-p $LOG_DIR     
    
fi
    
if 
[ ! -d $DATA_DIR ]; 
then
      
echo 
-e  
"\e[35mLog dir ${DATA_DIR} doesn't exist. Creating\e[0m"
      
mkdir 
-p $DATA_DIR
    
fi
    
chown 
-R $murder_user:$murder_user $DATA_DIR  $LOG_DIR
 
    
find_tracker_process
    
if 
"$PID" 
!= 
"" 
]; 
then
       
echo 
-e  
"\e[35m$name is already running!\e[0m"
    
else
       
daemon --user $murder_user  
nohup  
python $murder_tracker_bin  > 
/dev/null 
2>&1 &
       
echo 
-e 
"\e[35mStarting $name Done\e[0m"
    
fi
}
 
 
 
stop () {
    
find_tracker_process
    
if 
"$PID" 
!= 
"" 
]; 
then
        
echo 
-e 
"\e[35mStopping $name\e[0m"
        
kill 
$PID
    
else
        
echo 
-e 
"\e[35m$name is not running yet\e[0m"
    
fi
}
 
case 
$1 
in
start)
        
start
        
;;
stop)
        
stop
        
exit 
0
        
;;
reload)
        
stop
        
sleep 
2
        
start
        
;;
restart)
        
stop
        
sleep 
2
        
start
        
;;
status)
        
find_tracker_process
        
if 
"$PID" 
!= 
"" 
]; 
then
          
echo 
-e 
"\e[35m$name is running: $PID\e[0m"
          
exit 
0
        
else
          
echo 
-e 
"\e[35m$name is not running\e[0m"
          
exit 
1
        
fi
        
;;
*)
        
echo 
-e 
"\e[35mUsage: $0 {start|stop|restart|reload|status|configtest}\e[0m"
        
RETVAL=1
esac
exit 
0

根据自己情况修改相应的参数

2.创建torrent文件

1
python murder_make_torrent.py deploy.
tar
.gz tracker.twitter.com:8998 deploy.torrent

murder_make_torrent.py文件实际上调用的 BitTornado的makemetafile.py 文件

3.Seed the package播种需要分发的文件包

1
python murder_client.py seed  deploy.torrent deploy.
tar
.gz 172.28.2.200

最后一个参数是本机的IP地址

4.从所有peers节点获取文件包

1
python murder_client.py peer  deploy.torrent deploy.
tar
.gz 172.28.2.220

三 使用Ansible执行分发命令

tracker  172.168.2.171

seeder   172.168.2.179

peers    172.168.2.180~200

murder执行文件目录            /opt/app/murder

tracker和seeder的murder数据目录    /opt/data/murder

peers下载目录               /opt/software/download/

1.在tracker服务器上启动tracker

1
# ansible 172.168.2.171  -m service -a "name=murder-tracker state=started"

2.在seeder服务器上制作torrent文件并启动seeder

seeder启动脚本/etc/init.d/murder-seeder

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#! /bin/sh
#
# Start/Stop murder-seeder
#
# chkconfig: 345 99 99
# description: murder-seeder
# processname: murder-seeder
 
if 
[ -f 
/etc/rc
.d
/init
.d
/functions 
]; 
then
    
/etc/rc
.d
/init
.d
/functions
fi
 
 
 
 
name=
"murder-seeder"
murder_seeder_data=
"/opt/data/murder"
murder_seeder_log=
"/opt/logs/murder/murder_seeder.log"
murder_seeder_bin=
"/opt/app/murder/dist/murder_client.py"
murder_make_torrent_bin=
"/opt/app/murder/dist/murder_make_torrent.py"
murder_seeder_conf=
"/opt/app/murder/dist/seeder.conf"
deploy_file=$(
awk 
-F= 
'/deploy_file/{print $2}' 
/opt/app/murder/dist/seeder
.conf)
torrent_file=$(
awk 
-F= 
'/torrent_file/{print $2}' 
/opt/app/murder/dist/seeder
.conf)
tracker_ip=$(
awk 
-F= 
'/tracker_ip/{print $2}' 
/opt/app/murder/dist/seeder
.conf)
local_ip=$(
awk 
-F= 
'/local_ip/{print $2}' 
/opt/app/murder/dist/seeder
.conf)
murder_user=murder
 
 
find_seeder_process () {
    
PID=`
ps 
-ef | 
grep 
murder_client|
grep 
seed | 
grep 
python |
grep 
-
v 
$0|
grep 
-
v 
grep
awk 
'{ print $2 }'
`
    
#PID=`ps -ef | grep murder_client|grep seed | grep python |grep -v $0|grep -v grep|grep -v sh|grep -v root| awk '{ print $2 }'`
}
 
start () {
    
getent 
passwd 
$murder_user  >
/dev/null 
|| 
useradd 
-r  -s 
/sbin/nologin 
$murder_user
    
LOG_DIR=`
dirname 
${murder_seeder_log}`
    
DATA_DIR=${murder_seeder_data}
    
if 
[ ! -d $LOG_DIR ]; 
then
      
echo 
-e  
"\e[35mLog dir ${LOG_DIR} doesn't exist. Creating\e[0m"
      
mkdir 
-p $LOG_DIR     
    
fi
    
if 
[ ! -d $DATA_DIR ]; 
then
      
echo 
-e  
"\e[35mLog dir ${DATA_DIR} doesn't exist. Creating\e[0m"
      
mkdir 
-p $DATA_DIR
    
fi
  
    
####### make torrent 
    
python $murder_make_torrent_bin $deploy_file $tracker_ip $torrent_file   
    
#######
    
chown 
-R $murder_user:$murder_user $DATA_DIR  $LOG_DIR
 
    
find_seeder_process
    
if 
"$PID" 
!= 
"" 
]; 
then
       
echo 
-e  
"\e[35m$name is already running!\e[0m"
    
else
       
nohup  
python $murder_seeder_bin  seed  $torrent_file $deploy_file $local_ip     > $murder_seeder_log 2>&1 &
       
#daemon --user $murder_user  nohup  python $murder_seeder_bin  seed  $torrent_file $deploy_file $local_ip     > $murder_seeder_log 2>&1 &
       
echo 
-e 
"\e[35mStarting $name Done\e[0m"
    
fi
}
 
 
 
stop () {
    
find_seeder_process
    
if 
"$PID" 
!= 
"" 
]; 
then
        
echo 
-e 
"\e[35mStopping $name\e[0m"
        
kill 
$PID
    
else
        
echo 
-e 
"\e[35m$name is not running yet\e[0m"
    
fi
}
 
case 
$1 
in
start)
        
start
        
;;
stop)
        
stop
        
exit 
0
        
;;
reload)
        
stop
        
sleep 
2
        
start
        
;;
restart)
        
stop
        
sleep 
2
        
start
        
;;
status)
        
find_seeder_process
        
if 
"$PID" 
!= 
"" 
]; 
then
          
echo 
-e 
"\e[35m$name is running: $PID\e[0m"
          
exit 
0
        
else
          
echo 
-e 
"\e[35m$name is not running\e[0m"
          
exit 
1
        
fi
        
;;
*)
        
echo 
-e 
"\e[35mUsage: $0 {start|stop|restart|reload|status|configtest}\e[0m"
        
RETVAL=1
esac
exit 
0

启动脚本依赖一个配置文件seeder.conf

1
2
3
4
5
# cat /opt/app/murder/dist/seeder.conf 
deploy_file=/opt/data/murder/deploy.tar.gz
torrent_file=/opt/data/murder/deploy.torrent
tracker_ip=172.168.2.171:8998
local_ip=172.168.2.179

将需要的分发的文件打包成deploy.tar.gz

启动seeder

1
# ansible 172.168.2.179  -m service -a "name=murder-seeder state=started"

3.从seeder获取种子文件,然后分发到peers

1
# ansible 172.168.2.179  -m synchronize -a "mode=pull src=/opt/software/download/deploy.torrent dest=/opt/software/download"

调用synchronize模块,pull模式就是从远端获取文件到本地,默认是push模式,从本地推送文件到远端

然后将种子文件分发出去

1
# ansible all  -m synchronize -a "src=/opt/software/download/deploy.torrent dest=/opt/software/download"

4.在各个peers端执行下载任务

1
# ansible all  -m shell -a "sh /opt/app/murder/dist/peer_download.sh"

peer_download.sh

1
2
3
4
5
6
7
8
#!/bin/bash
#this file is used to download bt files
torrent_file=
/opt/software/download/deploy
.torrent
download_file=
/opt/software/download/deploy
.
tar
.gz
local_ip=$(
hostname 
-I|
awk 
'{print $1}'
)
murder_client_bin=
/opt/app/murder/dist/murder_client
.py
 
python  $murder_client_bin peer $torrent_file $download_file $local_ip

分发完成

可以将这些步骤写成Ansible playbooks

需要注意一下:

我需要分发的服务器是外网服务器,每台服务器开启了iptables防火墙。总共有60多台服务器同时下载220M左右的压缩包总共花了约20多分钟时间。这个时间有点怀疑,通过再次了解BT原理和查看源代码发现是防火墙设置的问题。BT下载之所以是下载点越多,下载速度越快,是因为各个下载点之间可以交换数据,也就是说需要开启TCP端口用于BT下载。这点在murder的文档中是没有说明的,twitter默认是每台服务器都关闭防火墙,并且是处于一个数据中心的彼此相互信任的内网服务器。murder封装的是BTTornado,代码中默认是启动一个10000~60000范围的随机端口,每个murder peer在下载的同时向其他peers提供下载服务就是通过这个随机端口,如果防火墙全部关闭,这个不成问题,但是如果开启了防火墙这么大的端口范围肯定不行的,就需要自己设置一个防火墙允许的范围。

如果不开端口也是可以上传数据的,但是会影响下载速度,因为其他peer端无法连接到彼此。

firewalled_peers1.jpg

firewalled_peers2.jpg

有关下载的参数在BitTornado/download_bt1.py中定义有

和端口相关的参数

1
2
3
4
(
'minport'
, 10000, 
'minimum port to listen on, counts up if unavailable'
),
(
'maxport'
, 60000, 
'maximum port to listen on'
),
(
'random_port'
, 1, 
'whether to choose randomly inside the port range ' 
+
        
'instead of counting up linearly'
),

这个范围太大,根据自己情况设置小一点,然后让防火墙通行

参考文档:

转载地址:http://odzxx.baihongyu.com/

你可能感兴趣的文章
Spring学习资料之 依赖注入(一)
查看>>
安装win7提示安装程序无法创建新的系统分区和定位现有系统分区
查看>>
快递查询接口的调用与解析案例
查看>>
服务器性能优化配置建议
查看>>
oracle sql语句实现累加、累减、累乘、累除
查看>>
3D地图的定时高亮和点击事件(基于echarts)
查看>>
接口由40秒到200ms优化记录
查看>>
java 视频播放 多人及时弹幕技术 代码生成器 websocket springmvc mybatis SSM
查看>>
Activiti6.0,spring5,SSM,工作流引擎,OA
查看>>
第十三章:SpringCloud Config Client的配置
查看>>
使用 GPUImage 实现一个简单相机
查看>>
CoinWhiteBook:区块链在慈善事业中的应用
查看>>
Mac上基于Github搭建Hexo博客
查看>>
阿里云服务器ECS开放8080端口
查看>>
Spring中实现监听的方法
查看>>
使用Tooltip会出现一个问题,如果行上出现复选框
查看>>
11.03T1 DP
查看>>
P2924 [USACO08DEC]大栅栏Largest Fence
查看>>
jQuery操作table tr td
查看>>
工作总结:MFC自写排序算法(升序)
查看>>