# 部署文档 ## 版本历史 | 版本 | 日期 | 作者 | 变更说明 | |------|------|------|----------| | 1.0 | 2024-01-20 | 运维团队 | 初始版本 | | 1.1 | 2024-09-21 | 运维团队 | 更新部署架构,简化技术栈 | ## 1. 部署概述 ### 1.1 部署架构 ```mermaid graph TB subgraph "负载均衡层" LB[Nginx负载均衡器] end subgraph "应用层" WEB1[Web服务器1] WEB2[Web服务器2] API1[API服务器1] API2[API服务器2] end subgraph "数据层" DB1[(MySQL主库)] DB2[(MySQL从库)] REDIS[(Redis集群)] MONGO[(MongoDB)] end subgraph "文件存储" OSS[对象存储] end LB --> WEB1 LB --> WEB2 LB --> API1 LB --> API2 WEB1 --> DB1 WEB2 --> DB1 API1 --> DB1 API2 --> DB1 DB1 --> DB2 API1 --> REDIS API2 --> REDIS API1 --> MONGO API2 --> MONGO API1 --> OSS API2 --> OSS ``` ### 1.2 部署环境 | 环境 | 用途 | 服务器配置 | 域名 | |------|------|------------|------| | 开发环境 | 开发测试 | 2核4G | dev.xlxumu.com | | 测试环境 | 功能测试 | 4核8G | test.xlxumu.com | | 预生产环境 | 生产前验证 | 8核16G | pre.xlxumu.com | | 生产环境 | 正式运行 | 16核32G | www.xlxumu.com | ### 1.3 技术栈版本 | 组件 | 版本 | 说明 | |------|------|------| | Node.js | 18.x | 后端运行时 | | MySQL | 8.0 | 主数据库 | | Redis | 6.x | 缓存数据库 | | MongoDB | 5.x | 文档数据库 | | Nginx | 1.20+ | Web服务器 | | Docker | 20.x | 容器化 | | Docker Compose | 2.x | 容器编排 | ## 2. 服务器环境准备 ### 2.1 系统要求 ```bash # CentOS 7/8 系统配置 # 1. 更新系统 sudo yum update -y # 2. 安装基础工具 sudo yum install -y wget curl git vim htop # 3. 配置防火墙 sudo firewall-cmd --permanent --add-port=80/tcp sudo firewall-cmd --permanent --add-port=443/tcp sudo firewall-cmd --permanent --add-port=3000/tcp sudo firewall-cmd --reload # 4. 设置时区 sudo timedatectl set-timezone Asia/Shanghai # 5. 配置系统限制 echo "* soft nofile 65536" >> /etc/security/limits.conf echo "* hard nofile 65536" >> /etc/security/limits.conf ``` ### 2.2 Docker环境安装 ```bash #!/bin/bash # install-docker.sh # 卸载旧版本 sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine # 安装依赖 sudo yum install -y yum-utils device-mapper-persistent-data lvm2 # 添加Docker仓库 sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 安装Docker CE sudo yum install -y docker-ce docker-ce-cli containerd.io # 启动Docker服务 sudo systemctl start docker sudo systemctl enable docker # 安装Docker Compose sudo curl -L "https://github.com/docker/compose/releases/download/v2.15.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose # 验证安装 docker --version docker-compose --version # 配置Docker镜像加速 sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": [ "https://mirror.ccs.tencentyun.com", "https://docker.mirrors.ustc.edu.cn" ], "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" } } EOF sudo systemctl daemon-reload sudo systemctl restart docker ``` ### 2.3 SSL证书配置 ```bash #!/bin/bash # setup-ssl.sh # 安装Certbot sudo yum install -y epel-release sudo yum install -y certbot python3-certbot-nginx # 申请SSL证书 sudo certbot --nginx -d www.xlxumu.com -d api.xlxumu.com # 设置自动续期 echo "0 12 * * * /usr/bin/certbot renew --quiet" | sudo crontab - ``` ## 3. 数据库部署 ### 3.1 MySQL部署配置 ```yaml # docker-compose.mysql.yml version: '3.8' services: mysql-master: image: mysql:8.0 container_name: mysql-master restart: always environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: xlxumu_db MYSQL_USER: xlxumu_user MYSQL_PASSWORD: ${MYSQL_PASSWORD} ports: - "3306:3306" volumes: - mysql_master_data:/var/lib/mysql - ./mysql/conf/master.cnf:/etc/mysql/conf.d/master.cnf - ./mysql/init:/docker-entrypoint-initdb.d command: --default-authentication-plugin=mysql_native_password networks: - xlxumu_network mysql-slave: image: mysql:8.0 container_name: mysql-slave restart: always environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: xlxumu_db MYSQL_USER: xlxumu_user MYSQL_PASSWORD: ${MYSQL_PASSWORD} ports: - "3307:3306" volumes: - mysql_slave_data:/var/lib/mysql - ./mysql/conf/slave.cnf:/etc/mysql/conf.d/slave.cnf command: --default-authentication-plugin=mysql_native_password depends_on: - mysql-master networks: - xlxumu_network volumes: mysql_master_data: mysql_slave_data: networks: xlxumu_network: external: true ``` #### MySQL主从配置 ```ini # mysql/conf/master.cnf [mysqld] server-id = 1 log-bin = mysql-bin binlog-format = ROW binlog-do-db = xlxumu_db expire_logs_days = 7 max_binlog_size = 100M # 性能优化 innodb_buffer_pool_size = 1G innodb_log_file_size = 256M innodb_flush_log_at_trx_commit = 2 sync_binlog = 0 ``` ```ini # mysql/conf/slave.cnf [mysqld] server-id = 2 relay-log = mysql-relay-bin log-bin = mysql-bin binlog-format = ROW replicate-do-db = xlxumu_db read_only = 1 ``` #### 数据库初始化脚本 ```sql -- mysql/init/01-init-database.sql CREATE DATABASE IF NOT EXISTS xlxumu_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE xlxumu_db; -- 创建用户表 CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL, email VARCHAR(100) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, role ENUM('admin', 'farmer', 'trader') NOT NULL, status ENUM('active', 'inactive', 'suspended') DEFAULT 'active', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_username (username), INDEX idx_email (email), INDEX idx_role (role) ); -- 创建养殖场表 CREATE TABLE farms ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, owner_id INT NOT NULL, location VARCHAR(200), area DECIMAL(10,2), description TEXT, status ENUM('active', 'inactive') DEFAULT 'active', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (owner_id) REFERENCES users(id), INDEX idx_owner (owner_id), INDEX idx_status (status) ); -- 创建动物表 CREATE TABLE animals ( id INT PRIMARY KEY AUTO_INCREMENT, farm_id INT NOT NULL, tag_number VARCHAR(50) UNIQUE NOT NULL, breed VARCHAR(50), birth_date DATE, gender ENUM('male', 'female'), weight DECIMAL(8,2), health_status ENUM('healthy', 'sick', 'quarantine', 'deceased') DEFAULT 'healthy', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (farm_id) REFERENCES farms(id), INDEX idx_farm (farm_id), INDEX idx_tag (tag_number), INDEX idx_breed (breed), INDEX idx_health_status (health_status) ); -- 创建交易表 CREATE TABLE transactions ( id INT PRIMARY KEY AUTO_INCREMENT, seller_id INT NOT NULL, buyer_id INT, animal_id INT NOT NULL, price DECIMAL(10,2) NOT NULL, status ENUM('pending', 'completed', 'cancelled') DEFAULT 'pending', transaction_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (seller_id) REFERENCES users(id), FOREIGN KEY (buyer_id) REFERENCES users(id), FOREIGN KEY (animal_id) REFERENCES animals(id), INDEX idx_seller (seller_id), INDEX idx_buyer (buyer_id), INDEX idx_animal (animal_id), INDEX idx_status (status) ); ``` ### 3.2 Redis集群部署 ```yaml # docker-compose.redis.yml version: '3.8' services: redis-master: image: redis:6-alpine container_name: redis-master restart: always ports: - "6379:6379" volumes: - redis_master_data:/data - ./redis/redis-master.conf:/usr/local/etc/redis/redis.conf command: redis-server /usr/local/etc/redis/redis.conf networks: - xlxumu_network redis-slave: image: redis:6-alpine container_name: redis-slave restart: always ports: - "6380:6379" volumes: - redis_slave_data:/data - ./redis/redis-slave.conf:/usr/local/etc/redis/redis.conf command: redis-server /usr/local/etc/redis/redis.conf depends_on: - redis-master networks: - xlxumu_network redis-sentinel: image: redis:6-alpine container_name: redis-sentinel restart: always ports: - "26379:26379" volumes: - ./redis/sentinel.conf:/usr/local/etc/redis/sentinel.conf command: redis-sentinel /usr/local/etc/redis/sentinel.conf depends_on: - redis-master - redis-slave networks: - xlxumu_network volumes: redis_master_data: redis_slave_data: networks: xlxumu_network: external: true ``` #### Redis配置文件 ```conf # redis/redis-master.conf bind 0.0.0.0 port 6379 requirepass your_redis_password masterauth your_redis_password # 持久化配置 save 900 1 save 300 10 save 60 10000 # 内存配置 maxmemory 1gb maxmemory-policy allkeys-lru # 日志配置 loglevel notice logfile /var/log/redis/redis-server.log ``` ```conf # redis/redis-slave.conf bind 0.0.0.0 port 6379 requirepass your_redis_password masterauth your_redis_password # 主从配置 replicaof redis-master 6379 replica-read-only yes # 持久化配置 save 900 1 save 300 10 save 60 10000 # 内存配置 maxmemory 1gb maxmemory-policy allkeys-lru ``` ### 3.3 MongoDB部署 ```yaml # docker-compose.mongodb.yml version: '3.8' services: mongodb: image: mongo:5 container_name: mongodb restart: always environment: MONGO_INITDB_ROOT_USERNAME: ${MONGO_ROOT_USERNAME} MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD} MONGO_INITDB_DATABASE: xlxumu_logs ports: - "27017:27017" volumes: - mongodb_data:/data/db - ./mongodb/init:/docker-entrypoint-initdb.d networks: - xlxumu_network volumes: mongodb_data: networks: xlxumu_network: external: true ``` ## 4. 应用部署 ### 4.1 后端API服务部署 ```dockerfile # backend/Dockerfile FROM node:18-alpine # 设置工作目录 WORKDIR /app # 复制package文件 COPY package*.json ./ # 安装依赖 RUN npm ci --only=production # 复制源代码 COPY . . # 创建非root用户 RUN addgroup -g 1001 -S nodejs RUN adduser -S nodejs -u 1001 # 更改文件所有者 RUN chown -R nodejs:nodejs /app USER nodejs # 暴露端口 EXPOSE 3000 # 健康检查 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:3000/health || exit 1 # 启动应用 CMD ["npm", "start"] ``` ```yaml # docker-compose.backend.yml version: '3.8' services: backend-api-1: build: context: ./backend dockerfile: Dockerfile container_name: backend-api-1 restart: always environment: NODE_ENV: production PORT: 3000 DATABASE_URL: mysql://xlxumu_user:${MYSQL_PASSWORD}@mysql-master:3306/xlxumu_db REDIS_URL: redis://:${REDIS_PASSWORD}@redis-master:6379 MONGODB_URL: mongodb://${MONGO_ROOT_USERNAME}:${MONGO_ROOT_PASSWORD}@mongodb:27017/xlxumu_logs JWT_SECRET: ${JWT_SECRET} ports: - "3001:3000" volumes: - ./logs:/app/logs depends_on: - mysql-master - redis-master - mongodb networks: - xlxumu_network healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 30s timeout: 10s retries: 3 backend-api-2: build: context: ./backend dockerfile: Dockerfile container_name: backend-api-2 restart: always environment: NODE_ENV: production PORT: 3000 DATABASE_URL: mysql://xlxumu_user:${MYSQL_PASSWORD}@mysql-master:3306/xlxumu_db REDIS_URL: redis://:${REDIS_PASSWORD}@redis-master:6379 MONGODB_URL: mongodb://${MONGO_ROOT_USERNAME}:${MONGO_ROOT_PASSWORD}@mongodb:27017/xlxumu_logs JWT_SECRET: ${JWT_SECRET} ports: - "3002:3000" volumes: - ./logs:/app/logs depends_on: - mysql-master - redis-master - mongodb networks: - xlxumu_network healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 30s timeout: 10s retries: 3 networks: xlxumu_network: external: true ``` ### 4.2 前端应用部署 ```dockerfile # admin-system/Dockerfile FROM node:18-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM nginx:alpine # 复制构建产物 COPY --from=builder /app/dist /usr/share/nginx/html # 复制nginx配置 COPY nginx.conf /etc/nginx/nginx.conf # 暴露端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"] ``` ```nginx # admin-system/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_min_length 1024; gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json; server { listen 80; server_name _; root /usr/share/nginx/html; index index.html; # 处理SPA路由 location / { try_files $uri $uri/ /index.html; } # 静态资源缓存 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { expires 1y; add_header Cache-Control "public, immutable"; } # 安全头 add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; } } ``` ### 4.3 Nginx负载均衡配置 ```nginx # 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; use epoll; multi_accept on; } 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" ' '$request_time $upstream_response_time'; access_log /var/log/nginx/access.log main; # 基础配置 sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; client_max_body_size 50M; # Gzip压缩 gzip on; gzip_vary on; gzip_min_length 1024; gzip_comp_level 6; gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json; # 上游服务器配置 upstream backend_api { least_conn; server backend-api-1:3000 max_fails=3 fail_timeout=30s; server backend-api-2:3000 max_fails=3 fail_timeout=30s; keepalive 32; } upstream admin_web { server admin-web-1:80 max_fails=3 fail_timeout=30s; server admin-web-2:80 max_fails=3 fail_timeout=30s; } # 限流配置 limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s; # API服务器配置 server { listen 80; server_name api.xlxumu.com; # 重定向到HTTPS return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name api.xlxumu.com; # SSL配置 ssl_certificate /etc/letsencrypt/live/api.xlxumu.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/api.xlxumu.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; # API代理 location /api/ { limit_req zone=api burst=20 nodelay; proxy_pass http://backend_api; 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 5s; proxy_send_timeout 60s; proxy_read_timeout 60s; # 缓冲配置 proxy_buffering on; proxy_buffer_size 4k; proxy_buffers 8 4k; } # 登录接口特殊限流 location /api/auth/login { limit_req zone=login burst=5 nodelay; proxy_pass http://backend_api; 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; } # 健康检查 location /health { access_log off; return 200 "healthy\n"; add_header Content-Type text/plain; } } # 管理后台配置 server { listen 80; server_name admin.xlxumu.com; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name admin.xlxumu.com; # SSL配置 ssl_certificate /etc/letsencrypt/live/admin.xlxumu.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/admin.xlxumu.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; # 静态文件代理 location / { proxy_pass http://admin_web; 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; } # 静态资源缓存 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { proxy_pass http://admin_web; expires 1y; add_header Cache-Control "public, immutable"; } } } ``` ## 5. 部署脚本 ### 5.1 一键部署脚本 ```bash #!/bin/bash # deploy.sh - 一键部署脚本 set -e # 配置变量 PROJECT_NAME="xlxumu" DEPLOY_ENV=${1:-production} BACKUP_DIR="/backup" LOG_FILE="/var/log/deploy.log" # 颜色输出 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # 日志函数 log() { echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}" | tee -a $LOG_FILE } error() { echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1${NC}" | tee -a $LOG_FILE exit 1 } warn() { echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING: $1${NC}" | tee -a $LOG_FILE } # 检查环境 check_environment() { log "检查部署环境..." # 检查Docker if ! command -v docker &> /dev/null; then error "Docker未安装" fi # 检查Docker Compose if ! command -v docker-compose &> /dev/null; then error "Docker Compose未安装" fi # 检查环境变量文件 if [ ! -f ".env.${DEPLOY_ENV}" ]; then error "环境变量文件 .env.${DEPLOY_ENV} 不存在" fi log "环境检查完成" } # 备份数据 backup_data() { log "开始数据备份..." BACKUP_DATE=$(date +%Y%m%d_%H%M%S) BACKUP_PATH="${BACKUP_DIR}/${PROJECT_NAME}_${BACKUP_DATE}" mkdir -p $BACKUP_PATH # 备份数据库 if docker ps | grep -q mysql-master; then log "备份MySQL数据库..." docker exec mysql-master mysqldump -u root -p${MYSQL_ROOT_PASSWORD} --all-databases > ${BACKUP_PATH}/mysql_backup.sql fi # 备份Redis数据 if docker ps | grep -q redis-master; then log "备份Redis数据..." docker exec redis-master redis-cli --rdb ${BACKUP_PATH}/redis_backup.rdb fi # 备份应用数据 if [ -d "./data" ]; then log "备份应用数据..." cp -r ./data ${BACKUP_PATH}/ fi log "数据备份完成: $BACKUP_PATH" } # 拉取最新代码 pull_code() { log "拉取最新代码..." git fetch origin git checkout main git pull origin main log "代码更新完成" } # 构建镜像 build_images() { log "构建Docker镜像..." # 复制环境变量文件 cp .env.${DEPLOY_ENV} .env # 构建后端镜像 log "构建后端API镜像..." docker build -t ${PROJECT_NAME}/backend:latest ./backend # 构建管理后台镜像 log "构建管理后台镜像..." docker build -t ${PROJECT_NAME}/admin:latest ./admin-system log "镜像构建完成" } # 停止旧服务 stop_services() { log "停止旧服务..." if [ -f "docker-compose.yml" ]; then docker-compose down fi log "旧服务已停止" } # 启动新服务 start_services() { log "启动新服务..." # 创建网络 docker network create xlxumu_network 2>/dev/null || true # 启动数据库服务 log "启动数据库服务..." docker-compose -f docker-compose.mysql.yml up -d docker-compose -f docker-compose.redis.yml up -d docker-compose -f docker-compose.mongodb.yml up -d # 等待数据库启动 log "等待数据库启动..." sleep 30 # 启动应用服务 log "启动应用服务..." docker-compose -f docker-compose.backend.yml up -d docker-compose -f docker-compose.frontend.yml up -d # 启动Nginx log "启动Nginx..." docker-compose -f docker-compose.nginx.yml up -d log "服务启动完成" } # 健康检查 health_check() { log "执行健康检查..." # 检查API服务 for i in {1..30}; do if curl -f http://localhost:3001/health &>/dev/null; then log "API服务1健康检查通过" break fi if [ $i -eq 30 ]; then error "API服务1健康检查失败" fi sleep 2 done for i in {1..30}; do if curl -f http://localhost:3002/health &>/dev/null; then log "API服务2健康检查通过" break fi if [ $i -eq 30 ]; then error "API服务2健康检查失败" fi sleep 2 done # 检查前端服务 for i in {1..30}; do if curl -f http://localhost:80 &>/dev/null; then log "前端服务健康检查通过" break fi if [ $i -eq 30 ]; then error "前端服务健康检查失败" fi sleep 2 done log "健康检查完成" } # 清理旧镜像 cleanup() { log "清理旧镜像..." # 删除未使用的镜像 docker image prune -f # 删除未使用的容器 docker container prune -f log "清理完成" } # 发送通知 send_notification() { log "发送部署通知..." # 这里可以集成钉钉、企业微信等通知 # curl -X POST "https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN" \ # -H 'Content-Type: application/json' \ # -d '{"msgtype": "text","text": {"content": "部署完成"}}' log "部署通知已发送" } # 主函数 main() { log "开始部署 ${PROJECT_NAME} 到 ${DEPLOY_ENV} 环境" check_environment backup_data pull_code build_images stop_services start_services health_check cleanup send_notification log "部署完成!" } # 错误处理 trap 'error "部署过程中发生错误"' ERR # 执行主函数 main "$@" ``` ### 5.2 回滚脚本 ```bash #!/bin/bash # rollback.sh - 回滚脚本 set -e BACKUP_DIR="/backup" PROJECT_NAME="xlxumu" # 颜色输出 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' log() { echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}" } error() { echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1${NC}" exit 1 } # 列出可用备份 list_backups() { log "可用备份列表:" ls -la ${BACKUP_DIR}/${PROJECT_NAME}_* 2>/dev/null || error "没有找到备份文件" } # 回滚到指定备份 rollback_to_backup() { local backup_name=$1 local backup_path="${BACKUP_DIR}/${backup_name}" if [ ! -d "$backup_path" ]; then error "备份目录不存在: $backup_path" fi log "开始回滚到备份: $backup_name" # 停止当前服务 log "停止当前服务..." docker-compose down # 恢复数据库 if [ -f "${backup_path}/mysql_backup.sql" ]; then log "恢复MySQL数据库..." docker-compose -f docker-compose.mysql.yml up -d mysql-master sleep 30 docker exec -i mysql-master mysql -u root -p${MYSQL_ROOT_PASSWORD} < ${backup_path}/mysql_backup.sql fi # 恢复Redis数据 if [ -f "${backup_path}/redis_backup.rdb" ]; then log "恢复Redis数据..." docker cp ${backup_path}/redis_backup.rdb redis-master:/data/dump.rdb docker restart redis-master fi # 恢复应用数据 if [ -d "${backup_path}/data" ]; then log "恢复应用数据..." rm -rf ./data cp -r ${backup_path}/data ./ fi # 重启服务 log "重启服务..." docker-compose up -d log "回滚完成" } # 主函数 main() { if [ $# -eq 0 ]; then list_backups echo "使用方法: $0 " exit 1 fi rollback_to_backup $1 } main "$@" ``` ### 5.3 监控脚本 ```bash #!/bin/bash # monitor.sh - 服务监控脚本 # 配置 SERVICES=("mysql-master" "redis-master" "mongodb" "backend-api-1" "backend-api-2" "nginx") LOG_FILE="/var/log/monitor.log" ALERT_EMAIL="admin@xlxumu.com" # 检查服务状态 check_service() { local service=$1 if docker ps --format "table {{.Names}}" | grep -q "^${service}$"; then echo "✅ $service 运行正常" return 0 else echo "❌ $service 服务异常" return 1 fi } # 检查服务健康状态 check_health() { local service=$1 case $service in "backend-api-1") curl -f http://localhost:3001/health &>/dev/null ;; "backend-api-2") curl -f http://localhost:3002/health &>/dev/null ;; "nginx") curl -f http://localhost:80/health &>/dev/null ;; *) return 0 ;; esac } # 检查系统资源 check_resources() { echo "=== 系统资源检查 ===" # CPU使用率 cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | awk -F'%' '{print $1}') echo "CPU使用率: ${cpu_usage}%" # 内存使用率 mem_usage=$(free | grep Mem | awk '{printf("%.2f"), $3/$2 * 100.0}') echo "内存使用率: ${mem_usage}%" # 磁盘使用率 disk_usage=$(df -h / | awk 'NR==2 {print $5}') echo "磁盘使用率: $disk_usage" # 检查阈值 if (( $(echo "$cpu_usage > 80" | bc -l) )); then echo "⚠️ CPU使用率过高: ${cpu_usage}%" fi if (( $(echo "$mem_usage > 80" | bc -l) )); then echo "⚠️ 内存使用率过高: ${mem_usage}%" fi } # 发送告警 send_alert() { local message=$1 # 发送邮件告警 echo "$message" | mail -s "服务告警" $ALERT_EMAIL # 记录日志 echo "[$(date)] ALERT: $message" >> $LOG_FILE } # 主监控循环 main() { echo "=== 服务监控开始 $(date) ===" failed_services=() # 检查所有服务 for service in "${SERVICES[@]}"; do if ! check_service $service; then failed_services+=($service) elif ! check_health $service; then echo "⚠️ $service 健康检查失败" failed_services+=($service) fi done # 检查系统资源 check_resources # 处理失败的服务 if [ ${#failed_services[@]} -gt 0 ]; then alert_message="以下服务异常: ${failed_services[*]}" echo "$alert_message" send_alert "$alert_message" else echo "✅ 所有服务运行正常" fi echo "=== 监控完成 $(date) ===" } # 如果作为定时任务运行 if [ "$1" = "cron" ]; then main >> $LOG_FILE 2>&1 else main fi ``` ## 6. 环境配置 ### 6.1 环境变量配置 ```bash # .env.production # 数据库配置 MYSQL_ROOT_PASSWORD=your_mysql_root_password MYSQL_PASSWORD=your_mysql_password REDIS_PASSWORD=your_redis_password MONGO_ROOT_USERNAME=admin MONGO_ROOT_PASSWORD=your_mongo_password # 应用配置 NODE_ENV=production JWT_SECRET=your_jwt_secret_key API_BASE_URL=https://api.xlxumu.com # 第三方服务 ALIYUN_ACCESS_KEY_ID=your_aliyun_access_key ALIYUN_ACCESS_KEY_SECRET=your_aliyun_secret_key WECHAT_APP_ID=your_wechat_app_id WECHAT_APP_SECRET=your_wechat_app_secret # 监控配置 SENTRY_DSN=your_sentry_dsn LOG_LEVEL=info ``` ```bash # .env.staging # 测试环境配置 MYSQL_ROOT_PASSWORD=staging_mysql_password MYSQL_PASSWORD=staging_mysql_password REDIS_PASSWORD=staging_redis_password MONGO_ROOT_USERNAME=admin MONGO_ROOT_PASSWORD=staging_mongo_password NODE_ENV=staging JWT_SECRET=staging_jwt_secret API_BASE_URL=https://test-api.xlxumu.com LOG_LEVEL=debug ``` ### 6.2 Docker Compose主配置 ```yaml # docker-compose.yml version: '3.8' services: # 数据库服务 mysql-master: extends: file: docker-compose.mysql.yml service: mysql-master redis-master: extends: file: docker-compose.redis.yml service: redis-master mongodb: extends: file: docker-compose.mongodb.yml service: mongodb # 应用服务 backend-api-1: extends: file: docker-compose.backend.yml service: backend-api-1 backend-api-2: extends: file: docker-compose.backend.yml service: backend-api-2 # 前端服务 admin-web: build: context: ./admin-system dockerfile: Dockerfile container_name: admin-web restart: always ports: - "8080:80" networks: - xlxumu_network # 负载均衡 nginx: image: nginx:alpine container_name: nginx-lb restart: always ports: - "80:80" - "443:443" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf - ./nginx/ssl:/etc/nginx/ssl - /etc/letsencrypt:/etc/letsencrypt depends_on: - backend-api-1 - backend-api-2 - admin-web networks: - xlxumu_network networks: xlxumu_network: driver: bridge ``` ## 7. 部署流程 ### 7.1 首次部署流程 ```bash # 1. 准备服务器环境 ./scripts/setup-server.sh # 2. 克隆项目代码 git clone https://github.com/your-org/xlxumu.git cd xlxumu # 3. 配置环境变量 cp .env.example .env.production vim .env.production # 4. 执行部署 ./scripts/deploy.sh production # 5. 验证部署 ./scripts/health-check.sh ``` ### 7.2 更新部署流程 ```bash # 1. 备份当前数据 ./scripts/backup.sh # 2. 拉取最新代码 git pull origin main # 3. 执行部署 ./scripts/deploy.sh production # 4. 验证部署 ./scripts/health-check.sh # 5. 如有问题,执行回滚 # ./scripts/rollback.sh backup_20240120_143000 ``` ### 7.3 蓝绿部署流程 ```bash #!/bin/bash # blue-green-deploy.sh CURRENT_ENV=$(docker ps --format "table {{.Names}}" | grep backend | head -1 | grep -o "blue\|green" || echo "blue") TARGET_ENV=$([ "$CURRENT_ENV" = "blue" ] && echo "green" || echo "blue") echo "当前环境: $CURRENT_ENV" echo "目标环境: $TARGET_ENV" # 1. 部署到目标环境 echo "部署到 $TARGET_ENV 环境..." docker-compose -f docker-compose.${TARGET_ENV}.yml up -d # 2. 健康检查 echo "执行健康检查..." sleep 30 if ! curl -f http://localhost:300${TARGET_ENV:0:1}/health; then echo "健康检查失败,回滚..." docker-compose -f docker-compose.${TARGET_ENV}.yml down exit 1 fi # 3. 切换流量 echo "切换流量到 $TARGET_ENV 环境..." sed -i "s/backend-${CURRENT_ENV}/backend-${TARGET_ENV}/g" nginx/nginx.conf docker exec nginx-lb nginx -s reload # 4. 停止旧环境 echo "停止 $CURRENT_ENV 环境..." sleep 60 # 等待连接排空 docker-compose -f docker-compose.${CURRENT_ENV}.yml down echo "蓝绿部署完成" ``` ## 8. 监控和日志 ### 8.1 日志收集配置 ```yaml # docker-compose.logging.yml version: '3.8' services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:7.15.0 container_name: elasticsearch environment: - discovery.type=single-node - "ES_JAVA_OPTS=-Xms512m -Xmx512m" ports: - "9200:9200" volumes: - elasticsearch_data:/usr/share/elasticsearch/data networks: - xlxumu_network logstash: image: docker.elastic.co/logstash/logstash:7.15.0 container_name: logstash volumes: - ./logstash/pipeline:/usr/share/logstash/pipeline - ./logs:/logs ports: - "5044:5044" depends_on: - elasticsearch networks: - xlxumu_network kibana: image: docker.elastic.co/kibana/kibana:7.15.0 container_name: kibana ports: - "5601:5601" environment: ELASTICSEARCH_HOSTS: http://elasticsearch:9200 depends_on: - elasticsearch networks: - xlxumu_network volumes: elasticsearch_data: networks: xlxumu_network: external: true ``` ### 8.2 Prometheus监控配置 ```yaml # prometheus/prometheus.yml global: scrape_interval: 15s evaluation_interval: 15s rule_files: - "alert_rules.yml" alerting: alertmanagers: - static_configs: - targets: - alertmanager:9093 scrape_configs: - job_name: 'prometheus' static_configs: - targets: ['localhost:9090'] - job_name: 'node-exporter' static_configs: - targets: ['node-exporter:9100'] - job_name: 'backend-api' static_configs: - targets: ['backend-api-1:3000', 'backend-api-2:3000'] metrics_path: '/metrics' - job_name: 'nginx' static_configs: - targets: ['nginx:9113'] - job_name: 'mysql' static_configs: - targets: ['mysql-exporter:9104'] - job_name: 'redis' static_configs: - targets: ['redis-exporter:9121'] ``` ## 9. 安全配置 ### 9.1 防火墙配置 ```bash #!/bin/bash # setup-firewall.sh # 清空现有规则 iptables -F iptables -X iptables -t nat -F iptables -t nat -X # 设置默认策略 iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT ACCEPT # 允许本地回环 iptables -A INPUT -i lo -j ACCEPT iptables -A OUTPUT -o lo -j ACCEPT # 允许已建立的连接 iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # 允许SSH iptables -A INPUT -p tcp --dport 22 -j ACCEPT # 允许HTTP/HTTPS iptables -A INPUT -p tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp --dport 443 -j ACCEPT # 允许内部网络访问数据库 iptables -A INPUT -s 172.18.0.0/16 -p tcp --dport 3306 -j ACCEPT iptables -A INPUT -s 172.18.0.0/16 -p tcp --dport 6379 -j ACCEPT iptables -A INPUT -s 172.18.0.0/16 -p tcp --dport 27017 -j ACCEPT # 防止DDoS攻击 iptables -A INPUT -p tcp --dport 80 -m limit --limit 25/minute --limit-burst 100 -j ACCEPT iptables -A INPUT -p tcp --dport 443 -m limit --limit 25/minute --limit-burst 100 -j ACCEPT # 保存规则 service iptables save ``` ### 9.2 SSL/TLS配置 ```bash #!/bin/bash # setup-ssl.sh # 生成强DH参数 openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048 # 配置SSL证书自动续期 cat > /etc/cron.d/certbot << EOF 0 12 * * * /usr/bin/certbot renew --quiet --post-hook "docker exec nginx-lb nginx -s reload" EOF ``` ## 10. 故障排查 ### 10.1 常见问题排查 ```bash #!/bin/bash # troubleshoot.sh echo "=== 系统故障排查 ===" # 检查Docker服务 echo "1. 检查Docker服务状态" systemctl status docker # 检查容器状态 echo "2. 检查容器状态" docker ps -a # 检查容器日志 echo "3. 检查容器日志" for container in $(docker ps --format "{{.Names}}"); do echo "--- $container 日志 ---" docker logs --tail 50 $container done # 检查网络连接 echo "4. 检查网络连接" netstat -tlnp | grep -E "(80|443|3000|3306|6379|27017)" # 检查磁盘空间 echo "5. 检查磁盘空间" df -h # 检查内存使用 echo "6. 检查内存使用" free -h # 检查CPU使用 echo "7. 检查CPU使用" top -bn1 | head -20 # 检查数据库连接 echo "8. 检查数据库连接" docker exec mysql-master mysql -u root -p${MYSQL_ROOT_PASSWORD} -e "SHOW PROCESSLIST;" # 检查Redis连接 echo "9. 检查Redis连接" docker exec redis-master redis-cli ping ``` ### 10.2 性能优化建议 ```bash # 系统内核参数优化 cat >> /etc/sysctl.conf << EOF # 网络优化 net.core.somaxconn = 65535 net.core.netdev_max_backlog = 5000 net.ipv4.tcp_max_syn_backlog = 65535 net.ipv4.tcp_fin_timeout = 10 net.ipv4.tcp_keepalive_time = 1200 net.ipv4.tcp_max_tw_buckets = 5000 # 文件描述符限制 fs.file-max = 65535 EOF sysctl -p ``` ## 11. 总结 ### 11.1 部署检查清单 - [ ] 服务器环境准备完成 - [ ] Docker和Docker Compose安装完成 - [ ] SSL证书配置完成 - [ ] 环境变量配置完成 - [ ] 数据库初始化完成 - [ ] 应用服务部署完成 - [ ] 负载均衡配置完成 - [ ] 监控系统配置完成 - [ ] 日志收集配置完成 - [ ] 备份策略配置完成 - [ ] 安全配置完成 - [ ] 健康检查通过 - [ ] 性能测试通过 ### 11.2 运维要点 1. **定期备份**:每日自动备份数据库和重要文件 2. **监控告警**:配置完善的监控和告警机制 3. **日志管理**:集中收集和分析日志 4. **安全更新**:定期更新系统和应用安全补丁 5. **性能优化**:持续监控和优化系统性能 6. **容灾准备**:制定完善的容灾恢复方案 ### 11.3 联系方式 - **运维团队**:ops@xlxumu.com - **紧急联系**:+86 138-0000-0000 - **技术支持**:support@xlxumu.com --- **文档版本**: v1.0.0 **最后更新**: 2024年12月 **维护团队**: 运维团队