たびとの旅路 ~電脳砂漠の冒険譚~

フロッピー頼りに歩き、クラウドの地平を見つめる今日まで。見つけたオアシス、迷い込んだ砂の迷宮、全てこの羊皮紙に。

二つの魂を、箱庭で結べ ~WebとDB、Dockerによる共存の儀~

旅の途中、興味深いオアシスを見つけた。忘れないうちに、この羊皮紙に記しておくとしよう。

Docker(コンテナ仮想化技術)という名の魔法の箱庭。そこで一体のゴーレムを創り出すのは、もはや造作もないことだ。しかし、真の冒険者は、そこで満足しない。WebサーバとDBサーバ(データベースサーバ)、二つの異なる魂を持つゴーレムを同じ箱庭に召喚し、互いに魂の対話をさせる。それこそが、より高度な世界を創造するための、次なるステップなのだ。

かつて、私はWeb/AP/DB(Webサーバ・アプリケーションサーバ・データベースサーバ)の三層構造という、巨大な神殿の建立に苦しんだ経験がある。今回は、その第一歩として、WebとDB、二つのサーバをDockerコンテナ(隔離実行環境)として錬成し、docker-compose(複数コンテナ管理ツール)という名の世界の契約書で、その絆を結びつける。これは、単一の魂から、複数の魂が共存する生態系を創り出す、壮大な儀式の記録である。

この羊皮紙のあらまし

この羊皮紙が導く者

  • Dockerという箱庭で、複数のゴーレムを召喚し、互いに対話させたいと願う者
  • コンテナ起動時に、DBサーバに初期テーブルという名の記憶を刻み込みたい探求者
  • マイクロサービスアーキテクチャの基礎を学びたい実務家
  • Web/AP/DB三層構造の第一歩を踏み出したい開発者

砂漠の道標

  • Docker - コンテナ仮想化技術。アプリケーションを隔離された環境で実行できる。
  • docker-compose - 複数のDockerコンテナを一括管理するツール。YAML形式で定義。
  • PostgreSQL - オープンソースの高機能リレーショナルデータベース。略称Postgres。
  • Nginx - 高性能なWebサーバ・リバースプロキシソフトウェア。エンジンエックスと読む。
  • コンテナネットワーク - Docker内の仮想ネットワーク。コンテナ間通信を可能にする。
  • 初期化スクリプト - コンテナ起動時に自動実行されるスクリプト。DB初期設定等に使用。
  • Alpine Linux - 軽量なLinuxディストリビューション。Dockerイメージでよく使われる。
  • 三層構造 - Web層・AP層・DB層に分離したシステムアーキテクチャ。保守性・拡張性が高い。

第一の儀式:世界の設計図(ファイル構成)

まずは、これから創造する世界の、完全な設計図を示す。webrdb(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を訪れ、歓迎の辞が表示されれば、門は開かれている証拠だ。

Nginx の確認

Webサーバの魂が、我々を歓迎している

次に、DBサーバのPostgreSQL(高機能データベース)が、我々の呼びかけに応えるかを確認する。まずは、DB接続ツール(A5:SQL Mk-2やpgAdmin等)で、その扉を叩いてみよう。

PostgreSQLへの接続テスト成功画面

DBサーバの魂は、我々の呼びかけに応えた

接続が成功したら、sampleテーブル(初期化スクリプトで作成されたテーブル)が存在するかを確認する。儀式が正しく執り行われていれば、初期記憶は確かに刻まれているはずだ。

init.shで作成されたsampleテーブルの確認画面

DBサーバの魂に、初期記憶は正しく刻まれた

魂と魂の対話

最後に、二つの魂が、互いの存在を認識できるかを確認する。 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という世界の契約書と、カスタムネットワークという魔法の絆があれば、その実現は決して難しくない。

この構成の優れた点

  1. docker-composeによる一括管理 - 複数コンテナを単一のYAMLファイルで定義・管理できる。世界全体を一つの契約書で統治する優雅さ。
  2. カスタムネットワークの恩恵 - コンテナ名で相互通信が可能になり、IPアドレスという不安定な数字の羅列から解放される。
  3. 初期化スクリプトの自動実行 - /docker-entrypoint-initdb.d配下のスクリプトでDB初期設定を自動化。召喚の度に手作業で記憶を刻む苦行から解放される。
  4. データ永続化の保証 - 名前付きボリュームにより、コンテナを破壊しても魂の記憶は失われない。
  5. 三層構造への拡張性 - この基礎構成に.NETアプリという第三の魂を加えれば、Web-AP-DBの完全な神殿が完成する。

注意すべき落とし穴

  • ポートの競合 - ホスト側で既に5432番ポートが使用されている場合、PostgreSQLコンテナは起動に失敗する。docker-compose.yml5433:5432のように異なるポート番号を割り当てることで回避できる。
  • 初期化スクリプトの実行タイミング - /docker-entrypoint-initdb.dのスクリプトは初回起動時のみ実行される。データボリュームが既に存在する場合は実行されないため、完全な再初期化にはボリュームの削除が必要となる。

まとめ

Docker Composeによる複数コンテナ管理は、マイクロサービスアーキテクチャの第一歩であり、現代のシステム構築における必須の技術だ。この旅で学んだ、二つの魂を名前で結びつける魔法は、あんたのこれからの冒険でも、必ずや役立つはずだ。

前回紹介した.NETアプリのビルド技術と、今回のWeb-DB連携技術を組み合わせれば、完全な三層構造という、より壮大な世界を創造できる。その可能性は、この箱庭の中に、確かに眠っている。

おっと、どうやら相棒が腹を空かせたようだ。今日はこのへんで筆を置くとしよう。

砂漠で見つけた魔法のランプ

ラクダの独り言

ご主人が、今度は箱の中に、二匹のゴーレムを創り出して、お互いに「ピンポン」とか言い合わせている。なんでも、そうやって仲良くさせないと、ちゃんとお仕事してくれないらしい。俺に言わせりゃ、最初から一匹の、もっとデカくて賢いゴーレムを創りゃいいだろうに。それに、名前で呼び合うだと?番号で管理した方が、よっぽどスッキリするだろうが。まったく、人間ってのは、面倒なことが好きだな。やれやれだぜ。