前提条件
- 第1回から第5回までの構築が完了していること
- 参照APIリポジトリの内容を理解していること
- Dockerの基本的な操作ができること
- TypeScript/Node.jsでの開発経験があること
基礎知識
コンテナ化とは
コンテナ化は、アプリケーションとその実行環境を一つのパッケージにまとめる技術です。
WEBアプリケーション開発者の視点から見ると
ローカル開発では「私の環境では動く」という問題がよく発生します。Node.jsのバージョン、依存パッケージ、環境変数などの違いが原因です。コンテナ化により、これらの実行環境をコードとして定義し、どこでも同じように動作させることができます。
なぜコンテナ化が必要か
課題 | コンテナ化による解決 |
---|---|
環境差異による動作不良 | Dockerfileで実行環境を完全に定義 |
依存関係の管理が複雑 | すべての依存関係をイメージに含める |
スケーリングが困難 | 同じイメージから複数のコンテナを起動 |
デプロイ手順が属人化 | イメージのプッシュ・プルで標準化 |
Twelve-Factor Appの原則
クラウドネイティブな設計では、Twelve-Factor Appの原則に従うことが重要らしく、大枠として今回のコンテナ化で特に意識すべき点は以下の通りです。
データベース接続情報などの設定はコードに含めず、環境変数で管理。ビルドしたイメージを開発・本番環境で共通利用でき、環境変数を変更するだけで異なる環境に対応できます。
またアプリケーションはステートレスに設計し、セッション情報などをコンテナ内に保持しません。コンテナはいつでも破棄・再作成される前提で設計する必要があるためです。
ログはファイルに書き込まず標準出力に出力します。CloudWatchなどの外部サービスがログを収集・管理するため、コンテナが削除されてもログは保持されます。
ECS Fargateの要件
ECS Fargateでコンテナを動かすために、以下の要件ですすめましょう。
ポート設定
設定項目 | 要件 | 理由 |
---|---|---|
公開ポート | 単一ポートのみ | ALBからのトラフィック受信のため |
ポート番号 | 環境変数で設定可能 | Task Definitionでの設定 |
プロトコル | HTTP/HTTPS | ALBとの通信に使用 |
ヘルスチェック対応
ALBとECSの両方から定期的にヘルスチェックが実行されます。/health
のエンドポイントを実装し、システムが正常稼働しているかを定期的にチェックします。
シグナル | 用途 | 実装内容 |
---|---|---|
SIGTERM | 正常終了 | 新規リクエスト受付停止、既存処理完了待機 |
SIGKILL | 強制終了 | SIGTERMから30秒後に自動発行 |
Secrets Manager連携
DBのパスワードなど、機密情報を安全に管理する方法は2つあります。
Task Definition経由(今回採用)
Task Definitionで環境変数にSecrets Managerの参照を設定します。コンテナ起動時にECSが自動的に値を取得し、環境変数として扱えます。
{
"name": "DB_PASSWORD",
"valueFrom": "arn:aws:secretsmanager:region:account:secret:name"
}
システム内での直接取得
AWS SDKを使用しシステム内でSecrets Managerから値を取得します。
※ 実装がちょっと手間です。
今回作成するネットワーク構成
リソース | 説明 | 数量 |
---|---|---|
Dockerイメージ | Hono APIアプリケーション | 1個 |
環境変数設定 | Secrets Manager連携を含む設定 | 複数 |
ヘルスチェックのエンドポイント | /health |
1個 |
APIエンドポイント | ユーザーCRUD操作 | 5個 |
本番用Dockerfile設計
ベースイメージの選定
Node.jsアプリケーションでは、以下の3つのベースイメージから選択します。
ベースイメージ | サイズ | 特徴 | 推奨用途 |
---|---|---|---|
node:20-alpine | 約50MB | 最小構成、高速起動 | 本番環境(今回採用) |
node:20-slim | 約150MB | 基本的なツール含む | 開発環境 |
node:20 | 約1GB | フル機能 | ビルド環境のみ |
Alpine Linuxベースを選択する理由は、ECRのストレージコストとpull時間の削減です。
マルチステージビルドの構成
ECRへのpushを考慮し、最終イメージサイズを最小化するマルチステージビルドを採用します。
# backend/Dockerfile
# ===== Build Stage =====
FROM node:20-alpine AS builder
WORKDIR /app
# 依存関係のインストール(キャッシュ効率化)
COPY package*.json ./
RUN npm ci --only=production && \
npm cache clean --force
# 開発依存関係のインストール(TypeScript等)
COPY package*.json ./
RUN npm ci
# ソースコードのコピーとビルド
COPY . .
RUN npm run build
# ===== Production Stage =====
FROM node:20-alpine
# セキュリティ: 非rootユーザーの作成
RUN addgroup -g 1001 -S nodejs && \
adduser -S appuser -u 1001 -G nodejs
# 必要なパッケージのインストール
RUN apk add --no-cache curl && \
rm -rf /var/cache/apk/*
WORKDIR /app
COPY --from=builder --chown=appuser:nodejs /app/dist ./dist
COPY --from=builder --chown=appuser:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:nodejs /app/package*.json ./
# ユーザー切り替え
USER appuser
# ポート公開
EXPOSE 3000
# ヘルスチェック設定
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
CMD ["node", "dist/index.js"]
環境変数の設計
ECS Task Definitionで設定する環境変数を考慮した設計を行います。
必須環境変数
変数名 | 用途 | デフォルト値 | 注入方法 |
---|---|---|---|
NODE_ENV | 実行環境 | production | 直接設定 |
PORT | リスニングポート | 3000 | 直接設定 |
DB_HOST | RDSエンドポイント | なし | Secrets Manager |
DB_PORT | データベースポート | 5432 | Secrets Manager |
DB_NAME | データベース名 | なし | Secrets Manager |
DB_USER | データベースユーザー | なし | Secrets Manager |
DB_PASSWORD | データベースパスワード | なし | Secrets Manager |
動作確認用環境変数
ローカルでの動作確認を容易にするための追加変数です。
変数名 | 用途 | 使用場面 |
---|---|---|
SKIP_DB_WAIT | DB接続待機をスキップ | ローカルテスト時 |
LOG_LEVEL | ログ出力レベル | デバッグ時 |
コンテナの動作確認
ビルドと起動確認
参照APIリポジトリのコードを使用して、ローカル環境でコンテナの動作を確認します。
# backendディレクトリに移動
cd backend
# Dockerイメージのビルド
docker build -t hono-api:latest .
# ビルド完了の確認
docker images | grep hono-api
期待される出力
hono-api latest abc123def456 2 minutes ago 150MB
基本動作の確認
データベース接続をスキップするモードで、APIの基本動作を確認します。
# コンテナ起動(バックグラウンド)
docker run -d \
--name hono-api-test \
-p 3001:3000 \
-e NODE_ENV=production \
-e PORT=3000 \
-e SKIP_DB_WAIT=true \
-e DB_HOST=dummy \
-e DB_PORT=5432 \
-e DB_NAME=dummy \
-e DB_USER=dummy \
-e DB_PASSWORD=dummy \
hono-api:latest
# 起動状態の確認
docker ps | grep hono-api-test
エンドポイントの動作確認
各エンドポイントが正しく応答することを確認します。
# ルートエンドポイント
curl http://localhost:3001/
# ヘルスチェックエンドポイント
curl http://localhost:3001/health
期待されるレスポンス
{
"message": "Hono CRUD API Server",
"version": "1.0.0",
"environment": "production"
}
ログ出力の確認
CloudWatchへの連携を想定し、標準出力へのログ出力を確認します。
# コンテナログの確認
docker logs hono-api-test
# リアルタイムログの監視
docker logs -f hono-api-test
コンテナの停止と削除
動作確認後、コンテナを適切に停止・削除します。
# Graceful Shutdownの確認
docker stop hono-api-test
# コンテナの削除
docker rm hono-api-test
イメージサイズの最適化
ECRのストレージコストを考慮したイメージサイズの確認と最適化を行います。
サイズ確認
# イメージサイズの詳細確認
docker images hono-api:latest --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
最適化のポイント(目安)
最適化項目 | 実装方法 | 削減効果 |
---|---|---|
マルチステージビルド | ビルドツールを含めない | 約500MB削減 |
Alpine Linux使用 | 最小限のOS | 約950MB削減 |
node_modulesの最適化 | production依存のみ | 約100MB削減 |
キャッシュクリア | npm cache clean | 約50MB削減 |
次のステップ
次回は、作成したDockerイメージをECRにプッシュし、ECS Task DefinitionとServiceを設定します。
- ECRへのイメージpush
- Task Definitionの作成(CPU・メモリ設定)
- Secrets Manager連携の設定
- ECS Serviceの作成とALB連携
- デプロイメントとローリングアップデート