Files
jiebanke/docs/系统集成和部署文档.md

42 KiB
Raw Blame History

系统集成和部署文档

概述

本文档详细描述了解班客平台的系统集成方案和部署流程包括开发环境搭建、生产环境部署、CI/CD流程、监控配置等内容。系统采用现代化的微服务架构支持容器化部署和自动化运维。

系统架构

整体架构图

graph TB
    subgraph "前端层"
        A[Web前端] --> B[移动端H5]
        A --> C[管理后台]
    end
    
    subgraph "网关层"
        D[Nginx反向代理]
        E[API网关]
    end
    
    subgraph "应用层"
        F[用户服务]
        G[动物服务]
        H[认领服务]
        I[管理服务]
    end
    
    subgraph "数据层"
        J[MySQL主库]
        K[MySQL从库]
        L[Redis缓存]
        M[文件存储]
    end
    
    subgraph "基础设施"
        N[日志系统]
        O[监控系统]
        P[告警系统]
    end
    
    A --> D
    B --> D
    C --> D
    D --> E
    E --> F
    E --> G
    E --> H
    E --> I
    F --> J
    G --> J
    H --> J
    I --> J
    J --> K
    F --> L
    G --> L
    H --> L
    I --> L
    F --> M
    G --> M
    H --> M
    I --> M
    F --> N
    G --> N
    H --> N
    I --> N
    N --> O
    O --> P

技术栈

前端技术栈

  • 框架: Vue.js 3.x
  • 构建工具: Vite
  • UI组件: Element Plus
  • 状态管理: Pinia
  • 路由: Vue Router
  • HTTP客户端: Axios
  • 样式: SCSS

后端技术栈

  • 运行时: Node.js 18+
  • 框架: Express.js
  • 数据库: MySQL 8.0
  • 缓存: Redis 6.0
  • ORM: Mongoose (MongoDB) / Sequelize (MySQL)
  • 认证: JWT
  • 文件上传: Multer + Sharp
  • 日志: Winston
  • 测试: Jest

基础设施

  • 容器化: Docker + Docker Compose
  • 反向代理: Nginx
  • 进程管理: PM2
  • 监控: Prometheus + Grafana
  • 日志收集: ELK Stack
  • CI/CD: GitHub Actions / GitLab CI

环境配置

开发环境

系统要求

  • 操作系统: macOS 10.15+ / Ubuntu 18.04+ / Windows 10+
  • Node.js: 18.0+
  • npm: 8.0+
  • MySQL: 8.0+
  • Redis: 6.0+
  • Docker: 20.0+ (可选)

环境搭建步骤

  1. 克隆项目
git clone https://github.com/your-org/jiebanke.git
cd jiebanke
  1. 安装依赖
# 安装后端依赖
cd backend
npm install

# 安装前端依赖
cd ../frontend
npm install

# 安装管理后台依赖
cd ../admin
npm install
  1. 配置环境变量
# 复制环境变量模板
cp backend/.env.example backend/.env
cp frontend/.env.example frontend/.env
cp admin/.env.example admin/.env

# 编辑环境变量文件
vim backend/.env
  1. 数据库初始化
# 创建数据库
mysql -u root -p -e "CREATE DATABASE jiebanke_dev CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"

# 运行数据库迁移
cd backend
npm run migrate

# 导入初始数据
npm run seed
  1. 启动服务
# 启动后端服务
cd backend
npm run dev

# 启动前端服务
cd ../frontend
npm run dev

# 启动管理后台
cd ../admin
npm run dev

开发环境配置文件

backend/.env

# 应用配置
NODE_ENV=development
PORT=3000
APP_NAME=解班客
APP_VERSION=1.0.0

# 数据库配置
DB_HOST=localhost
DB_PORT=3306
DB_NAME=jiebanke_dev
DB_USER=root
DB_PASSWORD=your_password

# Redis配置
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0

# JWT配置
JWT_SECRET=your_jwt_secret_key
JWT_EXPIRES_IN=7d
JWT_REFRESH_EXPIRES_IN=30d

# 文件上传配置
UPLOAD_PATH=./uploads
MAX_FILE_SIZE=10485760
ALLOWED_FILE_TYPES=jpg,jpeg,png,gif,pdf,doc,docx

# 邮件配置
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your_email@gmail.com
SMTP_PASS=your_app_password

# 日志配置
LOG_LEVEL=debug
LOG_FILE=logs/app.log

# 第三方服务
WECHAT_APP_ID=your_wechat_app_id
WECHAT_APP_SECRET=your_wechat_app_secret
ALIPAY_APP_ID=your_alipay_app_id
ALIPAY_PRIVATE_KEY=your_alipay_private_key

frontend/.env

# API配置
VITE_API_BASE_URL=http://localhost:3000/api/v1
VITE_UPLOAD_URL=http://localhost:3000/uploads

# 应用配置
VITE_APP_TITLE=解班客
VITE_APP_DESCRIPTION=宠物认领平台

# 第三方服务
VITE_WECHAT_APP_ID=your_wechat_app_id
VITE_MAP_API_KEY=your_map_api_key

测试环境

环境特点

  • 用途: 功能测试、集成测试、性能测试
  • 数据: 使用测试数据,定期重置
  • 配置: 接近生产环境,但资源配置较低
  • 访问: 内网访问需要VPN

配置差异

# backend/.env.test
NODE_ENV=test
DB_NAME=jiebanke_test
LOG_LEVEL=info
REDIS_DB=1

# 测试专用配置
TEST_MODE=true
MOCK_EXTERNAL_API=true
DISABLE_EMAIL=true

预生产环境

环境特点

  • 用途: 生产前最后验证
  • 数据: 生产数据副本或仿真数据
  • 配置: 与生产环境完全一致
  • 访问: 限制访问,仅核心团队

配置要求

  • 与生产环境相同的硬件配置
  • 相同的网络拓扑和安全配置
  • 完整的监控和日志系统
  • 自动化部署流程验证

生产环境

硬件要求

Web服务器

  • CPU: 4核心 2.4GHz+
  • 内存: 8GB+
  • 存储: 100GB SSD
  • 网络: 100Mbps+

数据库服务器

  • CPU: 8核心 2.4GHz+
  • 内存: 16GB+
  • 存储: 500GB SSD (RAID 1)
  • 网络: 1Gbps+

缓存服务器

  • CPU: 2核心 2.4GHz+
  • 内存: 4GB+
  • 存储: 50GB SSD
  • 网络: 100Mbps+

软件环境

  • 操作系统: Ubuntu 20.04 LTS
  • Node.js: 18.19.0 (LTS)
  • MySQL: 8.0.35
  • Redis: 6.2.14
  • Nginx: 1.18.0
  • Docker: 24.0.7
  • PM2: 5.3.0

生产环境配置

backend/.env.production

# 应用配置
NODE_ENV=production
PORT=3000
APP_NAME=解班客
APP_VERSION=1.0.0

# 数据库配置
DB_HOST=db.internal.jiebanke.com
DB_PORT=3306
DB_NAME=jiebanke_prod
DB_USER=jiebanke_user
DB_PASSWORD=complex_secure_password

# Redis配置
REDIS_HOST=redis.internal.jiebanke.com
REDIS_PORT=6379
REDIS_PASSWORD=redis_secure_password
REDIS_DB=0

# JWT配置
JWT_SECRET=very_complex_jwt_secret_key_for_production
JWT_EXPIRES_IN=24h
JWT_REFRESH_EXPIRES_IN=7d

# 文件上传配置
UPLOAD_PATH=/var/www/jiebanke/uploads
MAX_FILE_SIZE=10485760
ALLOWED_FILE_TYPES=jpg,jpeg,png,gif,pdf,doc,docx

# 邮件配置
SMTP_HOST=smtp.exmail.qq.com
SMTP_PORT=465
SMTP_SECURE=true
SMTP_USER=noreply@jiebanke.com
SMTP_PASS=email_secure_password

# 日志配置
LOG_LEVEL=info
LOG_FILE=/var/log/jiebanke/app.log

# 安全配置
CORS_ORIGIN=https://www.jiebanke.com,https://admin.jiebanke.com
RATE_LIMIT_WINDOW=15
RATE_LIMIT_MAX=100

# 监控配置
ENABLE_METRICS=true
METRICS_PORT=9090
HEALTH_CHECK_PATH=/health

# 第三方服务
WECHAT_APP_ID=production_wechat_app_id
WECHAT_APP_SECRET=production_wechat_app_secret
ALIPAY_APP_ID=production_alipay_app_id
ALIPAY_PRIVATE_KEY=production_alipay_private_key

容器化部署

Docker配置

后端Dockerfile

# backend/Dockerfile
FROM node:18-alpine AS builder

# 设置工作目录
WORKDIR /app

# 复制package文件
COPY package*.json ./

# 安装依赖
RUN npm ci --only=production && npm cache clean --force

# 复制源代码
COPY . .

# 构建应用
RUN npm run build

# 生产镜像
FROM node:18-alpine AS production

# 创建应用用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001

# 设置工作目录
WORKDIR /app

# 复制构建结果
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodejs:nodejs /app/package.json ./package.json

# 创建必要目录
RUN mkdir -p /app/logs /app/uploads && chown -R nodejs:nodejs /app

# 切换到应用用户
USER nodejs

# 暴露端口
EXPOSE 3000

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD node healthcheck.js

# 启动应用
CMD ["node", "dist/server.js"]

前端Dockerfile

# frontend/Dockerfile
FROM node:18-alpine AS builder

# 设置工作目录
WORKDIR /app

# 复制package文件
COPY package*.json ./

# 安装依赖
RUN npm ci

# 复制源代码
COPY . .

# 构建应用
RUN npm run build

# 生产镜像
FROM nginx:alpine AS production

# 复制构建结果
COPY --from=builder /app/dist /usr/share/nginx/html

# 复制nginx配置
COPY nginx.conf /etc/nginx/nginx.conf

# 暴露端口
EXPOSE 80

# 启动nginx
CMD ["nginx", "-g", "daemon off;"]

Docker Compose配置

# docker-compose.yml
version: '3.8'

services:
  # 数据库服务
  mysql:
    image: mysql:8.0
    container_name: jiebanke-mysql
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASSWORD}
    volumes:
      - mysql_data:/var/lib/mysql
      - ./mysql/init:/docker-entrypoint-initdb.d
      - ./mysql/conf:/etc/mysql/conf.d
    ports:
      - "3306:3306"
    networks:
      - jiebanke-network
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      timeout: 20s
      retries: 10

  # Redis服务
  redis:
    image: redis:6-alpine
    container_name: jiebanke-redis
    restart: unless-stopped
    command: redis-server --requirepass ${REDIS_PASSWORD}
    volumes:
      - redis_data:/data
      - ./redis/redis.conf:/usr/local/etc/redis/redis.conf
    ports:
      - "6379:6379"
    networks:
      - jiebanke-network
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      timeout: 3s
      retries: 5

  # 后端服务
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: jiebanke-backend
    restart: unless-stopped
    environment:
      - NODE_ENV=production
    env_file:
      - ./backend/.env.production
    volumes:
      - ./uploads:/app/uploads
      - ./logs:/app/logs
    ports:
      - "3000:3000"
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - jiebanke-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      timeout: 10s
      retries: 3

  # 前端服务
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    container_name: jiebanke-frontend
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/ssl:/etc/nginx/ssl
      - ./nginx/conf.d:/etc/nginx/conf.d
    depends_on:
      - backend
    networks:
      - jiebanke-network

  # 管理后台
  admin:
    build:
      context: ./admin
      dockerfile: Dockerfile
    container_name: jiebanke-admin
    restart: unless-stopped
    ports:
      - "8080:80"
    depends_on:
      - backend
    networks:
      - jiebanke-network

volumes:
  mysql_data:
  redis_data:

networks:
  jiebanke-network:
    driver: bridge

Kubernetes部署

命名空间配置

# k8s/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: jiebanke
  labels:
    name: jiebanke

ConfigMap配置

# k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: jiebanke-config
  namespace: jiebanke
data:
  NODE_ENV: "production"
  LOG_LEVEL: "info"
  CORS_ORIGIN: "https://www.jiebanke.com,https://admin.jiebanke.com"
  RATE_LIMIT_WINDOW: "15"
  RATE_LIMIT_MAX: "100"

Secret配置

# k8s/secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: jiebanke-secret
  namespace: jiebanke
type: Opaque
data:
  DB_PASSWORD: <base64-encoded-password>
  JWT_SECRET: <base64-encoded-jwt-secret>
  REDIS_PASSWORD: <base64-encoded-redis-password>

后端部署配置

# k8s/backend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: jiebanke-backend
  namespace: jiebanke
spec:
  replicas: 3
  selector:
    matchLabels:
      app: jiebanke-backend
  template:
    metadata:
      labels:
        app: jiebanke-backend
    spec:
      containers:
      - name: backend
        image: jiebanke/backend:latest
        ports:
        - containerPort: 3000
        env:
        - name: NODE_ENV
          valueFrom:
            configMapKeyRef:
              name: jiebanke-config
              key: NODE_ENV
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: jiebanke-secret
              key: DB_PASSWORD
        - name: JWT_SECRET
          valueFrom:
            secretKeyRef:
              name: jiebanke-secret
              key: JWT_SECRET
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 3000
          initialDelaySeconds: 5
          periodSeconds: 5
        volumeMounts:
        - name: uploads
          mountPath: /app/uploads
        - name: logs
          mountPath: /app/logs
      volumes:
      - name: uploads
        persistentVolumeClaim:
          claimName: jiebanke-uploads-pvc
      - name: logs
        persistentVolumeClaim:
          claimName: jiebanke-logs-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: jiebanke-backend-service
  namespace: jiebanke
spec:
  selector:
    app: jiebanke-backend
  ports:
  - protocol: TCP
    port: 80
    targetPort: 3000
  type: ClusterIP

Ingress配置

# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: jiebanke-ingress
  namespace: jiebanke
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "10m"
spec:
  tls:
  - hosts:
    - www.jiebanke.com
    - admin.jiebanke.com
    secretName: jiebanke-tls
  rules:
  - host: www.jiebanke.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: jiebanke-backend-service
            port:
              number: 80
      - path: /
        pathType: Prefix
        backend:
          service:
            name: jiebanke-frontend-service
            port:
              number: 80
  - host: admin.jiebanke.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: jiebanke-backend-service
            port:
              number: 80
      - path: /
        pathType: Prefix
        backend:
          service:
            name: jiebanke-admin-service
            port:
              number: 80

CI/CD流程

GitHub Actions配置

主工作流

# .github/workflows/main.yml
name: CI/CD Pipeline

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

env:
  NODE_VERSION: '18'
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  # 代码质量检查
  lint-and-test:
    runs-on: ubuntu-latest
    
    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_ROOT_PASSWORD: test_password
          MYSQL_DATABASE: jiebanke_test
        ports:
          - 3306:3306
        options: >-
          --health-cmd="mysqladmin ping"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=3
      
      redis:
        image: redis:6-alpine
        ports:
          - 6379:6379
        options: >-
          --health-cmd="redis-cli ping"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=3
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    
    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: ${{ env.NODE_VERSION }}
        cache: 'npm'
        cache-dependency-path: |
          backend/package-lock.json
          frontend/package-lock.json
          admin/package-lock.json
    
    - name: Install backend dependencies
      run: |
        cd backend
        npm ci
    
    - name: Install frontend dependencies
      run: |
        cd frontend
        npm ci
    
    - name: Install admin dependencies
      run: |
        cd admin
        npm ci
    
    - name: Run backend linting
      run: |
        cd backend
        npm run lint
    
    - name: Run frontend linting
      run: |
        cd frontend
        npm run lint
    
    - name: Run admin linting
      run: |
        cd admin
        npm run lint
    
    - name: Run backend tests
      run: |
        cd backend
        npm run test:coverage
      env:
        NODE_ENV: test
        DB_HOST: localhost
        DB_PORT: 3306
        DB_NAME: jiebanke_test
        DB_USER: root
        DB_PASSWORD: test_password
        REDIS_HOST: localhost
        REDIS_PORT: 6379
    
    - name: Run frontend tests
      run: |
        cd frontend
        npm run test:coverage
    
    - name: Upload coverage reports
      uses: codecov/codecov-action@v3
      with:
        files: |
          backend/coverage/lcov.info
          frontend/coverage/lcov.info
        flags: unittests
        name: codecov-umbrella

  # 安全扫描
  security-scan:
    runs-on: ubuntu-latest
    needs: lint-and-test
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    
    - name: Run Trivy vulnerability scanner
      uses: aquasecurity/trivy-action@master
      with:
        scan-type: 'fs'
        scan-ref: '.'
        format: 'sarif'
        output: 'trivy-results.sarif'
    
    - name: Upload Trivy scan results
      uses: github/codeql-action/upload-sarif@v2
      with:
        sarif_file: 'trivy-results.sarif'

  # 构建和推送镜像
  build-and-push:
    runs-on: ubuntu-latest
    needs: [lint-and-test, security-scan]
    if: github.ref == 'refs/heads/main'
    
    permissions:
      contents: read
      packages: write
    
    strategy:
      matrix:
        service: [backend, frontend, admin]
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    
    - name: Log in to Container Registry
      uses: docker/login-action@v3
      with:
        registry: ${{ env.REGISTRY }}
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}
    
    - name: Extract metadata
      id: meta
      uses: docker/metadata-action@v5
      with:
        images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.service }}
        tags: |
          type=ref,event=branch
          type=ref,event=pr
          type=sha,prefix={{branch}}-
          type=raw,value=latest,enable={{is_default_branch}}
    
    - name: Build and push Docker image
      uses: docker/build-push-action@v5
      with:
        context: ./${{ matrix.service }}
        push: true
        tags: ${{ steps.meta.outputs.tags }}
        labels: ${{ steps.meta.outputs.labels }}
        cache-from: type=gha
        cache-to: type=gha,mode=max

  # 部署到测试环境
  deploy-test:
    runs-on: ubuntu-latest
    needs: build-and-push
    if: github.ref == 'refs/heads/develop'
    environment: test
    
    steps:
    - name: Deploy to test environment
      run: |
        echo "Deploying to test environment..."
        # 这里添加部署到测试环境的脚本

  # 部署到生产环境
  deploy-prod:
    runs-on: ubuntu-latest
    needs: build-and-push
    if: github.ref == 'refs/heads/main'
    environment: production
    
    steps:
    - name: Deploy to production environment
      run: |
        echo "Deploying to production environment..."
        # 这里添加部署到生产环境的脚本

部署脚本

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

on:
  workflow_run:
    workflows: ["CI/CD Pipeline"]
    types:
      - completed
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    environment: production
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    
    - name: Setup kubectl
      uses: azure/setup-kubectl@v3
      with:
        version: 'v1.28.0'
    
    - name: Configure kubectl
      run: |
        echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > kubeconfig
        export KUBECONFIG=kubeconfig
    
    - name: Deploy to Kubernetes
      run: |
        export KUBECONFIG=kubeconfig
        
        # 更新镜像标签
        kubectl set image deployment/jiebanke-backend backend=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/backend:${{ github.sha }} -n jiebanke
        kubectl set image deployment/jiebanke-frontend frontend=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/frontend:${{ github.sha }} -n jiebanke
        kubectl set image deployment/jiebanke-admin admin=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/admin:${{ github.sha }} -n jiebanke
        
        # 等待部署完成
        kubectl rollout status deployment/jiebanke-backend -n jiebanke --timeout=300s
        kubectl rollout status deployment/jiebanke-frontend -n jiebanke --timeout=300s
        kubectl rollout status deployment/jiebanke-admin -n jiebanke --timeout=300s
    
    - name: Run smoke tests
      run: |
        # 运行冒烟测试
        curl -f https://www.jiebanke.com/health || exit 1
        curl -f https://admin.jiebanke.com/health || exit 1
    
    - name: Notify deployment success
      uses: 8398a7/action-slack@v3
      with:
        status: success
        text: '🎉 Production deployment successful!'
      env:
        SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

部署脚本

自动化部署脚本

#!/bin/bash
# scripts/deploy.sh

set -e

# 配置变量
ENVIRONMENT=${1:-production}
VERSION=${2:-latest}
COMPOSE_FILE="docker-compose.${ENVIRONMENT}.yml"

echo "🚀 开始部署到 ${ENVIRONMENT} 环境..."

# 检查环境
if [ ! -f "${COMPOSE_FILE}" ]; then
    echo "❌ 找不到配置文件: ${COMPOSE_FILE}"
    exit 1
fi

# 拉取最新镜像
echo "📦 拉取最新镜像..."
docker-compose -f ${COMPOSE_FILE} pull

# 停止旧服务
echo "🛑 停止旧服务..."
docker-compose -f ${COMPOSE_FILE} down

# 备份数据库
echo "💾 备份数据库..."
./scripts/backup-db.sh

# 启动新服务
echo "🔄 启动新服务..."
docker-compose -f ${COMPOSE_FILE} up -d

# 等待服务启动
echo "⏳ 等待服务启动..."
sleep 30

# 健康检查
echo "🔍 执行健康检查..."
./scripts/health-check.sh

# 运行数据库迁移
echo "🗄️ 运行数据库迁移..."
docker-compose -f ${COMPOSE_FILE} exec backend npm run migrate

echo "✅ 部署完成!"

# 发送通知
./scripts/notify-deployment.sh ${ENVIRONMENT} ${VERSION}

健康检查脚本

#!/bin/bash
# scripts/health-check.sh

set -e

BACKEND_URL="http://localhost:3000"
FRONTEND_URL="http://localhost:80"
ADMIN_URL="http://localhost:8080"

echo "🔍 开始健康检查..."

# 检查后端服务
echo "检查后端服务..."
if curl -f "${BACKEND_URL}/health" > /dev/null 2>&1; then
    echo "✅ 后端服务正常"
else
    echo "❌ 后端服务异常"
    exit 1
fi

# 检查前端服务
echo "检查前端服务..."
if curl -f "${FRONTEND_URL}" > /dev/null 2>&1; then
    echo "✅ 前端服务正常"
else
    echo "❌ 前端服务异常"
    exit 1
fi

# 检查管理后台
echo "检查管理后台..."
if curl -f "${ADMIN_URL}" > /dev/null 2>&1; then
    echo "✅ 管理后台正常"
else
    echo "❌ 管理后台异常"
    exit 1
fi

# 检查数据库连接
echo "检查数据库连接..."
if docker-compose exec backend npm run db:check > /dev/null 2>&1; then
    echo "✅ 数据库连接正常"
else
    echo "❌ 数据库连接异常"
    exit 1
fi

# 检查Redis连接
echo "检查Redis连接..."
if docker-compose exec backend npm run redis:check > /dev/null 2>&1; then
    echo "✅ Redis连接正常"
else
    echo "❌ Redis连接异常"
    exit 1
fi

echo "✅ 所有健康检查通过!"

数据库备份脚本

#!/bin/bash
# scripts/backup-db.sh

set -e

# 配置变量
DB_HOST=${DB_HOST:-localhost}
DB_PORT=${DB_PORT:-3306}
DB_NAME=${DB_NAME:-jiebanke_prod}
DB_USER=${DB_USER:-root}
DB_PASSWORD=${DB_PASSWORD}
BACKUP_DIR="/var/backups/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="${BACKUP_DIR}/${DB_NAME}_${DATE}.sql"

echo "💾 开始备份数据库..."

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

# 执行备份
mysqldump \
    --host=${DB_HOST} \
    --port=${DB_PORT} \
    --user=${DB_USER} \
    --password=${DB_PASSWORD} \
    --single-transaction \
    --routines \
    --triggers \
    --events \
    --hex-blob \
    ${DB_NAME} > ${BACKUP_FILE}

# 压缩备份文件
gzip ${BACKUP_FILE}

echo "✅ 数据库备份完成: ${BACKUP_FILE}.gz"

# 清理旧备份保留30天
find ${BACKUP_DIR} -name "*.sql.gz" -mtime +30 -delete

echo "🧹 清理旧备份完成"

监控和日志

Prometheus配置

Prometheus配置文件

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

rule_files:
  - "rules/*.yml"

alerting:
  alertmanagers:
    - static_configs:
        - targets:
          - alertmanager:9093

scrape_configs:
  # Prometheus自监控
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  # Node Exporter
  - job_name: 'node'
    static_configs:
      - targets: ['node-exporter:9100']

  # 应用监控
  - job_name: 'jiebanke-backend'
    static_configs:
      - targets: ['backend:9090']
    metrics_path: '/metrics'
    scrape_interval: 10s

  # MySQL监控
  - job_name: 'mysql'
    static_configs:
      - targets: ['mysql-exporter:9104']

  # Redis监控
  - job_name: 'redis'
    static_configs:
      - targets: ['redis-exporter:9121']

  # Nginx监控
  - job_name: 'nginx'
    static_configs:
      - targets: ['nginx-exporter:9113']

告警规则

# monitoring/rules/alerts.yml
groups:
- name: jiebanke.rules
  rules:
  # 服务可用性告警
  - alert: ServiceDown
    expr: up == 0
    for: 1m
    labels:
      severity: critical
    annotations:
      summary: "Service {{ $labels.job }} is down"
      description: "Service {{ $labels.job }} has been down for more than 1 minute."

  # 高错误率告警
  - alert: HighErrorRate
    expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.1
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "High error rate detected"
      description: "Error rate is {{ $value }} errors per second."

  # 高响应时间告警
  - alert: HighResponseTime
    expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 2
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "High response time detected"
      description: "95th percentile response time is {{ $value }} seconds."

  # 内存使用率告警
  - alert: HighMemoryUsage
    expr: (node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes > 0.8
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "High memory usage"
      description: "Memory usage is {{ $value | humanizePercentage }}."

  # CPU使用率告警
  - alert: HighCPUUsage
    expr: 100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "High CPU usage"
      description: "CPU usage is {{ $value }}%."

  # 磁盘空间告警
  - alert: DiskSpaceLow
    expr: (node_filesystem_avail_bytes / node_filesystem_size_bytes) < 0.1
    for: 1m
    labels:
      severity: critical
    annotations:
      summary: "Disk space low"
      description: "Disk space is {{ $value | humanizePercentage }} full."

  # 数据库连接告警
  - alert: DatabaseConnectionHigh
    expr: mysql_global_status_threads_connected / mysql_global_variables_max_connections > 0.8
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "High database connections"
      description: "Database connections are {{ $value | humanizePercentage }} of maximum."

Grafana仪表板

系统概览仪表板

{
  "dashboard": {
    "id": null,
    "title": "解班客系统概览",
    "tags": ["jiebanke"],
    "timezone": "browser",
    "panels": [
      {
        "id": 1,
        "title": "服务状态",
        "type": "stat",
        "targets": [
          {
            "expr": "up{job=~\"jiebanke-.*\"}",
            "legendFormat": "{{ job }}"
          }
        ],
        "fieldConfig": {
          "defaults": {
            "mappings": [
              {
                "options": {
                  "0": {
                    "text": "DOWN",
                    "color": "red"
                  },
                  "1": {
                    "text": "UP",
                    "color": "green"
                  }
                },
                "type": "value"
              }
            ]
          }
        }
      },
      {
        "id": 2,
        "title": "请求速率",
        "type": "graph",
        "targets": [
          {
            "expr": "rate(http_requests_total[5m])",
            "legendFormat": "{{ method }} {{ status }}"
          }
        ]
      },
      {
        "id": 3,
        "title": "响应时间",
        "type": "graph",
        "targets": [
          {
            "expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))",
            "legendFormat": "95th percentile"
          },
          {
            "expr": "histogram_quantile(0.50, rate(http_request_duration_seconds_bucket[5m]))",
            "legendFormat": "50th percentile"
          }
        ]
      },
      {
        "id": 4,
        "title": "系统资源",
        "type": "graph",
        "targets": [
          {
            "expr": "100 - (avg(irate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100)",
            "legendFormat": "CPU使用率"
          },
          {
            "expr": "(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100",
            "legendFormat": "内存使用率"
          }
        ]
      }
    ],
    "time": {
      "from": "now-1h",
      "to": "now"
    },
    "refresh": "5s"
  }
}

ELK Stack配置

Elasticsearch配置

# elk/elasticsearch.yml
cluster.name: jiebanke-logs
node.name: elasticsearch-1
network.host: 0.0.0.0
http.port: 9200
discovery.type: single-node

# 内存设置
bootstrap.memory_lock: true

# 索引设置
index.number_of_shards: 1
index.number_of_replicas: 0

# 安全设置
xpack.security.enabled: false

Logstash配置

# elk/logstash.conf
input {
  beats {
    port => 5044
  }
}

filter {
  if [fields][service] == "jiebanke-backend" {
    json {
      source => "message"
    }
    
    date {
      match => [ "timestamp", "ISO8601" ]
    }
    
    mutate {
      add_field => { "service" => "backend" }
    }
  }
  
  if [fields][service] == "nginx" {
    grok {
      match => { 
        "message" => "%{NGINXACCESS}" 
      }
    }
    
    date {
      match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
    }
    
    mutate {
      add_field => { "service" => "nginx" }
      convert => { "response" => "integer" }
      convert => { "bytes" => "integer" }
    }
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "jiebanke-logs-%{+YYYY.MM.dd}"
  }
  
  stdout {
    codec => rubydebug
  }
}

Filebeat配置

# elk/filebeat.yml
filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /var/log/jiebanke/*.log
  fields:
    service: jiebanke-backend
  fields_under_root: true
  multiline.pattern: '^\d{4}-\d{2}-\d{2}'
  multiline.negate: true
  multiline.match: after

- type: log
  enabled: true
  paths:
    - /var/log/nginx/access.log
  fields:
    service: nginx
  fields_under_root: true

output.logstash:
  hosts: ["logstash:5044"]

processors:
- add_host_metadata:
    when.not.contains.tags: forwarded

安全配置

SSL/TLS配置

Nginx SSL配置

# nginx/conf.d/ssl.conf
server {
    listen 443 ssl http2;
    server_name www.jiebanke.com;
    
    # SSL证书配置
    ssl_certificate /etc/nginx/ssl/jiebanke.com.crt;
    ssl_certificate_key /etc/nginx/ssl/jiebanke.com.key;
    
    # SSL安全配置
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    
    # HSTS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    
    # 其他安全头
    add_header X-Frame-Options DENY always;
    add_header X-Content-Type-Options nosniff always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    
    # CSP
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https:; media-src 'self'; object-src 'none'; child-src 'self'; frame-ancestors 'none'; form-action 'self'; base-uri 'self';" always;
    
    location / {
        root /usr/share/nginx/html;
        index index.html index.htm;
        try_files $uri $uri/ /index.html;
    }
    
    location /api {
        proxy_pass http://backend:3000;
        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_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        
        # 超时设置
        proxy_connect_timeout 30s;
        proxy_send_timeout 30s;
        proxy_read_timeout 30s;
    }
}

# HTTP重定向到HTTPS
server {
    listen 80;
    server_name www.jiebanke.com;
    return 301 https://$server_name$request_uri;
}

防火墙配置

UFW配置

#!/bin/bash
# scripts/setup-firewall.sh

# 重置防火墙规则
ufw --force reset

# 默认策略
ufw default deny incoming
ufw default allow outgoing

# SSH访问
ufw allow ssh

# HTTP/HTTPS
ufw allow 80/tcp
ufw allow 443/tcp

# 数据库(仅内网)
ufw allow from 10.0.0.0/8 to any port 3306
ufw allow from 172.16.0.0/12 to any port 3306
ufw allow from 192.168.0.0/16 to any port 3306

# Redis仅内网
ufw allow from 10.0.0.0/8 to any port 6379
ufw allow from 172.16.0.0/12 to any port 6379
ufw allow from 192.168.0.0/16 to any port 6379

# 监控端口(仅内网)
ufw allow from 10.0.0.0/8 to any port 9090
ufw allow from 172.16.0.0/12 to any port 9090
ufw allow from 192.168.0.0/16 to any port 9090

# 启用防火墙
ufw --force enable

echo "防火墙配置完成"

安全扫描

漏洞扫描脚本

#!/bin/bash
# scripts/security-scan.sh

echo "🔍 开始安全扫描..."

# 依赖漏洞扫描
echo "📦 扫描依赖漏洞..."
cd backend && npm audit --audit-level moderate
cd ../frontend && npm audit --audit-level moderate
cd ../admin && npm audit --audit-level moderate

# 容器镜像扫描
echo "🐳 扫描容器镜像..."
trivy image jiebanke/backend:latest
trivy image jiebanke/frontend:latest
trivy image jiebanke/admin:latest

# 文件系统扫描
echo "📁 扫描文件系统..."
trivy fs .

# 配置文件安全检查
echo "⚙️ 检查配置文件..."
# 检查是否有硬编码的密码
grep -r "password\|secret\|key" --include="*.js" --include="*.json" --include="*.yml" . | grep -v node_modules | grep -v ".git"

echo "✅ 安全扫描完成"

性能优化

数据库优化

MySQL配置优化

# mysql/conf/my.cnf
[mysqld]
# 基础配置
port = 3306
socket = /var/run/mysqld/mysqld.sock
pid-file = /var/run/mysqld/mysqld.pid
datadir = /var/lib/mysql

# 字符集
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

# 内存配置
innodb_buffer_pool_size = 1G
innodb_log_file_size = 256M
innodb_log_buffer_size = 16M
key_buffer_size = 256M
max_connections = 200
thread_cache_size = 50

# 查询缓存
query_cache_type = 1
query_cache_size = 128M
query_cache_limit = 2M

# 慢查询日志
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2

# 二进制日志
log-bin = mysql-bin
binlog_format = ROW
expire_logs_days = 7

# InnoDB配置
innodb_file_per_table = 1
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT

数据库索引优化

-- 用户表索引
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_status ON users(status);
CREATE INDEX idx_users_created_at ON users(created_at);

-- 动物表索引
CREATE INDEX idx_animals_status ON animals(status);
CREATE INDEX idx_animals_type ON animals(type);
CREATE INDEX idx_animals_location ON animals(location);
CREATE INDEX idx_animals_created_at ON animals(created_at);

-- 认领记录索引
CREATE INDEX idx_adoptions_user_id ON adoptions(user_id);
CREATE INDEX idx_adoptions_animal_id ON adoptions(animal_id);
CREATE INDEX idx_adoptions_status ON adoptions(status);
CREATE INDEX idx_adoptions_created_at ON adoptions(created_at);

-- 复合索引
CREATE INDEX idx_animals_status_type ON animals(status, type);
CREATE INDEX idx_adoptions_user_status ON adoptions(user_id, status);

缓存策略

Redis缓存配置

# redis/redis.conf
# 内存配置
maxmemory 2gb
maxmemory-policy allkeys-lru

# 持久化配置
save 900 1
save 300 10
save 60 10000

# AOF配置
appendonly yes
appendfsync everysec

# 网络配置
timeout 300
tcp-keepalive 300

# 安全配置
requirepass your_redis_password

应用层缓存策略

// utils/cache.js
const redis = require('redis');
const client = redis.createClient({
  host: process.env.REDIS_HOST,
  port: process.env.REDIS_PORT,
  password: process.env.REDIS_PASSWORD
});

class CacheManager {
  // 用户信息缓存
  async getUserCache(userId) {
    const key = `user:${userId}`;
    const cached = await client.get(key);
    
    if (cached) {
      return JSON.parse(cached);
    }
    
    const user = await User.findById(userId);
    if (user) {
      await client.setex(key, 3600, JSON.stringify(user)); // 1小时过期
    }
    
    return user;
  }
  
  // 动物列表缓存
  async getAnimalListCache(filters) {
    const key = `animals:${JSON.stringify(filters)}`;
    const cached = await client.get(key);
    
    if (cached) {
      return JSON.parse(cached);
    }
    
    const animals = await Animal.find(filters);
    await client.setex(key, 600, JSON.stringify(animals)); // 10分钟过期
    
    return animals;
  }
  
  // 清除用户相关缓存
  async clearUserCache(userId) {
    const keys = await client.keys(`user:${userId}*`);
    if (keys.length > 0) {
      await client.del(keys);
    }
  }
  
  // 清除动物相关缓存
  async clearAnimalCache() {
    const keys = await client.keys('animals:*');
    if (keys.length > 0) {
      await client.del(keys);
    }
  }
}

module.exports = new CacheManager();

CDN配置

静态资源CDN

// config/cdn.js
const CDN_CONFIG = {
  development: {
    baseUrl: 'http://localhost:3000',
    staticUrl: 'http://localhost:3000/static'
  },
  production: {
    baseUrl: 'https://cdn.jiebanke.com',
    staticUrl: 'https://static.jiebanke.com'
  }
};

function getCDNUrl(path) {
  const config = CDN_CONFIG[process.env.NODE_ENV] || CDN_CONFIG.development;
  return `${config.staticUrl}${path}`;
}

module.exports = {
  getCDNUrl,
  CDN_CONFIG
};

故障排除

常见问题

1. 服务启动失败

问题: 容器启动失败或服务无法访问 排查步骤:

# 查看容器状态
docker ps -a

# 查看容器日志
docker logs container_name

# 查看系统资源
docker stats

# 检查端口占用
netstat -tlnp | grep :3000

2. 数据库连接问题

问题: 应用无法连接数据库 排查步骤:

# 检查数据库服务状态
docker exec mysql mysqladmin ping

# 查看数据库日志
docker logs mysql

# 测试数据库连接
mysql -h localhost -u root -p -e "SELECT 1"

# 检查网络连接
docker network ls
docker network inspect network_name

3. 性能问题

问题: 应用响应缓慢 排查步骤:

# 查看系统负载
top
htop

# 查看内存使用
free -h

# 查看磁盘IO
iostat -x 1

# 查看网络连接
ss -tuln

# 分析慢查询
mysql -e "SELECT * FROM information_schema.processlist WHERE time > 10"

监控告警处理

告警响应流程

  1. 接收告警: 通过邮件、短信、钉钉等方式接收告警
  2. 初步评估: 评估告警严重程度和影响范围
  3. 快速响应: 根据告警类型执行相应的应急处理
  4. 问题定位: 使用监控工具和日志分析问题根因
  5. 问题修复: 实施修复方案并验证效果
  6. 总结改进: 记录问题处理过程并改进监控策略

常见告警处理

服务不可用告警

# 检查服务状态
kubectl get pods -n jiebanke

# 查看Pod日志
kubectl logs -f pod_name -n jiebanke

# 重启服务
kubectl rollout restart deployment/jiebanke-backend -n jiebanke

高内存使用告警

# 查看内存使用详情
kubectl top pods -n jiebanke

# 查看Pod资源限制
kubectl describe pod pod_name -n jiebanke

# 调整资源限制
kubectl patch deployment jiebanke-backend -p '{"spec":{"template":{"spec":{"containers":[{"name":"backend","resources":{"limits":{"memory":"1Gi"}}}]}}}}'

总结

本文档详细描述了解班客平台的系统集成和部署方案涵盖了从开发环境搭建到生产环境部署的完整流程。通过采用现代化的容器化技术、自动化CI/CD流程、完善的监控告警系统确保了系统的高可用性、可扩展性和可维护性。

关键特性包括:

  • 容器化部署: 使用Docker和Kubernetes实现标准化部署
  • 自动化CI/CD: 通过GitHub Actions实现自动化测试和部署
  • 完善监控: 使用Prometheus、Grafana和ELK Stack实现全方位监控
  • 安全防护: 实施多层安全防护措施
  • 性能优化: 通过缓存、CDN等技术提升系统性能

未来将继续完善部署流程,增加更多自动化工具和监控指标,为平台的稳定运行提供更强有力的保障。