#FastAPI Docker容器化部署完全指南
📂 所属阶段:第五阶段 — 工程化与部署(实战篇)
🔗 相关章节:Nginx与Gunicorn生产部署 · Pydantic Settings多环境配置
#目录
#为什么选择Docker?
在现代软件开发中,Docker已成为标准化部署方式,完美解决了传统"在我电脑上能跑"的问题:
传统部署困境:
┌─────────────────────────────────────────────────────┐
│ 开发:Python 3.11, PostgreSQL 13 → 测试:3.10, 14 → 生产:3.11, 15 │
│ ↓ 环境不一致 ↓ │
└─────────────────────────────────────────────────────┘
Docker解决方案:
┌─────────────────────────────────────────────────────┐
│ 项目代码 + Dockerfile → Image → Container → 随处一致运行 │
└─────────────────────────────────────────────────────┘#核心优势
- 环境一致性:开发/测试/生产环境完全统一
- 快速部署:秒级启动停止
- 资源隔离:避免依赖冲突
- 可移植性:一次构建,到处运行
- 弹性伸缩:根据负载自动扩缩容
#Dockerfile最佳实践
#生产级基础结构
# 使用官方轻量级Python镜像
FROM python:3.11-slim as base
# 设置优化环境变量
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 --no-install-recommends \
gcc \
g++ \
&& rm -rf /var/lib/apt/lists/*
# 安装Python依赖
COPY requirements.txt .
RUN pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt
# 最终运行镜像
FROM python:3.11-slim
# 复用环境变量
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
WORKDIR /app
# 从构建阶段复制依赖
COPY --from=base /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=base /usr/local/bin /usr/local/bin
# 创建非root用户(安全最佳实践)
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
# 复制应用代码并设置权限
COPY --chown=appuser:appgroup . .
USER appuser
EXPOSE 8000
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# 启动命令
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]#requirements.txt优化
fastapi>=0.109.0
uvicorn[standard]>=0.27.0
gunicorn>=21.2.0
uvloop>=0.19.0
httptools>=0.6.1
sqlalchemy[asyncio]>=2.0.0
asyncpg>=0.29.0
redis[hiredis]>=5.0.0
python-jose[cryptography]>=3.3.0
passlib[bcrypt]>=1.7.4
pydantic-settings>=2.0.0
python-multipart>=0.0.6#多阶段构建优化
使用Poetry的多阶段构建示例,显著减小最终镜像大小:
# 构建阶段
FROM python:3.11-slim as builder
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc g++ \
&& rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir poetry
COPY pyproject.toml poetry.lock* ./
RUN poetry config virtualenvs.create false && \
poetry install --no-dev --no-interaction --no-ansi
# 运行阶段
FROM python:3.11-slim as runtime
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
RUN groupadd -r appuser && useradd -r -g appuser appuser
COPY --chown=appuser:appgroup . .
USER appuser
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]#Docker Compose编排
#本地开发环境
# docker-compose.yml
version: "3.9"
services:
api:
build:
context: .
dockerfile: Dockerfile.dev
container_name: daoman_fastapi_dev
ports:
- "8000:8000"
environment:
- ENV=development
- DEBUG=true
- DATABASE_URL=postgresql+asyncpg://postgres:postgres@db:5432/daoman_dev
- REDIS_URL=redis://redis:6379/0
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- .:/app # 热重载
command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload
db:
image: postgres:16-alpine
container_name: daoman_postgres_dev
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: daoman_dev
ports:
- "5432:5432"
volumes:
- postgres_dev_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
container_name: daoman_redis_dev
ports:
- "6379:6379"
volumes:
- redis_dev_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 3
volumes:
postgres_dev_data:
redis_dev_data:#生产环境编排
# docker-compose.prod.yml
version: "3.9"
services:
nginx:
image: nginx:alpine
container_name: daoman_nginx_prod
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- api
networks:
- app-network
api:
build:
context: .
dockerfile: Dockerfile.prod
image: daoman_fastapi:latest
container_name: daoman_fastapi_prod
restart: always
expose:
- "8000"
environment:
- ENV=production
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
- JWT_SECRET=${JWT_SECRET}
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
networks:
- app-network
db:
image: postgres:16-alpine
container_name: daoman_postgres_prod
restart: always
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_prod_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
networks:
- app-network
redis:
image: redis:7-alpine
container_name: daoman_redis_prod
restart: always
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- redis_prod_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 3
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
postgres_prod_data:
redis_prod_data:#生产环境配置
#Nginx反向代理配置
# nginx/conf.d/fastapi.conf
upstream fastapi_app {
server api:8000;
keepalive 32;
}
server {
listen 80;
server_name your-domain.com;
access_log /var/log/nginx/fastapi.access.log;
error_log /var/log/nginx/fastapi.error.log;
# 安全头部
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
client_max_body_size 100M;
# Gzip压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types application/javascript application/json text/css text/plain;
location / {
proxy_pass http://fastapi_app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
proxy_buffering off;
}
location /health {
access_log off;
proxy_pass http://fastapi_app/health;
}
}#环境变量配置
# .env.production
ENV=production
DEBUG=false
DATABASE_URL=postgresql+asyncpg://user:password@host:5432/dbname
POSTGRES_DB=daoman_prod
POSTGRES_USER=daoman_user
POSTGRES_PASSWORD=secure_password_here
REDIS_URL=redis://redis:6379/0
JWT_SECRET=your_super_secret_jwt_key_here#安全最佳实践
#容器安全加固
FROM python:3.11-slim as base
# ... 省略与前面相同的构建步骤 ...
FROM python:3.11-slim
# ... 省略依赖复制步骤 ...
# 创建固定UID/GID的用户
RUN groupadd -r appgroup --gid 1001 && \
useradd -r -g appgroup --uid 1001 appuser
COPY --chown=appuser:appgroup . .
USER appuser
EXPOSE 8000
# ... 健康检查和启动命令 ...#Docker Compose安全配置
# 在生产服务中添加以下配置
services:
api:
# ... 其他配置 ...
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
read_only: true
tmpfs:
- /tmp
- /var/tmp
volumes:
- ./logs:/app/logs:rw
- ./uploads:/app/uploads:rw
sysctls:
- net.core.somaxconn=1024
ulimits:
nproc: 65535
nofile:
soft: 20000
hard: 40000#健康检查与监控
#应用健康检查端点
# health_check.py
from fastapi import APIRouter
from pydantic import BaseModel
from datetime import datetime
import asyncio
router = APIRouter()
class HealthStatus(BaseModel):
status: str
timestamp: str
services: dict
@router.get("/health", response_model=HealthStatus)
async def health_check():
services_status = {
"database": await check_database(),
"redis": await check_redis(),
}
overall_status = "healthy" if all(services_status.values()) else "degraded"
return HealthStatus(
status=overall_status,
timestamp=datetime.now().isoformat(),
services=services_status
)
async def check_database():
try:
await asyncio.sleep(0.1) # 替换为实际数据库检查
return True
except Exception:
return False
async def check_redis():
try:
await asyncio.sleep(0.05) # 替换为实际Redis检查
return True
except Exception:
return False#CI/CD集成
#GitHub Actions核心流程
# .github/workflows/docker.yml
name: Docker Build and Push
on:
push:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt pytest
- name: Run tests
run: pytest tests/ -v
build-and-push:
needs: test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Registry
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: your-registry/daoman-fastapi:latest
cache-from: type=gha
cache-to: type=gha,mode=max#常见问题与总结
#常见问题(FAQ)
Q: 如何优化Docker镜像大小?
A: 使用多阶段构建、选择轻量级基础镜像(如alpine/slim)、删除不必要的依赖、使用.dockerignore排除无关文件。
Q: 为什么要使用非root用户运行容器?
A: 提升安全性,即使容器被攻破,攻击者也只能获得有限权限,无法影响宿主机。
Q: Docker Compose在生产环境中如何管理敏感信息?
A: 使用环境变量文件(.env),不要将其提交到版本控制,或者使用Docker Secrets管理敏感数据。
#总结
Docker容器化部署是现代FastAPI应用的标准实践,核心要点包括:
- 🚀 使用多阶段构建优化镜像大小
- 🔒 非root用户运行提升安全性
- 🩺 健康检查确保服务可用性
- 🔄 CI/CD自动化部署流程
- 📊 Docker Compose编排生产环境
🔗 相关教程推荐
🏷️ 标签云: FastAPI部署 Docker容器化 Dockerfile Docker Compose 多阶段构建 生产部署 容器安全

