問題点Docker コンテナ内で systemctl start nginx や systemctl status ssh を実行しようとすると、サービスが起動する代わりに、以下の有名なエラーが発生することがあります。
Failed to get D-Bus connection: Operation not permitted
Docker コンテナは軽量かつ隔離されるように設計されています。デフォルトでは、ホストの D-Bus と通信したり、システムサービスを管理したりするための権限が不足しています。systemd は自身が最初のプロセス(PID 1)として動作することを前提としています。また、ホストのコントロールグループ(cgroups)への直接アクセスも必要としますが、Docker はセキュリティ上の理由からこれを制限しています。
なぜこの問題が発生するのかubuntu:22.04 や centos:7 のような標準イメージは、デフォルトでは init システムを起動しません。docker run -it ubuntu /bin/bash を実行すると、bash シェルが PID 1 になります。このとき systemd はバックグラウンドで動作すらしていません。systemctl が D-Bus を介して systemd デーモンを見つけようとしても、通信ができず失敗します。コンテナにはホストのバスと通信する権限がないため、操作が拒否されるのです。
解決策 1: 特権フラグと Cgroup のマウントAnsible プレイブックやレガシーなアプリケーションのテストなどで、どうしても systemd を実行しなければならない場合があります。最も手っ取り早い修正方法は、コンテナに拡張権限を付与し、ホストの cgroup ファイルシステムをマウントすることです。これにより、実質的にコンテナへホストのハードウェアに対する「root 同等」のアクセス権が与えられます。
手順:- 現在のコンテナを停止して削除します。- --privileged フラグを使用して新しいコンテナを起動します。- /sys/fs/cgroup を読み取り専用ボリュームとしてマウントします。CentOS 7 イメージの場合、コマンドは以下のようになります。
docker run -d \
--name my-container \
--privileged \
-v /sys/fs/cgroup:/sys/fs/cgroup:ro \
centos:7 /usr/sbin/init
注意: 起動コマンドとして /usr/sbin/init を指定する必要があります。これにより、コンテナ内で systemd プロセスが PID 1 として開始されます。
解決策 2: systemd 専用イメージの使用(推奨)バニラのベースイメージで systemd を設定するのは非常に手間がかかります。代わりに、この目的のために事前設定されたコミュニティ維持のイメージを使用してください。jrei のイメージはこの分野の業界標準です。Docker と systemd の間の複雑なやり取りを自動的に処理してくれます。
Ubuntu の場合:```
docker run -d \ --name systemd-ubuntu \ --privileged \ -v /sys/fs/cgroup:/sys/fs/cgroup:ro \ jrei/systemd-ubuntu
### CentOS の場合:```
docker run -d \
--name systemd-centos \
--privileged \
-v /sys/fs/cgroup:/sys/fs/cgroup:ro \
jrei/systemd-centos
起動後にコンテナへアクセスします。
docker exec -it systemd-ubuntu bash
systemctl status を実行してください。エラーの代わりに、正常に動作しているサービスツリーが表示されるはずです。
解決策 3: Dockerfile によるアプローチカスタムイメージを作成する場合は、systemd をインストールし、仮想環境で失敗する可能性のある不要なユニットをクリーンアップする必要があります。以下は Ubuntu 22.04 での実証済みのスニペットです。
FROM ubuntu:22.04
ENV container docker
RUN apt-get update && apt-get install -y systemd systemd-sysv && apt-get clean
# コンテナ内で動作させるために systemd をスリム化する
RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == \
systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;
VOLUME [ "/sys/fs/cgroup" ]
CMD ["/lib/systemd/systemd"]
検証: 修正の確認方法コンテナが起動したら、2つの簡単なチェックを行います。まず、systemd が PID 1 であることを確認します。
ps -p 1 -o comm=
systemd または init と返ってくるはずです。次に、システムの状態を確認します。
systemctl status
標準的な systemd のステータスツリーが表示されれば、D-Bus の制限を回避することに成功しています。
重要なセキュリティ上の警告--privileged フラグは非常に強力な権限を与えます。これはコンテナのサンドボックス構造を破壊し、コンテナ内のプロセスがホストシステムに干渉できる可能性を生じさせます。外部に公開される本番環境では、この設定を避けてください。あくまでローカル開発や管理された CI/CD テスト用として使用してください。
プロのヒント: 本当に systemd が必要ですか?Docker のベストプラクティスでは、1コンテナにつき1プロセスを実行することが推奨されています。systemctl start nginx を使う代わりに、サービスのバイナリを直接フォアグラウンドで実行してください。これにより、イメージを軽量に保ち(多くの場合 100MB 以上のディスク容量を節約できます)、セキュリティも大幅に向上します。
- Nginx の場合:
CMD ["nginx", "-g", "daemon off;"]- Apache の場合:CMD ["apache2ctl", "-D", "FOREGROUND"]systemd をスキップすることで、D-Bus の煩わしい問題を完全に回避し、クラウドネイティブな手法に従うことができます。

