Docker 化你的 Flask 博客:完整指南 + Fly.io 部署

Fred· Developer & Technical Writer4 min read

**最后验证:**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 服务器和生产环境中以相同的方式运行。

这种一致性消除了整个类别的 bug。不再有"但它在我的机器上可以工作"的讨论。不再发现生产环境有 Python 3.8 而你在 3.11 上开发。不再有你忘记记录的缺失系统库。

真正的胜利是部署速度。一旦你有了可工作的 Docker 设置,部署变得微不足道。推送你的代码,构建镜像,部署。回滚同样简单:重新部署之前的镜像,几秒钟内你就恢复了业务。

我们正在构建什么

我们将采用你现有的 Flask 博客并正确地容器化它。你将获得具有热重载的本地开发环境,以便你可以立即看到更改。我们将使用 PostgreSQL,因为 SQLite 不适合生产环境,无论任何人怎么说。然后我们将整个东西部署到 Fly.io,因为他们有慷慨的免费层级,并按原样运行你的 Docker 容器。

准备你的 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.0

Flask 的内置服务器仅用于开发。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

    # 一些服务使用 postgres:// 但 SQLAlchemy 需要 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 缓冲可能在容器中导致问题
ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PIP_NO_CACHE_DIR=1 \
    PIP_DISABLE_PIP_VERSION_CHECK=1

WORKDIR /app

# 安装系统依赖
RUN apt-get update && apt-get install -y \
    gcc \
    postgresql-client \
    && rm -rf /var/lib/apt/lists/*

# 首先复制 requirements 以获得更好的缓存
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

# 为安全创建非 root 用户
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  # 挂载你的代码以进行热重载
    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 up

访问 localhost:8080,你的博客应该正在运行。如果出了问题,日志会准确告诉你是什么。Docker 向你显示一切,这比没有错误消息的神秘生产故障要好得多。

要只查看你的应用日志而不包含 PostgreSQL 噪音,使用 docker-compose logs -f web

部署到 Fly.io

Fly.io 运行你的实际 Docker 容器,而不是某个解释版本。他们有一个不需要信用卡的免费层级,这在 2025 年是令人耳目一新的。有关 Fly.io 免费层级功能和限制 的更深入了解,包括性能比较和扩展策略,请查看我们的完整指南。

首先安装他们的 CLI。

在 Mac 上:

brew install flyctl

在 Windows 上,从他们的网站下载安装程序或使用 PowerShell:

pwsh -Command "iwr https://fly.io/install.ps1 -useb | iex"

在 Linux 上:

curl -L https://fly.io/install.sh | sh

你可能需要将 flyctl 添加到你的 PATH。如果需要,安装程序会告诉你并显示要运行的确切命令。

使用 flyctl launch 启动你的应用。当它询问时,给你的应用一个名称(或让它生成一个),选择一个靠近你或你的用户的区域,对 PostgreSQL 说是(选择 development 以获得免费层级),并对立即部署说否,因为我们需要先设置密钥。

这会创建一个配置你部署的 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 = 256

auto_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 应用程序。你的开发环境现在与生产环境完全匹配,消除了整个类别的部署 bug。

这是专业团队使用的相同工作流程。相同的 Docker,相同的部署过程,相同的一切。业余项目和生产应用程序之间的差距比大多数人想象的要小。主要是知道正确的工具。

先构建你的 Flask 应用程序

还没有要 Docker 化的 Flask 博客?从这里开始:

每个教程都包含逐步的 AI 提示,指导你构建生产就绪的 Flask 应用程序。构建完应用后,回到本指南进行 Docker 化和部署。

现在去构建一些很酷的东西吧。

Fred

Fred

AUTHOR

Full-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 →