Files
TakeoutSaaS.TenantApi/Document/05_部署运维.md
2025-12-01 12:06:39 +08:00

23 KiB
Raw Blame History

外卖SaaS系统 - 部署运维

1. 环境要求

1.1 开发环境

  • .NET SDK10.0 或更高版本
  • IDEVisual Studio 2022 / JetBrains Rider / VS Code
  • 数据库PostgreSQL 16+
  • 缓存Redis 7.0+
  • 消息队列RabbitMQ 3.12+
  • Git:版本控制
  • Docker Desktop:容器化开发(可选)

1.2 生产环境

  • 操作系统Linux (Ubuntu 22.04 LTS / CentOS 8+)
  • 运行时.NET Runtime 10.0
  • Web服务器Nginx 1.24+
  • 数据库PostgreSQL 16+ (主从复制)
  • 缓存Redis 7.0+ (哨兵模式)
  • 消息队列RabbitMQ 3.12+ (集群模式)
  • 对象存储MinIO / 阿里云OSS / 腾讯云COS
  • 监控Prometheus + Grafana
  • 日志ELK Stack (Elasticsearch + Logstash + Kibana)

1.3 硬件要求(生产环境)

  • 应用服务器4核8GB内存最低推荐8核16GB
  • 数据库服务器8核16GB内存SSD存储
  • Redis服务器4核8GB内存
  • 负载均衡器2核4GB内存

2. 本地开发环境搭建

2.1 安装.NET SDK

# Windows
# 从官网下载安装https://dotnet.microsoft.com/download

# Linux (Ubuntu)
wget https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
sudo apt-get update
sudo apt-get install -y dotnet-sdk-10.0

# 验证安装
dotnet --version

2.2 安装PostgreSQL

# Ubuntu
sudo apt-get update
sudo apt-get install -y postgresql-16 postgresql-contrib-16

# 启动服务
sudo systemctl start postgresql
sudo systemctl enable postgresql

# 创建数据库
sudo -u postgres psql
CREATE DATABASE takeout_saas;
CREATE USER takeout_user WITH PASSWORD 'your_password';
GRANT ALL PRIVILEGES ON DATABASE takeout_saas TO takeout_user;
\q

2.3 安装Redis

# Ubuntu
sudo apt-get install -y redis-server

# 启动服务
sudo systemctl start redis-server
sudo systemctl enable redis-server

# 测试连接
redis-cli ping

2.4 安装RabbitMQ

# Ubuntu
sudo apt-get install -y rabbitmq-server

# 启动服务
sudo systemctl start rabbitmq-server
sudo systemctl enable rabbitmq-server

# 启用管理插件
sudo rabbitmq-plugins enable rabbitmq_management

# 创建用户
sudo rabbitmqctl add_user admin password
sudo rabbitmqctl set_user_tags admin administrator
sudo rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"

# 访问管理界面http://localhost:15672

2.5 使用Docker Compose推荐

# docker-compose.yml
version: '3.8'

services:
  postgres:
    image: postgres:16
    container_name: takeout_postgres
    environment:
      POSTGRES_DB: takeout_saas
      POSTGRES_USER: takeout_user
      POSTGRES_PASSWORD: your_password
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    container_name: takeout_redis
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

  rabbitmq:
    image: rabbitmq:3.12-management
    container_name: takeout_rabbitmq
    environment:
      RABBITMQ_DEFAULT_USER: admin
      RABBITMQ_DEFAULT_PASS: password
    ports:
      - "5672:5672"
      - "15672:15672"
    volumes:
      - rabbitmq_data:/var/lib/rabbitmq

  minio:
    image: minio/minio:latest
    container_name: takeout_minio
    command: server /data --console-address ":9001"
    environment:
      MINIO_ROOT_USER: admin
      MINIO_ROOT_PASSWORD: password123
    ports:
      - "9000:9000"
      - "9001:9001"
    volumes:
      - minio_data:/data

volumes:
  postgres_data:
  redis_data:
  rabbitmq_data:
  minio_data:
# 启动所有服务
docker-compose up -d

# 查看服务状态
docker-compose ps

# 停止服务
docker-compose down

2.6 配置项目

# 克隆项目
git clone https://github.com/your-org/takeout-saas.git
cd takeout-saas

# 还原依赖
dotnet restore

# 配置appsettings.Development.json
{
  "ConnectionStrings": {
    "DefaultConnection": "Host=localhost;Port=5432;Database=takeout_saas;Username=takeout_user;Password=your_password"
  },
  "Redis": {
    "Configuration": "localhost:6379"
  },
  "RabbitMQ": {
    "Host": "localhost",
    "Port": 5672,
    "Username": "admin",
    "Password": "password"
  }
}

# 执行数据库迁移
cd src/TakeoutSaaS.Api
dotnet ef database update

# 运行项目
dotnet run

3. Docker部署

3.1 创建Dockerfile

# Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY ["src/TakeoutSaaS.Api/TakeoutSaaS.Api.csproj", "src/TakeoutSaaS.Api/"]
COPY ["src/TakeoutSaaS.Application/TakeoutSaaS.Application.csproj", "src/TakeoutSaaS.Application/"]
COPY ["src/TakeoutSaaS.Domain/TakeoutSaaS.Domain.csproj", "src/TakeoutSaaS.Domain/"]
COPY ["src/TakeoutSaaS.Infrastructure/TakeoutSaaS.Infrastructure.csproj", "src/TakeoutSaaS.Infrastructure/"]
COPY ["src/TakeoutSaaS.Shared/TakeoutSaaS.Shared.csproj", "src/TakeoutSaaS.Shared/"]
RUN dotnet restore "src/TakeoutSaaS.Api/TakeoutSaaS.Api.csproj"
COPY . .
WORKDIR "/src/src/TakeoutSaaS.Api"
RUN dotnet build "TakeoutSaaS.Api.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "TakeoutSaaS.Api.csproj" -c Release -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "TakeoutSaaS.Api.dll"]

3.2 构建镜像

# 构建镜像
docker build -t takeout-saas-api:latest .

# 查看镜像
docker images | grep takeout-saas

# 运行容器
docker run -d \
  --name takeout-api \
  -p 8080:80 \
  -e ASPNETCORE_ENVIRONMENT=Production \
  -e ConnectionStrings__DefaultConnection="Host=postgres;Port=5432;Database=takeout_saas;Username=takeout_user;Password=your_password" \
  takeout-saas-api:latest

3.3 生产环境Docker Compose

# docker-compose.prod.yml
version: '3.8'

services:
  api:
    image: takeout-saas-api:latest
    container_name: takeout_api
    restart: always
    environment:
      ASPNETCORE_ENVIRONMENT: Production
      ConnectionStrings__DefaultConnection: "Host=postgres;Port=5432;Database=takeout_saas;Username=takeout_user;Password=${DB_PASSWORD}"
      Redis__Configuration: "redis:6379"
      RabbitMQ__Host: "rabbitmq"
    ports:
      - "8080:80"
    depends_on:
      - postgres
      - redis
      - rabbitmq
    networks:
      - takeout_network

  nginx:
    image: nginx:latest
    container_name: takeout_nginx
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/ssl:/etc/nginx/ssl
    depends_on:
      - api
    networks:
      - takeout_network

networks:
  takeout_network:
    driver: bridge

4. Nginx配置

4.1 基础配置

# /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log /var/log/nginx/access.log main;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    # Gzip压缩
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css text/xml text/javascript
               application/json application/javascript application/xml+rss
               application/rss+xml font/truetype font/opentype
               application/vnd.ms-fontobject image/svg+xml;

    # 限流配置
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s;
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;

    include /etc/nginx/conf.d/*.conf;
}

4.2 API服务配置

# /etc/nginx/conf.d/api.conf
upstream api_backend {
    least_conn;
    server api1:80 weight=1 max_fails=3 fail_timeout=30s;
    server api2:80 weight=1 max_fails=3 fail_timeout=30s;
    keepalive 32;
}

server {
    listen 80;
    server_name api.example.com;

    # 重定向到HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name api.example.com;

    # SSL证书配置
    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    # 安全头
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    # 客户端请求体大小限制
    client_max_body_size 10M;

    # API接口
    location /api/ {
        # 限流
        limit_req zone=api_limit burst=20 nodelay;
        limit_conn conn_limit 10;

        proxy_pass http://api_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        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_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;

        # 缓冲设置
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 4k;
        proxy_busy_buffers_size 8k;
    }

    # WebSocket
    location /ws {
        proxy_pass http://api_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # WebSocket超时
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }

    # 健康检查
    location /health {
        proxy_pass http://api_backend;
        access_log off;
    }

    # 静态文件缓存
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
        proxy_pass http://api_backend;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
}

5. 数据库部署

5.1 PostgreSQL主从复制

# 主库配置 (postgresql.conf)
listen_addresses = '*'
wal_level = replica
max_wal_senders = 10
wal_keep_size = 64MB
hot_standby = on

# 主库配置 (pg_hba.conf)
host replication replicator 192.168.1.0/24 md5

# 创建复制用户
CREATE USER replicator WITH REPLICATION ENCRYPTED PASSWORD 'repl_password';

# 从库配置
# 1. 停止从库
sudo systemctl stop postgresql

# 2. 清空从库数据目录
rm -rf /var/lib/postgresql/16/main/*

# 3. 从主库复制数据
pg_basebackup -h master_ip -D /var/lib/postgresql/16/main -U replicator -P -v -R -X stream -C -S replica1

# 4. 启动从库
sudo systemctl start postgresql

# 5. 验证复制状态
# 主库执行
SELECT * FROM pg_stat_replication;

5.2 数据库备份脚本

#!/bin/bash
# backup_db.sh

BACKUP_DIR="/backup/postgres"
DATE=$(date +%Y%m%d_%H%M%S)
DB_NAME="takeout_saas"
DB_USER="takeout_user"
RETENTION_DAYS=30

# 创建备份目录
mkdir -p $BACKUP_DIR

# 全量备份
pg_dump -h localhost -U $DB_USER -d $DB_NAME -F c -f $BACKUP_DIR/full_$DATE.dump

# 压缩备份
gzip $BACKUP_DIR/full_$DATE.dump

# 删除过期备份
find $BACKUP_DIR -name "full_*.dump.gz" -mtime +$RETENTION_DAYS -delete

# 上传到对象存储(可选)
# aws s3 cp $BACKUP_DIR/full_$DATE.dump.gz s3://your-bucket/backups/

echo "Backup completed: full_$DATE.dump.gz"

5.3 定时备份Crontab

# 编辑crontab
crontab -e

# 每天凌晨2点执行备份
0 2 * * * /path/to/backup_db.sh >> /var/log/backup.log 2>&1

TODO基础设施部署脚本

  • PostgreSQL 主从:整理主库/从库初始化脚本、basebackup 步骤与故障切换手册。
  • Redis 哨兵/集群:补充 redis.conf/sentinel.conf 模板以及一主两从搭建命令。
  • RabbitMQ编写单节点到镜像队列的安装脚本记录 VHost、用户、权限、监控等操作。
  • 腾讯云 COS整理桶创建、ACL、CDN 绑定与密钥轮换流程,并提供 coscmd/SDK 示例。
  • Hangfire 存储:确认 PostgreSQL Schema 初始化脚本,补充定期备份、清理、监控的 SOP。

6. Redis部署

6.1 Redis哨兵模式

# redis.conf (主节点)
bind 0.0.0.0
port 6379
requirepass your_password
masterauth your_password

# sentinel.conf
port 26379
sentinel monitor mymaster 192.168.1.100 6379 2
sentinel auth-pass mymaster your_password
sentinel down-after-milliseconds mymaster 5000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 10000

6.2 Redis持久化配置

# redis.conf
# RDB持久化
save 900 1
save 300 10
save 60 10000
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb

# AOF持久化
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

7. CI/CD配置

7.1 GitHub Actions

# .github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    branches: [ main ]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3

    - name: Setup .NET
      uses: actions/setup-dotnet@v3
      with:
        dotnet-version: '10.0.x'

    - name: Restore dependencies
      run: dotnet restore

    - name: Build
      run: dotnet build --configuration Release --no-restore

    - name: Test
      run: dotnet test --no-build --verbosity normal

    - name: Publish
      run: dotnet publish src/TakeoutSaaS.Api/TakeoutSaaS.Api.csproj -c Release -o ./publish

    - name: Build Docker image
      run: |
        docker build -t takeout-saas-api:${{ github.sha }} .
        docker tag takeout-saas-api:${{ github.sha }} takeout-saas-api:latest

    - name: Push to Registry
      run: |
        echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
        docker push takeout-saas-api:${{ github.sha }}
        docker push takeout-saas-api:latest

    - name: Deploy to Server
      uses: appleboy/ssh-action@master
      with:
        host: ${{ secrets.SERVER_HOST }}
        username: ${{ secrets.SERVER_USER }}
        key: ${{ secrets.SSH_PRIVATE_KEY }}
        script: |
          cd /opt/takeout-saas
          docker-compose pull
          docker-compose up -d
          docker system prune -f

7.2 GitLab CI

# .gitlab-ci.yml
stages:
  - build
  - test
  - deploy

variables:
  DOCKER_IMAGE: registry.example.com/takeout-saas-api

build:
  stage: build
  image: mcr.microsoft.com/dotnet/sdk:10.0
  script:
    - dotnet restore
    - dotnet build --configuration Release
  artifacts:
    paths:
      - src/*/bin/Release/

test:
  stage: test
  image: mcr.microsoft.com/dotnet/sdk:10.0
  script:
    - dotnet test --configuration Release

deploy:
  stage: deploy
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker build -t $DOCKER_IMAGE:$CI_COMMIT_SHA .
    - docker tag $DOCKER_IMAGE:$CI_COMMIT_SHA $DOCKER_IMAGE:latest
    - docker push $DOCKER_IMAGE:$CI_COMMIT_SHA
    - docker push $DOCKER_IMAGE:latest
  only:
    - main

8. 监控告警

8.1 Prometheus配置

# prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: 'takeout-api'
    static_configs:
      - targets: ['api:80']
    metrics_path: '/metrics'

  - job_name: 'postgres'
    static_configs:
      - targets: ['postgres-exporter:9187']

  - job_name: 'redis'
    static_configs:
      - targets: ['redis-exporter:9121']

  - job_name: 'node'
    static_configs:
      - targets: ['node-exporter:9100']

8.2 应用监控指标

// Program.cs - 添加Prometheus监控
builder.Services.AddPrometheusMetrics();

app.UseMetricServer(); // /metrics端点
app.UseHttpMetrics(); // HTTP请求指标

// 自定义指标
public class MetricsService
{
    private static readonly Counter OrderCreatedCounter = Metrics
        .CreateCounter("orders_created_total", "Total orders created");

    private static readonly Histogram OrderProcessingDuration = Metrics
        .CreateHistogram("order_processing_duration_seconds", "Order processing duration");

    public void RecordOrderCreated()
    {
        OrderCreatedCounter.Inc();
    }

    public IDisposable MeasureOrderProcessing()
    {
        return OrderProcessingDuration.NewTimer();
    }
}

8.3 Grafana仪表板

{
  "dashboard": {
    "title": "外卖SaaS系统监控",
    "panels": [
      {
        "title": "API请求速率",
        "targets": [
          {
            "expr": "rate(http_requests_total[5m])"
          }
        ]
      },
      {
        "title": "订单创建数",
        "targets": [
          {
            "expr": "increase(orders_created_total[1h])"
          }
        ]
      },
      {
        "title": "数据库连接数",
        "targets": [
          {
            "expr": "pg_stat_activity_count"
          }
        ]
      }
    ]
  }
}

8.4 告警规则

# alert.rules.yml
groups:
  - name: takeout_alerts
    interval: 30s
    rules:
      - alert: HighErrorRate
        expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.05
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "高错误率告警"
          description: "API错误率超过5%"

      - alert: DatabaseDown
        expr: up{job="postgres"} == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "数据库宕机"
          description: "PostgreSQL数据库不可用"

      - alert: HighMemoryUsage
        expr: (node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes > 0.9
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "内存使用率过高"
          description: "内存使用率超过90%"

9. 日志管理

9.1 Serilog配置

{
  "Serilog": {
    "Using": ["Serilog.Sinks.Console", "Serilog.Sinks.File", "Serilog.Sinks.Elasticsearch"],
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Warning",
        "System": "Warning"
      }
    },
    "WriteTo": [
      {
        "Name": "Console",
        "Args": {
          "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
        }
      },
      {
        "Name": "File",
        "Args": {
          "path": "logs/log-.txt",
          "rollingInterval": "Day",
          "retainedFileCountLimit": 30
        }
      },
      {
        "Name": "Elasticsearch",
        "Args": {
          "nodeUris": "http://elasticsearch:9200",
          "indexFormat": "takeout-logs-{0:yyyy.MM.dd}",
          "autoRegisterTemplate": true
        }
      }
    ],
    "Enrich": ["FromLogContext", "WithMachineName", "WithThreadId"]
  }
}

9.2 ELK Stack部署

# docker-compose.elk.yml
version: '3.8'

services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
    environment:
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ports:
      - "9200:9200"
    volumes:
      - es_data:/usr/share/elasticsearch/data

  logstash:
    image: docker.elastic.co/logstash/logstash:8.11.0
    volumes:
      - ./logstash/pipeline:/usr/share/logstash/pipeline
    ports:
      - "5044:5044"
    depends_on:
      - elasticsearch

  kibana:
    image: docker.elastic.co/kibana/kibana:8.11.0
    ports:
      - "5601:5601"
    environment:
      ELASTICSEARCH_HOSTS: http://elasticsearch:9200
    depends_on:
      - elasticsearch

volumes:
  es_data:

10. 安全加固

10.1 防火墙配置

# UFW防火墙
sudo ufw enable
sudo ufw allow 22/tcp    # SSH
sudo ufw allow 80/tcp    # HTTP
sudo ufw allow 443/tcp   # HTTPS
sudo ufw deny 5432/tcp   # 禁止外部访问数据库
sudo ufw deny 6379/tcp   # 禁止外部访问Redis

10.2 SSL证书Let's Encrypt

# 安装Certbot
sudo apt-get install certbot python3-certbot-nginx

# 获取证书
sudo certbot --nginx -d api.example.com

# 自动续期
sudo certbot renew --dry-run

# 添加定时任务
0 3 * * * certbot renew --quiet

10.3 应用安全配置

// Program.cs
builder.Services.AddHsts(options =>
{
    options.MaxAge = TimeSpan.FromDays(365);
    options.IncludeSubDomains = true;
    options.Preload = true;
});

builder.Services.AddHttpsRedirection(options =>
{
    options.RedirectStatusCode = StatusCodes.Status308PermanentRedirect;
    options.HttpsPort = 443;
});

// 添加安全头
app.Use(async (context, next) =>
{
    context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
    context.Response.Headers.Add("X-Frame-Options", "DENY");
    context.Response.Headers.Add("X-XSS-Protection", "1; mode=block");
    context.Response.Headers.Add("Referrer-Policy", "no-referrer");
    await next();
});

11. 性能优化

11.1 数据库连接池

{
  "ConnectionStrings": {
    "DefaultConnection": "Host=localhost;Port=5432;Database=takeout_saas;Username=user;Password=pass;Pooling=true;MinPoolSize=5;MaxPoolSize=100;ConnectionLifetime=300"
  }
}

11.2 Redis连接池

services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = configuration["Redis:Configuration"];
    options.InstanceName = "TakeoutSaaS:";
});

11.3 响应压缩

builder.Services.AddResponseCompression(options =>
{
    options.EnableForHttps = true;
    options.Providers.Add<GzipCompressionProvider>();
    options.Providers.Add<BrotliCompressionProvider>();
});

12. 故障恢复

12.1 数据库恢复

# 从备份恢复
pg_restore -h localhost -U takeout_user -d takeout_saas -v /backup/full_20240101.dump

# PITR恢复到指定时间点
# 1. 停止数据库
sudo systemctl stop postgresql

# 2. 恢复基础备份
rm -rf /var/lib/postgresql/16/main/*
tar -xzf /backup/base_backup.tar.gz -C /var/lib/postgresql/16/main/

# 3. 配置recovery.conf
restore_command = 'cp /backup/wal_archive/%f %p'
recovery_target_time = '2024-01-01 12:00:00'

# 4. 启动数据库
sudo systemctl start postgresql

12.2 应用回滚

# Docker回滚到上一个版本
docker-compose down
docker-compose up -d --force-recreate --no-deps api

# 或使用特定版本
docker pull takeout-saas-api:previous-version
docker-compose up -d