はじめに
Dockerの基本操作に慣れてきたら、複数のコンテナをまとめて扱ってみましょう。Webアプリケーションの開発では、APIとデータベースを同時に起動することがあります。
この記事では、Node.jsのExpressで小さなAPIを作成し、Dockerfileでイメージにします。その後、PostgreSQLと一緒にDocker Composeで起動します。ブラウザやcurlからAPIにアクセスし、PostgreSQLにはpsqlで接続して、それぞれ動作を確認します。
今回のAPIは「Hello, World!」を返すだけです。APIからPostgreSQLへ接続する処理は含みません。 まずはアプリケーションとDBを別々のコンテナとして起動する流れをつかみましょう。
前提条件
- DockerとDocker Composeが使えること
- ターミナルでコマンドを実行できること
- ホスト側の3000番ポートが空いていること
Docker Desktopを使う場合は、アプリを起動してから以下のコマンドを実行してください。バージョンが表示され、docker infoでエラーが出なければ準備完了です。
docker --version
docker compose version
docker info
ローカルPCへのNode.jsやPostgreSQLのインストールは不要です。
1. 作業用ディレクトリを作る
mkdir docker-express-postgres
cd docker-express-postgres
最終的なファイル構成は次のとおりです。
docker-express-postgres/
├── app.js
├── package.json
├── Dockerfile
└── compose.yaml
2. ExpressのAPIを作る
package.jsonを作成します。ExpressはNode.jsでAPIを作るためのパッケージです。
{
"name": "docker-express-postgres",
"version": "1.0.0",
"private": true,
"dependencies": {
"express": "^5.1.0"
}
}
続いて、app.jsを作成します。
const express = require('express');
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.send('Hello, World!');
});
app.listen(port, '0.0.0.0', () => {
console.log(`API is listening on port ${port}`);
});
/へアクセスすると、Hello, World!という文字列を返します。ホスト名に0.0.0.0を指定し、コンテナの外から公開ポートを通じてアクセスできるようにしています。
3. DockerfileでAPIのイメージを作る
同じディレクトリに、次の内容でDockerfileを作成します。
FROM node:24-alpine
WORKDIR /usr/src/app
COPY package.json ./
RUN npm install --omit=dev
COPY app.js ./
CMD ["node", "app.js"]
各行の役割は次のとおりです。
| 命令 | この例での役割 |
|---|---|
FROM | Node.jsを使えるベースイメージを指定する |
WORKDIR | 以降の作業ディレクトリを決める |
COPY | package.jsonとAPIのコードをイメージへコピーする |
RUN | イメージのビルド中にExpressをインストールする |
CMD | コンテナ起動時にAPIを実行する |
ここでpackage.jsonを先にコピーして依存パッケージをインストールし、その後でapp.jsをコピーしています。
まずはDocker Composeを使わず、API単体で動作を確認します。
docker build -t my-express-api .
docker run --rm --name my-express-api -p 3000:3000 my-express-api
-p 3000:3000は、PCの3000番ポートとコンテナの3000番ポートを接続する指定です。--rmを付けると、コンテナの終了時にそのコンテナを削除します。
別のターミナルを開いて、次のコマンドを実行してください。
curl http://localhost:3000/
次の結果が表示されれば、APIをコンテナ内で起動できています。
Hello, World!
元のターミナルでControl + Cを押し、APIを停止してから次へ進みます。同じポートを使うため、単体起動のコンテナを動かしたままにしないでください。
4. ComposeファイルにAPIとDBを定義する
compose.yamlを作成します。
services:
api:
build: .
ports:
- "3000:3000"
depends_on:
- db
db:
image: postgres:17
environment:
POSTGRES_PASSWORD: examplepassword
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
apiサービスは同じディレクトリのDockerfileからイメージをビルドします。dbサービスは既存のPostgreSQLイメージを使います。POSTGRES_PASSWORDは、このローカル実習で使うDB管理ユーザーのパスワードです。公開環境でこの値をそのまま使わないでください。
depends_onはdbを先に起動する指定です。ただし、DBへの接続準備が完了するまで待つ設定ではありません。今回のAPIはDBに接続しないため、起動順序を確認する例として記載しています。
pgdataはPostgreSQLのデータを保存する名前付きボリュームです。DBの5432番ポートはPCへ公開していません。今回はコンテナ内のpsqlからDBを操作します。
5. 2つのコンテナを起動する
compose.yamlがあるディレクトリで実行します。
docker compose up --build -d
--buildを付けるとAPIのイメージをビルドしてから起動します。-dはバックグラウンドで実行する指定です。
起動状態を確認しましょう。
docker compose ps
apiとdbの両方が実行中であれば成功です。APIにもアクセスします。
curl http://localhost:3000/
Hello, World!
APIが起動しない場合は、次のコマンドでログを確認できます。
docker compose logs api
6. PostgreSQLにも接続してみる
起動中のDBコンテナでpsqlを実行します。
docker compose exec db psql -U postgres -d postgres
postgres=#という表示になったら、次のSQLを入力してください。
SELECT current_database();
postgresと表示されれば、DBに接続できています。終了するには次を入力します。
\q
ここまでで、ExpressのAPIとPostgreSQLをDocker Composeで同時に起動し、両方の動作を確認できました。APIがDBへ問い合わせたわけではない点は区別しておきましょう。
7. コンテナを停止する
実習が終わったら、Composeで作成したコンテナとネットワークを停止・削除します。
docker compose down
このコマンドでは、名前付きボリュームpgdataは残ります。実習用のDBデータも含めて削除したい場合だけ、次のコマンドを使用してください。
docker compose down -v
-vを付けるとボリューム内のデータも削除されます。
まとめ
この記事では、ExpressでAPIを作り、Dockerfileでイメージをビルドしました。さらにDocker ComposeでPostgreSQLと同時に起動し、APIへのHTTPアクセスとpsqlによるDB接続を確認しました。
- Dockerfileの
build: .で、自作APIのイメージを使える - Composeの
servicesにAPIとDBを並べて定義できる docker compose up --build -dで両方を起動できるdocker compose execで起動中のDBコンテナ内のコマンドを実行できる
この構成は「APIとDBを同時に起動する」ところまでの入門例です。APIからDBを読み書きする処理は、ここでは実装していません。