1969 lines
42 KiB
Markdown
1969 lines
42 KiB
Markdown
|
|
# 系统集成和部署文档
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
本文档详细描述了解班客平台的系统集成方案和部署流程,包括开发环境搭建、生产环境部署、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: <base64-encoded-password>
|
|||
|
|
JWT_SECRET: <base64-encoded-jwt-secret>
|
|||
|
|
REDIS_PASSWORD: <base64-encoded-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等技术提升系统性能
|
|||
|
|
|
|||
|
|
未来将继续完善部署流程,增加更多自动化工具和监控指标,为平台的稳定运行提供更强有力的保障。
|