はじめに:rsyncに限らず、高速にAzureにデータをコピーできる他の手段でも良いなら、azcopyを使おう。
1TBのデータをrsyncでインターネット経由でAzureに同期したいわけですが、普通に
$ rsync source_directory/ rsync://myrsyncserver.japaneast.cloudapp.azure.com/target_directory/
なんてやってたら、超絶時間がかかるわけで、何とかしたい。
拙宅ではauひかりホーム10Gを導入しているので、Azure東日本リージョンまでそこそこのスピードが出るはず。
Standard_D16s_v3のVMにUbuntu 18.04-LTS、OS DiskはIOPSをある程度確保するために512GBのPremium_SSD、Data DiskはPremium SSDではIOPSの限界である7,500を出せる2TB(P40)を2本付けてRAID0に。Read/Writeキャッシュ共に設定しておくこと。Azure Marketplaceからデプロイする場合、Azure用カーネルを採用しているディストリビューションを使った方が性能が良いので、今回はUbuntu 18とした。
$ sudo fdisk /dev/sdc(d)
して、Linux RAID用にタイプfdを設定したパーティションを作成後、
$ sudo mdadm --create /dev/md0 --level=raid0 --raid-devices=2 /dev/sdc1 /dev/sdd1
$ sudo mkfs.xfs /dev/md0
$ sudo blkid /dev/md0
で得られたUUIDを/etc/fstab
に追加しておく。
UUID=3149b0c8-fdb1-439f-a5a2-a14771077c24 /opt/ext xfs defaults,discard 0 0
で、マウント出来るかチェック。
$ sudo mkdir /opt/ext
$ sudo mount /opt/ext
fioをインストールして簡易チェック。
$ sudo apt-get install fio
$ fio -filename=/opt/ext/test2g -direct=1 -rw=write -bs=4k -size=2G -numjobs=64 -runtime=10 -group_reporting -name=file1 | grep "write:"
write: IOPS=32.8k, BW=128MiB/s (134MB/s)(1281MiB/10002msec)
$ fio -filename=/opt/ext/test2g -direct=1 -rw=randwrite -bs=4k -size=2G -numjobs=64 -runtime=10 -group_reporting -name=file1 | grep "write:"
write: IOPS=32.7k, BW=128MiB/s (134MB/s)(1280MiB/10020msec)
$ fio -filename=/opt/ext/test2g -direct=1 -rw=write -bs=32m -size=2G -numjobs=64 -runtime=10 -group_reporting -name=file1 | grep "write:"
write: IOPS=8, BW=256MiB/s (269MB/s)(4384MiB/17094msec)
まあ、そこそこ出てます。拙宅のサーバのデータ転送元となるNVMe SSDだと、
$ fio -filename=./test2g -direct=1 -rw=write -bs=4k -size=2G -numjobs=64 -runtime=10 -group_reporting -name=file1 | grep "write:"
write: IOPS=101k, BW=395MiB/s (414MB/s)(3951MiB/10002msec)
$ fio -filename=./test2g -direct=1 -rw=randwrite -bs=4k -size=2G -numjobs=64 -runtime=10 -group_reporting -name=file1 | grep "write:"
write: IOPS=248k, BW=967MiB/s (1014MB/s)(9673MiB/10002msec)
$ fio -filename=./test2g -direct=1 -rw=write -bs=32m -size=2G -numjobs=64 -runtime=10 -group_reporting -name=file1 | grep "write:"
write: IOPS=38, BW=1236MiB/s (1296MB/s)(13.9GiB/11517msec)
こんな感じだったりするわけだけど。
Standard_D16s_v3はAccelerated Networkingに対応してるので、念の為、IPv4関連のカーネルパラメータを設定。/etc/sysctl.conf
に下記パラメータを追加。
net.ipv4.tcp_timestamps=0
net.ipv4.tcp_sack=0
net.ipv4.tcp_low_latency=1
net.ipv4.tcp_window_scaling=0
net.ipv4.tcp_dsack=0
net.ipv4.tcp_tw_reuse=1
net.core.netdev_max_backlog=250000
net.core.rmem_max=16777216
net.core.wmem_max=16777216
net.core.rmem_default=16777216
net.core.wmem_default=16777216
net.core.optmem_max=16777216
net.ipv4.tcp_rmem=4096 87380 16777216
net.ipv4.tcp_wmem=4096 65536 16777216
で、適用してみたんだけど、特に性能に変化は見られなかったので、必要ないかも知れない。備忘録として残しておく。
$ sudo sysctl -p /etc/sysctl.conf
iperf3をインストールして、Network Security Groupで5201/tcpを開ける。
$ sudo apt-get install iperf3
これでベンチの準備おけ。
$ iperf3 -s
で5201/tcpをリッスン。
クライアント側でiperf3を実行してみる。クライアント側の性能の方がサーバ側より低ければ、先にサチるのはクライアント側となる。拙宅のサーバの方が非力なので、どこでサチるか探る。
bw=8; while [ $bw -le 100 ]; do iperf3 -c myrsyncserver.japaneast.cloudapp.azure.com -l $bw -b 200M | grep 'sender'; bw=$(( $bw + 8 )); done
[ 4] 0.00-10.00 sec 35.9 MBytes 30.1 Mbits/sec 5 sender (8)
[ 4] 0.00-10.00 sec 70.7 MBytes 59.3 Mbits/sec 9 sender (16)
[ 4] 0.00-10.00 sec 105 MBytes 87.9 Mbits/sec 11 sender (24)
[ 4] 0.00-10.00 sec 140 MBytes 117 Mbits/sec 12 sender (32)
[ 4] 0.00-10.00 sec 134 MBytes 113 Mbits/sec 25 sender (40)
[ 4] 0.00-10.00 sec 166 MBytes 139 Mbits/sec 14 sender (48)
[ 4] 0.00-10.00 sec 146 MBytes 123 Mbits/sec 13 sender (56)
[ 4] 0.00-10.00 sec 165 MBytes 138 Mbits/sec 14 sender (64)
[ 4] 0.00-10.00 sec 167 MBytes 140 Mbits/sec 12 sender (72)
[ 4] 0.00-10.00 sec 145 MBytes 122 Mbits/sec 10 sender (80)
[ 4] 0.00-10.00 sec 155 MBytes 130 Mbits/sec 13 sender (88)
[ 4] 0.00-10.00 sec 165 MBytes 138 Mbits/sec 12 sender (96)
40コネクションあたりからサチってるので、その周辺だけ再度測定。
(修正)ここで変化させていたのは”-l”ですので、”length of buffer to read or write”でした。指摘をいただいた、Fumiyasu Satohさん、ありがとうございます!
bw=36; while [ $bw -le 48 ]; do iperf3 -c myrsyncserver.japaneast.cloudapp.azure.com -l $bw -b 200M | grep 'sender'; bw=$(( $bw + 2 )); done
[ 4] 0.00-10.00 sec 136 MBytes 114 Mbits/sec 6 sender (36)
[ 4] 0.00-10.00 sec 161 MBytes 135 Mbits/sec 3 sender (38)
[ 4] 0.00-10.00 sec 138 MBytes 116 Mbits/sec 4 sender (40)
[ 4] 0.00-10.00 sec 169 MBytes 142 Mbits/sec 3 sender (42)
[ 4] 0.00-10.00 sec 147 MBytes 123 Mbits/sec 2 sender (44)
[ 4] 0.00-10.00 sec 173 MBytes 145 Mbits/sec 0 sender (46)
[ 4] 0.00-10.00 sec 136 MBytes 114 Mbits/sec 0 sender (48)
バラついたので、もう一回。
bw=36; while [ $bw -le 48 ]; do iperf3 -c myrsyncserver.japaneast.cloudapp.azure.com -l $bw -b 200M | grep 'sender'; bw=$(( $bw + 2 )); done
[ 4] 0.00-10.00 sec 157 MBytes 132 Mbits/sec 12 sender (36)
[ 4] 0.00-10.00 sec 159 MBytes 134 Mbits/sec 5 sender (38)
[ 4] 0.00-10.00 sec 168 MBytes 141 Mbits/sec 3 sender (40)
[ 4] 0.00-10.00 sec 171 MBytes 143 Mbits/sec 1 sender (42)
[ 4] 0.00-10.00 sec 165 MBytes 139 Mbits/sec 2 sender (44)
[ 4] 0.00-10.00 sec 160 MBytes 134 Mbits/sec 3 sender (46)
[ 4] 0.00-10.00 sec 158 MBytes 132 Mbits/sec 1 sender
40 or 42コネクションが最適っぽいことが分かる。
(修正)iperfの”-P”オプションを変化させながら、最適なコネクション数を探ってみる。
con=8; while [ $con -le 100 ]; do iperf3 -c myrsyncserver.japaneast.cloudapp.azure.com -P $con -b 200M | grep 'SUM' | grep 'sender'; con=$(( $con + 8 )); done
[SUM] 0.00-10.00 sec 1.23 GBytes 1.06 Gbits/sec 112 sender (8)
[SUM] 0.00-10.00 sec 2.58 GBytes 2.22 Gbits/sec 244 sender (16)
[SUM] 0.00-10.00 sec 4.09 GBytes 3.51 Gbits/sec 1564 sender (24)
[SUM] 0.00-10.00 sec 5.23 GBytes 4.49 Gbits/sec 3598 sender (32)
[SUM] 0.00-10.00 sec 6.79 GBytes 5.84 Gbits/sec 3178 sender (40)
[SUM] 0.00-10.00 sec 7.34 GBytes 6.31 Gbits/sec 6773 sender (48)
[SUM] 0.00-10.00 sec 7.34 GBytes 6.31 Gbits/sec 16697 sender (56)
[SUM] 0.00-10.00 sec 7.50 GBytes 6.44 Gbits/sec 22062 sender (64)
[SUM] 0.00-10.00 sec 7.49 GBytes 6.43 Gbits/sec 28432 sender (72)
[SUM] 0.00-10.00 sec 7.66 GBytes 6.58 Gbits/sec 35196 sender (80)
[SUM] 0.00-10.00 sec 7.66 GBytes 6.58 Gbits/sec 43696 sender (88)
[SUM] 0.00-10.00 sec 7.65 GBytes 6.57 Gbits/sec 47673 sender (96)
48コネクションあたりでサチっていて、6.6Gbpsあたりが限界っぽい結果。
さて、rsyncサーバを立てる。コネクションは40 or 42 48まではイケるっぽいのでその上のキリが良い数字で64にして、/etc/rsyncd.conf
を以下のような感じで書いて、
#
# Global options
#
#
# Module options
#
max connections = 64
charset = utf-8
[Data]
path = /opt/ext/Data
comment =
use chroot = true
uid = root
gid = root
read only = false
hosts allow = xxx.xxx.xxx.xxx/32 10.0.0.0/24
hosts deny = *
list = true
dont compress = *.gz *.tgz *.zip *.pdf *.sit *.sitx *.lzh *.bz2 *.jpg *.gif *.png *.JPEG *.jpeg *.mov *.MP4 .MOV
んで、startする。
$ sudo systemctl --now start rsync
Network Security Groupで、873/tcpを開けるのを忘れないように。
では、まず普通にrsyncしてみる。転送するのは34GBのデータ。
$ rsync -av --progress /opt/Data/ rsync://myrsyncserver.japaneast.cloudapp.azure.com:/Data/
sent 36,179,638,248 bytes received 1,669 bytes 16,155,231.04 bytes/sec
total size is 36,170,802,020 speedup is 1.00
15.4MB/sしか出ない。圧縮したら速くなると考えるかもしれないけれど、rsyncd.conf
で設定している通り、そもそも圧縮されているファイル形式だと速くならないどころか、圧縮処理がオーバーヘッドになってむしろ遅くなる。
さて、今回の本題は、これを速くしたい。
でもrsyncは便利なので、他のプロトコルには変えたくない。
GNU Parallelでrsyncを並列実行すれば、速くなる?でも、面倒くさいよね。
というわけで、Infinibandでの転送にも使われるparsyncfpを。要はrsyncのラッパーなので、サーバ側は何も変更しなくて良い。拙宅のサーバはRHEL 7なので、perl-Envとfpartをインストールし、parsyncfpをgit cloneして適当に配置する。
$ sudo yum-config-manager --add-repo https://copr.fedorainfracloud.org/coprs/survient/fpart/repo/epel-7/survient-fpart-epel-7.repo
$ sudo yum -y install fpart perl-Env
$ git clone https://github.com/martymac/fpart
$ sudo cp fpart/parsyncfp /usr/local/bin/
$ sudo chmod 755 /usr/local/bin/parsyncfp
$ sudo chcon -u system_u -r object_r -t bin_t -l s0 /usr/local/bin/parsyncfp
$ parsyncfp
parsyncfp version 1.57
Mar 20, 2019
by Harry Mangalam >hjmangalam@gmail.com<
parsyncfp is a Perl script that wraps Andrew Tridgell's miraculous
......
使い方の詳細が表示されるので、適宜引数を調整するとして、上で転送した34GBのデータを転送してみる。
チャンクサイズとrsyncのフォーク数、加えて転送する個別のファイル容量やファイルシステムによって時間が変わるであろうことが推測出来るけど、チャンクサイズとフォーク数のみをマトリックスにしてみた。フォーク数32がどうやら安定して性能が出る様子。
下記は、チャンクサイズ32M、フォーク数16の例。
$ parsyncfp --rsyncopts="-a" --chunksize=32M --nowait --NP=16 --startdir='/opt/Data/' testdata rsync://myrsyncserver.japaneast.cloudapp.azure.com:/Data/
which: no perfquery in (/root/perl5/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin)
WARN: About to remove all the old cached chunkfiles from [/root/.parsyncfp/fpcache].
Enter ^C to stop this.
If you specified '--nowait', cache will be cleared in 3s regardless.
Otherwise, hit [Enter] and I'll clear them.
INFO: The fpart chunk files [/root/.parsyncfp/fpcache/f*] are cleared .. continuing.
INFO: Forking fpart. Check [/root/.parsyncfp/fpcache/fpart.log.14.49.42_2019-04-21] for errors if it hangs.
INFO: Starting the 1st [32] rsyncs ..
| Elapsed | 1m | [enp10s0] MB/s | Running || Susp'd | Chunks [2019-04-21]
Time | time(m) | Load | TCP / RDMA out | PIDs || PIDs | [UpTo] of [ToDo]
14.49.52 0.05 0.04 408.66 / 0.00 32 <> 0 [32] of [50]
14.49.55 0.10 0.04 406.73 / 0.00 28 <> 0 [42] of [50]
14.49.59 0.17 0.04 424.60 / 0.00 24 <> 0 [50] of [50]
14.50.02 0.22 0.04 407.13 / 0.00 22 <> 0 [50] of [50]
......
14.53.52 4.05 0.28 19.22 / 0.00 1 <> 0 [50] of [50]
14.53.56 4.12 0.26 13.91 / 0.00 1 <> 0 [50] of [50]
14.53.59 4.17 0.23 9.49 / 0.00 1 <> 0 [50] of [50]
250秒で34GBを転送したので、139.2MB/s。単なるrsyncの9倍の速度。
実際にフルセットのデータ、1.1TBを転送したところ、119.57 minutesで、avg 151.88 MB/sだった。