はじめに
以前、「Linux版dropboxをsystemdでサービスとして起動する」という記事を書いたが、最近Linux版のDropboxクライアントのシステム要件が変更になり、CentOS7では動作しなくなってしまった。そこで、新たにDocker版のDropboxクライアントコンテナと、その起動用のSystemdのユニットファイルを作成したので紹介する。
2020/11/07 追記
- Dropboxクライアントのバージョンアップに伴い、Alpine ではライブラリ依存を解決できず起動できなくなってしまったので、Ubuntu18.04 ベースのイメージ(
littlef/dropbox:1.1.0
)に作り直しました。 コンテナのイメージサイズは大きくなってしまいますが、きちんと動作するようになりました。 - 新しいバージョンで動かすには、一度サービスを止めてから既存のイメージを削除して再起動してください。
$ sudo systemctl stop dropbox@XXX
$ docker rmi docker.io/littlef/dropbox:latest
$ sudo systemctl start dropbox@XXX
2019/12/16 追記
- 最新のDropboxクライアントがxfsに対応するようになったので、本文後半の対応(ファイルシステムの問題)は不要になりました。
- Dropboxクライアントの変更に伴い、公開していたDockerコンテナが起動に失敗するようになっていたので、修正版を公開しました。 使い方等はGithubリポジトリをご覧ください。
Linux版Dropboxクライアントのシステム要件
2018年10月頃、LinuxのDropboxクライアントに加わった仕様変更は以下の通り。
- glibc2.19 以降が必要になった
-
Dropboxクライアントの同期先ディレクトリのファイルシステムが ext4 のみとなった(2019年12月段階ではxfsにも対応した)
詳細は https://help.dropbox.com/ja-jp/desktop-web/system-requirements を参照。
一方、現時点で最新のCentOS7(7.6.1810)は以下のような状況で、どちらの条件も満たさない。
- glibc のバージョンは 2.17
- 標準のファイルシステムは XFS
ファイルシステムについては、ext4でフォーマットしなおせば良いが、今さらDropboxだけのために戻したくはない。
解決策
1. glibc 問題の解決
Dropbox クライアントを Docker コンテナで実行する
glibc はあらゆるコマンドから利用されているので、おいそれとバージョンを上げるわけにはいかない。このため、要件を満たすバージョンの glibc がインストールされた Docker コンテナ上で Dropbox を動作させることでクリアした。
glibcがインストールされた Alpine Linux の Docker イメージ([frolvlad/alpine-glibc])にDropboxクライアントをインストールしたものを用意する。同様の Docker イメージはいくつか公開されているが、今回はいくつか修正したい要件があったので、dylansm/dropboxを元に自作した。
さらにこのイメージを systemd 経由で簡単に起動できるようにしたので、本稿の後半で紹介する。
Dropbox同期ディレクトリのowner問題を解決する
ホスト側に用意したDropbox同期ディレクトリ(通常は~/Dropbox
)をDockerコンテナ側にマウントするが、その際に問題となるのがUID、GIDの問題。コンテナ内で動作するプロセスは root 権限で実行されるため、このプロセスが作成したファイルのオーナーは、ホスト側でも root になってしまう。
このため、コンテナ内のDockerクライアントは、ホスト側のDropbox同期ディレクトリのオーナーと同じUID、GIDのユーザーとして実行する必要がある。(この解決方法については、「dockerでvolumeをマウントしたときのファイルのowner問題」を参考にさせていただいた。)
2.ファイルシステム問題の解決
※ 2019年12月現在、Dropboxクライアントがxfsにも対応するようになったので、本対応は不要になった
ファイルをループバックマウントする
既存のパーティション内に用意したファイルをループバックマウント1し、ext4でフォーマットして、Dockerコンテナにマウントすることで解決。この解決策に関しては、「btrfsでDropboxを使う」を参考にさせていただいた。この記事はAlpineLinuxでの作成例。CentOS7でもほぼ同じだが、いちおう紹介しておく。
まず、イメージファイルを作成。どこに作成しても構わないが、ここではホームディレクトリ配下で ~/.img/dropbox.img
という名前で1Gbytesのファイルを作成した。
ファイルはスパースファイルなので、最初から1Gの容量を使用するわけではない。
$ mkdir ~/.img
$ truncate -s 1G ~/.img/dropbox.img
作成したファイルを、ext4 でフォーマットする。対象がブロックデバイスではないので確認のプロンプトが表示されるが、y
を回答して進める。
$ mkfs.ext4 ~/.img/dropbox.img
mke2fs 1.42.9 (28-Dec-2013)
dropbox.img is not a block special device.
Proceed anyway? (y,n) y
Discarding device blocks: done
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
655360 inodes, 2621440 blocks
131072 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=2151677952
80 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632
Allocating group tables: done
Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done
次に、Dropbox同期用ディレクトリ ~/Dropbox
を作成する。
$ mkdir ~/Dropbox
このディレクトリに先ほど作成したファイルをループバックマウントする。マウントにはroot権限が必要だが、その際に~/Dropbox
のオーナーがrootになってしまうので、chownコマンドで修正しておく。
$ sudo mount -o loop -t ext4 ~/.img/dropbox.img ~/Dropbox
$ sudo chown -R ユーザ名:グループ名 ~/Dropbox
ただしくマウントできたか、確認してみる。ディレクトリのファイルシステムが /dev/loop*
、タイプがext4
となっているので、ただしくフォーマット・マウントできていることがわかる。
$ df -T ~/Dropbox
Filesystem Type 1K-blocks Used Available Use% Mounted on
/dev/loop2 ext4 10190100 7964172 1685256 83% /home/ユーザ名/Dropbox
systemdのマウントユニットとして保存する
システム起動時に上記のファイルを自動実行するようにしておく。従来なら /etc/fstab
に記述するが、せっかくなので systemd のマウントユニットを作成してみる。
たとえば、同期ディレクトリが /home/bob/Dropbox
の場合、以下のような内容で /etc/systemd/system/home-bob-Dropbox.mount
という名前のファイルを作成する。
[Unit]
Description=Mount Dropbox directory
After=network-online.target
Wants=network-online.target
[Mount]
What=/home/bob/.img/dropbox.img
Where=/home/bob/Dropbox
Options=loop
Type=ext4
TimeoutSec=30
[Install]
WantedBy=multi-user.target
ここで、mountファイルのファイル名は、マウント先パスのスラッシュ(/
)をハイフン(-
)に置き換えたものにするルール。なので、/home/bob/Dropbox
ならば、home-bob-Dropbox.mount
としなくてはならない。
マウントファイルを作成したら、systemd に読み込ませ、
$ sudo systemctl daemon-reload
以下のコマンドでマウントできる。
$ sudo systemctl start home-bob-Dropbox.mount
さらに、以下のコマンドで自動マウントするようにしておく。
$ sudo systemctl enable home-bob-Dropbox.mount
systemdでDocker版Dropboxの起動する
unitファイルのダウンロードと登録
systemdのunitファイルを Github からダウンロードして、/etc/systemd/system
配下に配置する。
$ cd /etc/systemd/system
$ sudo curl -o dropbox@.service https://raw.githubusercontent.com/little-forest/docker-dropbox/master/dropbox%40.service
$ sudo systemctl daemon-reload && echo OK
OK
Dropboxサービスの起動
systemctlコマンドで、サービスを起動。テンプレートユニットにしてあるので、@
以降にはドロップボックス同期ディレクトリのオーナーユーザー名を指定する。ここでは、同期ディレクトリが/home/bob/Dropbox
なので、dropbox@bob
とする。
また、サービス起動直後にログを確認できるように、journalctlも実行するようにした。
$ sudo systemctl start dropbox@bob.service && sudo journalctl -fu dropbox@bob.service
初回起動時はDockerイメージをダウンロードするため、少々時間がかかる。しばらくすると、以下のようなメッセージが表示されるので、表示されたURLをブラウザで表示してDropboxアカウントでログインすると、リンクされる。
This computer isn't linked to any Dropbox account...
Please visit https://www.dropbox.com/cli_link_nonce?nonce=**************************************** to link this device.
しばらく待つと、同期がはじまる。
ステータスの確認
筆者が作成したDockerイメージ(little-forest/docker-dropbox)では、ホスト側からDropboxの管理コマンドを実行してステータス等を確認できるようにした。
ホスト側から以下のように実行することで、同期ステータスを確認することができる。
$ docker exec -it <コンテナ名> /dropbox status
Up to date
本稿で紹介したsystemdのユニットファイルからDockerコンテナを起動した場合、コンテナ名はdropbox_<systemdサービスのインスタンス名>
となっている。dropbox@bob.service
で起動していれば、dropbox_bob
がコンテナ名。
(参考)Dropboxクライアントサービスのユニットファイル
ユニットファイルは以下のような感じ。単にDockerコンテナを起動するだけでなく、ExecStartPre
ディレクティブを使って事前チェックや下準備をするようにしている。
[Unit]
Description=Dropbox client (user: %i)
Requires=docker.service
After=docker.service
[Service]
Type=simple
Environment=DBOX_HOME=/home/dbox
# check user existence
ExecStartPre=/bin/bash -c "id %i > /dev/null 2>&1 || (echo '[ERROR] User not found. : %i'; exit 1; )"
ExecStartPre=/bin/bash -c "if [[ ! -d /home/%i ]]; then echo '[ERROR] Directory not found. : /home/%i'; exit 1; fi"
# prepare user's uid & gid
ExecStartPre=/bin/bash -c "echo USER_ID=`id %i | sed -re 's/.*uid=([0-9]+).*/\1/'` > /tmp/dropbox_%i"
ExecStartPre=/bin/bash -c "echo GROUP_ID=`id %i | sed -re 's/.*uid=([0-9]+).*/\1/'` >> /tmp/dropbox_%i"
# make dropbox directory
ExecStartPre=/bin/bash -c "[[ -d /home/%i/Dropbox ]] || (mkdir /home/%i/Dropbox; chown %i:%i /home/%i/Dropbox;)"
ExecStartPre=/bin/bash -c "[[ -d /home/%i/.dropbox ]] || (mkdir /home/%i/.dropbox; chown %i:%i /home/%i/.dropbox;)"
# stop & remove existence container
ExecStartPre=/bin/bash -c "/usr/bin/docker container ls -q -f name=dropbox_%i | xargs -r docker stop"
ExecStartPre=/bin/bash -c "/usr/bin/docker ps -a -q -f name=dropbox_%i | xargs -r docker rm"
ExecStart=/usr/bin/docker run --rm --name=dropbox_%i --env-file /tmp/dropbox_%i -v /home/%i/Dropbox:${DBOX_HOME}/Dropbox -v /home/%i/.dropbox:${DBOX_HOME}/.dropbox -v /etc/localtime:/etc/localtime:ro littlef/dropbox
ExecStop=/usr/bin/docker stop dropbox_%i
[Install]
WantedBy=multi-user.target
おわりに
DropboxクライアントをDockerイメージ化し、systemdで管理されたサービスにしたことで、クライアントのシステム要件を満たさない、CentOS7 でも簡単にDropboxクライアントを運用できるようになった。
2019年4月からはDropboxの無料プランの制限がきつくなり、同時接続できるデバイスが3つにまでになってしまった。LinuxサーバをDropboxに接続すれば、家庭内に限ればLinux上のDropbox同期ディレクトリをSamba共有して、他のデバイスからDropboxを利用するといった使い方もできるようになる。
-
任意のファイルをブロックデバイスとして扱うLinuxの仕組み。 ↩