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ユーザー設定
それでは、また明日お会いしましょう!