部署与生产环境
2026/3/20大约 10 分钟
部署与生产环境
第一章:生产环境配置
生产配置类
# config.py
import os
from datetime import timedelta
class ProductionConfig:
"""生产环境配置"""
# 安全
SECRET_KEY = os.environ.get('SECRET_KEY')
if not SECRET_KEY:
raise ValueError('SECRET_KEY environment variable is required')
# 调试关闭
DEBUG = False
TESTING = False
# 数据库
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL')
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_ENGINE_OPTIONS = {
'pool_size': 10,
'pool_recycle': 3600,
'pool_pre_ping': True,
}
# 会话安全
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax'
PERMANENT_SESSION_LIFETIME = timedelta(days=7)
# CSRF
WTF_CSRF_ENABLED = True
WTF_CSRF_TIME_LIMIT = 3600
# 邮件
MAIL_SERVER = os.environ.get('MAIL_SERVER')
MAIL_PORT = int(os.environ.get('MAIL_PORT', 587))
MAIL_USE_TLS = True
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
# 缓存
CACHE_TYPE = 'redis'
CACHE_REDIS_URL = os.environ.get('REDIS_URL')
# 日志
LOG_LEVEL = 'INFO'
LOG_FILE = '/var/log/flask/app.log'
@classmethod
def init_app(cls, app):
"""生产环境初始化"""
# 配置日志
import logging
from logging.handlers import RotatingFileHandler
file_handler = RotatingFileHandler(
cls.LOG_FILE,
maxBytes=10 * 1024 * 1024, # 10MB
backupCount=10
)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
))
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)
# 处理代理头
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(
app.wsgi_app,
x_for=1,
x_proto=1,
x_host=1,
x_prefix=1
)
环境变量管理
# .env.production
SECRET_KEY=your-super-secret-key-here
DATABASE_URL=postgresql://user:password@localhost:5432/myapp
REDIS_URL=redis://localhost:6379/0
MAIL_SERVER=smtp.example.com
MAIL_PORT=587
MAIL_USERNAME=your-email@example.com
MAIL_PASSWORD=your-email-password
# Flask 配置
FLASK_APP=run.py
FLASK_ENV=production
# 加载环境变量
from dotenv import load_dotenv
load_dotenv('.env.production')
# 或使用 python-decouple
from decouple import config
SECRET_KEY = config('SECRET_KEY')
DATABASE_URL = config('DATABASE_URL')
DEBUG = config('DEBUG', default=False, cast=bool)
第二章:WSGI 服务器
Gunicorn
pip install gunicorn
# 基本启动
gunicorn run:app
# 指定工作进程数
gunicorn -w 4 run:app
# 指定绑定地址和端口
gunicorn -b 0.0.0.0:8000 run:app
# 完整配置
gunicorn \
--workers 4 \
--bind 0.0.0.0:8000 \
--timeout 120 \
--access-logfile /var/log/gunicorn/access.log \
--error-logfile /var/log/gunicorn/error.log \
--capture-output \
--enable-stdio-inheritance \
run:app
# gunicorn.conf.py
import multiprocessing
# 绑定地址
bind = "0.0.0.0:8000"
# 工作进程数 (CPU 核心数 * 2 + 1)
workers = multiprocessing.cpu_count() * 2 + 1
# 工作模式
worker_class = "sync" # 或 "gevent", "eventlet"
# 超时
timeout = 120
graceful_timeout = 30
keepalive = 5
# 日志
accesslog = "/var/log/gunicorn/access.log"
errorlog = "/var/log/gunicorn/error.log"
loglevel = "info"
capture_output = True
# 进程名
proc_name = "flask_app"
# 守护进程
daemon = False
# 重载
reload = False
# 预加载应用
preload_app = True
# 用户和组
user = "www-data"
group = "www-data"
# 工作进程回收
max_requests = 1000
max_requests_jitter = 50
def on_starting(server):
"""启动前钩子"""
pass
def on_exit(server):
"""退出钩子"""
pass
def worker_exit(server, worker):
"""工作进程退出钩子"""
pass
uWSGI
pip install uwsgi
# uwsgi.ini
[uwsgi]
module = run:app
master = true
processes = 4
threads = 2
socket = /tmp/uwsgi.sock
chmod-socket = 660
vacuum = true
die-on-term = true
# 日志
logto = /var/log/uwsgi/app.log
# 统计
stats = /tmp/uwsgi-stats.sock
# 优化
enable-threads = true
single-interpreter = true
lazy-apps = true
# 重载
py-autoreload = 0
# 缓冲
buffer-size = 32768
post-buffering = 1
# 启动 uWSGI
uwsgi --ini uwsgi.ini
# 使用 HTTP 模式
uwsgi --http :8000 --wsgi-file run.py --callable app
Waitress(Windows 兼容)
pip install waitress
# wsgi.py
from waitress import serve
from app import create_app
app = create_app('production')
if __name__ == '__main__':
serve(app, host='0.0.0.0', port=8000)
第三章:反向代理配置
Nginx
# /etc/nginx/sites-available/flask_app
upstream flask_app {
server 127.0.0.1:8000;
# 多个后端服务器(负载均衡)
# server 127.0.0.1:8001;
# server 127.0.0.1:8002;
}
server {
listen 80;
server_name example.com www.example.com;
# 重定向到 HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# SSL 证书
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# SSL 配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# 日志
access_log /var/log/nginx/flask_access.log;
error_log /var/log/nginx/flask_error.log;
# 静态文件
location /static {
alias /var/www/flask_app/static;
expires 30d;
add_header Cache-Control "public, immutable";
}
# 上传文件
location /uploads {
alias /var/www/flask_app/uploads;
expires 7d;
}
# API 和应用
location / {
proxy_pass http://flask_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;
# WebSocket 支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 超时
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# 缓冲
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 24 4k;
}
# 上传大小限制
client_max_body_size 16M;
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml;
}
Apache
# /etc/apache2/sites-available/flask_app.conf
<VirtualHost *:80>
ServerName example.com
Redirect permanent / https://example.com/
</VirtualHost>
<VirtualHost *:443>
ServerName example.com
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
# 静态文件
Alias /static /var/www/flask_app/static
<Directory /var/www/flask_app/static>
Require all granted
</Directory>
# WSGI
WSGIDaemonProcess flask_app python-home=/var/www/flask_app/venv python-path=/var/www/flask_app
WSGIProcessGroup flask_app
WSGIScriptAlias / /var/www/flask_app/wsgi.py
<Directory /var/www/flask_app>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
# 日志
ErrorLog ${APACHE_LOG_DIR}/flask_error.log
CustomLog ${APACHE_LOG_DIR}/flask_access.log combined
</VirtualHost>
第四章:Docker 部署
Dockerfile
# Dockerfile
FROM python:3.11-slim
# 设置工作目录
WORKDIR /app
# 设置环境变量
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
# 安装系统依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
# 复制依赖文件
COPY requirements.txt .
# 安装 Python 依赖
RUN pip install --no-cache-dir -r requirements.txt
# 复制应用代码
COPY . .
# 创建非 root 用户
RUN adduser --disabled-password --gecos '' appuser && \
chown -R appuser:appuser /app
USER appuser
# 暴露端口
EXPOSE 8000
# 健康检查
HEALTHCHECK \
CMD curl -f http://localhost:8000/health || exit 1
# 启动命令
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "run:app"]
Docker Compose
# docker-compose.yml
version: "3.8"
services:
web:
build: .
ports:
- "8000:8000"
environment:
- FLASK_ENV=production
- DATABASE_URL=postgresql://postgres:password@db:5432/flask_app
- REDIS_URL=redis://redis:6379/0
- SECRET_KEY=${SECRET_KEY}
depends_on:
- db
- redis
volumes:
- uploads:/app/uploads
restart: unless-stopped
networks:
- app-network
db:
image: postgres:15
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
- POSTGRES_DB=flask_app
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
networks:
- app-network
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
restart: unless-stopped
networks:
- app-network
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./static:/var/www/static:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- web
restart: unless-stopped
networks:
- app-network
celery:
build: .
command: celery -A app.celery worker --loglevel=info
environment:
- DATABASE_URL=postgresql://postgres:password@db:5432/flask_app
- REDIS_URL=redis://redis:6379/0
depends_on:
- db
- redis
restart: unless-stopped
networks:
- app-network
celery-beat:
build: .
command: celery -A app.celery beat --loglevel=info
environment:
- DATABASE_URL=postgresql://postgres:password@db:5432/flask_app
- REDIS_URL=redis://redis:6379/0
depends_on:
- db
- redis
restart: unless-stopped
networks:
- app-network
volumes:
postgres_data:
redis_data:
uploads:
networks:
app-network:
driver: bridge
多阶段构建
# Dockerfile.multistage
# 构建阶段
FROM python:3.11-slim AS builder
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
libpq-dev
COPY requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt
# 运行阶段
FROM python:3.11-slim
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends \
libpq5 \
&& rm -rf /var/lib/apt/lists/*
COPY /app/wheels /wheels
RUN pip install --no-cache /wheels/*
COPY . .
RUN adduser --disabled-password --gecos '' appuser && \
chown -R appuser:appuser /app
USER appuser
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "run:app"]
第五章:云平台部署
Heroku
# 安装 Heroku CLI
# https://devcenter.heroku.com/articles/heroku-cli
# 登录
heroku login
# 创建应用
heroku create myapp-name
# 设置环境变量
heroku config:set SECRET_KEY=your-secret-key
heroku config:set FLASK_ENV=production
# 添加数据库
heroku addons:create heroku-postgresql:hobby-dev
# 添加 Redis
heroku addons:create heroku-redis:hobby-dev
# 部署
git push heroku main
# 运行迁移
heroku run flask db upgrade
# 查看日志
heroku logs --tail
# Procfile
web: gunicorn run:app
worker: celery -A app.celery worker --loglevel=info
release: flask db upgrade
# runtime.txt
python-3.11.0
AWS Elastic Beanstalk
# .ebextensions/01_packages.config
packages:
yum:
postgresql-devel: []
# .ebextensions/02_python.config
option_settings:
aws:elasticbeanstalk:container:python:
WSGIPath: run:app
aws:elasticbeanstalk:application:environment:
FLASK_ENV: production
commands:
01_upgrade_pip:
command: "/var/app/venv/*/bin/pip install --upgrade pip"
Google Cloud Run
# cloudbuild.yaml
steps:
# 构建镜像
- name: "gcr.io/cloud-builders/docker"
args: ["build", "-t", "gcr.io/$PROJECT_ID/flask-app", "."]
# 推送镜像
- name: "gcr.io/cloud-builders/docker"
args: ["push", "gcr.io/$PROJECT_ID/flask-app"]
# 部署到 Cloud Run
- name: "gcr.io/google.com/cloudsdktool/cloud-sdk"
entrypoint: gcloud
args:
- "run"
- "deploy"
- "flask-app"
- "--image"
- "gcr.io/$PROJECT_ID/flask-app"
- "--region"
- "us-central1"
- "--platform"
- "managed"
- "--allow-unauthenticated"
images:
- "gcr.io/$PROJECT_ID/flask-app"
第六章:进程管理
Systemd
# /etc/systemd/system/flask_app.service
[Unit]
Description=Flask Application
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/flask_app
Environment="PATH=/var/www/flask_app/venv/bin"
EnvironmentFile=/var/www/flask_app/.env
ExecStart=/var/www/flask_app/venv/bin/gunicorn --config gunicorn.conf.py run:app
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
# 启用服务
sudo systemctl enable flask_app
# 启动服务
sudo systemctl start flask_app
# 查看状态
sudo systemctl status flask_app
# 查看日志
sudo journalctl -u flask_app -f
# 重启服务
sudo systemctl restart flask_app
Supervisor
# /etc/supervisor/conf.d/flask_app.conf
[program:flask_app]
command=/var/www/flask_app/venv/bin/gunicorn --bind unix:/tmp/flask_app.sock run:app
directory=/var/www/flask_app
user=www-data
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/flask_app/gunicorn.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=10
environment=FLASK_ENV="production"
[program:celery_worker]
command=/var/www/flask_app/venv/bin/celery -A app.celery worker --loglevel=info
directory=/var/www/flask_app
user=www-data
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/flask_app/celery.log
[group:flask]
programs=flask_app,celery_worker
# 重新读取配置
sudo supervisorctl reread
# 更新
sudo supervisorctl update
# 启动
sudo supervisorctl start flask:*
# 查看状态
sudo supervisorctl status
第七章:监控与日志
日志配置
# logging_config.py
import logging
import logging.handlers
from flask import Flask
def configure_logging(app: Flask):
"""配置应用日志"""
# 移除默认处理器
app.logger.handlers.clear()
# 格式化器
formatter = logging.Formatter(
'[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
)
# 文件处理器
file_handler = logging.handlers.RotatingFileHandler(
app.config.get('LOG_FILE', 'logs/app.log'),
maxBytes=10 * 1024 * 1024,
backupCount=10
)
file_handler.setFormatter(formatter)
file_handler.setLevel(logging.INFO)
# 邮件处理器(错误通知)
if not app.debug:
mail_handler = logging.handlers.SMTPHandler(
mailhost=(app.config['MAIL_SERVER'], app.config['MAIL_PORT']),
fromaddr=app.config['MAIL_USERNAME'],
toaddrs=[app.config['ADMIN_EMAIL']],
subject='Application Error',
credentials=(app.config['MAIL_USERNAME'], app.config['MAIL_PASSWORD']),
secure=()
)
mail_handler.setLevel(logging.ERROR)
mail_handler.setFormatter(formatter)
app.logger.addHandler(mail_handler)
# JSON 日志(用于日志聚合)
class JSONFormatter(logging.Formatter):
def format(self, record):
import json
return json.dumps({
'timestamp': self.formatTime(record),
'level': record.levelname,
'module': record.module,
'message': record.getMessage(),
'pathname': record.pathname,
'lineno': record.lineno
})
json_handler = logging.handlers.RotatingFileHandler(
'logs/app.json.log',
maxBytes=10 * 1024 * 1024,
backupCount=10
)
json_handler.setFormatter(JSONFormatter())
app.logger.addHandler(file_handler)
app.logger.addHandler(json_handler)
app.logger.setLevel(logging.INFO)
健康检查端点
from flask import Blueprint, jsonify
from app import db
health = Blueprint('health', __name__)
@health.route('/health')
def health_check():
"""健康检查端点"""
return jsonify({'status': 'healthy'})
@health.route('/health/live')
def liveness():
"""存活探针"""
return jsonify({'status': 'alive'})
@health.route('/health/ready')
def readiness():
"""就绪探针"""
try:
# 检查数据库连接
db.session.execute('SELECT 1')
# 检查 Redis 连接
from app.extensions import cache
cache.set('health_check', 'ok', timeout=1)
cache.get('health_check')
return jsonify({
'status': 'ready',
'checks': {
'database': 'ok',
'cache': 'ok'
}
})
except Exception as e:
return jsonify({
'status': 'not_ready',
'error': str(e)
}), 503
Prometheus 指标
pip install prometheus-flask-exporter
from flask import Flask
from prometheus_flask_exporter import PrometheusMetrics
app = Flask(__name__)
metrics = PrometheusMetrics(app)
# 自定义指标
metrics.info('app_info', 'Application info', version='1.0.0')
# 按端点分组
@app.route('/api/users')
@metrics.counter('api_users_requests', 'Number of requests to users API',
labels={'status': lambda resp: resp.status_code})
def get_users():
return {'users': []}
# 访问 /metrics 获取指标
Sentry 错误追踪
pip install sentry-sdk[flask]
import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
sentry_sdk.init(
dsn=os.environ.get('SENTRY_DSN'),
integrations=[
FlaskIntegration(),
SqlalchemyIntegration(),
],
traces_sample_rate=0.1, # 10% 采样
environment=os.environ.get('FLASK_ENV', 'production'),
release=os.environ.get('APP_VERSION', '1.0.0'),
)
第八章:性能优化
数据库优化
# 连接池配置
SQLALCHEMY_ENGINE_OPTIONS = {
'pool_size': 10,
'pool_recycle': 3600,
'pool_pre_ping': True,
'pool_timeout': 30,
'max_overflow': 20,
}
# 查询优化
# 使用预加载避免 N+1 问题
from sqlalchemy.orm import joinedload, selectinload
users = User.query.options(
selectinload(User.posts).selectinload(Post.comments)
).all()
# 使用索引
class User(db.Model):
email = db.Column(db.String(120), unique=True, index=True)
created_at = db.Column(db.DateTime, index=True)
__table_args__ = (
db.Index('idx_user_status_created', 'status', 'created_at'),
)
缓存策略
from flask_caching import Cache
cache = Cache(app, config={
'CACHE_TYPE': 'redis',
'CACHE_REDIS_URL': os.environ.get('REDIS_URL'),
'CACHE_DEFAULT_TIMEOUT': 300
})
# 视图缓存
@app.route('/api/posts')
@cache.cached(timeout=60, query_string=True)
def get_posts():
return {'posts': Post.query.all()}
# 函数缓存
@cache.memoize(timeout=300)
def get_user_stats(user_id):
return calculate_stats(user_id)
# 条件缓存
@app.route('/api/user/<int:user_id>')
@cache.cached(timeout=300, unless=lambda: current_user.is_admin)
def get_user(user_id):
return User.query.get_or_404(user_id)
静态资源优化
from flask import Flask
from flask_compress import Compress
from flask_static_digest import FlaskStaticDigest
app = Flask(__name__)
# Gzip 压缩
Compress(app)
# 静态文件哈希(缓存破解)
FlaskStaticDigest(app)
# 配置
app.config['COMPRESS_MIMETYPES'] = [
'text/html', 'text/css', 'text/xml',
'application/json', 'application/javascript'
]
app.config['COMPRESS_LEVEL'] = 6
# Nginx 静态文件缓存
location /static {
alias /var/www/app/static;
expires 1y;
add_header Cache-Control "public, immutable";
# Gzip
gzip_static on;
}
异步处理
# 使用 Celery 处理耗时任务
from celery import Celery
celery = Celery('tasks', broker='redis://localhost:6379/0')
@celery.task
def send_email_async(to, subject, body):
"""异步发送邮件"""
mail.send_message(subject=subject, recipients=[to], body=body)
@celery.task
def generate_report(user_id):
"""生成报告"""
user = User.query.get(user_id)
report = create_report(user)
save_report(report)
return report.id
# 视图中使用
@app.route('/api/reports', methods=['POST'])
def create_report():
task = generate_report.delay(current_user.id)
return {'task_id': task.id}, 202
第九章:安全加固
HTTPS 配置
# 使用 Let's Encrypt
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
# 自动续期
sudo certbot renew --dry-run
安全头配置
from flask_talisman import Talisman
csp = {
'default-src': "'self'",
'script-src': ["'self'", 'cdn.example.com'],
'style-src': ["'self'", "'unsafe-inline'"],
'img-src': ["'self'", 'data:', 'https:'],
'font-src': ["'self'", 'fonts.googleapis.com'],
}
Talisman(
app,
force_https=True,
strict_transport_security=True,
strict_transport_security_max_age=31536000,
content_security_policy=csp,
referrer_policy='strict-origin-when-cross-origin'
)
安全检查清单
# security_checklist.py
def security_audit(app):
"""安全审计检查"""
issues = []
# 检查 DEBUG 模式
if app.config.get('DEBUG'):
issues.append('DEBUG mode is enabled')
# 检查 SECRET_KEY
if len(app.config.get('SECRET_KEY', '')) < 32:
issues.append('SECRET_KEY is too short')
# 检查 Cookie 安全设置
if not app.config.get('SESSION_COOKIE_SECURE'):
issues.append('SESSION_COOKIE_SECURE is not set')
if not app.config.get('SESSION_COOKIE_HTTPONLY'):
issues.append('SESSION_COOKIE_HTTPONLY is not set')
# 检查 CSRF 保护
if not app.config.get('WTF_CSRF_ENABLED', True):
issues.append('CSRF protection is disabled')
# 检查数据库 URL
db_url = app.config.get('SQLALCHEMY_DATABASE_URI', '')
if 'sqlite' in db_url.lower() and 'memory' not in db_url.lower():
issues.append('Using SQLite in production')
return issues
第十章:部署自动化
部署脚本
#!/bin/bash
# deploy.sh
set -e
APP_DIR="/var/www/flask_app"
VENV_DIR="$APP_DIR/venv"
BACKUP_DIR="/var/backups/flask_app"
echo "Starting deployment..."
# 备份当前版本
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
tar -czf "$BACKUP_DIR/backup_$TIMESTAMP.tar.gz" $APP_DIR
# 拉取最新代码
cd $APP_DIR
git fetch origin
git reset --hard origin/main
# 更新依赖
source $VENV_DIR/bin/activate
pip install -r requirements.txt
# 运行迁移
flask db upgrade
# 收集静态文件
flask static collect
# 重启服务
sudo systemctl restart flask_app
sudo systemctl restart celery_worker
echo "Deployment completed!"
GitHub Actions 自动部署
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.11"
- name: Install dependencies
run: pip install -r requirements.txt
- name: Run tests
run: pytest
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- name: Deploy to server
uses: appleboy/ssh-action@v0.1.10
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
cd /var/www/flask_app
./deploy.sh
总结
本章详细介绍了 Flask 部署与生产环境:
- 生产配置:安全设置、环境变量管理
- WSGI 服务器:Gunicorn、uWSGI、Waitress
- 反向代理:Nginx、Apache 配置
- Docker 部署:Dockerfile、Docker Compose
- 云平台:Heroku、AWS、Google Cloud
- 进程管理:Systemd、Supervisor
- 监控日志:日志配置、健康检查、Prometheus、Sentry
- 性能优化:数据库、缓存、静态资源
- 安全加固:HTTPS、安全头、检查清单
- 自动化部署:脚本、CI/CD
下一章我们将通过实战项目案例来综合应用这些知识。