# 系统集成和部署文档 ## 概述 本文档详细描述了解班客平台的系统集成方案和部署流程,包括开发环境搭建、生产环境部署、CI/CD流程、监控配置等内容。系统采用现代化的微服务架构,支持容器化部署和自动化运维。 ## 系统架构 ### 整体架构图 ```mermaid 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. **克隆项目** ```bash git clone https://github.com/your-org/jiebanke.git cd jiebanke ``` 2. **安装依赖** ```bash # 安装后端依赖 cd backend npm install # 安装前端依赖 cd ../frontend npm install # 安装管理后台依赖 cd ../admin npm install ``` 3. **配置环境变量** ```bash # 复制环境变量模板 cp backend/.env.example backend/.env cp frontend/.env.example frontend/.env cp admin/.env.example admin/.env # 编辑环境变量文件 vim backend/.env ``` 4. **数据库初始化** ```bash # 创建数据库 mysql -u root -p -e "CREATE DATABASE jiebanke_dev CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" # 运行数据库迁移 cd backend npm run migrate # 导入初始数据 npm run seed ``` 5. **启动服务** ```bash # 启动后端服务 cd backend npm run dev # 启动前端服务 cd ../frontend npm run dev # 启动管理后台 cd ../admin npm run dev ``` #### 开发环境配置文件 **backend/.env** ```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** ```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 #### 配置差异 ```env # 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** ```env # 应用配置 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 ```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 ```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配置 ```yaml # 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部署 #### 命名空间配置 ```yaml # k8s/namespace.yaml apiVersion: v1 kind: Namespace metadata: name: jiebanke labels: name: jiebanke ``` #### ConfigMap配置 ```yaml # 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配置 ```yaml # k8s/secret.yaml apiVersion: v1 kind: Secret metadata: name: jiebanke-secret namespace: jiebanke type: Opaque data: DB_PASSWORD: JWT_SECRET: REDIS_PASSWORD: ``` #### 后端部署配置 ```yaml # 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配置 ```yaml # 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配置 #### 主工作流 ```yaml # .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..." # 这里添加部署到生产环境的脚本 ``` #### 部署脚本 ```yaml # .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 }} ``` ### 部署脚本 #### 自动化部署脚本 ```bash #!/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} ``` #### 健康检查脚本 ```bash #!/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 "✅ 所有健康检查通过!" ``` #### 数据库备份脚本 ```bash #!/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配置文件 ```yaml # 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'] ``` #### 告警规则 ```yaml # 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仪表板 #### 系统概览仪表板 ```json { "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配置 ```yaml # 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配置 ```ruby # 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配置 ```yaml # 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 # 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配置 ```bash #!/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 "防火墙配置完成" ``` ### 安全扫描 #### 漏洞扫描脚本 ```bash #!/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配置优化 ```ini # 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 ``` #### 数据库索引优化 ```sql -- 用户表索引 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/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 ``` #### 应用层缓存策略 ```javascript // 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 ```javascript // 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. 服务启动失败 **问题**: 容器启动失败或服务无法访问 **排查步骤**: ```bash # 查看容器状态 docker ps -a # 查看容器日志 docker logs container_name # 查看系统资源 docker stats # 检查端口占用 netstat -tlnp | grep :3000 ``` #### 2. 数据库连接问题 **问题**: 应用无法连接数据库 **排查步骤**: ```bash # 检查数据库服务状态 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. 性能问题 **问题**: 应用响应缓慢 **排查步骤**: ```bash # 查看系统负载 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. **总结改进**: 记录问题处理过程并改进监控策略 #### 常见告警处理 **服务不可用告警** ```bash # 检查服务状态 kubectl get pods -n jiebanke # 查看Pod日志 kubectl logs -f pod_name -n jiebanke # 重启服务 kubectl rollout restart deployment/jiebanke-backend -n jiebanke ``` **高内存使用告警** ```bash # 查看内存使用详情 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等技术提升系统性能 未来将继续完善部署流程,增加更多自动化工具和监控指标,为平台的稳定运行提供更强有力的保障。