うっかり(でもない)Docker
をアップデートしたら、これまで動いていたコンテナのApache
が動かなくなって四苦八苦した際の備忘録です。
結論としては、コンテナでsystemctl
コマンドを使うのをやめました。
以下環境での話です。
# Dockerを動かしている環境
WSL2 (Ubuntu 20.04.5 LTS)
# Dockerのバージョン
Docker version 23.0.3, build 3e7cbfd
# docker-composeのバージョン
docker-compose version 1.29.2, build 5becea4c
Docker Desktop for Windows
を使用していましたが、ある日それ自体がエラーで起動しなくなり、アンイストールして最新のDocker Desktop for Windows
をインストールし直したら、それまで動いていたWebサーバ(Apache)コンテナが動かなくなったのでムキーッとなりCLIに移行してみたものの同様の事象は解消されず、解決を試みたという話です。
ちなみにこの仮想環境は前任者の方が作成したもので、自分は今回初めてじっくり読み込んだ感じです(そのため言い回しが推量になっている箇所があります)。
ディレクトリ構成とソースコード
以下のような構成でした。
実際にはMySQLコンテナとアプリケーションのソースコードを配置するディレクトリもありましたが、今回の説明ではそちらは割愛。
├── app
│ ├── Dockerfile
│ ├── httpd
│ │ ├── conf
│ │ │ └── httpd.conf
│ │ └── conf.d
│ │ └── project.conf
│ └── index.html
└── docker-compose.yml
docker-compose.yml
docker-compose.yml
の設定は以下の通り。
コンテナ内でsystemctl
コマンドを使用するために、コンテナを特権モードで実行するように指定されていました(privileged: true
)。
version: "3"
services:
app:
ports:
- 8000:80
container_name: test_app
build: ./app
# コンテナを特権モードで実行する
privileged: true
command: /sbin/init
Dockerfile
上記test-app
コンテナのDockerfile
は以下。
実際のDockerfileではPHPのインストール等を行っていましたが、今回の本筋ではないため最小限にしています。
ざっくり言うと、Apacheをインストールし、起動させるようにしています。
FROM amazonlinux:2
RUN yum update -y && \
yum install -y \
# 確認用にpsコマンドを使用するためインストール
procps \
# Apacheのインストール
httpd
# Apache設定ファイルの配置
COPY httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf
COPY httpd/conf.d/project.conf /etc/httpd/conf.d/project.conf
# 動作確認用のindex.htmlを配置
COPY ./index.html /var/www/html/index.html
# サービス自動起動有効
RUN systemctl enable httpd.service
RUN yum clean all && rm -rf /var/cache/yum/*
# Apache start
CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"]
確認用のindex.html
確認用のindex.html
はHello World!を出力するだけのものです。
<html>
<body>
Hello World!
</body>
</html>
発生した事象
コンテナのビルドも起動もエラーなく完了しますが、ブラウザでhttp://localhost:8000
へアクセスしても「このページは動作していません」とだけ表示され、想定の画面が表示されません。
コンテナのApacheエラーログを確認しますが、そもそもエラーログが出力されていません。
Apacheが動いていないのでは?と思い状態を確認しようとsystemctl
コマンドを叩いたら、見慣れないエラーメッセージに遭遇しました。
$ systemctl status httpd
Failed to get D-Bus connection: No such file or directory
ちなみにps
コマンドの結果は以下です。
bash-4.2# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 40604 3456 ? Ss 02:50 0:00 /sbin/init
root 7 0.2 0.0 11700 2924 pts/0 Ss 02:51 0:00 bash
root 13 0.0 0.0 49808 3280 pts/0 R+ 02:51 0:00 ps aux
Dockerとsystemd
そもそもsystemctl
とはsystemd
システムおよびサービスマネージャーを操作するためのコマンドです。systemd
を簡単に言うとシステムの管理をしてくれるやつ。OSが最初に生成するプロセスであり(systemd
のPIDは1)、全てのプロセスの創始。きちんと説明できる自信がないので概要of概要に止めておきます(勉強しよ...)。
ただし、Docker
ではsystemd
を利用することができないということです。何故?の部分に触れている記事等が少なかったのですが、以下では2点挙げられています。
理由1: cgroupsの壁
まず、Systemdは、LinuxのcgroupsというKernel提供の機能に依存しているのですが、通常のDocker containerはそもそもcgroupsの機能を使って隔離された環境で動作するため、cgroups自体を操作する権限が限定されてしまいます。
理由2: initの壁
そもそもDockerコンテナはinitから起動しない(initがない)ので、initプロセスに依存するスクリプトは軒並み動きません。serviceコマンドで操作できないサービスは、たいていこれが原因です。
daemon化を前提とするサービスの多くがこの影響を受けます。PID 1のinitプロセスを前提としてdaemon化するプログラムは動作しません。
そして「コンテナでsystemctl
コマンドを使う」ための方法として記事等でよく挙げられているのが、コンテナを特権モードで実行するです。
先述のソースコードで言えば、docker-compose.yml
内の以下がそれにあたります。
# コンテナを特権モードで実行する
privileged: true
command: /sbin/init
「コンテナを特権モードで実行する」とは
自分もこの「コンテナを特権モードで実行する」が何のか知らなかったのですが、調べてみるとそれはコンテナ(プロセス)にホストの特権(root)ユーザーと同等の権限を持たせるということが分かりました。
これだけでもうなんか(セキュリティ的に)アカン匂いがする...
(そして「コンテナ 特権」とかでググると危険性を指摘する記事が多いので察する。)
そもそも特権コンテナはsystemctl
コマンドを使うために設定されていたので、systemctl
コマンドを利用せずに(特権コンテナにもせずに)Apache
を起動する方法に切り替えようと思いました。
ソースコードを修正
docker-compose.ymlの修正
docker-compose.yml
からコンテナを特権モードで実行させる箇所の記述を削除します。
- # コンテナを特権モードで実行する
- privileged: true
- command: /sbin/init
Dockerfileの修正
Dockerfile
からはsystemctl
を使用している箇所を削除します。
Apache
の起動はCMD ["/usr/sbin/httpd", "-D", "FOREGROUND"]
の箇所でできるため追記は不要です。
- # サービス自動起動有効
- RUN systemctl enable httpd.service
最終的なソースコード
version: "3"
services:
app:
ports:
- 8000:80
container_name: test_app
build: ./app
FROM amazonlinux:2
RUN yum update -y && \
yum install -y \
# 確認用にpsコマンドを使用するためインストール
procps \
# Apacheのインストール
httpd
# Apache設定ファイルの配置
COPY httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf
COPY httpd/conf.d/project.conf /etc/httpd/conf.d/project.conf
# 動作確認用のindex.htmlを配置
COPY ./index.html /var/www/html/index.html
RUN yum clean all && rm -rf /var/cache/yum/*
# Apache start
CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"]
動作確認用に配置したindex.html
がブラウザから確認できました
ps
コマンドからもhttpd
プロセスが確認できました
bash-4.2# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 249200 9120 ? Ss 02:55 0:00 /usr/sbin/httpd -D FOREGROUND
apache 7 0.0 0.1 486984 8336 ? Sl 02:55 0:00 /usr/sbin/httpd -D FOREGROUND
apache 8 0.0 0.0 290448 6876 ? Sl 02:55 0:00 /usr/sbin/httpd -D FOREGROUND
apache 10 0.0 0.0 290312 6292 ? Sl 02:55 0:00 /usr/sbin/httpd -D FOREGROUND
apache 17 0.0 0.0 290312 6292 ? Sl 02:55 0:00 /usr/sbin/httpd -D FOREGROUND
apache 37 0.0 0.0 290312 6292 ? Sl 02:55 0:00 /usr/sbin/httpd -D FOREGROUND
apache 53 0.0 0.0 290312 6300 ? Sl 02:55 0:00 /usr/sbin/httpd -D FOREGROUND
root 59 0.5 0.0 11700 2880 pts/0 Ss 03:07 0:00 bash
root 65 0.0 0.0 49808 3344 pts/0 R+ 03:07 0:00 ps aux
まとめのようなひとりごと
なぜそれまで動いていたコンテナのApache
が動かなくなったのか?のなぜ、については解明できていないのですが、調べを進める内に現状のコンテナの作りがあまり良いものではないものだということが分かりました。
たしかに同プロジェクトの運用環境ではAmazon EC2
インスタンスのAmazon Linux 2
上にApache
やMySQL
を動かしているためsystemctl
での起動が必要です。
しかしコンテナの考え方では、1つのコンテナで動かすのは1つのプロセスの想定なので、複数のプロセスを管理するためのsystemd
を必要としないのだなと思いました。
そう考えると運用環境もコンテナで動くようにしたほうがいいのかな(専門外だが一回やってみたい感がある)、そもそも読み込むイメージがamazonlinux
ではなくhttpd
イメージにすることはできないのか、とか色々思うところがありました。もっと良い提案ができるだけの知識を身に付けたいです。
Dockerを中心としたコンテナ技術や仮想環境についてはまだまだ勉強中ですが、Linux周りのこともまだまだ...勉強します。