一、容器化部署的核心价值
1.1 为什么需要容器化部署
在生产环境中部署 FastAPI 应用时,容器化能解决以下核心问题:
- 环境一致性:解决 "在我机器上是好的" 问题,确保开发/测试/生产环境完全一致
- 资源隔离:避免应用间的资源竞争和依赖冲突
- 快速扩展:秒级启动新容器应对流量高峰
- 部署标准化:统一交付物格式,简化 CI/CD 流程
1.2 容器化部署架构图
graph LR
A[开发者] -->|推送镜像| B[容器仓库]
B -->|拉取镜像| C[Kubernetes 集群]
C --> D[FastAPI Pod]
C --> E[Celery Worker Pod]
C --> F[Redis Pod]
D -->|异步任务| E
E -->|消息队列| F
二、FastAPI 容器化实战
2.1 Dockerfile 最佳实践
Dockerfile
# 使用官方 Python 基础镜像
FROM python:3.11-slim-bullseye
# 设置工作目录
WORKDIR /app
# 先安装依赖 - 利用 Docker 缓存层
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制应用代码
COPY . .
# 设置环境变量
ENV PYTHONPATH=/app \
PORT=8000
# 暴露端口
EXPOSE $PORT
# 启动命令
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
依赖文件 requirements.txt
:
ini
fastapi==0.110.0
uvicorn==0.29.0
pydantic==2.6.4
celery==5.4.0
redis==5.0.3
2.2 多阶段构建优化
Dockerfile
# 构建阶段
FROM python:3.11 as builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user -r requirements.txt
# 运行时阶段
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
CMD ["uvicorn", "main:app", "--host", "0.0.0.0"]
2.3 Kubernetes 部署配置
fastapi-deployment.yaml
:
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: fastapi-app
spec:
replicas: 3
selector:
matchLabels:
app: fastapi
template:
metadata:
labels:
app: fastapi
spec:
containers:
- name: fastapi
image: your-registry/fastapi-app:v1.0
ports:
- containerPort: 8000
env:
- name: REDIS_HOST
value: "redis-service"
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
三、异步任务系统集成
3.1 Celery 任务示例
python
from celery import Celery
from pydantic import BaseModel
app = Celery(
'tasks',
broker='redis://redis-service:6379/0',
backend='redis://redis-service:6379/1'
)
class ProcessRequest(BaseModel):
user_id: int
data: str
@app.task
def process_data(request: dict):
# 使用 Pydantic 验证输入数据
validated = ProcessRequest(**request)
# 模拟耗时操作
result = f"Processed {validated.data} for user {validated.user_id}"
# 返回处理结果
return {"status": "success", "result": result}
3.2 FastAPI 任务触发端点
python
from fastapi import APIRouter
from .tasks import process_data
router = APIRouter()
@router.post("/submit-task")
async def submit_task(request: dict):
# 异步发送任务到 Celery
task = process_data.delay(request)
return {
"message": "Task submitted successfully",
"task_id": task.id
}
3.3 任务状态检查端点
python
@router.get("/task-status/{task_id}")
async def get_task_status(task_id: str):
# 获取 Celery 任务结果
result = AsyncResult(task_id)
return {
"task_id": task_id,
"status": result.status,
"result": result.result if result.ready() else None
}
四、健康检查与监控
4.1 健康检查端点
python
from fastapi import APIRouter, Depends
from redis import Redis
router = APIRouter()
def get_redis():
return Redis(host="redis-service", port=6379)
@router.get("/health")
async def health_check(redis: Redis = Depends(get_redis)):
# 检查 Redis 连接
redis.ping()
# 检查数据库连接等
return {"status": "healthy"}
4.2 Prometheus 监控集成
在 requirements.txt
添加:
ini
prometheus-fastapi-instrumentator==6.3.0
配置代码:
python
from prometheus_fastapi_instrumentator import Instrumentator
def setup_metrics(app):
Instrumentator().instrument(app).expose(app)
五、配置管理策略
5.1 环境变量优先配置
python
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
redis_host: str = "localhost"
redis_port: int = 6379
worker_count: int = 4
class Config:
env_file = ".env"
settings = Settings()
5.2 Kubernetes ConfigMap 示例
yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: fastapi-config
data:
REDIS_HOST: "redis-cluster"
MAX_WORKERS: "8"
六、安全加固措施
6.1 容器安全配置
Dockerfile
# 添加安全用户
RUN groupadd -g 999 appuser && \
useradd -r -u 999 -g appuser appuser
USER appuser
# 设置只读文件系统
RUN chmod -R 544 /app
6.2 API 请求限制
使用中间件实现速率限制:
python
from fastapi import FastAPI, Request
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
limiter = Limiter(key_func=get_remote_address)
app = FastAPI()
app.state.limiter = limiter
app.add_exception_handler(429, _rate_limit_exceeded_handler)
@app.get("/api/resource")
@limiter.limit("100/minute")
async def get_resource(request: Request):
return {"data": "protected resource"}
课后 Quiz
-
问题:为什么在 Dockerfile 中使用多阶段构建?
答案:多阶段构建可以显著减小最终镜像大小。第一阶段安装依赖生成 wheel 文件,第二阶段仅复制运行时所需文件,避免将构建工具和中间文件包含在最终镜像中 -
问题:如何确保 Celery 任务在容器重启后不丢失?
答案 :需要配置可靠的消息中间件(如 Redis/RabbitMQ)和结果后端持久化策略。同时设置task_acks_late=True
和task_reject_on_worker_lost=True
常见报错解决方案
问题 1:422 Unprocessable Entity
错误示例:
json
{
"detail": [
{
"loc": ["body", "user_id"],
"msg": "field required",
"type": "value_error.missing"
}
]
}
解决方案:
- 检查请求体是否包含所有必填字段
- 使用 Pydantic 模型验证数据:
python
class UserRequest(BaseModel):
user_id: int
username: str = Field(min_length=3)
@app.post("/create")
def create_user(user: UserRequest):
...
问题 2:Celery 任务状态一直为 PENDING
可能原因:
- 未正确配置消息代理(Redis/RabbitMQ)
- Worker 未连接到消息队列
- 任务路由配置错误
解决步骤:
- 检查 Redis 服务是否运行:
redis-cli ping
- 确认 Worker 连接状态:
celery -A tasks inspect ping
- 验证队列绑定:
celery -A tasks list bindings