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

1969 lines
42 KiB
Markdown
Raw Normal View History

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