docker compose入門:WEBアプリケーションを構築
※この記事で使用しているリソースを一括でダウンロードすることができます。
これまでDockerに関する記事をいくつか書いてきました。今回は開発していくにあたって必ずと言っていいほど使う「docker compose」について解説していきます。
docker composeを使用することで簡単に複数コンテナやネットワーク設定、volume設定などを一括で行うことができます。
前提
Dockerにおけるポートフォアーディング設定については理解していること。
作成するアプリのイメージは「Webアプリケーション(front)からバックグラウンドアプリケーションを呼び出し、データベース(postgresql)からユーザー一覧を取得して、Web画面に出力する」という内容になります。
リソースの準備
postgresのコンテナとpythonのコンテナについて準備していきましょう。ディレクトリ構成は以下のようになります。
ディレクトリ構成. ├── docker │ ├── front │ │ └── Dockerfile # アプリケーション(フロントエンド)のDockerfile │ ├── back │ │ └── Dockerfile # アプリケーション(バックエンド)のDockerfile │ └── db │ └── Dockerfile # データベース用のDockerfile ├── src/ # アプリケーションのソースコード │ ├── front │ │ └── index.html # フロントエンド用のhtmlファイル │ └── back | ├── app.py # バックエンド用のソースコード │ └── get_user.py # データベースからuserデータ取得する処理を記載 └── docker-compose.yml # Docker Compose 設定ファイル
pythonのコンテナ用のDockerfile
こちらはメインではないので軽く説明します。 pythonの3.11.10のイメージを使用して、psycopg2-binaryとFlask、flask-corsをpipインストールしています。
- psycopg2-binaryはDB(postgres)に接続するためのモジュールになります
- Flask、flask-corsはpythonでWebアプリケーションを作成するためのモジュールになります
./docker/back/DockerfileFROM python:3.11.10 RUN pip install psycopg2-binary RUN pip install Flask RUN pip install flask-cors
postgresのコンテナ用のDockerfile
こちらもメインではないので軽く説明します。 postgresのイメージを作成するためににDockerfileを作成します。
docker-entrypoint-initdb.dディレクトリにsqlファイルを入れておくことで、コンテナ作成時にテーブル作成や初期データの投入などを行うことができます。これを利用してpostgresコンテナ作成時にusersテーブルとユーザのデータを入るようにしておきます。
./docker/db/DockerfileFROM postgres:17.0 ENV POSTGRES_PASSWORD=postgres # 初期化用SQLスクリプトを作成 RUN echo "CREATE TABLE users (id SERIAL PRIMARY KEY, username TEXT NOT NULL);" \ > /docker-entrypoint-initdb.d/init.sql \ && echo "INSERT INTO users (username) VALUES ('admin'),('user1'),('user2');" \ >> /docker-entrypoint-initdb.d/init.sql
pythonソースコードの準備
こちらもメインではないので軽く説明します。 get_user.pyではDB(postgres)に接続してユーザの一覧を取得しています。
./src/back/get_user.pyimport psycopg2 def get_user(): try: # `with`で接続を管理 with psycopg2.connect(host="db", database="postgres", user="postgres", password="postgres", port=5432) as connection: # `with`でカーソルを管理 with connection.cursor() as cursor: # SQLクエリを実行 query = "SELECT * FROM users;" cursor.execute(query) # 結果を取得して処理 rows = cursor.fetchall() users = [] for row in rows: print(row) id, name = row users.append({"id": id, "name": name}) return users except psycopg2.Error as e: print("データベース操作中にエラーが発生しました:", e) if __name__ == "__main__": id, name = get_user() print(id) print(name)
app.pyでは「/user」にアクセスされたときにget_user.pyを使用してユーザ情報を取得して呼び元に返します。 特に重要なのは「port=3000」の個所でこれによって3000番ポートで待ち受けることになります。
./src/back/app.pyfrom flask import Flask, jsonify from flask_cors import CORS from get_user import get_user app = Flask(__name__) # 特定のオリジンだけを許可する cors = CORS(app, resources={r"/*": {"origins": ["http://localhost"]}}) @app.route("/user") def user(): users = get_user() return jsonify(users), 200 if __name__ == "__main__": app.run(port=3000, host='0.0.0.0', debug=True)
Webソースコードの準備
こちらもメインではないので軽く説明します。 Webでアクセスしたときに「pythonソースコード」で書いたバックエンドアプリケーションの「/user」にリクエストしています。 そして結果を画面に出力するという内容になっています。
./src/front/index.html<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="user"></div> <script> const xhr = new XMLHttpRequest(); xhr.open("GET", "http://localhost:8080/user"); xhr.send(); xhr.responseType = "json"; xhr.onload = () => { if (xhr.readyState == 4 && xhr.status == 200) { const data = xhr.response; const container = document.getElementById('user'); console.log(container); console.log(data); // データをループして要素を作成 data.forEach((item, index) => { console.log(item) const div = document.createElement('div'); div.textContent = `ユーザID : ${item.id}、ユーザ名 : ${item.name}` container.appendChild(div); }); } else { console.log(`Error: ${xhr.status}`); } }; </script> </body> </html>
docker-compose.ymlの準備
今回のメインの内容になります。
全体としては、3つのコンテナ(back、front、db)が立ち上がるように設定しています。
./docker-compose.ymlservices: # バックエンドアプリケーション用のコンテナ back: build: ./docker/back ports: - "8080:3000" volumes: - ./src/back/:/app working_dir: /app command: python app.py tty: true # Web(フロントエンド)アプリケーション用のコンテナ front: image: httpd:2.4 ports: - "80:80" volumes: - ./src/front/:/usr/local/apache2/htdocs/ # データベース用のコンテナ db: build: ./docker/db ports: - "5432:5432" volumes: - postgres-volume:/var/lib/postgresql/data volumes: postgres-volume:
それぞれ詳しく説明していきます。まずbackから見ていきましょう。
- build: Dockerファイルのパスを指定
- ports: ポートフォアーディング設定(ホストの8080にアクセスするとコンテナの3000ポートに転送します)
- volumes: ここでは「./src/back/」を渡し、変更時に即反映されるようにbindマウントしています
- working_dir: コンテナに接続したときの作業ディレクトリを指定しています。
- command: コンテナ起動時位に実行するコマンドを指定しています。(これによって自動で3000ポートでバックエンドアプリケーションが上がります)
- tty: 「docker run」の-itと同じ動きをします(停止しないようにする設定)
docker-compose.ymlのback部分back: build: ./docker/back ports: - "8080:3000" volumes: - ./src/back/:/app working_dir: /app command: python app.py tty: true
次にfrontを見て行きます
- image: Dockerfileを使用せず直接イメージを指定しています。
- ports: ポートフォアーディング設定(ホストの80にアクセスするとコンテナの80ポートに転送します)
- volumes: ここでは「./src/front/」を渡し、変更時に即反映されるようにbindマウントしています
docker-compose.ymlのfront部分front: image: httpd:2.4 ports: - "80:80" volumes: - ./src/front/:/usr/local/apache2/htdocs/
最後にdbを見て行きます
- build: Dockerファイルのパスを指定
- ports: ポートフォアーディング設定(ホストの5432にアクセスするとコンテナの5432ポートに転送します)
- volumes: ここではvolumeマウントを使用してデータを永続化しています。(volumeマウントする場合は一番下に書いてあるようにvolumeを定義する必要があります。)
docker-compose.ymlのdb部分db: build: ./docker/db ports: - "5432:5432" volumes: - postgres-volume:/var/lib/postgresql/data volumes: postgres-volume:
docker-composeを実行
起動時はdocker-composeコマンドで起動することができます。
--buildでDockerfileに変更などがあった場合ビルドしてくれます。 -dでバックグラウンド実行してくれます。
ターミナル(docker-compose.ymlがある階層)docker-compose up --build -d
vscodeを使用している場合、docker-compose.ymlを右クリックしてコンテナを立ち上げえることもできます。
Webアプリケーションに接続する
Webアプリケーションは80ポートで上がっているのでブラウザから接続してみましょう。
ブラウザhttp://localhost/
以下のようにユーザ情報が出力されていればOKです。これによって複数コンテナの連携設定をしつつ一括で起動することが確認できました。 お疲れさまでした!
最後に
今回はなかなかのボリュームになりましたが、今回の内容でdocker-composeがとても便利だということがわかったのではないでしょうか?
今回の記事が役に立った、ほかにも記事が見たいという方は最新情報を公式Xで配信しているのでフォローお願いします!