【AWSハンズオン】第6回 ECR連携 APIのコンテナ実装

【AWSハンズオン】第6回 ECR連携 APIのコンテナ実装

前回構築したRDSデータベースとECRリポジトリの基盤を利用し、HonoアプリケーションをDockerコンテナ化してECS Fargate環境で動作させる準備を行います。

AWS #AWS#初学者向け#API

【AWSハンズオン】第6回 ECR連携 APIのコンテナ実装

サムネイル

前回構築したRDSデータベースとECRリポジトリの基盤を利用し、HonoアプリケーションをDockerコンテナ化してECS Fargate環境で動作させる準備を行います。

更新日: 8/13/2025

前提条件

  • 第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連携
  • デプロイメントとローリングアップデート

検索

検索条件に一致する記事が見つかりませんでした