Docker インフラ構築プロジェクト - システム設計・アーキテクチャ解説書
このドキュメントは、本プロジェクトにおけるシステム設計およびアーキテクチャを効果的に説明、実演するための概要書です。 各項目について「設定の説明」「実演(デモ)方法」「背景知識」をまとめています。
NOTE
ドメイン名について: 本ドキュメントでは例として local.dev と記述していますが、実際の環境に合わせて適宜読み替えてください。
1. TLSv1.2 / TLSv1.3 の実演確認
要件: NGINXコンテナはTLSv1.2またはTLSv1.3 のみ を使用する。
✅ 実演方法 (デモ)
ブラウザでの確認(基本):
- ブラウザで
https://local.devにアクセス。 - アドレスバーの鍵アイコンをクリック -> 「この接続は保護されています」 -> 「詳細を表示」または「証明書」を確認。
- プロトコルの欄に
TLS 1.2またはTLS 1.3と表示されていることを示す。
- ブラウザで
コマンドラインでの厳密な確認:
- 以下のコマンドを実行して、古いプロトコルで接続できないことを証明する。
bash# TLS 1.0 で接続試行(接続拒否またはハンドシェイク失敗すること) openssl s_client -connect local.dev:443 -tls1 # 期待される出力 (NGパターン): # 140366254589248:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:ssl/record/rec_layer_s3.c:1543:SSL alert number 40 # または "Secure Renegotiation IS NOT supported" など、接続が確立されないこと。 # TLS 1.1 で接続試行(同様に失敗すること) openssl s_client -connect local.dev:443 -tls1_1 # TLS 1.2/1.3 で成功すること openssl s_client -connect local.dev:443 -tls1_2 openssl s_client -connect local.dev:443 -tls1_3 # 期待される出力 (OKパターン): # New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 # ... # SSL-Session: # Protocol : TLSv1.3 # ...証明書のドメイン名 (CN) の確認:
- 以下のコマンドで、証明書の発行先ドメイン名 (
CN) が正しく設定されているか確認できます。
bash# コンテナ内の証明書情報を表示し、Subject(所有者情報)の行を抽出 docker exec nginx openssl x509 -in /etc/nginx/ssl/inception.crt -text -noout | grep "Subject:" # 出力例: # Subject: C = JP, ST = Tokyo, L = Minato, O = MyOrganization, OU = Dev, CN = local.devCN = ...の部分が、設定したドメイン名になっていればOKです。
- 以下のコマンドで、証明書の発行先ドメイン名 (
🔑 補足: 自己署名証明書作成コマンドの解説
Dockerfile で実行している openssl コマンドの各オプションの意味は以下の通りです。
openssl req -x509 -nodes -days 365 -newkey rsa:2048 ...req: 証明書署名要求 (CSR) の作成や処理を行うサブコマンド。-x509: CSRではなく「自己署名証明書」を直接出力します。-nodes: "No DES" の略。秘密鍵を暗号化しません(パスワードなし)。これを設定しないとNginx起動時にパスワード入力を求められ、自動起動できません。-days 365: 有効期限を365日に設定します。-newkey rsa:2048: 新しい2048ビットのRSA秘密鍵を作成します。-keyout/-out: それぞれ秘密鍵と証明書の出力先パスです。-subj: 証明書の所有者情報(Subject)をコマンドで指定します。CN=...(Common Name) にドメイン名を設定することが最も重要です。詳しくは.envのDOMAIN_NAMEが反映されます。
📝 背景解説 (TLS)
- 概要: TLS (Transport Layer Security) はインターネット通信を暗号化するプロトコル。SSLの後継。
- 歴史と安全性: TLS 1.0/1.1 は脆弱性(POODLE, BEAST等)があるため現在非推奨。TLS 1.2/1.3 は安全性が高く、現在のウェブ標準。
- 仕組み:
- 非セキュア(HTTP): 平文で送信。盗聴・改ざんが可能。
- セキュア(HTTPS/TLS): 「ハンドシェイク」で鍵交換を行い、共通鍵で通信内容を暗号化。サーバー証明書により「接続先が正当であること」を保証。
2. システム構成図 (C4モデル / UML)
要件: システム全体のアーキテクチャを図解で説明できること。
🖼 図解のポイント (ホワイトボードや画面で説明)
Docker Compose 構成図
- 解説の流れ:
- Entrypoint: すべての外部トラフィックはNGINXだけが受け取る(ポート443のみ開放)。
- Isolation: WordPressやMariaDBは外部にポートを公開せず、内部ネットワーク
inception内でのみ通信する。 - Persistence: データの永続化のためにボリュームを使用。
3. WordPress & MariaDB の連携・動作実演
要件: 各コンテナが正しく動作し、連携していることを示す。
✅ 実演方法
- WordPress動作確認:
- 記事の投稿、コメント、画像のアップロードを行い、正常に動作することを見せる。
- 管理画面:
wp-adminにアクセスし、ユーザー一覧を見せる(adminという名前が含まれていないこと)。
- DB連携の確認:
- ターミナルからMariaDBコンテナに入る:bash
docker exec -it mariadb mariadb -u user -p # パスワード入力 - SQLを実行してWordPressのデータを確認:sql
USE wordpress; SELECT user_login FROM wp_users; - ブラウザ上のユーザー名とDB内のデータが一致していることを示す。
- ターミナルからMariaDBコンテナに入る:
📝 背景解説 (DBとWordPress)
- 仕組み: WordPress (PHPプログラム) は、ページを表示するたびにMariaDB (データベース) にクエリを投げ、記事本文や設定を取得してHTMLを生成している。
- PHP-FPM: NGINXは静的ファイルしか扱えないため、
.phpリクエストは FastCGI プロトコルで WordPress コンテナ(PHP-FPM)に転送し、実行結果を受け取る。
4. ネットワークと自動再起動 (Docker)
要件: ネットワークの分離、コンテナクラッシュ時の挙動。
✅ 実演方法
- ネットワーク分離の証明(Network: hostお断り):
- ホストマシンから
mysql -h 127.0.0.1 -P 3306 ...を実行し、接続できないことを示す(ポートが開いていないため)。 - これにより「NGINX経由以外でアクセスできない=安全」であることを証明。
- ホストマシンから
- 自動再起動 (Restart Policy):
- 稼働中のMariaDBコンテナを強制終了させる(擬似クラッシュ):bash
docker exec -it mariadb pkill mysqld - 直後に
docker psを連打し、STATUSが数秒でUpに戻る(または再起動中になる)様子を見せる。 Restart: alwaysによりデーモン化されたコンテナが維持されていることを説明。
- 稼働中のMariaDBコンテナを強制終了させる(擬似クラッシュ):
📝 背景解説
- Docker Network:
bridgeモードを使用(デフォルト)。コンテナ同士はコンテナ名(wordpress,mariadb)で名前解決(DNS)して通信する。 - 禁止事項:
network: hostはコンテナの隔離性を失うため禁止。--linkは古い機能で非推奨のため禁止。
5. ボリュームとデータ永続化
要件: /home/login/data へのマウントと理由。
✅ 実演方法
- ファイル存在確認(自動設定):
docker-compose.ymlで${USER}環境変数を使用しており、実行ユーザー(あなた)のホームディレクトリ下に自動的にディレクトリが割り当てられます。- ホスト側の
/home/${USER}/data/wordpressにファイルがあることをls -laで見せる。
- 永続化の証明:
make downでコンテナを削除。make upで再構築。- 記事やDBデータが消えていないことをブラウザで確認。
📝 背景解説
- 理由: コンテナは使い捨て(Ephemeral)。コンテナを消すと内部データも消えるため、重要なデータ(DBファイル、アップロード画像)はコンテナ外(ボリューム)に保存する必要がある。
- 自動割り当て:
docker-compose.yml内のdevice: /home/${USER}/data/...記述により、誰が実行してもそのユーザーのホーム配下にデータ領域が確保される仕組みになっている。 - Dockerコンテナ vs VM: コンテナはOSカーネルをホストと共有し、プロセスとして隔離されている(軽量)。VMはハードウェアごと仮想化しOSを丸ごと動かす(重量)。
6. Dockerfile & Best Practices
要件: PID 1問題、latest禁止、Secrets。
📝 技術解説
- PID 1 問題:
- コンテナのメインプロセス(PID 1)はシグナル(Ctrl+Cなど)を受け取って子プロセスに伝播させる責任がある。
- シェル形式(
CMD echo hi)だと/bin/shがPID 1になり、アプリにシグナルが届かず正常終了できない問題が起きる。 - ✅ 対策:
exec "$@"をスクリプトの最後で使うことで、メインプロセスをシェルから対象アプリ(nginx, mysqld等)に置き換えている。
- latestタグ禁止:
debian:latestだと、いつビルドするかによってバージョンが変わってしまい、再現性がなくなる(壊れる可能性がある)。debian:bullseyeなど固定タグを使うべき。
- 環境変数とSecrets:
- パスワード等をDockerfileに書くのは危険(GitHubに残る)。
.envファイルや Docker Secrets 機能を使って外部から注入するのがセキュア。
X. 校舎PC(sudo権限なし)でのレビュー対応
校舎のiMacなど、ホストOSの /etc/hosts を編集する権限がない環境で、「ブラウザでドメイン名アクセス」 の要件をどう証明するかのアプローチです。
アプローチA: 「IPアクセス+証明書確認」で代用する(推奨)
ブラウザのアドレスバーにドメイン名を入れることはできませんが、「このIPアドレスが正しいドメイン(証明書)を持っていること」 は証明できます。
- ホストOSのブラウザで、VMのIPアドレス(またはポート転送していれば
localhost)にアクセス。https://192.168.65.11またはhttps://localhost - 「保護されていない通信」等の警告画面が出ます。
- 「詳細設定」→「証明書を表示(View Certificate)」 をクリック。
- 証明書の内容が表示されるので、Subject (CN) が
local.devになっていること を確認します。 - 同時に、
curlコマンド(下記)を使って、「ドメイン名でアクセスした場合に正しくつながる」技術的裏付けを見せます。bash# ホストOSのターミナルで実行 curl -v -k --resolve local.dev:443:192.168.65.11 https://local.dev
アプローチB: VM内にGUIを入れてブラウザを使う(最強の証明)
「どうしてもブラウザのアドレスバーにドメイン名を表示させたい」とレビュワーが譲らない場合の最終手段です。 ゲストOS(Debian VM)の中にデスクトップ環境とFirefoxをインストールし、ゲストOSの中でWebサイトを開きます。
# ゲストOSで実行(インストールに数分〜十数分かかります)
sudo apt-get update
sudo apt-get install -y task-gnome-desktop firefox-esr
sudo reboot再起動後、VMがグラフィカルモードで立ち上がるので、ログインしてFirefoxを開き https://local.dev にアクセスすれば、設定なしで繋がります(ゲストOSの /etc/hosts は設定済みのため)。
アプローチC: SSH SOCKSプロキシ(上級者向け・推奨)
「ホストのブラウザを使いたい」かつ「ホストの設定は変えられない」場合の最もスマートな解決策です。 SSHの「ダイナミックポートフォワード」機能を使って、ブラウザの通信をすべてVM経由にします。
SSHトンネルの作成: ホストOSのターミナルで以下のコマンドを実行します。
bash# -D 8080: ローカルの8080ポートをSOCKSプロキシとして開く # -N: シェルを実行せず転送のみ行う(フォアグラウンドで待機) ssh -D 8080 -N debian@192.168.65.11(パスワードを聞かれたら入力し、そのまま画面が止まった状態にします)
ブラウザの設定 (Firefox推奨): Mac/Windowsの Firefox は、システム設定とは独立してプロキシ設定ができるため最適です。
- Firefox設定 -> 一般 -> ネットワーク設定 -> 「接続設定...」
- 「手動でプロキシを設定する」 を選択
- SOCKS ホスト:
localhost, ポート:8080 - SOCKS v5 を選択
- ✅ 「SOCKS v5 使用時は DNS もプロキシする (Proxy DNS when using SOCKS v5)」 にチェックを入れる(最重要)
- 「OK」で保存
接続確認: ホストのFirefoxで
https://local.devにアクセスします。- ブラウザからのリクエストがSSHトンネルを通ってVMに届きます。
- VM側で DNS解決が行われるため、VM内の
/etc/hostsが適用されます。 - 結果、きれいにサイトが表示されます。
補足: ゲストOS内でドメイン解決を確認するコマンド
「本当にドメインが通っているのか?」と聞かれた場合、以下のコマンドで設定状況を証明できます。
# ドメイン名がどのIPに変換されるか確認
getent hosts local.dev
# 出力例: 192.168.65.11 local.dev
# ドメイン名で通信できるか確認
ping -c 3 local.dev7. その他チェックリスト
- [ ] ドメイン設定: ゲストOSの
/etc/hostsに127.0.0.1 local.devが設定されていること。 - [ ] ユーザー名: WordPress管理者ユーザー名に
adminやAdminが含まれていないこと。 - [ ] 解析ツール: ブラウザの開発者ツール (F12) > Networkタブで、HTTPヘッダ情報を見てServer情報等を確認するデモも有効。
8. Makefile コマンド解説
Makefileは、長く複雑なdockerコマンドを短いショートカットで実行するためのものです。開発時やレビュー時に頻繁に使用します。
| コマンド | 説明・使用シーン | 実際の処理内容 |
|---|---|---|
make all | 【基本】システム起動。 最初に環境を立ち上げる時や、停止後に再開する時に使います。必要なデータディレクトリも自動作成します。 | 1. /home/${USER}/data/ 配下にディレクトリ作成2. docker compose up -d --build でビルドしてバックグラウンド起動 |
make down | システム停止。 作業終了時や、一旦クリーンにしたい時に使います。コンテナとネットワークを削除しますが、ボリューム(データ)は残ります。 | docker compose down |
make re | 再構築(リブート)。 設定ファイルを書き換えた後に反映させたい時や、調子が悪い時に「電源オフ・オン」の感覚で使います。 | make down してから make all を実行 |
make clean | 完全削除(データ以外)。 コンテナ、ネットワークに加え、作成したDockerイメージやボリューム定義も削除します。※ホスト側のデータは消えません。 | docker compose down --rmi all --volumes |
make fclean | 初期化(全消去)。 Dockerシステム上の未使用データを全て削除します。ディスク容量を空けたい時などに使います。 | make clean 後に docker system prune -af |
💡 レビュー時のポイント
- 「どうやって起動しますか?」と聞かれたら、自信を持って 「
make allで一発で起動します」 と答え、実演してください。 - 「データを残したまま終了するには?」と聞かれたら 「
make downです。データはホスト側のボリュームに残ります」 と答えます。
9. docker-composeを使うイメージと使わないイメージの違い
要件: 「docker-composeを使う場合と使わない場合で、Dockerイメージに違いはあるか?」という質問への回答。
📝 回答のポイント
「イメージ自体は同じですが、動く環境(コンテキスト)が異なります」 が正解です。
イメージの共通性:
Dockerfileから作られるイメージそのものは、docker buildで作ろうがdocker compose buildで作ろうが、中身(ファイルシステム、インストールされたアプリ)は同じです。
実行時の違い (Runtime Context):
- docker-compose使用時:
- ネットワーク: 自動的に専用のDNS名(サービス名)で通信できる(例:
wordpressという名前で接続可能)。 - 環境変数:
.envファイルやdocker-compose.ymlから自動注入される。 - ボリューム: 宣言的に管理され、自動マウントされる。
- ネットワーク: 自動的に専用のDNS名(サービス名)で通信できる(例:
- docker-compose未使用時 (docker run):
- これらを全て手動オプション(
--network,--env,-v)で指定しないと、同じようには動かない。 - 特に本プロジェクトのような「DBホスト名を
mariadbと決め打ちしている」コンテナ構成は、--network設定なしの単独起動では名前解決できずにエラーになる。
- これらを全て手動オプション(
- docker-compose使用時:
具体的な説明例
「WordPressのイメージ自体は変わりませんが、Composeを使わない場合は docker run --network inception -e MYSQL_HOSTNAME=mariadb ... のように長いオプションを全部手動で書く必要があります。Composeはそれを『コード化』して自動セットアップしてくれるツールです。」
10. Docker Compose vs Docker CLI (非使用)
本プロジェクトでは docker-compose を採用していますが、「なぜ docker-compose を使うのか?」「docker コマンド単体ではダメなのか?」という疑問についての解説です。
🐢 Docker CLI (Compose非使用)
docker run コマンドでコンテナを1つずつ起動する方法です。
- 特徴:
- コンテナ単体の細かい制御や、一時的な検証に向いています。
- 依存関係(DBが立ち上がってからWordPressを起動など)を人間が管理する必要があります。
- デメリット:
- コマンドが複雑化する: ネットワーク、ボリューム、環境変数をすべてオプションで指定する必要があります。bash
# 例: ネットワーク接続とボリュームマウントの手動実行 docker run -d --name mariadb --network inception -v db_data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=... mariadb:bullseye - 再現性が低い: 長いコマンドを打ち間違えたり、オプションを忘れたりしやすく、チーム開発での再現が困難です。
- 自動化が手間: シェルスクリプト (
.sh) で自動化することも可能ですが、保守性に欠けます。
- コマンドが複雑化する: ネットワーク、ボリューム、環境変数をすべてオプションで指定する必要があります。
🐙 Docker Compose (Compose使用)
複数のコンテナ定義を docker-compose.yml という1つのファイルにまとめて管理する方法です。
- 特徴:
- Infrastructure as Code (IaC): インフラ構成をコード(YAML)として保存・バージョン管理できます。誰が実行しても同じ環境が立ち上がります。
- オーケストレーション: 複数のコンテナの起動順序、依存関係、ネットワーク接続を一括で管理できます。
- メリット (本プロジェクトでの採用理由):
- 一括操作:
docker compose upだけで全サービス(NGINX, WordPress, MariaDB)が正しい順序と設定で立ち上がります。終了もdown一発です。 - 自動ネットワーキング: 明示的に
docker network createしなくても、プロジェクト専用のネットワークを作成し、サービス名(mariadbなど)で名前解決できるようにしてくれます。 - 保守性: 設定変更は YAML ファイルを書き換えるだけ。コマンドオプションを極力減らすことができます。
- 一括操作:
結論: 複雑な連携が必要な本プロジェクトの Web サービスにおいて、設定ミスを防ぎ、一貫した環境を即座に構築するために Docker Compose は不可欠です。