最終確認: 2025年10月 | Docker: 24.x | Flask: 3.0.0 | PostgreSQL: 15 | Python: 3.11
デプロイメントはフラストレーションがたまります。自分のマシンでFlaskアプリが完璧に動作するようにしても、デプロイしようとするとサーバー設定、Pythonバージョン、不足している依存関係と何時間も格闘することになります。Dockerは、アプリが必要とするすべてをコンテナにパッケージ化することでこれを解決します。コンテナはどこでも同じように動作します。
このガイドでは、Flaskブログをコンテナ化してFly.ioにデプロイする方法を示します。やり方を知っていれば、全プロセスは約15分かかります。サーバー設定なし、依存関係地獄なし、ただ本番で動作するアプリだけ。
このガイドの対象者
ローカルで動作するFlaskアプリがあり、通常の頭痛なしにデプロイしたい場合、これはあなた向けです。なぜラップトップでは動くのに本番では壊れるのかデバッグするのに疲れているかもしれません。何週間もDevOpsを学ぶことなく、モダンなデプロイメントプラクティスを使いたいかもしれません。
基本的なPythonとFlaskの知識が必要です。以前にFlaskアプリを構築したことがあれば、準備はできています。コマンドラインに慣れている必要があり、Docker Desktopがインストールされている必要があります。それだけです。
最後には、本番と正確に一致するローカル開発環境、数時間ではなく数分のデプロイメント、Pythonバージョンの競合がなくなります。
なぜDockerが役立つか
Dockerは複雑だという悪評がありますが、コアのアイデアはシンプルです:コードのための箱です。アプリは実行に必要なすべてと一緒に箱に入ります。その箱はラップトップ、CIサーバー、本番で同じように動作します。
この一貫性はカテゴリ全体のバグを排除します。「でも私のマシンでは動く」という議論はもうありません。開発を3.11で行ったのに本番がPython 3.8だったことを発見することもありません。ドキュメント化し忘れたシステムライブラリが足りないこともありません。
本当の勝利はデプロイメント速度です。動作するDockerセットアップがあれば、デプロイは些細なものになります。コードをプッシュし、イメージをビルドし、デプロイ。ロールバックも同様に簡単です:以前のイメージを再デプロイすれば、数秒でビジネスに戻れます。
何を構築するか
既存のFlaskブログを適切にコンテナ化します。変更を即座に確認できるホットリロード付きのローカル開発を取得します。SQLiteは本番向けではないので、PostgreSQLを使用します。誰が何と言っても。そして、寛大な無料ティアがあり、Dockerコンテナをそのまま実行するFly.ioに全体をデプロイします。
Flaskアプリの準備
まず、requirements.txtを修正しましょう。バージョン番号なしで「Flask」とだけ書いてあると、問題を求めています。本番に必要なものはこちら:
Flask==3.0.0
Flask-SQLAlchemy==3.1.1
Flask-Migrate==4.0.5
psycopg2-binary==2.9.9
python-dotenv==1.0.0
gunicorn==21.2.0Flaskの組み込みサーバーは開発専用です。Gunicornは実際のトラフィックを処理できる本番対応のWSGIサーバーです。psycopg2-binaryパッケージでPostgreSQLと通信できます。すべてにバージョン番号があります。なぜなら、バージョンなしの「Flask」は、異なる環境で異なるバージョンを取得する可能性があり、それは全体のポイントを台無しにします。
アプリケーションエントリポイントとしてwsgi.pyファイルを作成します:
from app import create_app
import os
app = create_app()
if __name__ == "__main__":
app.run(
host="0.0.0.0",
port=int(os.environ.get("PORT", 8080))
)そのhost="0.0.0.0"は重要です。「すべてのネットワークインターフェースでリッスンする」という意味です。デフォルトの127.0.0.1はローカルでのみリッスンし、コンテナ内では機能しません。この小さな詳細が多くの開発者にとって何時間ものデバッグの原因となっています。
config.pyは機密性の高いものに環境変数を使用する必要があります:
import os
from dotenv import load_dotenv
basedir = os.path.abspath(os.path.dirname(__file__))
load_dotenv(os.path.join(basedir, '.env'))
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-secret-key-change-in-production'
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
f'sqlite:///{os.path.join(basedir, "app.db")}'
SQLALCHEMY_TRACK_MODIFICATIONS = False
# Some services use postgres:// but SQLAlchemy needs postgresql://
if SQLALCHEMY_DATABASE_URI.startswith("postgres://"):
SQLALCHEMY_DATABASE_URI = SQLALCHEMY_DATABASE_URI.replace(
"postgres://", "postgresql://", 1
)シークレットをハードコードしないでください。環境変数を使用してください。誤って本番データベースのパスワードをGitHubにコミットしなくて済むとき、将来のあなたが感謝するでしょう。
動作するDockerfileを書く
本番で動作するDockerfileはこちら:
FROM python:3.11-slim as base
# Python buffering can cause issues in containers
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y \
gcc \
postgresql-client \
&& rm -rf /var/lib/apt/lists/*
# Copy requirements first for better caching
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# Create non-root user for security
RUN useradd -m -u 1000 flaskuser && \
chown -R flaskuser:flaskuser /app
USER flaskuser
EXPOSE 8080
CMD ["gunicorn", "--bind", "0.0.0.0:8080", "--workers", "2", "wsgi:app"]順序が重要です。コードをコピーする前にrequirements.txtをコピーすることで、Dockerがpip installレイヤーをキャッシュできます。依存関係ではなくコードを変更すると、Dockerはすべてのパッケージがすでにインストールされたキャッシュレイヤーを再利用します。これにより5分のリビルドが10秒に変わります。
非rootユーザーとして実行することはセキュリティのベストプラクティスです。誰かがアプリを侵害しても、コンテナへのroot権限は持っていません。
ローカル開発用Docker Compose
Docker Composeを使用すると、複数のコンテナを一緒に実行できます。FlaskアプリをPostgreSQLとセットアップするdocker-compose.ymlはこちら:
version: '3.8'
services:
web:
build: .
ports:
- "8080:8080"
environment:
- DATABASE_URL=postgresql://postgres:postgres@db:5432/flask_blog
- SECRET_KEY=dev-secret-key
- FLASK_ENV=development
volumes:
- .:/app # Mount your code for hot reloading
depends_on:
- db
command: flask run --host=0.0.0.0 --port=8080 --reload
db:
image: postgres:15-alpine
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=flask_blog
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:ボリュームマウント(.:/app)は、コンテナを再ビルドせずにコードへの変更が即座に反映されることを意味します。postgres_dataボリュームは、コンテナを停止してもデータベースが永続化されることを保証します。
そのdepends_onは起動順序を制御するだけです。PostgreSQLが準備できるまで待ちません。そのため、最初の起動時にFlaskアプリがクラッシュするかもしれません。再起動してください。これは正常で、一度だけ起こります。
すべてを実行する
最初にイメージをビルドします:
docker-compose build最初のビルドは、ベースイメージをダウンロードしてパッケージをインストールするため時間がかかります。後続のビルドはDockerのレイヤーキャッシングのおかげでずっと速くなります。
データベースを開始します:
docker-compose up -d dbまだであればデータベースマイグレーションを初期化します:
docker-compose run --rm web flask db init
docker-compose run --rm web flask db migrate -m "Initial migration"
docker-compose run --rm web flask db upgrade次にすべてを開始します:
docker-compose uplocalhost:8080にアクセスすると、ブログが動作しているはずです。何か問題があれば、ログが正確に何がおかしいか教えてくれます。Dockerはすべてを表示します。これはエラーメッセージのない謎めいた本番障害よりずっと良いです。
PostgreSQLのノイズなしでアプリのログだけを見るには、docker-compose logs -f webを使用します。
Fly.ioへのデプロイ
Fly.ioは実際のDockerコンテナを実行します。解釈されたバージョンではありません。クレジットカード不要の無料ティアがあり、2025年には新鮮です。**Fly.ioの無料ティアの機能と制限**についてのより詳細な説明(パフォーマンス比較とスケーリング戦略を含む)は、完全ガイドをご覧ください。
最初にCLIをインストールします。
Macでは:
brew install flyctlWindowsでは、ウェブサイトからインストーラーをダウンロードするか、PowerShellを使用します:
pwsh -Command "iwr https://fly.io/install.ps1 -useb | iex"Linuxでは:
curl -L https://fly.io/install.sh | shflyctlをPATHに追加する必要があるかもしれません。インストーラーはこれが必要かどうかを教え、実行する正確なコマンドを表示します。
flyctl launchでアプリを起動します。尋ねられたら、アプリに名前を付けるか(または生成させる)、あなたまたはユーザーの近くのリージョンを選び、PostgreSQLにyesと言い(無料ティアにはdevelopmentを選択)、最初にシークレットを設定する必要があるのですぐにデプロイするにはnoと言います。
これはデプロイメントを設定するfly.tomlファイルを作成します:
app = "your-app-name"
primary_region = "iad"
[build]
[env]
PORT = "8080"
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 0
[[vm]]
cpu_kind = "shared"
cpus = 1
memory_mb = 256auto_stop_machines設定は、誰も使用していないときにアプリを停止し、無料ティアの制限内に収まるのに役立ちます。誰かが訪問すると自動的に再起動します。
シークレットキーを設定します:
flyctl secrets set SECRET_KEY=$(openssl rand -hex 32)アプリをデプロイします:
flyctl deployこれはDockerイメージをビルドしてデプロイします。約2分かかります。完了したら、データベースマイグレーションを実行します:
flyctl ssh console -C "flask db upgrade"flyctl openでデプロイされたアプリを開きます。HTTPS、自動再起動、データベースバックアップ付きの本番Flaskアプリができました。サーバー設定は不要でした。
一般的な問題と解決策
「ポートは既に使用中」と表示されたら、おそらく別のコンテナが実行中です。docker-compose downですべてを停止するか、ポートを変更してください。Docker Desktopが混乱することがあり、再起動で修正されることがあります。
起動時のデータベース接続エラーは、通常FlaskがPostgreSQLの準備ができる前に接続しようとしていることを意味します。Flaskコンテナを再起動してください。データベース接続コードにリトライロジックを追加できますが、ローカル開発では単純な再起動で十分です。
Docker DesktopがDockerデーモンに接続できないと言ったら、Docker Desktopを再起動してください。それでも動かなければ、コンピュータを再起動してください。Docker Desktopには癖があります。
Fly.ioの無料ティアは256MBのRAMを提供します。アプリがもっと必要なら、flyctl scale memory 512を実行してください。または、アプリを最適化してください。おそらくすべてを一度にメモリにロードする必要はありません。
ローカルで変更が表示されない場合は、docker-compose.ymlのボリュームマウントを確認してください。本番では、flyctl deployで再ビルドしてデプロイする必要があります。Dockerは本番ではコードの変更を自動的に検出しません。
時間を節約するヒント
Dockerイメージは巨大になる可能性があります。スリムなベースイメージを使用し、.dockerignoreファイルを追加してください:
__pycache__
*.pyc
.git
.env
venv/
node_modules/
*.db
.DS_Storeアプリに適切なログを追加してください。本番で何かが壊れたとき、ログは何が起こったかへの唯一の窓です:
import logging
logging.basicConfig(level=logging.INFO)
app.logger.info('App starting up')今作成したDockerセットアップは、多くの企業が本番で使用しているものと同じです。これはおもちゃのセットアップではありません。たまたま無料のプロフェッショナルグレードのデプロイメントです。
やりました
Flask アプリケーションを本番データベース、ローカルホットリロード、ワンコマンドデプロイメントでコンテナ化することに成功しました。開発環境は今や本番と正確に一致し、デプロイメントバグのクラス全体を排除しました。
これはプロフェッショナルチームが使用するのと同じワークフローです。同じDocker、同じデプロイメントプロセス、すべて同じです。趣味のプロジェクトと本番アプリケーションの間のギャップは、ほとんどの人が思っているより小さいです。それは主に正しいツールを知っているだけです。
まずFlaskアプリケーションを構築する
コンテナ化するFlaskブログがまだありませんか?ここから始めてください:
- FlaskでブログをЛuild - PostgreSQLと認証機能を備えた完全なFlaskブログをゼロから作成
- Flaskでポートフォリオを構築 - プロフェッショナルなポートフォリオサイトでFlaskの基本を学ぶ
- FlaskでEコマースを構築 - ショッピングカートと決済統合のフル機能でFlaskをマスター
各チュートリアルには、本番対応のFlaskアプリケーションを構築するためのステップバイステップのAIプロンプトが含まれています。アプリを構築したら、このガイドに戻ってコンテナ化してデプロイしてください。
さあ、何か素晴らしいものを構築しましょう。
Fred
AUTHORFull-stack developer with 10+ years building production applications. I've containerized dozens of production applications and learned these lessons the hard way.
Need a developer who gets it?
POC builds, vibe-coded fixes, and real engineering. Let's talk.
Hire Me →
