Ubuntu Serverを起動した際に、特定のdocker composeコマンドを自動で実行したい。ホームディレクトリにある docker-compose.yaml のDockerサービスは自動で起動できている。
ぱっと思いついたのが、systemdサービスを作成すること。この記事では、ユーザーサービスとしてsystemdサービスを登録し、RootlessモードでDocker Composeを自動起動する手順をまとめておく。
背景
やりたいことはシンプルで、
Seafileを起動するためのdocker compose --env-file seafile/.env -f seafile/seafile_compose.yaml startコマンドをサーバー起動時に自動実行する。
すでに、ユーザーサービスの定義ファイルを置くためのディレクトリを作成されていた。
$ ls -l ~/.config/systemd/user/
合計 12
drwxr-xr-x 2 arimasou16 arimasou16 4096 8月 22 09:05 default.target.wants
-rw-rw-r-- 1 arimasou16 arimasou16 603 2月 10 2024 docker.service
-rw-rw-r-- 1 arimasou16 arimasou16 463 8月 22 09:03 seafile.service
ちなみにdocker.serviceの中身はこうなっている。
~/.config/systemd/user/docker.service
[Unit]
Description=Docker Application Container Engine (Rootless)
Documentation=https://docs.docker.com/go/rootless/
[Service]
Environment=PATH=/usr/bin:/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/opt/nvim-linux64/bin
ExecStart=/usr/bin/dockerd-rootless.sh
・・・以下、後略・・・
次に、ユーザーサービスの定義ディレクトリ内にseafile.serviceという名前でサービスファイルを作成する。
~/.config/systemd/user/seafile.service
[Unit]
Description=Seafile Docker Compose User Service
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
Environment="DOCKER_HOST=unix:///run/user/1000/docker.sock"
WorkingDirectory=/home/arimasou16
ExecStart=/usr/bin/docker compose --env-file seafile/.env -f seafile/seafile_compose.yaml start
ExecStop=/usr/bin/docker compose --env-file seafile/.env -f seafile/seafile_compose.yaml stop
[Install]
WantedBy=default.target
- [Unit]セクション:
docker.serviceの起動後にこのサービスが起動するように依存関係を設定する。 - [Service]セクション:
Type=oneshot: 単発のタスクとして実行する。RemainAfterExit=yes:ExecStartのプロセスが終了してもサービスを有効状態に保ち、ExecStopが実行できるようにする。WorkingDirectory: コマンドを実行するディレクトリを絶対パスで指定する。
- [Install]セクション:
WantedBy=default.targetは、ユーザーがログインしたときにサービスが自動的に起動するようにするための標準的な設定をする。
2. サービスを有効化して起動
ファイルを作成したら、設定をリロードしてサービスを有効化する。ユーザーサービスなので--userフラグを付け、sudoは不要。
# 設定を再読み込み
systemctl --user daemon-reload
# ユーザーログイン時の自動起動を有効化
systemctl --user enable seafile.service
# サービスを手動で起動してみる
systemctl --user start seafile.service
3. トラブルシューティング
上記の手順で進めたところ、いくつかのエラーに遭遇した。
エラー1: Unit network-online.target not found.
当初、ネットワークが有効になってからサービスを起動しようとAfter=network-online.targetを追加していたが、このエラーが発生した。
これは、network-online.targetがユーザーセッションでは必ずしも有効ではないために起こった。
解決策:
docker.service自体がネットワークの準備ができてから起動するため、seafile.serviceがdocker.serviceの後に起動するように設定されば、間接的にネットワークの問題は解決される。そのため、サービスファイルからnetwork-online.targetの記述を削除した(上記の設定ファイルは修正済みのもの)。
エラー2: DockerソケットへのPermission denied
permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock
これは、DockerをRootlessモードで運用していることが原因だった。
RootlessモードのDockerソケットは、システム用の/var/run/docker.sockではなく、ユーザーディレクトリ内の/run/user/1000/docker.sock(1000はユーザーID)にある。systemdのユーザーサービスが、デフォルトのシステム用ソケットに接続しようとしてエラーになっていた。
解決策:
サービスファイルに環境変数DOCKER_HOSTを追加し、正しいソケットのパスを明示的に指定する。
最終的なseafile.serviceファイルは上記のようになった。
この修正後に再度systemctl --user daemon-reloadとsystemctl --user start seafile.serviceを実行すると、無事にサービスが起動できた。
まとめ
以上の設定で、ユーザーログイン時に自動でDocker Composeのコンテナが起動するようになった。