旅の途中、興味深いオアシスを見つけた。忘れないうちに、この羊皮紙に記しておくとしよう。
Docker(コンテナ仮想化技術)という名の魔法の箱庭。そこで一体のゴーレムを創り出すのは、もはや造作もないことだ。しかし、真の冒険者は、そこで満足しない。WebサーバとDBサーバ(データベースサーバ)、二つの異なる魂を持つゴーレムを同じ箱庭に召喚し、互いに魂の対話をさせる。それこそが、より高度な世界を創造するための、次なるステップなのだ。
かつて、私はWeb/AP/DB(Webサーバ・アプリケーションサーバ・データベースサーバ)の三層構造という、巨大な神殿の建立に苦しんだ経験がある。今回は、その第一歩として、WebとDB、二つのサーバをDockerコンテナ(隔離実行環境)として錬成し、docker-compose(複数コンテナ管理ツール)という名の世界の契約書で、その絆を結びつける。これは、単一の魂から、複数の魂が共存する生態系を創り出す、壮大な儀式の記録である。
この羊皮紙のあらまし
- この羊皮紙のあらまし
- この羊皮紙が導く者
- 砂漠の道標
- 第一の儀式:世界の設計図(ファイル構成)
- 第二の儀式:世界の契約書を記す(docker-compose.yml)
- 第三の儀式:魂の設計図を記す(Dockerfileとinit.sh)
- 最終儀式:世界の創造と、魂の対話
- 羊皮紙を巻く前に
- 砂漠で見つけた魔法のランプ
- ラクダの独り言
この羊皮紙が導く者
- Dockerという箱庭で、複数のゴーレムを召喚し、互いに対話させたいと願う者
- コンテナ起動時に、DBサーバに初期テーブルという名の記憶を刻み込みたい探求者
- マイクロサービスアーキテクチャの基礎を学びたい実務家
- Web/AP/DB三層構造の第一歩を踏み出したい開発者
砂漠の道標
- Docker - コンテナ仮想化技術。アプリケーションを隔離された環境で実行できる。
- docker-compose - 複数のDockerコンテナを一括管理するツール。YAML形式で定義。
- PostgreSQL - オープンソースの高機能リレーショナルデータベース。略称Postgres。
- Nginx - 高性能なWebサーバ・リバースプロキシソフトウェア。エンジンエックスと読む。
- コンテナネットワーク - Docker内の仮想ネットワーク。コンテナ間通信を可能にする。
- 初期化スクリプト - コンテナ起動時に自動実行されるスクリプト。DB初期設定等に使用。
- Alpine Linux - 軽量なLinuxディストリビューション。Dockerイメージでよく使われる。
- 三層構造 - Web層・AP層・DB層に分離したシステムアーキテクチャ。保守性・拡張性が高い。
第一の儀式:世界の設計図(ファイル構成)
まずは、これから創造する世界の、完全な設計図を示す。webとrdb(Relational Database:リレーショナルデータベース)、二つの魂のための区画を設け、それぞれに魂の設計図(Dockerfile:イメージビルド定義)と、世界の契約書(docker-compose.yml:コンテナ管理設定)を配置する。
第二の儀式:世界の契約書を記す(docker-compose.yml)
これが、二つの魂の運命を司る、最も重要な古文書だ。
rdb(PostgreSQL:高機能データベース)とweb(Nginx:高性能Webサーバ)、二つのサービスを定義する。そして、最も重要な呪文がnetworks(コンテナネットワーク設定)だ。mynetworkという名の魔法のネットワークを創造し、二つの魂をそこに所属させることで、彼らは互いを「名前」で呼び合うことができるようになる。
version: '3.8' services: rdb: # データベースサービス container_name: 'postgres' build: ./rdb ports: - "5433:5432" # ホスト:コンテナのポートマッピング environment: POSTGRES_DB: postgres # データベース名 POSTGRES_USER: postgres # ユーザー名 POSTGRES_PASSWORD: Password01# # パスワード DATABASE_HOST: localhost volumes: - rdb_data:/var/lib/postgresql/data:rw # データ永続化 - ./rdb/initdb:/docker-entrypoint-initdb.d # 初期化スクリプト tty: true restart: always networks: - mynetwork # カスタムネットワークに接続 web: # Webサーバサービス container_name: 'nginx' build: ./web ports: - "8080:80" # HTTPポート environment: - LANG=ja_JP.utf8 - TZ=Asia/Tokyo tty: true restart: always networks: - mynetwork # カスタムネットワークに接続 volumes: rdb_data: # 名前付きボリューム(データ永続化用) networks: mynetwork: # カスタムネットワーク定義 driver: bridge # ブリッジドライバー使用 ipam: driver: default
第三の儀式:魂の設計図を記す(Dockerfileとinit.sh)
Webサーバの魂(./web/Dockerfile)
魂には、軽量にして高速なnginx:alpine(Alpine Linux版Nginx)を選ぶ。
FROM nginx:alpine ENV LANG ja_JP.utf8 ENV TZ Asia/Tokyo ENTRYPOINT /usr/sbin/nginx -g "daemon off;" -c /etc/nginx/nginx.conf
DBサーバの魂(./rdb/Dockerfile)
魂には、信頼性の高いpostgres:alpine(Alpine Linux版PostgreSQL)を選ぶ。
FROM postgres:alpine ENV LANG ja_JP.utf8 ENV TZ Asia/Tokyo
DBの初期記憶(./rdb/initdb/init.sh)
そして、init.sh(初期化シェルスクリプト)という名の羊皮紙に、召喚時に実行すべき「初期記憶の刻印」の儀式を記しておく。
PostgreSQLコンテナは、/docker-entrypoint-initdb.d(初期化スクリプト配置ディレクトリ)に配置されたスクリプトを、初回起動時に自動実行する。
set -e psql -U postgres postgres << ENDSQL -- CREATE TABLE sample ( -- サンプルテーブル作成 id text primary key, name text ); COMMENT ON COLUMN sample.id is 'ID'; COMMENT ON COLUMN sample.name is '名前'; -- ENDSQL
最終儀式:世界の創造と、魂の対話
全ての設計図が揃ったら、docker-compose build(イメージビルド)で二つの魂を錬成し、docker-compose up -d(コンテナ起動)で世界を創造する。
世界の確認
まずは、WebサーバのNginx(高性能Webサーバ)が、外界からの呼びかけに応えるかを確認する。ブラウザでhttp://localhost:8080を訪れ、歓迎の辞が表示されれば、門は開かれている証拠だ。
次に、DBサーバのPostgreSQL(高機能データベース)が、我々の呼びかけに応えるかを確認する。まずは、DB接続ツール(A5:SQL Mk-2やpgAdmin等)で、その扉を叩いてみよう。
接続が成功したら、sampleテーブル(初期化スクリプトで作成されたテーブル)が存在するかを確認する。儀式が正しく執り行われていれば、初期記憶は確かに刻まれているはずだ。
魂と魂の対話
最後に、二つの魂が、互いの存在を認識できるかを確認する。
Webサーバの魂に入り込み(docker-compose exec webでコンテナ内部に接続)、ping rdb(rdbコンテナへの疎通確認)と問いかける。DBサーバからも同様に、ping webと。
互いに確かな応答があれば、我々の箱庭に、完璧な生態系が誕生したことになる。
$ docker-compose exec web /bin/sh / # ping rdb # コンテナ名でネットワーク通信可能 PING rdb (172.19.0.3): 56 data bytes 64 bytes from 172.19.0.3: seq=0 ttl=64 time=1.050 ms ...
羊皮紙を巻く前に
今回の旅で、私はDockerという箱庭に、WebサーバとDBサーバという二つの異なる魂を召喚し、それらを名前で呼び合わせることに成功した。単一のゴーレムを創り出すことと、複数の魂を調和させることは、全く異なる次元の挑戦だった。しかし、docker-composeという世界の契約書と、カスタムネットワークという魔法の絆があれば、その実現は決して難しくない。
この構成の優れた点
- docker-composeによる一括管理 - 複数コンテナを単一のYAMLファイルで定義・管理できる。世界全体を一つの契約書で統治する優雅さ。
- カスタムネットワークの恩恵 - コンテナ名で相互通信が可能になり、IPアドレスという不安定な数字の羅列から解放される。
- 初期化スクリプトの自動実行 -
/docker-entrypoint-initdb.d配下のスクリプトでDB初期設定を自動化。召喚の度に手作業で記憶を刻む苦行から解放される。 - データ永続化の保証 - 名前付きボリュームにより、コンテナを破壊しても魂の記憶は失われない。
- 三層構造への拡張性 - この基礎構成に.NETアプリという第三の魂を加えれば、Web-AP-DBの完全な神殿が完成する。
注意すべき落とし穴
- ポートの競合 - ホスト側で既に5432番ポートが使用されている場合、PostgreSQLコンテナは起動に失敗する。
docker-compose.ymlで5433:5432のように異なるポート番号を割り当てることで回避できる。 - 初期化スクリプトの実行タイミング -
/docker-entrypoint-initdb.dのスクリプトは初回起動時のみ実行される。データボリュームが既に存在する場合は実行されないため、完全な再初期化にはボリュームの削除が必要となる。
まとめ
Docker Composeによる複数コンテナ管理は、マイクロサービスアーキテクチャの第一歩であり、現代のシステム構築における必須の技術だ。この旅で学んだ、二つの魂を名前で結びつける魔法は、あんたのこれからの冒険でも、必ずや役立つはずだ。
前回紹介した.NETアプリのビルド技術と、今回のWeb-DB連携技術を組み合わせれば、完全な三層構造という、より壮大な世界を創造できる。その可能性は、この箱庭の中に、確かに眠っている。
おっと、どうやら相棒が腹を空かせたようだ。今日はこのへんで筆を置くとしよう。
砂漠で見つけた魔法のランプ
- 蜃気楼との決別 ~Docker Desktopを捨て、WSL2に拠点を築く~ | Dockerのインストールに関する古文書
- 魂に、定刻の鼓動を ~Dockerfileとcronで操る、.NETバッチ処理~ | 前回の冒険の記録(.NETアプリのビルド編)
- Docker Compose ドキュメント | 公式の古文書
- PostgreSQL 初期化スクリプト - Docker Hub | DB初期化の賢者の言葉
ラクダの独り言
ご主人が、今度は箱の中に、二匹のゴーレムを創り出して、お互いに「ピンポン」とか言い合わせている。なんでも、そうやって仲良くさせないと、ちゃんとお仕事してくれないらしい。俺に言わせりゃ、最初から一匹の、もっとデカくて賢いゴーレムを創りゃいいだろうに。それに、名前で呼び合うだと?番号で管理した方が、よっぽどスッキリするだろうが。まったく、人間ってのは、面倒なことが好きだな。やれやれだぜ。