前回の記事では、WasabiからBackblaze B2へのデータ移行についてまとめました。これでデータそのものは移動できましたが、それだけではWebアプリケーションから利用することはできません。

Redmineから見れば、これまで通り「files」ディレクトリが存在しているだけであり、その保存先がローカルSSDなのか、Backblaze B2なのかは意識しない構成が理想です。

そのためには、Backblaze B2をLinuxへマウントし、通常のディレクトリとして扱えるようにする必要があります。

今回は、そのための仕組みとしてrclone mountを利用しました。問題は、「どうやって常駐させるか」です。

古くからLinuxでは/etc/fstabへ記述して起動時にマウントする方法が紹介されています。しかし今回は、その方法は採用しませんでした。理由は単純です。クラウドストレージはネットワークの向こう側にあります。

ローカルディスクと同じ感覚でOS起動時にマウントしようとすると、起動順序や通信断など、物理ディスクでは考えなくてよい問題が発生します。

今回はそうしたリスクを避けるため、systemdサービスとしてrclone mountを常駐させる構成にしました。

systemdを選んだ理由

/etc/fstabは非常に便利ですが、クラウドストレージでは少し事情が変わります。

OS起動時、まだネットワークやDNSが利用できないタイミングでマウント処理が実行されると、ホスト名の名前解決に失敗し、そのまま起動処理が止まってしまうことがあります。

設定によってはレスキューモードへ入ってしまうため、「ストレージが利用できない」だけでは済みません。

また、運用中に通信断などでrclone mountが終了してしまった場合も、/etc/fstabには再起動する仕組みがありません。

つまり、一度落ちると手動で復旧するまで、Redmineから添付ファイルへアクセスできない状態が続いてしまいます。systemdであれば、この問題を比較的素直に解決できます。

After=network-online.targetによってネットワークが利用可能になるまで起動を待機でき、さらにRestart=alwaysを指定すれば、万が一プロセスが終了しても自動的に再起動してくれます。

単に「起動時にマウントする」のではなく、「サービスとして運用する」という考え方です。

FUSEの準備を行う

rclone mountはFUSE(Filesystem in Userspace)を利用して動作します。

今回は一般ユーザーでマウントを実行しつつ、Webサーバー(www-data)から読み書きできるようにしたかったため、まずFUSE側の設定を変更しました。

念のためバックアップを取得した上で、/etc/fuse.confのuser_allow_otherを有効にします。

  • バックアップ
sudo cp -p /etc/fuse.conf /etc/fuse.conf.orig

user_allow_other を有効化

sudo sed -i 's/#user_allow_other/user_allow_other/' /etc/fuse.conf

続いてマウントポイントを作成します。

sudo mkdir -p /mnt/app_storage
sudo chown -R $USER:www-data /mnt/app_storage
sudo chmod 775 /mnt/app_storage

今回は実行ユーザーが管理しつつ、Webサーバーからも書き込み可能な権限にしました。

systemdサービスを作成する

続いて、rclone mountをsystemdサービスとして登録します。

sudo tee /etc/systemd/system/rclone-b2-app.service << 'EOF'
[Unit]
Description=Rclone Mount Backblaze B2 for Application Storage
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
User=appuser
Group=appuser
ExecStart=/usr/bin/rclone mount b2-remote:dest-backup/app_data /mnt/app_storage \
  --allow-other \
  --vfs-cache-mode full \
  --vfs-cache-max-size 10G \
  --vfs-cache-max-age 24h \
  --uid 33 --gid 33 \
  --umask 002 \
  --log-level INFO \
  --log-file /var/log/rclone-b2-app.log
ExecStop=/bin/fusermount3 -u -z /mnt/app_storage
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

今回の設定で特に意識したのは、アプリケーションとの互換性です。

--vfs-cache-mode fullを指定することで、読み書きをローカルキャッシュ経由で処理し、多くのWebアプリケーションが通常のファイルシステムと同じように扱えるようになります。

また、

--uid 33
--gid 33

を指定し、見かけ上の所有者をwww-dataへ変更しています。これにより、Redmineなどから見ても通常の添付ファイルディレクトリとして扱うことができます。

ログについてもsystemd任せにせず、専用ファイルへ出力するよう設定しました。

sudo touch /var/log/rclone-b2-app.log
sudo chown appuser:appuser /var/log/rclone-b2-app.log

問題が起きた際に、原因を追いやすくするためです。

サービスとして起動する

設定が終わったら、systemdへ登録します。

sudo systemctl daemon-reload
sudo systemctl enable --now rclone-b2-app

状態は、

sudo systemctl status rclone-b2-app

で確認できます。Active: active (running)となっていれば正常です。

続いて、

ls -la /mnt/app_storage/files/

などで、Backblaze B2上のデータがマウントされていることを確認します。ここまで来れば、Linuxからは通常のディレクトリとして扱える状態になっています。

Redmineを止めずに切り替える

最後は、Redmineが参照する添付ファイルディレクトリを切り替えます。今回はシンボリックリンクを利用していたため、

cd /var/www/redmine
ln -sfn /mnt/app_storage/files files

だけで切り替えられました。Linuxではシンボリックリンクそのものを置き換える処理は一瞬で完了します。

つまり、Redmineから見ると「files」という入り口が別の場所を指すようになるだけであり、中途半端な状態が発生しません。

このような、一度に切り替える方法は一般にアトミックな切り替えと呼ばれます。アプリケーションを停止することなく保存先だけを差し替えられるため、運用中のシステムでは非常に扱いやすい方法です。

最後は実際に読み書きを確認する

切り替えたら、必ずWebアプリケーションから動作を確認します。

まずは既存チケットを開き、添付ファイルや画像が問題なく表示・ダウンロードできることを確認しました。

続いて、新しいチケットを作成し、テスト用ファイルを添付します。

Redmine上で正常に保存できることに加え、Backblaze B2の管理画面でもファイル数と使用容量が増えていることを確認しました。

読み込みだけでなく、書き込みまで正常に動作することが確認できれば、移行作業は完了です。

おわりに

Backblaze B2への移行は、データをコピーしただけでは終わりではありません。

Webアプリケーションからこれまで通り利用でき、通信断が発生しても自動的に復旧し、運用者が特別なことを意識しなくても済む状態になって初めて、「移行が終わった」と言えるのだと思います。

今回はrclone mountをsystemdサービスとして管理することで、起動順序や通信断といったクラウドストレージ特有の課題にも対応できる構成になりました。

今後は、この構成でRedmineやPiwigoなどを実際に運用しながら、VFSキャッシュのサイズやメモリ消費、レスポンスへの影響なども継続して検証していく予定です。