Files
xlxumu/docs/operations/部署文档.md

1747 lines
39 KiB
Markdown
Raw Permalink Normal View History

# 部署文档
## 版本历史
| 版本 | 日期 | 作者 | 变更说明 |
|------|------|------|----------|
| 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 <backup_name>"
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月
**维护团队**: 运维团队