CUBE SUGAR CONTAINER

技術系のこと書きます。

rsync で効率的にファイルを同期する

rsync はファイルを別のディレクトリやリモートのホストと同期させるためのプログラムだ。 例えば rsync の代わりに cp コマンドを使った場合、宛先の状態に関わらず全てのファイルがコピーされてしまう。 それに対し rsync では、宛先の状態を確認して異なる箇所 (差分) だけをコピーするため効率が良い。

今回はその rsync を使ってみよう。 検証の環境には CentOS7 を使った。

$ cat /etc/redhat-release
CentOS Linux release 7.1.1503 (Core)
$ uname -r
3.10.0-229.11.1.el7.x86_64

rsync をインストールする

rsync がインストールされていない場合は yum を使って入れる。 基本的には最初から入っているはず。

$ sudo yum -y install rsync

同じホストの中でシンプルに使ってみる

まずは同じホストで、異なるディレクトリを使って動作を確認しよう。 コピー元とコピー先のディレクトリを作っておく。

$ mkdir from to

コピー元となるディレクトリにファイルを用意する。

$ cat << EOF > from/sample.txt
Hello, World
EOF

準備ができたら rsync コマンドを実行する。

$ rsync -av from/ to/
sending incremental file list
./
sample.txt

sent 123 bytes  received 34 bytes  314.00 bytes/sec
total size is 13  speedup is 0.08

オプションの意味は次の通り。

オプション 意味
-a -rlptgoD と同意 (アーカイブモードと呼ばれる)
-v 処理内容の詳細を表示する
-r 再帰的にディレクトリを走査する
-l シムリンクをシムリンクとしてコピーする
-p パーミッションを維持する
-t 変更時間を維持する
-g グループを維持する
-o オーナーを維持する
-D --devices --specials と同意 (デバイスファイルを維持する)

実行するとコピー先のディレクトリにファイルができる。

$ cat to/sample.txt
Hello, World

もちろん、コピー元のファイルに変更を加えた上で再度実行すればその内容が同期される。

$ sed -i -e "s:World:Rsync:g" from/sample.txt
$ rsync -av from/ to/
sending incremental file list
./
sample.txt

sent 123 bytes  received 34 bytes  314.00 bytes/sec
total size is 13  speedup is 0.08
$ cat to/sample.txt
Hello, Rsync

では、コピー元のファイルを削除するとどうなるだろうか?

$ rm from/sample.txt
$ rsync -av from/ to/
sending incremental file list
./

sent 48 bytes  received 15 bytes  126.00 bytes/sec
total size is 0  speedup is 0.00

今度は rsync を実行してもファイルが残ったままになっている。

$ ls to/
sample.txt

ファイルの削除も反映するには --delete オプションをつける必要がある。 これはコピー元とコピー先を完全に同期することになる。

$ rsync -av --delete from/ to/
sending incremental file list
deleting sample.txt

sent 45 bytes  received 12 bytes  114.00 bytes/sec
total size is 0  speedup is 0.00
$ find to
to

同期に含めたくないファイルがある場合

コピー元のディレクトリの中に同期したくないファイルが存在することもあるはず。 試しに exclude.txt というファイルを同期から除外する対象にしてみよう。

$ ls from/
exclude.txt  sample.txt

同期したくないものについては --exclude オプションでファイル名のパターンを指定する。

$ rsync -av --exclude='exclude.*' from/ to/
sending incremental file list
./

sent 71 bytes  received 15 bytes  172.00 bytes/sec
total size is 13  speedup is 0.15

exclude という名前のファイルを同期の対象から除外しているのでコピー先には exclude.txt が同期されない。

$ ls to/
sample.txt

実行前に何が起こるかを確認する

実際に同期する前に、コマンドを叩くことで何が起こるかを確認したい場合は -n (--dry-run) オプションを付けて実行すると良い。

$ rsync -avn from/ to/
sending incremental file list
exclude.txt

sent 91 bytes  received 15 bytes  212.00 bytes/sec
total size is 13  speedup is 0.12 (DRY RUN)

ドライ・ランでは実際の処理は実行されないためコピー先のディレクトリに変化はない。

$ ls to/
sample.txt

--include オプションは --exclude オプションと併用する

rsync はコピー元ディレクトリを丸ごと同期するので --include オプションは単体で使っても特に意味がない。 --exclude オプションで同期対象から除外した中で、これだけは特別に同期したいといったものを指定するのに使う。

$ rsync -av --include='sample.txt' from/ to/
sending incremental file list
exclude.txt

sent 127 bytes  received 31 bytes  316.00 bytes/sec
total size is 13  speedup is 0.08
$ ls to
exclude.txt  sample.txt

リモートホストと同期してみる

ここまでは同じホスト内でディレクトリを同期させていたけど、それよりはリモートホストとの間で同期させる方が使用場面が多いはず。 何故なら rsync は複数のホストの間でファイルの状態を同期させたり、あるいはバックアップを取る目的で使われることが多いため。 今回は 192.168.33.12 という IP アドレスを持ったホストとディレクトリを同期させてみる。

$ ping -c 3 192.168.33.12
PING 192.168.33.12 (192.168.33.12) 56(84) bytes of data.
64 bytes from 192.168.33.12: icmp_seq=1 ttl=64 time=0.530 ms
64 bytes from 192.168.33.12: icmp_seq=2 ttl=64 time=0.321 ms
64 bytes from 192.168.33.12: icmp_seq=3 ttl=64 time=0.316 ms

--- 192.168.33.12 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.316/0.389/0.530/0.099 ms

パスワード認証は面倒なので、まずは公開鍵を作って設置することにしよう。 ssh-keygen コマンドで公開鍵ペアを作っておく。

$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/vagrant/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/vagrant/.ssh/id_rsa.
Your public key has been saved in /home/vagrant/.ssh/id_rsa.pub.
The key fingerprint is:
d0:7b:f3:0e:df:a0:4b:c4:fd:bc:68:ad:9f:cf:9c:ae vagrant@localhost.localdomain
The key's randomart image is:
+--[ RSA 2048]----+
|                 |
|       .         |
|      . .        |
|       . o .     |
|        S = .    |
|         o o o   |
|          o o.o  |
|         . =.+.=.|
|          oo=EB++|
+-----------------+

ssh-copy-id コマンドを使って、生成した公開鍵をリモートホストに登録する。

$ sudo yum -y install openssh-clients
$ ssh-copy-id -i ~/.ssh/id_rsa.pub 192.168.33.12
The authenticity of host '192.168.33.12 (192.168.33.12)' can't be established.
ECDSA key fingerprint is 0e:ae:85:dc:52:d7:70:4d:fd:6f:9a:f7:0c:2f:a9:99.
Are you sure you want to continue connecting (yes/no)? yes
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
vagrant@192.168.33.12's password:

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh '192.168.33.12'"
and check to make sure that only the key(s) you wanted were added.

これで準備はできた。 あとは rsync コマンドでこれまでコピー先に指定していたディレクトリの前部分にリモートホストの IP アドレスを : (コロン) と一緒に付けるだけ。

$ rsync -av /tmp/from/ 192.168.33.12:/tmp/to/
sending incremental file list
created directory /tmp/to
./
exclude.txt
sample.txt

sent 186 bytes  received 53 bytes  478.00 bytes/sec
total size is 13  speedup is 0.05

ssh コマンドを使って確認すると、ちゃんとディレクトリの内容が同期されていることが分かる。

$ ssh 192.168.33.12 "find /tmp/to"
/tmp/to
/tmp/to/exclude.txt
/tmp/to/sample.txt

リモートホストと同期する際の注意点

リモートホストと同期する場合には、リモートホストにも rsync がインストールされている必要がある。 もし rsync がない場合には、以下のようにエラーになってしまうので注意が必要だ。

$ rsync -av /tmp/from/ 192.168.33.12:/tmp/to/
bash: rsync: コマンドが見つかりません
rsync: connection unexpectedly closed (0 bytes received so far) [sender]
rsync error: remote command not found (code 127) at io.c(605) [sender=3.0.9]

まとめ

今回は rsync を使ってファイルを効率的に同期する方法について書いた。 rsync は複数ホストの間でファイルの状態を同期したり、あるいはファイルのバックアップにと様々な場面で使われる。 また、MySQL をマルチマスタで冗長化できる Galera Cluster においても、変更内容の転送部分に rsync が使われていたりする。 このように、rsync はホストを管理する上で重要なプログラムなので使い方は覚えておきたい。