5
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

「Dockerはわかるけど…」から卒業!AWSでコンテナを動かすための30日集中講座 - Day 6: 実践!既存アプリケーションをDocker化してみよう

5
Last updated at Posted at 2025-08-16

Day 6: 実践!既存アプリケーションをDocker化してみよう 🚀

皆さん、こんにちは!30日集中講座、Day 6へようこそ。

この1週間で、Dockerの基礎から応用まで、以下の重要なスキルを学びました。

  • Day 1: コンテナとDockerの概念、その重要性
  • Day 2: dockerコマンドの基本(pull, run, psなど)
  • Day 3: Dockerfileを使ったイメージ作成とベストプラクティス
  • Day 4: Docker Composeを使った複数コンテナ管理
  • Day 5: ボリュームとネットワークによるデータの永続化と通信

この知識を定着させるため、今日は「既存のアプリケーションをDocker化する」という実践的な演習を行います。これにより、これまでの学びを統合し、実務で使えるスキルへと昇華させます。


1. 今回のお題:シンプルなPython Webアプリケーション

今回は、Pythonの軽量なWebフレームワークであるFlaskを使ったシンプルなWebアプリケーションをDocker化します。

プロジェクトのファイル構成

my-web-app/
├── src/                # アプリケーションのソースコード
│   ├── app.py
│   └── requirements.txt
├── .env.example        # 環境変数テンプレート
├── .gitignore          # Git管理から除外するファイル
├── .dockerignore       # ビルドコンテキストから除外するファイル
├── Dockerfile          # コンテナイメージのレシピ (本番用)
├── docker-compose.yml    # 複数コンテナの定義 (本番用)
└── docker-compose.dev.yml  # 開発用設定

src/app.py

import os
import time
import psycopg2
from flask import Flask, jsonify

app = Flask(__name__)

def get_db_connection():
    database_url = os.environ.get('DATABASE_URL')
    if not database_url:
        raise ValueError("DATABASE_URL environment variable is not set")
    
    max_retries = 10
    retry_delay = 5
    for i in range(max_retries):
        try:
            conn = psycopg2.connect(database_url, connect_timeout=10)
            print(f"Database connection successful! (attempt {i+1})")
            return conn
        except psycopg2.OperationalError as e:
            print(f"Database connection failed (attempt {i+1}): {e}")
            if i < max_retries - 1:
                time.sleep(retry_delay)
    raise Exception("Failed to connect to database after multiple retries.")

@app.route('/health')
def health_check():
    return jsonify({'status': 'healthy'}), 200

@app.route('/')
def hello_db():
    try:
        conn = get_db_connection()
        cur = conn.cursor()
        cur.execute("SELECT version()")
        db_version = cur.fetchone()
        cur.close()
        conn.close()
        return jsonify({
            'message': 'Database connection successful!',
            'db_version': db_version[0]
        })
    except Exception as e:
        return jsonify({'error': f'Database connection failed: {str(e)}'}), 500

if __name__ == '__main__':
    debug_mode = os.environ.get('FLASK_DEBUG', 'False').lower() == 'true'
    app.run(debug=debug_mode, host='0.0.0.0', port=5000)

src/requirements.txt

Flask==2.2.2
psycopg2-binary==2.9.3
gunicorn

2. ステップ・バイ・ステップ Docker化プロセス

ステップ1: Dockerfile を作成する

本番環境向けには、マルチステージビルドと非rootユーザー設定が重要です。これにより、セキュリティを強化し、イメージサイズを最小化します。

# Dockerfile: 本番向け
# -------------------------------------------------------------
# ビルドステージ: 依存関係のインストールとビルド
FROM python:3.9-slim as builder
WORKDIR /app
COPY ./src/requirements.txt .
# 依存ライブラリを/app/dependenciesにインストール
RUN pip install --prefix=/app/dependencies -r requirements.txt

# -------------------------------------------------------------
# 実行ステージ: アプリケーションの実行環境
FROM python:3.9-slim
# 非rootユーザーの作成(セキュリティ向上)
RUN addgroup --system --gid 1001 appgroup && \
    adduser --system --uid 1001 --gid 1001 appuser
WORKDIR /app

# ビルドステージから依存関係をコピー
COPY --from=builder /app/dependencies /usr/local
# アプリケーションコードをコピーし、所有者を非rootユーザーに設定
COPY --chown=appuser:appgroup ./src/ .
# 非rootユーザーに切り替え
USER appuser
# アプリケーションがリッスンするポートの公開
EXPOSE 5000
# Gunicornを使ってアプリケーションを起動
CMD ["gunicorn", "-w", "${WORKERS:-4}", "-b", "0.0.0.0:5000", "app:app"]

CMD にWORKERSを環境変数として設定することで、コンテナの起動時にGunicornのワーカープロセス数を柔軟に調整できます。

ステップ2: docker-compose ファイルを作成する

開発用と本番用の設定を分離すると、運用がシンプルになります。

docker-compose.yml (本番用)

services:
  web:
    build: .
    ports:
      - "5000:5000"
    env_file:
      - .env
    depends_on:
      db:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "python -c 'import urllib.request; urllib.request.urlopen(\"http://localhost:5000/health\")'"]
      interval: 30s
      timeout: 10s
      retries: 3
  
  db:
    image: postgres:13
    volumes:
      - postgres_data:/var/lib/postgresql/data
    env_file:
      - .env
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  postgres_data:

docker-compose.dev.yml (開発用)

services:
  web:
    build: .
    volumes:
      # ソースコードをコンテナに同期し、リアルタイムで変更を反映
      - ./src:/app
    environment:
      # デバッグモードを有効化
      - FLASK_DEBUG=true

開発時には docker compose -f docker-compose.yml -f docker-compose.dev.yml up コマンドで両方の設定を適用します。

ステップ3: 環境設定ファイルを作成する

.env.example

DATABASE_URL=postgresql://postgres:your_password@db:5432/postgres
FLASK_DEBUG=true

.env

DATABASE_URL=postgresql://postgres:secret_password@db:5432/postgres
FLASK_DEBUG=true

重要な注意: docker composeは.envファイルを参照しますが、Gitリポジトリにコミットしないよう .gitignoreに必ず追加してください 。本番環境では、AWS Secrets ManagerやKMSといった専用のシークレット管理サービスを使用するのがセキュリティのベストプラクティスです。


3. 本番運用を意識したログ管理とセキュリティ

ログ管理

現在のdocker-compose.ymlでは、ログはjson-fileドライバで管理されます。これはファイルサイズとファイル数を指定して自動でローテーション(世代管理)する機能があります。
本番環境では、AWS CloudWatch LogsやDatadogなどの集中ログ管理サービスに転送することで、ログの検索・分析・監視が容易になります。

セキュリティスキャン

ビルドしたイメージに脆弱性がないか、Docker Scoutのようなツールで定期的にスキャンしましょう。

# イメージの脆弱性スキャン
docker scout cves my-web-app:latest

4. まとめ

これで、皆さんはローカル環境でのコンテナ開発の基礎を完全にマスターしました。今日の実践演習で、これまでの知識が単なる理論ではなく、実際に動くアプリケーションを構築するためのツールであることが実感できたはずです。

来週からは、この知識を活かして、いよいよAWS上でコンテナを動かす旅に出発します。


次回の予告
Day 7: いざ、AWSへ!AWSアカウント作成とIAMユーザー設定

それでは、また明日お会いしましょう!

5
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?