Skip to content

Dockerの基礎概念と仕組み

1. Docker と Docker Compose の違い

Docker (Engine / CLI)

  • 役割: コンテナを1つずつ作成・実行・管理する技術です。
  • コマンド: docker run, docker build, docker images など。
  • 限界: 複数のコンテナ(Webサーバー + DB + WordPress)を連携させる時、毎回長いコマンドでネットワーク接続やボリューム設定を書くのは大変でミスが起きやすいです。

Docker Compose

  • 役割: 「複数のコンテナの構成」をコード (docker-compose.yml) で定義・管理するツールです。
  • メリット:
    1. 一括管理: docker compose up 一発で全コンテナが正しい順序・設定で起動します。
    2. 再現性: YAMLファイルがあれば、誰でも同じ構成を再現できます。
    3. ネットワーク自動化: プロジェクト専用のネットワークを自動で作ってくれます。

参考: Docker Compose overview

2. Dockerネットワークの仕組み

Docker Infrastructureでは docker-compose.yml でネットワークを定義しています。なぜこれが必要なのでしょうか?

デフォルト (Bridge Network)

コンテナは起動すると、デフォルトで bridge0 という仮想ネットワークに繋がります。 しかし、デフォルトブリッジでは**「コンテナ名での名前解決(DNS)」ができません**。IPアドレスで通信する必要がありますが、コンテナのIPは再起動のたびに変わります。

ユーザー定義ネットワーク (User-defined Bridge)

docker-compose.yml でネットワークを定義すると、Dockerは新しい仮想ブリッジを作成します。 最大の特徴: 自動DNS解決 (Service Discovery)

  • 同じネットワーク内のコンテナ同士は、サービス名(例: mariadb で通信できます。
  • WordPressの設定で「DBホスト: mariadb」と書けるのは、この機能のおかげです。

参考: Networking in Compose

3. PID 1 問題とDockerfileベストプラクティス

「PID 1 (Process ID 1)」 は、Linuxシステムで最初に起動するプロセス(init/systemd)です。 役割: システムの初期化と、孤児プロセス(親がいなくなったゾンビプロセス)の回収(reap)。

コンテナ内の PID 1

コンテナ内では、ENTRYPOINTCMD で指定したコマンドが PID 1 になります。

問題点: シェル形式 vs exec形式

Dockerfileでの書き方によって、挙動が変わります。

  1. シェル形式: CMD /script.sh

    • 実際には /bin/sh -c /script.sh が PID 1 になります。
    • 問題: シェルが信号(SIGTERM/Ctrl+C)を受け取っても、子プロセス(アプリ)に転送しないことがあります。結果、コンテナが正常終了できず、強制終了(Kill)されることになります(データの破損リスク)。
  2. exec形式 (推奨): CMD ["/script.sh"]

    • スクリプト自体が PID 1 になります。
    • スクリプト内で exec "$@" を使うことで、最終的に実行するアプリ(nginxなど)に PID 1 を譲ることができます。これにより、シグナルハンドリングが正常に機能します。

ベストプラクティス

  • 必ず exec形式 (ブラケット [] で囲む) を使う。
  • エントリーポイントスクリプトの最後は exec "$@" で終わる(または exec nginx -g ... のように exec をつける)。

参考: Docker and the PID 1 zombie reaping problem

4. 詳細 Q&A: PID 1 とシグナル、デーモン化

Q1. なぜ tail -f /dev/null で常駐させるのは推奨されないのか?

コンテナを終了させずに動かし続けるためのハックとして ENTRYPOINT ["tail", "-f", "/dev/null"] が使われることがありますが、アンチパターンです。

  • 理由: tail コマンドは、docker stop が送ってくる停止信号(SIGTERM)を受け取っても、それを無視してしまいます(正しくハンドリングしません)。
  • 結果: Dockerは停止を待ちますが、反応がないため10秒後に 強制終了 (SIGKILL) します。
  • 影響: データベースのデータが保存されずに破損したり、書きかけのファイルが壊れたりするリスクがあります。正しくは「アプリ自体をフォアグラウンドで動かす」べきです。

Q2. docker stop と Ctrl+C の違い

  • docker stop: 外部からコンテナに優しく「終わってください」という信号 (SIGTERM) を送ります。アプリはこれを受け取って、終了処理(DB切断やログ保存)をしてから終了します。
  • Ctrl+C: ターミナルで実行中のプロセスに SIGINT (Interrupt) を送ります。基本的には SIGTERM と似た「中断」の意味ですが、対話的なプログラムでは挙動が異なる場合があります。
  • 重要: PID 1問題があると、これらの信号がアプリに届かず、「Ctrl+Cを押しても止まらない!」 という事態になります。

Q3. tini コマンドは邪道か?

いいえ、むしろ業界標準のベストプラクティスの一つです。tini は「超軽量な init プロセス」で、PID 1 として起動し、受け取った信号を適切に子プロセスに渡したり、ゾンビ回収を行ったりします(Docker自体にも --init フラグとして組み込まれています)。

  • Docker Infrastructureでは: 「仕組みを理解しているか?」を問うために、あえて tini を使わず、シェルスクリプトの exec で解決することを求められる傾向があります。使っても減点にはなりにくいですが、「なぜ必要か」の説明は必須です。

5. デーモン(Daemon)とDockerの関係

デーモンとは?

「バックグラウンド(裏)で動き続ける常駐プログラム」 のことです(Windowsでいう「サービス」)。 LinuxのWebサーバー(nginx)やDB(mariadb)は、通常コマンドを打つとすぐに制御をターミナルに返し、裏でこっそり動き始めます(デーモン化)。

Dockerでの矛盾

Dockerコンテナは 「PID 1 のプロセスが動いている間だけ生き続ける」 という仕様です。

  • もし nginx を普通のデーモンモードで起動すると…
    1. nginx コマンド実行 → 裏で起動して、コマンド自体はすぐ終了する。
    2. PID 1(コマンド)が終了した!
    3. Docker「あ、メインプロセス終わったね。じゃあコンテナ停止するわ」
    4. コンテナが即座に停止してしまう。

解決策: フォアグラウンド実行

そのため、Docker内ではサービスを 「デーモン化させない(フォアグラウンドで動かす)」 設定が必要です。

  • Nginx: daemon off; を設定ファイルに書く。
  • MariaDB/WordPress: 専用の起動スクリプトで、裏に回らないようにする。

6. Docker Infrastructureの実装分析

今回のプロジェクトでは、以下のように対策されています。

  1. execの使用: 各コンテナの ENTRYPOINT スクリプトの最後は exec "$@" または exec nginx ... となっています。これにより、シェルではなくアプリケーション自体が PID 1 になり、信号を直接受け取れます。
  2. フォアグラウンド設定: Nginxの設定で daemon off; が指定されており、勝手に終了しないようになっています。
  3. シグナル停止: docker stop を実行すると、NginxやMariaDBが正しく終了処理を行ってからコンテナが閉じます(10秒待たされません)。

Released under the MIT License.