オフィシャルサイト: rsync

rsync

主にバックアップを目的とした rsync の実装方法について述べる。バックアップサーバ上で rsyncデーモンを待機させ、クライアントからファイルを同期できるようにする。rsyncデーモンは xinetd から起動するのが基本だが、よりダイハードでコントローラブルにするため、ここでは Upstart を利用して起動を制御する方法を紹介する。検証は RHEL/CentOS 6.6, CentOS 7.2 で行なった。

Table of Contents

rsyncデーモンのコンフィグ

rsyncd.conf

rsyncd というものは存在しない。クライアントとして使うのと同じ rsync コマンドに、--daemon オプションを与えるとデーモンとしての待機モードで起動するというだけだ。この時、rsyncデーモンは /etc/rsyncd.conf から設定を読み込む。既定ではこれだが、起動オプション --config=/PATH/TO/CONF を指定すれば変えることも可能。

rsyncd.conf の内容はグローバルコンテキストとモジュールブロックに別れる。モジュールとは、SMBNFSv4の共有名と同種のもので、下記の例ではサーバの /backupshares/bakshare/ に "SERVER::bakshare/" でアクセスできることになる。モジュールブロックで指定できるディレクティブはグローバルコンテキストでも指定でき、グローバル指定は各モジュールの既定値として働き、モジュールブロック内での指定はグローバル設定をオーバーライドする。幾つかグローバルコンテキストでしか使用できないディレクティブもあるが下記では使用していない。

ディレクティブは `name = value' の形。`=' の直前/直後のスペースは無視されるので入れても入れなくても構わない。ブーリアン(真偽)値は yes/no の他 0/1, true/false も通用する。先頭に # を置けばその行はコメントと解釈されるが、`name = var #comment' という風に途中からコメントにすることはできない。

uid = root                        (1)
gid = root
max connections = 10
timeout = 600                     (2)
use chroot = yes                  (3)
strict modes = yes                (4)
 
[bakshare]
    path = /backupshares/./bakshare     (5)
    read only = no
    list = no                           (6)
    auth users = rsyncusr, rsyncusr2    (7)
    secrets file = /etc/rsync.pwd       (8)
    hosts allow = 127.0.0.1, 192.168.1.0/24  (9)
    transfer logging = yes              (10)
  ディレクティブ 説明
(1) uid gid 転送に使用するシステムアカウント権限。当例はシステム系設定ファイルも含むバックアップが目的なので、あらゆるオーナーのファイル属性ビットが忠実に保持できるよう root とする。既定は nobody nobody。
(2) timeout クライアントからのI/Oリクエストタイムアウト(秒)。既定は0つまり無限に待ち続けてしまう。
(3) use chroot モジュールpathへchrootするかどうか。yes にするには rsyncデーモンは root で起動されなければならない(上記の転送ユーザ指定 uid, gid と混同するな)。
- numeric ids ファイル転送の中でファイルやディレクトリのオーナー/グループを名前でなく数値表現のuid/gidのまま遣り取りする。rsyncクライアントとサーバとで存在するユーザ/グループとそれらのuid/gidが統一されていれば `numeric id=no' でも気にする必要はないのだが、そうでない場合、例えばクライアント側のhogeのuidが500でサーバ側では501という環境では、オーナーhogeのファイルがサーバ上へはuid=501となって格納されてしまう。`use chroot=no' の時の既定値はnoつまり名前ベースだが、`use chroot=yes'時は既定値が暗黙的にyesとなる。chroot環境でユーザ名解決をするには環境内に etc/passwd, group ファイルなどが要ることを忘れずに。chroot環境の整備については vsftpd の記事をご参考に。
(4) strict modes (8)のパスワードファイルが rsyncデーモン起動ユーザ以外の読めるパーミッションになっていないかチェックする。
- log file rsyncに自力でログファイル出力をさせたい場合はそのPATHを指定。既定ではsyslogに送られる。
- log facility syslog送信の場合のログファシリティ。既定は daemon。
(5) path モジュールにマッピングするファイルシステムPATH。`use chroot=yes' の場合、例のようにドット・スラッシュを入れると、/backupshares/chroot して baksharecd する動きとなり、後述する logソケットの配備などに都合がよい。
(6) list list=no とすることにより、クライアントからのモジュール一覧取得命令にこのモジュールを開示しないようにする。
(7) auth users 接続を許可する rsyncユーザ。ここで言うユーザはシステムアカウントとは無関係で、次の secrets file 内で定義するrsync固有のもの。複数指定する場合はスペースまたはカンマで区切る。
(8) secrets file rsyncユーザのユーザ名とパスワードのリスト。`strict modes=yes' の時はファイルのパーミッションは 600 でなくてはならない。
(9) hosts allow 接続を許可するクライアントホストのリスト。複数指定する場合はスペースまたはカンマで区切る。IP, IP/PREFIX, IP/MASK, ホスト名(ワイルドカード使用可)が使用可能で、ホスト名の場合は、接続してきたIPからリバースルックアップによってホスト名を牽いてチェックされる。
- hosts deny 接続を拒否するクライアントホストのリスト。通常 `hosts allow' しか指定しない方がよい。両方設定されている場合は、先に allow が評価されてマッチすれば即許可、allow にも deny にもヒットしないホストは許可される。
(10) transfer logging 読んで字の如し。`use chroot=yes' の場合は chroot先にlogソケットを作っておく必要がある(後述)。

パスワードファイル

上記 `secrets file' で指定したパスにユーザリストを作成し、rsyncデーモン起動システムユーザだけの読めるパーミッションにしておく。当例だと /etc/rsync.pwd (root:root 600) の内容はこんな塩梅。

rsyncusr:hispassword
rsyncusr2:herpassword

Syslog設定

/etc/rsyslog.conf を調整。ただし、Systemd管理のシステム(RHEL/CentOS 7など)で rsyncdsystemd で駆動する場合は、journald だけでログを扱う限りは調整は不要だ。

ログファイル振り分け

既定のローカルログフィルタ(/var/log/messsagesなど)より手前に、下記のフィルタを挿入する。

if $programname startswith 'rsync' then         -/var/log/rsyncd.log
& ~         <-rsyslog v7の場合は `& stop' と書くべき

logソケットの作成

rsyncd.confchroot を有効にしている場合には、chroot先ディレクトリに syslogソケットを作っておくべき。なくても基本的なログは出るが、転送ログが出力できない。先に /backupshares/dev ディレクトリ (root:root 755) を作成してから、rsyslog.conf を修正。

$ModLoad imuxsock  <-既定で存在するこの記述の後あたりに、
$AddUnixListenSocket /backupshares/dev/log

rsyncd.conf のモジュールの path 指定に ./ を入れたのはこのためだ。他のモジュールも /backupshares/ の配下に置く限り、モジュール追加の度に logソケットを追加しなくて済む。

`service rsyslog restart' して /backupshares/dev/log ファイルができたことを確認。

Rateリミットの緩和

rsyslogのデフォルトでは、syslogのUNIXソケットは入力レコードが 5秒間あたり 200メッセージを超えると Rateリミットが作動してメッセージを破棄するようになっている。Rateリミットが働くと messages に報告される。rsync は非常に高速にファイルを処理するので、rsyncd.conftransfer logging = yes としている場合、これに引っかかることが多々ある。まずは、レートを倍にすることから始めて、調整するとよい。SystemLogRateLimit*imuxsock モジュールのオプションなので、 rsyslog.conf の "$ModLoad imuxsock" ディレクティブより後、なお且つどのログ振り分けルールよりも前に書かなくてはいけない。

# $SystemLogRateLimitInterval 5
$SystemLogRateLimitBurst 400

上記は実は古い書き方で、rsyslog7 からの新しい設定書式で書くなら、デフォルト設定ファイルの "$ModLoad imuxsock" 行を下記のように丸ごと置き換える。

module(load="imuxsock" SysSock.RateLimit.Interval="5" SysSock.RateLimit.Burst="400")

これでもまだ破棄されるようであれば、~Burst値をもっと増やすか、逆に、単位時間を 3秒や 2秒のように短くするかしてさらに調整。rsyslog の新しい書式についてもっとよく知りたい輩は rsyslogのページをご参照いただきたい。

rsyncデーモンの起動の仕組み作り

rsyncデーモンの待機動作の面で、やり方は大きく分けてふたつに別れる。

主プロセスがコネクション毎に子プロセスを起こすパターン
最初に起動されたrsyncデーモンは制御プロセスとして待機し、クライアントからのコネクションが来ると制御プロセスが子プロセスを起こして実転送業務を行う。ふたつ以上のクライアントが同時に接続すれば子プロセスはその数だけ起動される。Upstartだけで簡単に仕組みが作れる。
縁のないrsyncデーモンプロセスをコネクション毎に起動するパターン
コネクションが来る度に、親子関係のない独立した rsyncデーモンが立ち上がる。昔ながらの xinetd 経由の起動はこのタイプだ。当文書では、よりコントローラブルにするため Upstart + tcpserver の組み合わせを紹介する。

rsync の設計からすると、どちらかといえば前者の子プロセスを起こすパターンの方が適切な気がする。後者では、rsyncd.conf でログを自力でファイルへ直接書くように設定 している場合、複数のクライアントがつないで来た時に問題が起こるのではなかろうか。

Upstartのみ

ジョブファイルの作成

/etc/init ディレクトリに root:root 644 で rsyncd.conf というJobファイルを作成する。

# rsyncd - rsync server daemon
#
# Starts rsync daemon
 
start on started rc RUNLEVEL=[2345]
stop on started rc RUNLEVEL=[S016]
 
console output
respawn
respawn limit 10 120
exec /usr/bin/rsync --daemon --no-detach

respawn なので不意に落ちたらまた立ち上げられるが、limit により、120秒以内に 10回死んだら再立ち上げはされなくなる。肝は、rsyncコマンドに追加した `--no-detach' オプションだ。これを付けることによって rsync は、デーモンモードでありながら fork() することなくフォアグラウンドのままになる。同じことは、下の書き方でも実装できる - fork() させないか fork()Upstart に追いかけさせるかの違いだけだ。2回 fork() することを期待する `expect daemon' ではダメだった。

# rsyncd - rsync server daemon
#
# Starts rsync daemon
 
start on started rc RUNLEVEL=[2345]
stop on started rc RUNLEVEL=[S016]
 
console output
expect fork
respawn
respawn limit 10 120
exec /usr/bin/rsync --daemon

ジョブファイルができたら、

root# initctl reload-configuration
root# start rsyncd
root# status rsyncd

Upstart + tcpserver

tcpserverのインストール

tcpserverのページを参照のこと。ルールファイルは作らなくていい(より厳重にするなら作ってもいいが)。

Upstartジョブファイルの作成

/etc/init ディレクトリに root:root 644 で rsyncd.conf というJobファイルを作成する。

# rsyncd - rsync server daemon
#
# Starts rsync daemon
 
start on started rc RUNLEVEL=[2345]
stop on started rc RUNLEVEL=[S016]
 
console output
respawn
respawn limit 10 120
exec /usr/local/bin/tcpserver -H -R -v -l0 0 rsync /usr/bin/rsync --daemon

この設定により、rsyncの既定TCPポート 873 で待ち受ける tcpserver が立ち上がる。respawn なので不意に落ちたらまた立ち上げられるが、limit により、tcpserver が 120秒以内に 10回死んだら再立ち上げはされなくなる。ジョブファイルができたら、

root# initctl reload-configuration
root# start rsyncd
root# status rsyncd
tcpserver の立ち上げるプログラム (もちろんここでは/usr/bin/srync) は、バックグラウンドへフォークしてはならない。ただし、rsync は、STDIN がソケットにつながっていることを検出すると inetd 経由で呼ばれたと判断し、--daemon モードであってもフォークを行わないようにできている。tcpserver もプログラムの STDIN をネットワークソケットにつなぐので、exec 行に敢えて --no-detach オプションを付加する必要はないようだ。

Systemd

サービスユニットファイルの作成

rsyncパッケージがインストールしてあれば /usr/lib/systemd/system/rsyncd.service がインストールされているので、これを /etc/systemd/system/ へコピー (root:root 644) して土台にする。ほとんどいじる必要もない。敢えて言うなら CGroup によるリソース制限を掛けてやるくらいだ。

[Unit]Description=fast remote file copy program daemon
ConditionPathExists=/etc/rsyncd.conf
 
[Service]
CPUShares=2048
MemoryLimit=200M
EnvironmentFile=/etc/sysconfig/rsyncd
ExecStart=/usr/bin/rsync --daemon --no-detach "$OPTIONS"
 
[Install]
WantedBy=multi-user.target
# systemctl daemon-reload
# systemctl start rsyncd.service
# systemctl enable rsyncd.service

ちなみに、/usr/lib/systemd/system/ には、xinetd的にソケットで待ち受けてサービスへ引き渡す構造のユニットファイルセットも用意されている。そちらの方式を使ってみたい人は、rsyncd.socket ファイルと rsyncd@.service ファイルを /etc/systemd/system/ へそのままの名前でコピーし、rsyncd.socket の方を start および enable すればよい。ああ、IPv6を殺している人は rsyncd.socketListenStream=873ListenStream=0.0.0.0:873 と変えないと動かないことを申し添えておこう。

クライアントからの接続

パスワードファイル作成

rsyncサーバ側でユーザ認証を掛けているので、接続の際にはパスワードを指定しなくてはならない。コマンドラインでその都度指定するか対話式でタイプしてもいいのだが、パスワードファイルを作っておくとスクリプト化する時に便利だ。当例ではrsyncクライアントを root で使うので root 権限で、

root# echo hispassword >~/.rsync.pwd
root# chmod 600 ~/.rsync.pwd

rsyncの実行

rsyncサーバのIPが 192.168.1.1 だとすると、こんな塩梅。--password-file の引数は相対パスでもOKなのだが、~/.rsync.pwd とやるとチルダが展開されずに "No such file" エラーになってしまう。

root# rsync -azc --delete --password-file /root/.rsync.pwd \
      /my/srcdir/ rsyncusr@192.168.1.1::bakshare/destdir/

URL は "protocol://" の形式でも指定できる。そちらで書くと、

root# rsync -azc --delete --password-file /root/.rsync.pwd \
      /my/srcdir/ rsync://rsyncusr@192.168.1.1/bakshare/destdir/