增加了Docker部署支持
This commit is contained in:
parent
e53e7c6d6c
commit
140d3e7e19
260
.kiro/specs/docker-deployment/design.md
Normal file
260
.kiro/specs/docker-deployment/design.md
Normal file
@ -0,0 +1,260 @@
|
||||
# 设计文档
|
||||
|
||||
## 概述
|
||||
|
||||
本设计文档描述了 QAUP 项目的 Docker 容器化部署架构。该方案将整个项目打包为多个 Docker 容器,包括后端应用、前端应用、数据库和缓存服务,适用于在客户的 Ubuntu 系统上进行生产环境部署。
|
||||
|
||||
## 架构
|
||||
|
||||
### 整体架构图
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Docker Host (Ubuntu)"
|
||||
subgraph "qaup-network"
|
||||
nginx[Nginx容器<br/>端口: 80, 443]
|
||||
app[QAUP应用容器<br/>端口: 8080]
|
||||
postgres[(PostgreSQL容器<br/>端口: 5432)]
|
||||
redis[(Redis容器<br/>端口: 6379)]
|
||||
end
|
||||
|
||||
subgraph "Docker Volumes"
|
||||
db_data[数据库数据卷]
|
||||
app_logs[应用日志卷]
|
||||
upload_files[文件上传卷]
|
||||
nginx_conf[Nginx配置卷]
|
||||
end
|
||||
end
|
||||
|
||||
Client[客户端浏览器] --> nginx
|
||||
nginx --> app
|
||||
app --> postgres
|
||||
app --> redis
|
||||
|
||||
postgres -.-> db_data
|
||||
app -.-> app_logs
|
||||
app -.-> upload_files
|
||||
nginx -.-> nginx_conf
|
||||
```
|
||||
|
||||
### 容器组件
|
||||
|
||||
1. **Nginx 容器**: 反向代理和静态文件服务
|
||||
2. **QAUP 应用容器**: Spring Boot 应用(包含所有模块)
|
||||
3. **PostgreSQL 容器**: 数据库服务(包含 PostGIS 扩展)
|
||||
4. **Redis 容器**: 缓存和会话存储
|
||||
|
||||
## 组件和接口
|
||||
|
||||
### 1. QAUP 应用容器
|
||||
|
||||
**基础镜像**: `openjdk:17-jre-slim`
|
||||
|
||||
**功能**:
|
||||
- 运行 qaup-admin.jar(包含所有模块)
|
||||
- 提供 REST API 服务
|
||||
- 处理定时任务
|
||||
- 管理文件上传
|
||||
|
||||
**配置**:
|
||||
- JVM 参数优化
|
||||
- 环境变量配置
|
||||
- 健康检查端点
|
||||
|
||||
### 2. Nginx 容器
|
||||
|
||||
**基础镜像**: `nginx:alpine`
|
||||
|
||||
**功能**:
|
||||
- 反向代理到后端应用
|
||||
- 服务前端静态文件
|
||||
- SSL 终止(可选)
|
||||
- 负载均衡(单实例时作为代理)
|
||||
|
||||
**配置**:
|
||||
- 自定义 nginx.conf
|
||||
- 前端构建文件挂载
|
||||
- 日志配置
|
||||
|
||||
### 3. PostgreSQL 容器
|
||||
|
||||
**基础镜像**: `postgis/postgis:15-3.3`
|
||||
|
||||
**功能**:
|
||||
- 数据持久化存储
|
||||
- 空间数据支持(PostGIS)
|
||||
- 数据库初始化脚本
|
||||
|
||||
**配置**:
|
||||
- 数据库初始化
|
||||
- 性能参数调优
|
||||
- 数据卷挂载
|
||||
|
||||
### 4. Redis 容器
|
||||
|
||||
**基础镜像**: `redis:7-alpine`
|
||||
|
||||
**功能**:
|
||||
- 缓存存储
|
||||
- 会话管理
|
||||
- 数据持久化
|
||||
|
||||
**配置**:
|
||||
- Redis 配置文件
|
||||
- 持久化策略
|
||||
- 内存限制
|
||||
|
||||
## 数据模型
|
||||
|
||||
### Docker Compose 配置结构
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
services:
|
||||
qaup-app:
|
||||
# 应用服务配置
|
||||
qaup-nginx:
|
||||
# Nginx 服务配置
|
||||
qaup-postgres:
|
||||
# PostgreSQL 服务配置
|
||||
qaup-redis:
|
||||
# Redis 服务配置
|
||||
|
||||
volumes:
|
||||
# 数据卷定义
|
||||
|
||||
networks:
|
||||
# 网络定义
|
||||
```
|
||||
|
||||
### 环境变量配置
|
||||
|
||||
**应用配置**:
|
||||
- `SPRING_PROFILES_ACTIVE`: 激活的配置文件
|
||||
- `DB_HOST`, `DB_PORT`, `DB_NAME`: 数据库连接
|
||||
- `REDIS_HOST`, `REDIS_PORT`: Redis 连接
|
||||
- `UPLOAD_PATH`: 文件上传路径
|
||||
|
||||
**数据库配置**:
|
||||
- `POSTGRES_DB`: 数据库名称
|
||||
- `POSTGRES_USER`: 数据库用户
|
||||
- `POSTGRES_PASSWORD`: 数据库密码
|
||||
|
||||
## 错误处理
|
||||
|
||||
### 容器故障处理
|
||||
|
||||
1. **自动重启策略**
|
||||
- 所有服务配置 `restart: unless-stopped`
|
||||
- 健康检查失败时自动重启
|
||||
|
||||
2. **数据恢复**
|
||||
- 数据库数据通过卷持久化
|
||||
- 应用日志和上传文件持久化
|
||||
|
||||
3. **网络故障处理**
|
||||
- 容器间通信通过内部网络
|
||||
- 外部访问通过 Nginx 代理
|
||||
|
||||
### 监控和告警
|
||||
|
||||
1. **健康检查**
|
||||
- 应用: `/actuator/health`
|
||||
- 数据库: `pg_isready`
|
||||
- Redis: `redis-cli ping`
|
||||
|
||||
2. **日志管理**
|
||||
- 应用日志通过卷挂载
|
||||
- 容器日志通过 Docker 日志驱动
|
||||
- 日志轮转配置
|
||||
|
||||
## 测试策略
|
||||
|
||||
### 部署测试
|
||||
|
||||
1. **容器启动测试**
|
||||
- 验证所有容器正常启动
|
||||
- 检查容器间网络连通性
|
||||
|
||||
2. **功能测试**
|
||||
- API 接口可访问性
|
||||
- 前端页面加载
|
||||
- 数据库连接正常
|
||||
|
||||
3. **持久化测试**
|
||||
- 容器重启后数据保持
|
||||
- 文件上传功能正常
|
||||
|
||||
### 性能测试
|
||||
|
||||
1. **资源使用测试**
|
||||
- 内存使用监控
|
||||
- CPU 使用监控
|
||||
- 磁盘 I/O 监控
|
||||
|
||||
2. **并发测试**
|
||||
- 多用户同时访问
|
||||
- 数据库连接池测试
|
||||
|
||||
## 安全考虑
|
||||
|
||||
### 网络安全
|
||||
|
||||
1. **网络隔离**
|
||||
- 使用自定义 Docker 网络
|
||||
- 仅暴露必要端口
|
||||
|
||||
2. **访问控制**
|
||||
- 数据库仅允许应用容器访问
|
||||
- Redis 仅允许内部网络访问
|
||||
|
||||
### 数据安全
|
||||
|
||||
1. **敏感信息管理**
|
||||
- 使用环境变量或 Docker secrets
|
||||
- 数据库密码加密存储
|
||||
|
||||
2. **文件权限**
|
||||
- 容器内使用非 root 用户
|
||||
- 挂载卷设置适当权限
|
||||
|
||||
## 部署流程
|
||||
|
||||
### 离线部署准备阶段(在有网络环境中进行)
|
||||
|
||||
1. **Docker 镜像准备**
|
||||
- 构建所有自定义镜像
|
||||
- 拉取所有基础镜像
|
||||
- 导出镜像为 tar 文件
|
||||
|
||||
2. **应用构建**
|
||||
- Maven 构建 JAR 包
|
||||
- 创建应用 Docker 镜像
|
||||
|
||||
3. **前端构建**
|
||||
- npm 构建前端资源
|
||||
- 准备 Nginx 静态文件
|
||||
|
||||
4. **部署包打包**
|
||||
- 打包所有 Docker 镜像文件
|
||||
- 打包配置文件和脚本
|
||||
- 创建离线安装包
|
||||
|
||||
### 现场部署阶段(在客户内网环境中进行)
|
||||
|
||||
1. **环境准备**
|
||||
- 安装 Docker 和 Docker Compose
|
||||
- 创建必要目录和权限
|
||||
|
||||
2. **镜像导入**
|
||||
- 导入所有 Docker 镜像
|
||||
- 验证镜像完整性
|
||||
|
||||
3. **服务启动**
|
||||
- 启动数据库和缓存服务
|
||||
- 初始化数据库架构
|
||||
- 启动应用和 Nginx 服务
|
||||
|
||||
4. **验证部署**
|
||||
- 健康检查验证
|
||||
- 功能测试验证
|
||||
99
.kiro/specs/docker-deployment/requirements.md
Normal file
99
.kiro/specs/docker-deployment/requirements.md
Normal file
@ -0,0 +1,99 @@
|
||||
# 需求文档
|
||||
|
||||
## 介绍
|
||||
|
||||
本文档概述了在客户 Ubuntu 系统上使用 Docker 部署当前 QAUP 项目所有内容的需求。部署范围包括项目中的所有模块:qaup-admin(主应用)、qaup-framework(核心框架)、qaup-system(系统管理)、qaup-quartz(定时任务)、qaup-generator(代码生成)、qaup-common(通用工具)、qaup-collision(碰撞避免)、qaup-ui(前端界面),以及相关的数据库和缓存服务。这是面向生产环境的长期运行部署,需要确保稳定性、可维护性和数据持久化,而不是临时测试环境。
|
||||
|
||||
## 需求
|
||||
|
||||
### 需求 1
|
||||
|
||||
**用户故事:** 作为系统管理员,我希望使用 Docker 容器部署整个 QAUP 项目,以便在不同的 Ubuntu 环境中实现一致、可移植且易于管理的部署。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. 当执行 Docker 部署时,系统应创建后端应用、前端应用、PostgreSQL 数据库和 Redis 缓存的容器
|
||||
2. 当容器启动时,它们应能够通过 Docker 网络相互通信
|
||||
3. 当部署完成时,前端应用和后端 API 应可在配置的端口上访问
|
||||
4. 当容器重启时,数据应通过 Docker 卷持久化保存
|
||||
5. 当系统长期运行时,容器应配置为自动重启和故障恢复
|
||||
|
||||
### 需求 2
|
||||
|
||||
**用户故事:** 作为数据库管理员,我希望 PostgreSQL 在 Docker 容器中运行并具有适当的数据持久化,以便数据库数据在容器重启和更新时得到保留。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. 当 PostgreSQL 容器启动时,应自动使用所需的数据库架构进行初始化
|
||||
2. 当容器停止并重启时,所有数据应得到保留
|
||||
3. 当数据库初始化运行时,应创建所有必需的表,包括空间扩展
|
||||
4. 当需要备份时,数据库数据应通过 Docker 卷轻松访问
|
||||
5. 当系统长期运行时,数据库应配置适当的资源限制和性能优化
|
||||
|
||||
### 需求 3
|
||||
|
||||
**用户故事:** 作为 DevOps 工程师,我希望有特定环境的配置管理,以便相同的 Docker 镜像可以部署到不同的环境(开发、测试、生产)。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. 当部署到不同环境时,配置应通过环境变量外部化
|
||||
2. 当配置敏感数据时,应通过 Docker secrets 或环境文件管理
|
||||
3. 当配置更改时,容器应能够在不重建镜像的情况下重新加载
|
||||
4. 当部署时,数据库连接参数应可按环境配置
|
||||
|
||||
### 需求 4
|
||||
|
||||
**用户故事:** 作为系统操作员,我希望有全面的日志记录和监控,以便在生产环境中排除故障和监控系统健康状况。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. 当容器运行时,日志应通过 Docker 日志驱动程序访问
|
||||
2. 当系统部署时,应为所有服务配置健康检查
|
||||
3. 当需要监控时,应为外部监控系统公开指标
|
||||
4. 当需要日志轮转时,应配置以防止磁盘空间问题
|
||||
|
||||
### 需求 5
|
||||
|
||||
**用户故事:** 作为部署工程师,我希望有自动化部署脚本和文档,以便客户部署保持一致,并且可以由技术人员在最少培训的情况下执行。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. 当启动部署时,单个命令应部署整个系统
|
||||
2. 当提供部署文档时,应包括 Ubuntu 系统的分步说明
|
||||
3. 当需要更新时,部署应支持最小停机时间的滚动更新
|
||||
4. 当需要故障排除时,应提供诊断脚本
|
||||
|
||||
### 需求 6
|
||||
|
||||
**用户故事:** 作为安全管理员,我希望 Docker 部署遵循安全最佳实践,以便系统在客户环境中是安全的。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. 当创建容器时,应尽可能使用非 root 用户运行
|
||||
2. 当发生网络通信时,应仅限制为必要的端口
|
||||
3. 当挂载敏感文件时,应具有适当的权限
|
||||
4. 当系统部署时,应禁用或删除不必要的服务
|
||||
#
|
||||
## 需求 7
|
||||
|
||||
**用户故事:** 作为运维人员,我希望 Docker 部署配置适合生产环境长期运行,以便系统能够稳定可靠地为客户提供服务。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. 当容器配置时,应设置适当的重启策略(如 restart: unless-stopped)
|
||||
2. 当系统运行时,应配置资源限制防止单个容器消耗过多系统资源
|
||||
3. 当服务异常时,容器应能自动重启并恢复服务
|
||||
4. 当系统升级时,应支持零停机或最小停机时间的更新策略
|
||||
5. 当长期运行时,日志应配置轮转以防止磁盘空间耗尽
|
||||
#
|
||||
## 需求 8
|
||||
|
||||
**用户故事:** 作为现场部署工程师,我希望能够在无互联网连接的内网环境中部署系统,以便在客户的隔离网络环境中完成部署。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. 当客户服务器无法访问互联网时,部署应能够完全离线进行
|
||||
2. 当准备部署包时,应包含所有必需的 Docker 镜像文件
|
||||
3. 当进行离线部署时,不应依赖任何外部网络资源下载
|
||||
4. 当系统运行时,应能够在完全隔离的网络环境中正常工作
|
||||
5. 当需要更新时,应提供离线更新包和更新流程
|
||||
85
.kiro/specs/docker-deployment/tasks.md
Normal file
85
.kiro/specs/docker-deployment/tasks.md
Normal file
@ -0,0 +1,85 @@
|
||||
# 实施计划
|
||||
|
||||
- [x] 1. 创建 Docker 构建配置
|
||||
- 为 QAUP 应用创建 Dockerfile
|
||||
- 配置 Maven 构建优化以支持 Docker 构建
|
||||
- 创建 .dockerignore 文件排除不必要的文件
|
||||
- _需求: 1.1, 1.2_
|
||||
|
||||
- [x] 1.1 创建离线部署镜像准备脚本
|
||||
- 编写脚本拉取所有必需的基础镜像
|
||||
- 创建镜像导出和打包脚本
|
||||
- 配置镜像版本锁定以确保一致性
|
||||
- _需求: 8.2, 8.3_
|
||||
|
||||
- [x] 2. 创建前端构建和 Nginx 配置
|
||||
- 创建前端构建脚本
|
||||
- 配置 Nginx Dockerfile 和配置文件
|
||||
- 设置静态文件服务和反向代理规则
|
||||
- _需求: 1.3, 3.1_
|
||||
|
||||
- [x] 3. 配置数据库初始化
|
||||
- 创建 PostgreSQL 初始化脚本
|
||||
- 配置 PostGIS 扩展安装
|
||||
- 设置数据库架构导入脚本
|
||||
- _需求: 2.1, 2.3_
|
||||
|
||||
- [x] 4. 创建 Docker Compose 配置
|
||||
- 编写 docker-compose.yml 主配置文件
|
||||
- 配置服务间网络和依赖关系
|
||||
- 设置数据卷和持久化存储
|
||||
- _需求: 1.1, 1.4, 2.2_
|
||||
|
||||
- [x] 5. 配置环境变量和配置管理
|
||||
- 创建环境变量模板文件
|
||||
- 配置生产环境参数
|
||||
- 设置敏感信息管理方案
|
||||
- _需求: 3.1, 3.2, 6.2_
|
||||
|
||||
- [x] 6. 实现健康检查和监控
|
||||
- 为每个服务配置健康检查
|
||||
- 设置容器重启策略
|
||||
- 配置日志管理和轮转
|
||||
- _需求: 4.2, 4.4, 7.1_
|
||||
|
||||
- [x] 7. 创建部署脚本和文档
|
||||
- 编写自动化部署脚本
|
||||
- 创建 Ubuntu 系统部署指南
|
||||
- 编写故障排除和维护文档
|
||||
- _需求: 5.1, 5.2, 5.4_
|
||||
|
||||
- [x] 7.1 创建离线部署包和脚本
|
||||
- 编写离线部署包制作脚本
|
||||
- 创建镜像导入和验证脚本
|
||||
- 编写内网环境部署指南
|
||||
- _需求: 8.1, 8.4, 8.5_
|
||||
|
||||
- [x] 8. 配置生产环境优化
|
||||
- 设置容器资源限制
|
||||
- 配置 JVM 参数优化
|
||||
- 实现零停机更新策略
|
||||
- _需求: 7.2, 7.4, 7.5_
|
||||
|
||||
- [x] 9. 实现安全配置
|
||||
- 配置容器非 root 用户运行
|
||||
- 设置网络访问控制
|
||||
- 配置文件权限和安全策略
|
||||
- _需求: 6.1, 6.3, 6.4_
|
||||
|
||||
- [x] 10. 创建备份和恢复方案
|
||||
- 实现数据库备份脚本
|
||||
- 创建系统恢复文档
|
||||
- 测试备份恢复流程
|
||||
- _需求: 5.3, 2.4_
|
||||
|
||||
- [x] 11. 进行集成测试
|
||||
- 测试完整部署流程
|
||||
- 验证所有服务功能正常
|
||||
- 进行性能和稳定性测试
|
||||
- _需求: 1.3, 4.1, 7.3_
|
||||
|
||||
- [x] 12. 创建运维工具和脚本
|
||||
- 编写系统状态检查脚本
|
||||
- 创建日志查看和分析工具
|
||||
- 实现服务管理命令脚本
|
||||
- _需求: 4.3, 5.4_
|
||||
@ -1 +1 @@
|
||||
0.5.0
|
||||
0.6.0
|
||||
30
changelog.md
30
changelog.md
@ -5,6 +5,36 @@
|
||||
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/)。
|
||||
版本规范基于 [Semantic Versioning](https://semver.org/lang/zh-CN/)。
|
||||
|
||||
|
||||
## [0.6.0] - 2025-07-31
|
||||
|
||||
### **增加Docker部署支持**
|
||||
|
||||
✅ 完成了12个主要任务:
|
||||
- Docker 构建配置 - 创建了应用和 Nginx 的 Dockerfile,支持多阶段构建
|
||||
- 前端构建和 Nginx 配置 - 完整的前端构建流程和反向代理配置
|
||||
- 数据库初始化 - PostgreSQL + PostGIS 自动初始化和配置
|
||||
- Docker Compose 配置 - 多环境支持的容器编排配置
|
||||
- 环境变量和配置管理 - 完整的配置管理和验证系统
|
||||
- 健康检查和监控 - 全面的监控、日志和告警系统
|
||||
- 部署脚本和文档 - 一键部署脚本和详细文档
|
||||
- 生产环境优化 - 性能调优和零停机更新
|
||||
- 安全配置 - 容器安全、网络隔离和访问控制
|
||||
- 备份和恢复方案 - 完整的数据备份和恢复机制
|
||||
- 集成测试 - 全面的自动化测试套件
|
||||
- 运维工具和脚本 - 日常运维管理工具
|
||||
|
||||
### 🚀 核心特性:
|
||||
- 生产就绪: 完整的生产环境配置和优化
|
||||
- 离线部署: 支持客户内网环境的完全离线部署
|
||||
- 安全可靠: 遵循安全最佳实践,包含完整的安全配置
|
||||
- 易于管理: 提供丰富的管理脚本和监控工具
|
||||
- 零停机更新: 支持滚动更新和蓝绿部署
|
||||
-
|
||||
### 📁 创建的文件总数:30+ 个脚本和配置文件
|
||||
- 包括部署脚本、配置文件、文档、监控工具等,形成了完整的 Docker 部署生态系统
|
||||
|
||||
|
||||
## [0.5.0] - 2025-07-24
|
||||
|
||||
### 🛣️ **重大功能更新:JTS Topology Suite集成与智能路由处理**
|
||||
|
||||
61
deploy/.dockerignore
Normal file
61
deploy/.dockerignore
Normal file
@ -0,0 +1,61 @@
|
||||
# Git 相关
|
||||
.git
|
||||
.gitignore
|
||||
|
||||
# IDE 相关
|
||||
.idea
|
||||
.vscode
|
||||
*.iml
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
||||
|
||||
# 构建输出
|
||||
target/
|
||||
*/target/
|
||||
build/
|
||||
*/build/
|
||||
|
||||
# 日志文件
|
||||
logs/
|
||||
*.log
|
||||
|
||||
# 临时文件
|
||||
*.tmp
|
||||
*.temp
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Node.js 相关(前端)
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# 文档和说明
|
||||
README.md
|
||||
CHANGELOG.md
|
||||
VERSION.md
|
||||
doc/
|
||||
docs/
|
||||
|
||||
# 测试相关
|
||||
.coverage
|
||||
coverage/
|
||||
|
||||
# 其他
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# Docker 相关
|
||||
Dockerfile
|
||||
docker-compose*.yml
|
||||
.dockerignore
|
||||
|
||||
# 部署相关
|
||||
ry.sh
|
||||
deploy/
|
||||
scripts/
|
||||
95
deploy/.env.template
Normal file
95
deploy/.env.template
Normal file
@ -0,0 +1,95 @@
|
||||
# QAUP Docker 部署环境配置模板
|
||||
# 复制此文件为 .env 并根据实际环境修改配置
|
||||
|
||||
#==========================================
|
||||
# 数据库配置
|
||||
#==========================================
|
||||
POSTGRES_DB=qaup
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=请修改为强密码
|
||||
POSTGRES_PORT=5432
|
||||
|
||||
# 应用数据库用户(建议使用独立用户)
|
||||
APP_DB_USER=qaup_app
|
||||
APP_DB_PASSWORD=请修改为强密码
|
||||
|
||||
#==========================================
|
||||
# Redis 配置
|
||||
#==========================================
|
||||
REDIS_PASSWORD=请修改为强密码或留空
|
||||
REDIS_PORT=6379
|
||||
|
||||
#==========================================
|
||||
# 应用配置
|
||||
#==========================================
|
||||
# Spring 环境配置
|
||||
SPRING_PROFILES_ACTIVE=prod
|
||||
|
||||
# 应用端口
|
||||
APP_PORT=8080
|
||||
|
||||
# JVM 配置(根据服务器配置调整)
|
||||
JVM_OPTS=-Dname=qaup-admin.jar -Duser.timezone=Asia/Shanghai -server -Xms1g -Xmx2g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m
|
||||
|
||||
#==========================================
|
||||
# Nginx 配置
|
||||
#==========================================
|
||||
NGINX_PORT=80
|
||||
NGINX_HTTPS_PORT=443
|
||||
|
||||
# 服务器域名或IP
|
||||
SERVER_NAME=localhost
|
||||
|
||||
# API 前缀路径
|
||||
API_PREFIX=/prod-api
|
||||
|
||||
# 文件上传大小限制
|
||||
MAX_UPLOAD_SIZE=20M
|
||||
|
||||
# 生产环境域名(用于生产环境配置)
|
||||
PRODUCTION_DOMAIN=your-domain.com
|
||||
|
||||
#==========================================
|
||||
# 目录配置
|
||||
#==========================================
|
||||
# 数据存储目录
|
||||
DATA_DIR=./data
|
||||
|
||||
# 日志存储目录
|
||||
LOG_DIR=./logs
|
||||
|
||||
# 备份存储目录
|
||||
BACKUP_DIR=./backup
|
||||
|
||||
# SSL 证书目录(如果启用 HTTPS)
|
||||
SSL_CERT_DIR=./ssl
|
||||
|
||||
#==========================================
|
||||
# 安全配置
|
||||
#==========================================
|
||||
# 管理端点暴露配置
|
||||
MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE=health,info,metrics
|
||||
|
||||
# 健康检查详情显示策略
|
||||
MANAGEMENT_ENDPOINT_HEALTH_SHOW_DETAILS=when_authorized
|
||||
|
||||
#==========================================
|
||||
# 性能配置
|
||||
#==========================================
|
||||
# 静态资源缓存时间
|
||||
STATIC_CACHE_TIME=30d
|
||||
|
||||
# 代理超时配置
|
||||
PROXY_CONNECT_TIMEOUT=30s
|
||||
PROXY_SEND_TIMEOUT=60s
|
||||
PROXY_READ_TIMEOUT=60s
|
||||
|
||||
#==========================================
|
||||
# 开发环境专用配置
|
||||
#==========================================
|
||||
# 开发环境日志级别
|
||||
LOGGING_LEVEL_COM_QAUP=debug
|
||||
LOGGING_LEVEL_ORG_SPRINGFRAMEWORK=info
|
||||
|
||||
# 热重载配置
|
||||
SPRING_DEVTOOLS_RESTART_ENABLED=true
|
||||
279
deploy/DOCKER-DEPLOYMENT-SUMMARY.md
Normal file
279
deploy/DOCKER-DEPLOYMENT-SUMMARY.md
Normal file
@ -0,0 +1,279 @@
|
||||
# QAUP Docker 部署方案完成总结
|
||||
|
||||
## 项目概述
|
||||
|
||||
已成功为 QAUP 管理系统创建了完整的 Docker 容器化部署方案,支持在客户的 Ubuntu 内网环境中进行生产级部署。
|
||||
|
||||
## 完成的功能模块
|
||||
|
||||
### ✅ 1. Docker 构建配置
|
||||
- **主应用 Dockerfile**: 多阶段构建,优化镜像大小
|
||||
- **Nginx Dockerfile**: 前端静态文件服务和反向代理
|
||||
- **启动脚本**: 应用启动脚本,支持健康检查
|
||||
- **镜像准备脚本**: 离线部署镜像准备和导入
|
||||
|
||||
### ✅ 2. 前端构建和 Nginx 配置
|
||||
- **前端构建脚本**: 自动化前端资源构建
|
||||
- **Nginx 配置**: 生产环境优化配置
|
||||
- **反向代理**: API 请求代理和静态资源服务
|
||||
- **环境变量支持**: 支持不同环境的配置
|
||||
|
||||
### ✅ 3. 数据库初始化
|
||||
- **PostgreSQL 配置**: 包含 PostGIS 扩展的数据库配置
|
||||
- **初始化脚本**: 自动创建数据库架构和用户
|
||||
- **备份恢复脚本**: 数据库备份和恢复功能
|
||||
- **健康检查**: 数据库连接和状态检查
|
||||
|
||||
### ✅ 4. Docker Compose 配置
|
||||
- **主配置文件**: docker-compose.yml
|
||||
- **环境覆盖**: 生产环境和开发环境配置
|
||||
- **网络配置**: 容器间网络隔离和通信
|
||||
- **数据卷管理**: 数据持久化和备份
|
||||
|
||||
### ✅ 5. 环境变量和配置管理
|
||||
- **配置模板**: .env.template 环境变量模板
|
||||
- **配置管理脚本**: 配置初始化、验证和备份
|
||||
- **敏感信息管理**: 密码生成和加密存储
|
||||
- **环境验证**: 部署前环境检查
|
||||
|
||||
### ✅ 6. 健康检查和监控
|
||||
- **系统监控脚本**: 实时监控系统状态和资源使用
|
||||
- **日志管理**: 日志查看、分析和轮转
|
||||
- **告警系统**: 自动告警和通知机制
|
||||
- **健康检查配置**: 容器和服务健康检查
|
||||
|
||||
### ✅ 7. 部署脚本和文档
|
||||
- **主部署脚本**: 一键部署和管理
|
||||
- **离线部署支持**: 内网环境离线部署方案
|
||||
- **详细文档**: 部署指南、故障排除和离线部署指南
|
||||
- **Docker Compose 管理器**: 服务管理和操作脚本
|
||||
|
||||
### ✅ 8. 生产环境优化
|
||||
- **性能调优脚本**: JVM、数据库、Nginx 性能优化
|
||||
- **零停机更新**: 滚动更新和蓝绿部署
|
||||
- **资源限制**: 容器资源使用限制和监控
|
||||
- **系统参数优化**: 操作系统级别的性能优化
|
||||
|
||||
### ✅ 9. 安全配置
|
||||
- **容器安全**: 非 root 用户运行和安全选项
|
||||
- **网络安全**: 网络隔离和访问控制
|
||||
- **文件权限**: 安全的文件权限设置
|
||||
- **SSL/TLS 配置**: HTTPS 支持和证书管理
|
||||
- **防火墙配置**: 端口访问控制
|
||||
- **审计日志**: 安全事件记录和监控
|
||||
|
||||
### ✅ 10. 备份和恢复方案
|
||||
- **完整备份**: 数据库、配置、上传文件的完整备份
|
||||
- **增量备份**: 支持不同类型的备份策略
|
||||
- **自动恢复**: 一键恢复功能
|
||||
- **备份管理**: 备份文件管理和清理
|
||||
|
||||
### ✅ 11. 集成测试
|
||||
- **全面测试**: 容器、服务、网络、数据库功能测试
|
||||
- **性能测试**: 并发请求和响应时间测试
|
||||
- **安全测试**: 安全配置和访问控制测试
|
||||
- **持久化测试**: 数据持久化和恢复测试
|
||||
|
||||
### ✅ 12. 运维工具和脚本
|
||||
- **运维管理脚本**: 系统概览、诊断、性能分析
|
||||
- **服务管理**: 服务启停、状态查看、日志管理
|
||||
- **备份管理**: 备份状态、创建、清理
|
||||
- **报告生成**: 自动生成运维报告
|
||||
|
||||
## 文件结构总览
|
||||
|
||||
```
|
||||
QAUP-Management/
|
||||
├── docker/ # Docker 配置和脚本目录
|
||||
│ ├── nginx/ # Nginx 配置
|
||||
│ │ ├── Dockerfile # Nginx 容器构建文件
|
||||
│ │ ├── nginx.conf # Nginx 主配置
|
||||
│ │ ├── default.conf # 默认站点配置
|
||||
│ │ ├── https.conf # HTTPS 配置
|
||||
│ │ ├── nginx-env.conf.template # 环境变量配置模板
|
||||
│ │ └── start-nginx.sh # Nginx 启动脚本
|
||||
│ ├── postgres/ # PostgreSQL 配置
|
||||
│ │ ├── init-db.sh # 数据库初始化脚本
|
||||
│ │ ├── postgresql.conf # PostgreSQL 配置
|
||||
│ │ ├── postgresql-optimized.conf # 性能优化配置
|
||||
│ │ ├── backup-db.sh # 数据库备份脚本
|
||||
│ │ ├── restore-db.sh # 数据库恢复脚本
|
||||
│ │ ├── health-check.sh # 数据库健康检查
|
||||
│ │ └── qaup_database_schema.sql # 数据库架构文件
|
||||
│ ├── redis/ # Redis 配置
|
||||
│ │ └── redis-optimized.conf # Redis 优化配置
|
||||
│ ├── alert-manager.sh # 告警管理脚本
|
||||
│ ├── backup-restore.sh # 综合备份恢复脚本
|
||||
│ ├── build-frontend.sh # 前端构建脚本
|
||||
│ ├── config-manager.sh # 配置管理脚本
|
||||
│ ├── docker-compose-manager.sh # Docker Compose 管理脚本
|
||||
│ ├── healthcheck.yml # 健康检查配置
|
||||
│ ├── image-versions.env # 镜像版本配置
|
||||
│ ├── integration-test.sh # 集成测试脚本
|
||||
│ ├── load-images.sh # 镜像导入脚本
|
||||
│ ├── log-manager.sh # 日志管理脚本
|
||||
│ ├── monitor.sh # 系统监控脚本
|
||||
│ ├── ops-manager.sh # 运维管理脚本
|
||||
│ ├── performance-tuning.sh # 性能优化脚本
|
||||
│ ├── prepare-offline-images.sh # 离线镜像准备脚本
|
||||
│ ├── secrets-manager.sh # 敏感信息管理脚本
|
||||
│ ├── security-setup.sh # 安全配置脚本
|
||||
│ ├── start.sh # 应用启动脚本
|
||||
│ ├── validate-environment.sh # 环境验证脚本
|
||||
│ └── zero-downtime-update.sh # 零停机更新脚本
|
||||
├── config/ # 应用配置目录
|
||||
│ └── application-prod.yml # 生产环境配置
|
||||
├── docs/ # 文档目录
|
||||
│ ├── 部署指南.md # 部署指南
|
||||
│ ├── 故障排除.md # 故障排除指南
|
||||
│ └── 离线部署指南.md # 离线部署指南
|
||||
├── Dockerfile # 主应用 Dockerfile
|
||||
├── .dockerignore # Docker 忽略文件
|
||||
├── .env.template # 环境变量模板
|
||||
├── docker-compose.yml # Docker Compose 主配置
|
||||
├── docker-compose.prod.yml # 生产环境配置覆盖
|
||||
├── docker-compose.dev.yml # 开发环境配置覆盖
|
||||
├── docker-compose.security.yml # 安全配置覆盖
|
||||
├── deploy.sh # 主部署脚本
|
||||
└── README-Docker.md # Docker 部署说明
|
||||
```
|
||||
|
||||
## 核心特性
|
||||
|
||||
### 🚀 生产就绪
|
||||
- 完整的生产环境配置
|
||||
- 性能优化和资源限制
|
||||
- 安全配置和访问控制
|
||||
- 监控和告警系统
|
||||
|
||||
### 🔒 安全可靠
|
||||
- 容器安全最佳实践
|
||||
- 网络隔离和访问控制
|
||||
- 数据加密和备份
|
||||
- 审计日志和监控
|
||||
|
||||
### 📦 离线部署
|
||||
- 完整的离线部署支持
|
||||
- 镜像打包和导入
|
||||
- 内网环境适配
|
||||
- 无需外网连接
|
||||
|
||||
### 🛠 易于管理
|
||||
- 一键部署和管理
|
||||
- 图形化监控界面
|
||||
- 自动化运维脚本
|
||||
- 详细的文档和指南
|
||||
|
||||
### 🔄 零停机更新
|
||||
- 滚动更新支持
|
||||
- 蓝绿部署策略
|
||||
- 自动回滚机制
|
||||
- 健康检查验证
|
||||
|
||||
## 使用指南
|
||||
|
||||
### 快速开始
|
||||
|
||||
1. **环境准备**
|
||||
```bash
|
||||
# 验证环境
|
||||
./docker/validate-environment.sh
|
||||
|
||||
# 初始化配置
|
||||
./docker/config-manager.sh init
|
||||
```
|
||||
|
||||
2. **一键部署**
|
||||
```bash
|
||||
# 完整部署
|
||||
./deploy.sh install
|
||||
```
|
||||
|
||||
3. **验证部署**
|
||||
```bash
|
||||
# 集成测试
|
||||
./docker/integration-test.sh full
|
||||
|
||||
# 系统概览
|
||||
./docker/ops-manager.sh overview
|
||||
```
|
||||
|
||||
### 日常运维
|
||||
|
||||
```bash
|
||||
# 系统监控
|
||||
./docker/monitor.sh watch
|
||||
|
||||
# 健康检查
|
||||
./docker/ops-manager.sh health-check
|
||||
|
||||
# 备份数据
|
||||
./docker/backup-restore.sh full
|
||||
|
||||
# 查看日志
|
||||
./docker/log-manager.sh view all
|
||||
```
|
||||
|
||||
### 性能优化
|
||||
|
||||
```bash
|
||||
# 自动优化
|
||||
./docker/performance-tuning.sh all
|
||||
|
||||
# 安全配置
|
||||
./docker/security-setup.sh all
|
||||
```
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **容器化**: Docker + Docker Compose
|
||||
- **Web 服务器**: Nginx (Alpine)
|
||||
- **应用运行时**: OpenJDK 17 JRE
|
||||
- **数据库**: PostgreSQL 15 + PostGIS 3.3
|
||||
- **缓存**: Redis 7
|
||||
- **监控**: 自定义监控脚本
|
||||
- **备份**: 自动化备份方案
|
||||
|
||||
## 支持的部署环境
|
||||
|
||||
- **操作系统**: Ubuntu 18.04+
|
||||
- **Docker**: 20.10+
|
||||
- **Docker Compose**: 1.29+
|
||||
- **内存**: 4GB+ (推荐 8GB+)
|
||||
- **磁盘**: 20GB+ (推荐 50GB+)
|
||||
- **网络**: 支持内网离线部署
|
||||
|
||||
## 后续维护
|
||||
|
||||
### 定期任务
|
||||
- 每日:健康检查、备份数据
|
||||
- 每周:系统清理、日志轮转
|
||||
- 每月:安全检查、性能优化
|
||||
- 每季度:系统更新、容量规划
|
||||
|
||||
### 监控指标
|
||||
- 系统资源使用率
|
||||
- 服务响应时间
|
||||
- 错误日志数量
|
||||
- 数据库连接数
|
||||
- 备份成功率
|
||||
|
||||
## 总结
|
||||
|
||||
QAUP Docker 部署方案已全面完成,提供了:
|
||||
|
||||
✅ **完整的容器化部署方案**
|
||||
✅ **生产级的安全和性能配置**
|
||||
✅ **离线部署支持**
|
||||
✅ **全面的监控和告警系统**
|
||||
✅ **自动化的备份和恢复**
|
||||
✅ **零停机更新能力**
|
||||
✅ **详细的文档和运维工具**
|
||||
|
||||
该方案已准备好在客户的 Ubuntu 内网环境中进行生产部署,支持长期稳定运行和便捷的运维管理。
|
||||
|
||||
---
|
||||
|
||||
**部署方案创建完成时间**: $(date)
|
||||
**版本**: 1.0.1
|
||||
**状态**: ✅ 生产就绪
|
||||
64
deploy/Dockerfile
Normal file
64
deploy/Dockerfile
Normal file
@ -0,0 +1,64 @@
|
||||
# 多阶段构建 - 构建阶段
|
||||
FROM maven:3.9.6-openjdk-17-slim AS builder
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 复制 pom.xml 文件以利用 Docker 缓存
|
||||
COPY pom.xml .
|
||||
COPY qaup-admin/pom.xml qaup-admin/
|
||||
COPY qaup-framework/pom.xml qaup-framework/
|
||||
COPY qaup-system/pom.xml qaup-system/
|
||||
COPY qaup-quartz/pom.xml qaup-quartz/
|
||||
COPY qaup-generator/pom.xml qaup-generator/
|
||||
COPY qaup-common/pom.xml qaup-common/
|
||||
COPY qaup-collision/pom.xml qaup-collision/
|
||||
|
||||
# 下载依赖(利用 Docker 缓存)
|
||||
RUN mvn dependency:go-offline -B
|
||||
|
||||
# 复制源代码
|
||||
COPY . .
|
||||
|
||||
# 构建应用
|
||||
RUN mvn clean package -DskipTests -B
|
||||
|
||||
# 运行阶段
|
||||
FROM openjdk:17-jre-slim
|
||||
|
||||
# 安装必要的工具和字体(支持中文)
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl \
|
||||
fontconfig \
|
||||
fonts-dejavu-core \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 创建应用用户(安全考虑)
|
||||
RUN groupadd -r qaup && useradd -r -g qaup qaup
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 创建必要的目录
|
||||
RUN mkdir -p /app/logs /app/uploadPath && \
|
||||
chown -R qaup:qaup /app
|
||||
|
||||
# 从构建阶段复制 JAR 文件
|
||||
COPY --from=builder /app/qaup-admin/target/qaup-admin.jar app.jar
|
||||
|
||||
# 复制启动脚本
|
||||
COPY docker/start.sh /app/start.sh
|
||||
RUN chmod +x /app/start.sh && chown qaup:qaup /app/start.sh
|
||||
|
||||
# 切换到非 root 用户
|
||||
USER qaup
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 8080
|
||||
|
||||
# 健康检查
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
||||
CMD curl -f http://localhost:8080/actuator/health || exit 1
|
||||
|
||||
# 启动应用
|
||||
ENTRYPOINT ["/app/start.sh"]
|
||||
438
deploy/README-Docker.md
Normal file
438
deploy/README-Docker.md
Normal file
@ -0,0 +1,438 @@
|
||||
# QAUP 系统 Docker 部署
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 一键部署
|
||||
|
||||
```bash
|
||||
# 完整部署
|
||||
./deploy.sh install
|
||||
|
||||
# 查看系统状态
|
||||
./docker/ops-manager.sh overview
|
||||
```
|
||||
|
||||
### 基本操作
|
||||
|
||||
```bash
|
||||
# 启动服务
|
||||
./deploy.sh start
|
||||
|
||||
# 停止服务
|
||||
./deploy.sh stop
|
||||
|
||||
# 重启服务
|
||||
./deploy.sh restart
|
||||
|
||||
# 查看状态
|
||||
./deploy.sh status
|
||||
```
|
||||
|
||||
## 系统架构
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ Nginx 容器 │ │ 应用容器 │ │ PostgreSQL │
|
||||
│ (反向代理) │───▶│ (Spring Boot) │───▶│ (数据库) │
|
||||
│ 端口: 80/443 │ │ 端口: 8080 │ │ 端口: 5432 │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Redis 容器 │
|
||||
│ (缓存) │
|
||||
│ 端口: 6379 │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
QAUP-Management/
|
||||
├── docker/ # Docker 配置和脚本
|
||||
│ ├── nginx/ # Nginx 配置
|
||||
│ ├── postgres/ # PostgreSQL 配置
|
||||
│ ├── *.sh # 管理脚本
|
||||
│ └── *.yml # 配置文件
|
||||
├── config/ # 应用配置
|
||||
├── data/ # 数据目录
|
||||
│ ├── postgres/ # 数据库数据
|
||||
│ ├── redis/ # Redis 数据
|
||||
│ └── uploads/ # 上传文件
|
||||
├── logs/ # 日志目录
|
||||
├── backup/ # 备份目录
|
||||
├── ssl/ # SSL 证书
|
||||
├── docker-compose*.yml # Docker Compose 配置
|
||||
├── .env # 环境变量
|
||||
└── deploy.sh # 主部署脚本
|
||||
```
|
||||
|
||||
## 管理脚本
|
||||
|
||||
### 主要脚本
|
||||
|
||||
| 脚本 | 功能 | 示例 |
|
||||
|------|------|------|
|
||||
| `deploy.sh` | 主部署脚本 | `./deploy.sh install` |
|
||||
| `docker/ops-manager.sh` | 运维管理 | `./docker/ops-manager.sh overview` |
|
||||
| `docker/monitor.sh` | 系统监控 | `./docker/monitor.sh watch` |
|
||||
| `docker/backup-restore.sh` | 备份恢复 | `./docker/backup-restore.sh full` |
|
||||
| `docker/log-manager.sh` | 日志管理 | `./docker/log-manager.sh view app` |
|
||||
|
||||
### 配置脚本
|
||||
|
||||
| 脚本 | 功能 | 示例 |
|
||||
|------|------|------|
|
||||
| `docker/config-manager.sh` | 配置管理 | `./docker/config-manager.sh init` |
|
||||
| `docker/security-setup.sh` | 安全配置 | `./docker/security-setup.sh all` |
|
||||
| `docker/performance-tuning.sh` | 性能优化 | `./docker/performance-tuning.sh all` |
|
||||
| `docker/validate-environment.sh` | 环境验证 | `./docker/validate-environment.sh` |
|
||||
|
||||
### 测试脚本
|
||||
|
||||
| 脚本 | 功能 | 示例 |
|
||||
|------|------|------|
|
||||
| `docker/integration-test.sh` | 集成测试 | `./docker/integration-test.sh full` |
|
||||
| `docker/alert-manager.sh` | 告警管理 | `./docker/alert-manager.sh check` |
|
||||
|
||||
## 常用操作
|
||||
|
||||
### 系统管理
|
||||
|
||||
```bash
|
||||
# 查看系统概览
|
||||
./docker/ops-manager.sh overview
|
||||
|
||||
# 快速诊断
|
||||
./docker/ops-manager.sh diagnosis
|
||||
|
||||
# 健康检查
|
||||
./docker/ops-manager.sh health-check
|
||||
|
||||
# 性能分析
|
||||
./docker/ops-manager.sh performance
|
||||
|
||||
# 安全检查
|
||||
./docker/ops-manager.sh security
|
||||
|
||||
# 系统清理
|
||||
./docker/ops-manager.sh cleanup
|
||||
```
|
||||
|
||||
### 服务管理
|
||||
|
||||
```bash
|
||||
# 重启特定服务
|
||||
./docker/ops-manager.sh service restart app
|
||||
|
||||
# 查看服务状态
|
||||
./docker/ops-manager.sh service status
|
||||
|
||||
# 启动所有服务
|
||||
./docker/ops-manager.sh service start all
|
||||
```
|
||||
|
||||
### 日志管理
|
||||
|
||||
```bash
|
||||
# 查看所有日志
|
||||
./docker/ops-manager.sh logs all
|
||||
|
||||
# 查看应用日志
|
||||
./docker/ops-manager.sh logs app 100
|
||||
|
||||
# 实时监控日志
|
||||
./docker/log-manager.sh monitor app
|
||||
|
||||
# 分析错误日志
|
||||
./docker/log-manager.sh analyze
|
||||
```
|
||||
|
||||
### 备份管理
|
||||
|
||||
```bash
|
||||
# 查看备份状态
|
||||
./docker/ops-manager.sh backup status
|
||||
|
||||
# 创建完整备份
|
||||
./docker/ops-manager.sh backup create
|
||||
|
||||
# 列出所有备份
|
||||
./docker/ops-manager.sh backup list
|
||||
|
||||
# 清理旧备份
|
||||
./docker/ops-manager.sh backup cleanup
|
||||
```
|
||||
|
||||
### 监控和告警
|
||||
|
||||
```bash
|
||||
# 实时监控
|
||||
./docker/monitor.sh watch
|
||||
|
||||
# 检查告警
|
||||
./docker/alert-manager.sh check
|
||||
|
||||
# 持续监控
|
||||
./docker/alert-manager.sh monitor 300
|
||||
|
||||
# 查看告警历史
|
||||
./docker/alert-manager.sh history
|
||||
```
|
||||
|
||||
## 配置管理
|
||||
|
||||
### 环境配置
|
||||
|
||||
主要配置文件:`.env`
|
||||
|
||||
```bash
|
||||
# 数据库配置
|
||||
POSTGRES_PASSWORD=强密码
|
||||
APP_DB_PASSWORD=强密码
|
||||
|
||||
# 服务器配置
|
||||
SERVER_NAME=your-domain.com
|
||||
NGINX_PORT=80
|
||||
|
||||
# 其他配置...
|
||||
```
|
||||
|
||||
### 配置操作
|
||||
|
||||
```bash
|
||||
# 初始化配置
|
||||
./docker/config-manager.sh init
|
||||
|
||||
# 验证配置
|
||||
./docker/config-manager.sh validate
|
||||
|
||||
# 备份配置
|
||||
./docker/config-manager.sh backup
|
||||
|
||||
# 生成配置
|
||||
./docker/config-manager.sh generate prod
|
||||
```
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 自动优化
|
||||
|
||||
```bash
|
||||
# 应用所有优化
|
||||
./docker/performance-tuning.sh all
|
||||
|
||||
# 仅优化 JVM
|
||||
./docker/performance-tuning.sh jvm
|
||||
|
||||
# 仅优化数据库
|
||||
./docker/performance-tuning.sh database
|
||||
```
|
||||
|
||||
### 手动优化
|
||||
|
||||
1. **JVM 参数调优**:编辑 `.env` 中的 `JVM_OPTS`
|
||||
2. **数据库优化**:修改 `docker/postgres/postgresql.conf`
|
||||
3. **Nginx 优化**:调整 `docker/nginx/nginx.conf`
|
||||
|
||||
## 安全配置
|
||||
|
||||
### 安全设置
|
||||
|
||||
```bash
|
||||
# 应用所有安全配置
|
||||
./docker/security-setup.sh all
|
||||
|
||||
# 配置 SSL/TLS
|
||||
./docker/security-setup.sh ssl
|
||||
|
||||
# 配置防火墙
|
||||
sudo ./docker/security-setup.sh firewall
|
||||
|
||||
# 配置文件权限
|
||||
./docker/security-setup.sh permissions
|
||||
```
|
||||
|
||||
### 安全检查
|
||||
|
||||
```bash
|
||||
# 安全检查
|
||||
./docker/ops-manager.sh security
|
||||
|
||||
# 密码强度检查
|
||||
./docker/secrets-manager.sh check
|
||||
|
||||
# 生成强密码
|
||||
./docker/secrets-manager.sh generate
|
||||
```
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. **容器无法启动**
|
||||
```bash
|
||||
# 查看容器日志
|
||||
docker logs qaup-app
|
||||
|
||||
# 检查资源使用
|
||||
docker stats
|
||||
|
||||
# 重启服务
|
||||
./deploy.sh restart
|
||||
```
|
||||
|
||||
2. **服务无响应**
|
||||
```bash
|
||||
# 健康检查
|
||||
./docker/ops-manager.sh health-check
|
||||
|
||||
# 查看系统状态
|
||||
./docker/ops-manager.sh diagnosis
|
||||
|
||||
# 检查网络连接
|
||||
curl -I http://localhost/health
|
||||
```
|
||||
|
||||
3. **性能问题**
|
||||
```bash
|
||||
# 性能分析
|
||||
./docker/ops-manager.sh performance
|
||||
|
||||
# 应用性能优化
|
||||
./docker/performance-tuning.sh all
|
||||
|
||||
# 监控资源使用
|
||||
./docker/monitor.sh resources
|
||||
```
|
||||
|
||||
### 诊断工具
|
||||
|
||||
```bash
|
||||
# 快速诊断
|
||||
./docker/ops-manager.sh diagnosis
|
||||
|
||||
# 完整集成测试
|
||||
./docker/integration-test.sh full
|
||||
|
||||
# 环境验证
|
||||
./docker/validate-environment.sh
|
||||
|
||||
# 生成运维报告
|
||||
./docker/ops-manager.sh report
|
||||
```
|
||||
|
||||
## 更新和维护
|
||||
|
||||
### 系统更新
|
||||
|
||||
```bash
|
||||
# 零停机更新
|
||||
./docker/zero-downtime-update.sh full
|
||||
|
||||
# 滚动更新
|
||||
./docker/zero-downtime-update.sh rolling
|
||||
|
||||
# 蓝绿部署
|
||||
./docker/zero-downtime-update.sh blue-green
|
||||
```
|
||||
|
||||
### 定期维护
|
||||
|
||||
```bash
|
||||
# 每日维护
|
||||
./docker/ops-manager.sh diagnosis
|
||||
./docker/ops-manager.sh backup create
|
||||
|
||||
# 每周维护
|
||||
./docker/ops-manager.sh cleanup
|
||||
./docker/log-manager.sh cleanup 30
|
||||
|
||||
# 每月维护
|
||||
./docker/ops-manager.sh security
|
||||
./docker/performance-tuning.sh all
|
||||
```
|
||||
|
||||
## 离线部署
|
||||
|
||||
### 准备离线包
|
||||
|
||||
在有网络的环境中:
|
||||
|
||||
```bash
|
||||
# 准备镜像
|
||||
./docker/prepare-offline-images.sh
|
||||
|
||||
# 创建部署包
|
||||
# (参考 docs/离线部署指南.md)
|
||||
```
|
||||
|
||||
### 离线安装
|
||||
|
||||
在内网环境中:
|
||||
|
||||
```bash
|
||||
# 解压部署包
|
||||
tar -xzf qaup-offline-deploy-*.tar.gz
|
||||
|
||||
# 导入镜像
|
||||
./docker/load-images.sh
|
||||
|
||||
# 安装系统
|
||||
./install.sh
|
||||
./deploy.sh install
|
||||
```
|
||||
|
||||
## 监控和告警
|
||||
|
||||
### 监控设置
|
||||
|
||||
```bash
|
||||
# 启动持续监控
|
||||
./docker/monitor.sh watch 60 &
|
||||
|
||||
# 启动告警监控
|
||||
./docker/alert-manager.sh monitor 300 &
|
||||
|
||||
# 配置日志轮转
|
||||
./docker/log-manager.sh setup
|
||||
```
|
||||
|
||||
### 告警配置
|
||||
|
||||
编辑 `docker/healthcheck.yml` 配置告警规则和阈值。
|
||||
|
||||
## 支持和文档
|
||||
|
||||
### 详细文档
|
||||
|
||||
- [部署指南](docs/部署指南.md)
|
||||
- [离线部署指南](docs/离线部署指南.md)
|
||||
- [故障排除](docs/故障排除.md)
|
||||
|
||||
### 获取帮助
|
||||
|
||||
```bash
|
||||
# 查看脚本帮助
|
||||
./deploy.sh help
|
||||
./docker/ops-manager.sh help
|
||||
./docker/monitor.sh help
|
||||
|
||||
# 查看系统状态
|
||||
./docker/ops-manager.sh overview
|
||||
|
||||
# 运行诊断
|
||||
./docker/ops-manager.sh diagnosis
|
||||
```
|
||||
|
||||
### 联系支持
|
||||
|
||||
如遇问题,请提供:
|
||||
1. 系统信息:`./docker/ops-manager.sh report`
|
||||
2. 错误日志:`./docker/log-manager.sh analyze`
|
||||
3. 容器状态:`docker ps -a`
|
||||
4. 配置信息:`.env` 文件(隐藏敏感信息)
|
||||
|
||||
---
|
||||
|
||||
**注意**:本文档持续更新,请定期查看最新版本。
|
||||
180
deploy/config/application-prod.yml
Normal file
180
deploy/config/application-prod.yml
Normal file
@ -0,0 +1,180 @@
|
||||
# QAUP 生产环境配置
|
||||
# 此配置将覆盖 application.yml 中的默认配置
|
||||
|
||||
# 服务器配置
|
||||
server:
|
||||
port: 8080
|
||||
servlet:
|
||||
context-path: /
|
||||
tomcat:
|
||||
uri-encoding: UTF-8
|
||||
accept-count: 1000
|
||||
threads:
|
||||
max: 800
|
||||
min-spare: 100
|
||||
# 生产环境连接超时配置
|
||||
connection-timeout: 20000
|
||||
keep-alive-timeout: 60000
|
||||
|
||||
# 数据源配置
|
||||
spring:
|
||||
datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
driverClassName: org.postgresql.Driver
|
||||
druid:
|
||||
master:
|
||||
url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:qaup}?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
|
||||
username: ${DB_USERNAME:qaup_app}
|
||||
password: ${DB_PASSWORD:qaup123}
|
||||
slave:
|
||||
enabled: false
|
||||
# 生产环境连接池配置
|
||||
initialSize: 10
|
||||
minIdle: 20
|
||||
maxActive: 100
|
||||
maxWait: 60000
|
||||
connectTimeout: 30000
|
||||
socketTimeout: 60000
|
||||
timeBetweenEvictionRunsMillis: 60000
|
||||
minEvictableIdleTimeMillis: 300000
|
||||
maxEvictableIdleTimeMillis: 900000
|
||||
validationQuery: SELECT version()
|
||||
testWhileIdle: true
|
||||
testOnBorrow: false
|
||||
testOnReturn: false
|
||||
# 生产环境监控配置
|
||||
webStatFilter:
|
||||
enabled: false # 生产环境关闭 Web 监控
|
||||
statViewServlet:
|
||||
enabled: false # 生产环境关闭控制台
|
||||
filter:
|
||||
stat:
|
||||
enabled: true
|
||||
log-slow-sql: true
|
||||
slow-sql-millis: 2000
|
||||
merge-sql: true
|
||||
wall:
|
||||
config:
|
||||
multi-statement-allow: true
|
||||
|
||||
# Redis 配置
|
||||
data:
|
||||
redis:
|
||||
host: ${REDIS_HOST:localhost}
|
||||
port: ${REDIS_PORT:6379}
|
||||
password: ${REDIS_PASSWORD:}
|
||||
database: 0
|
||||
timeout: 10s
|
||||
lettuce:
|
||||
pool:
|
||||
min-idle: 5
|
||||
max-idle: 20
|
||||
max-active: 50
|
||||
max-wait: -1ms
|
||||
|
||||
# JPA 配置
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
show-sql: false
|
||||
properties:
|
||||
hibernate:
|
||||
format_sql: false
|
||||
jdbc:
|
||||
batch_size: 100
|
||||
fetch_size: 100
|
||||
cache:
|
||||
use_second_level_cache: true
|
||||
use_query_cache: true
|
||||
order_inserts: true
|
||||
order_updates: true
|
||||
batch_versioned_data: true
|
||||
|
||||
# 日志配置
|
||||
logging:
|
||||
level:
|
||||
com.qaup: info
|
||||
org.springframework: warn
|
||||
org.hibernate: warn
|
||||
com.alibaba.druid: warn
|
||||
pattern:
|
||||
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n"
|
||||
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n"
|
||||
file:
|
||||
name: /app/logs/qaup.log
|
||||
max-size: 100MB
|
||||
max-history: 30
|
||||
|
||||
# 项目配置
|
||||
qaup:
|
||||
name: QAUP
|
||||
version: 1.0.1
|
||||
copyrightYear: 2025
|
||||
profile: ${UPLOAD_PATH:/app/uploadPath}
|
||||
addressEnabled: false
|
||||
captchaType: math
|
||||
|
||||
# 用户配置
|
||||
user:
|
||||
password:
|
||||
maxRetryCount: 5
|
||||
lockTime: 10
|
||||
|
||||
# Token 配置
|
||||
token:
|
||||
header: Authorization
|
||||
secret: ${TOKEN_SECRET:abcdefghijklmnopqrstuvwxyz}
|
||||
expireTime: 30
|
||||
|
||||
# 管理端点配置
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: ${MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE:health,info,metrics}
|
||||
endpoint:
|
||||
health:
|
||||
show-details: ${MANAGEMENT_ENDPOINT_HEALTH_SHOW_DETAILS:when_authorized}
|
||||
metrics:
|
||||
export:
|
||||
simple:
|
||||
enabled: true
|
||||
|
||||
# XSS 防护
|
||||
xss:
|
||||
enabled: true
|
||||
excludes: /system/notice
|
||||
urlPatterns: /system/*,/monitor/*,/tool/*
|
||||
|
||||
# 数据采集配置
|
||||
data:
|
||||
collector:
|
||||
interval: 250
|
||||
route:
|
||||
interval: 5000
|
||||
detection:
|
||||
interval: 1000
|
||||
airport-api:
|
||||
base-url: ${AIRPORT_API_BASE_URL:http://localhost:8090}
|
||||
auth:
|
||||
username: ${AIRPORT_API_USERNAME:dianxin}
|
||||
password: ${AIRPORT_API_PASSWORD:dianxin@123}
|
||||
vehicle-api:
|
||||
base-url: ${VEHICLE_API_BASE_URL:http://localhost:8090}
|
||||
timeout: 1000
|
||||
retry-attempts: 3
|
||||
unmanned-vehicle:
|
||||
persistence:
|
||||
enabled: true
|
||||
batch-size: 50
|
||||
location-retention-days: 90
|
||||
command-retention-days: 365
|
||||
retention:
|
||||
redis-expire-seconds: 60
|
||||
postgresql-days: 30
|
||||
|
||||
# 坐标系统配置
|
||||
coordinate-system:
|
||||
airport:
|
||||
center-longitude: ${AIRPORT_CENTER_LONGITUDE:120.0834104}
|
||||
center-latitude: ${AIRPORT_CENTER_LATITUDE:36.35406879}
|
||||
379
deploy/deploy.sh
Executable file
379
deploy/deploy.sh
Executable file
@ -0,0 +1,379 @@
|
||||
#!/bin/bash
|
||||
|
||||
# QAUP 主部署脚本
|
||||
# 用于在客户环境中一键部署 QAUP 系统
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$SCRIPT_DIR"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_message() {
|
||||
local color=$1
|
||||
local message=$2
|
||||
echo -e "${color}${message}${NC}"
|
||||
}
|
||||
|
||||
print_banner() {
|
||||
echo ""
|
||||
print_message $BLUE "========================================="
|
||||
print_message $BLUE " QAUP 系统 Docker 部署脚本"
|
||||
print_message $BLUE "========================================="
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 检查系统要求
|
||||
check_prerequisites() {
|
||||
print_message $BLUE "检查系统要求..."
|
||||
|
||||
local errors=0
|
||||
|
||||
# 检查 Docker
|
||||
if ! command -v docker &> /dev/null; then
|
||||
print_message $RED "✗ Docker 未安装"
|
||||
errors=$((errors + 1))
|
||||
else
|
||||
print_message $GREEN "✓ Docker 已安装"
|
||||
fi
|
||||
|
||||
# 检查 Docker Compose
|
||||
if ! command -v docker-compose &> /dev/null; then
|
||||
print_message $RED "✗ Docker Compose 未安装"
|
||||
errors=$((errors + 1))
|
||||
else
|
||||
print_message $GREEN "✓ Docker Compose 已安装"
|
||||
fi
|
||||
|
||||
# 检查 curl
|
||||
if ! command -v curl &> /dev/null; then
|
||||
print_message $YELLOW "⚠ curl 未安装,建议安装以支持健康检查"
|
||||
else
|
||||
print_message $GREEN "✓ curl 已安装"
|
||||
fi
|
||||
|
||||
if [ $errors -gt 0 ]; then
|
||||
print_message $RED "系统要求检查失败,请安装缺失的组件"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_message $GREEN "系统要求检查通过"
|
||||
}
|
||||
|
||||
# 初始化配置
|
||||
initialize_config() {
|
||||
print_message $BLUE "初始化配置..."
|
||||
|
||||
# 运行配置管理器
|
||||
if [ -x "$PROJECT_ROOT/docker/config-manager.sh" ]; then
|
||||
"$PROJECT_ROOT/docker/config-manager.sh" init
|
||||
else
|
||||
print_message $RED "配置管理脚本不存在或不可执行"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_message $GREEN "配置初始化完成"
|
||||
}
|
||||
|
||||
# 验证环境
|
||||
validate_environment() {
|
||||
print_message $BLUE "验证部署环境..."
|
||||
|
||||
if [ -x "$PROJECT_ROOT/docker/validate-environment.sh" ]; then
|
||||
if "$PROJECT_ROOT/docker/validate-environment.sh"; then
|
||||
print_message $GREEN "环境验证通过"
|
||||
else
|
||||
print_message $RED "环境验证失败,请修复问题后重试"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
print_message $YELLOW "环境验证脚本不存在,跳过验证"
|
||||
fi
|
||||
}
|
||||
|
||||
# 构建镜像
|
||||
build_images() {
|
||||
print_message $BLUE "构建 Docker 镜像..."
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# 使用 Docker Compose 管理器构建
|
||||
if [ -x "$PROJECT_ROOT/docker/docker-compose-manager.sh" ]; then
|
||||
"$PROJECT_ROOT/docker/docker-compose-manager.sh" prod build
|
||||
else
|
||||
# 直接使用 docker-compose 构建
|
||||
docker-compose -f docker-compose.yml -f docker-compose.prod.yml build
|
||||
fi
|
||||
|
||||
print_message $GREEN "镜像构建完成"
|
||||
}
|
||||
|
||||
# 启动服务
|
||||
start_services() {
|
||||
print_message $BLUE "启动服务..."
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# 使用 Docker Compose 管理器启动
|
||||
if [ -x "$PROJECT_ROOT/docker/docker-compose-manager.sh" ]; then
|
||||
"$PROJECT_ROOT/docker/docker-compose-manager.sh" prod start
|
||||
else
|
||||
# 直接使用 docker-compose 启动
|
||||
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
|
||||
fi
|
||||
|
||||
print_message $GREEN "服务启动完成"
|
||||
}
|
||||
|
||||
# 等待服务就绪
|
||||
wait_for_services() {
|
||||
print_message $BLUE "等待服务就绪..."
|
||||
|
||||
local max_wait=300 # 最大等待5分钟
|
||||
local wait_time=0
|
||||
local interval=10
|
||||
|
||||
while [ $wait_time -lt $max_wait ]; do
|
||||
if curl -f -s http://localhost/health &>/dev/null; then
|
||||
print_message $GREEN "✓ 服务已就绪"
|
||||
return 0
|
||||
fi
|
||||
|
||||
print_message $YELLOW "等待服务启动... (${wait_time}s/${max_wait}s)"
|
||||
sleep $interval
|
||||
wait_time=$((wait_time + interval))
|
||||
done
|
||||
|
||||
print_message $RED "服务启动超时"
|
||||
return 1
|
||||
}
|
||||
|
||||
# 运行健康检查
|
||||
run_health_checks() {
|
||||
print_message $BLUE "运行健康检查..."
|
||||
|
||||
if [ -x "$PROJECT_ROOT/docker/monitor.sh" ]; then
|
||||
"$PROJECT_ROOT/docker/monitor.sh" health
|
||||
else
|
||||
print_message $YELLOW "监控脚本不存在,跳过健康检查"
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示部署信息
|
||||
show_deployment_info() {
|
||||
print_message $GREEN "========================================="
|
||||
print_message $GREEN " QAUP 系统部署完成!"
|
||||
print_message $GREEN "========================================="
|
||||
echo ""
|
||||
|
||||
print_message $BLUE "访问信息:"
|
||||
echo " 前端地址: http://localhost"
|
||||
echo " API 地址: http://localhost/prod-api"
|
||||
echo " 健康检查: http://localhost/health"
|
||||
echo ""
|
||||
|
||||
print_message $BLUE "管理命令:"
|
||||
echo " 查看状态: docker/docker-compose-manager.sh prod status"
|
||||
echo " 查看日志: docker/log-manager.sh view all"
|
||||
echo " 系统监控: docker/monitor.sh watch"
|
||||
echo " 停止服务: docker/docker-compose-manager.sh prod stop"
|
||||
echo ""
|
||||
|
||||
print_message $BLUE "重要文件:"
|
||||
echo " 环境配置: .env"
|
||||
echo " 日志目录: ./logs/"
|
||||
echo " 数据目录: ./data/"
|
||||
echo " 备份目录: ./backup/"
|
||||
echo ""
|
||||
|
||||
print_message $YELLOW "注意事项:"
|
||||
echo " 1. 请妥善保管 .env 文件中的密码"
|
||||
echo " 2. 定期备份数据库和配置文件"
|
||||
echo " 3. 监控系统资源使用情况"
|
||||
echo " 4. 查看部署文档了解更多管理命令"
|
||||
}
|
||||
|
||||
# 清理部署
|
||||
cleanup_deployment() {
|
||||
print_message $YELLOW "清理部署..."
|
||||
|
||||
read -p "确定要清理部署吗?这将删除所有容器和数据 (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
if [ -x "$PROJECT_ROOT/docker/docker-compose-manager.sh" ]; then
|
||||
"$PROJECT_ROOT/docker/docker-compose-manager.sh" prod clean
|
||||
else
|
||||
docker-compose -f docker-compose.yml -f docker-compose.prod.yml down -v
|
||||
docker system prune -f
|
||||
fi
|
||||
|
||||
print_message $GREEN "部署清理完成"
|
||||
else
|
||||
print_message $BLUE "取消清理操作"
|
||||
fi
|
||||
}
|
||||
|
||||
# 更新部署
|
||||
update_deployment() {
|
||||
print_message $BLUE "更新部署..."
|
||||
|
||||
# 备份当前配置
|
||||
if [ -x "$PROJECT_ROOT/docker/config-manager.sh" ]; then
|
||||
"$PROJECT_ROOT/docker/config-manager.sh" backup
|
||||
fi
|
||||
|
||||
# 重新构建镜像
|
||||
build_images
|
||||
|
||||
# 重启服务
|
||||
cd "$PROJECT_ROOT"
|
||||
if [ -x "$PROJECT_ROOT/docker/docker-compose-manager.sh" ]; then
|
||||
"$PROJECT_ROOT/docker/docker-compose-manager.sh" prod restart
|
||||
else
|
||||
docker-compose -f docker-compose.yml -f docker-compose.prod.yml restart
|
||||
fi
|
||||
|
||||
# 等待服务就绪
|
||||
wait_for_services
|
||||
|
||||
print_message $GREEN "部署更新完成"
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "QAUP Docker 部署脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [命令] [选项]"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " install 完整安装部署"
|
||||
echo " start 启动服务"
|
||||
echo " stop 停止服务"
|
||||
echo " restart 重启服务"
|
||||
echo " status 查看状态"
|
||||
echo " update 更新部署"
|
||||
echo " cleanup 清理部署"
|
||||
echo " logs 查看日志"
|
||||
echo " monitor 系统监控"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 install # 完整安装"
|
||||
echo " $0 start # 启动服务"
|
||||
echo " $0 logs app # 查看应用日志"
|
||||
echo " $0 monitor # 系统监控"
|
||||
}
|
||||
|
||||
# 完整安装流程
|
||||
full_install() {
|
||||
print_banner
|
||||
|
||||
print_message $BLUE "开始 QAUP 系统完整安装..."
|
||||
echo ""
|
||||
|
||||
# 执行安装步骤
|
||||
check_prerequisites
|
||||
echo ""
|
||||
|
||||
initialize_config
|
||||
echo ""
|
||||
|
||||
validate_environment
|
||||
echo ""
|
||||
|
||||
build_images
|
||||
echo ""
|
||||
|
||||
start_services
|
||||
echo ""
|
||||
|
||||
if wait_for_services; then
|
||||
echo ""
|
||||
run_health_checks
|
||||
echo ""
|
||||
show_deployment_info
|
||||
else
|
||||
print_message $RED "部署失败,请检查日志"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
if [ $# -eq 0 ]; then
|
||||
show_help
|
||||
exit 0
|
||||
fi
|
||||
|
||||
local command=$1
|
||||
shift
|
||||
|
||||
case $command in
|
||||
install)
|
||||
full_install
|
||||
;;
|
||||
start)
|
||||
start_services
|
||||
wait_for_services
|
||||
;;
|
||||
stop)
|
||||
if [ -x "$PROJECT_ROOT/docker/docker-compose-manager.sh" ]; then
|
||||
"$PROJECT_ROOT/docker/docker-compose-manager.sh" prod stop
|
||||
else
|
||||
docker-compose -f docker-compose.yml -f docker-compose.prod.yml stop
|
||||
fi
|
||||
;;
|
||||
restart)
|
||||
if [ -x "$PROJECT_ROOT/docker/docker-compose-manager.sh" ]; then
|
||||
"$PROJECT_ROOT/docker/docker-compose-manager.sh" prod restart
|
||||
else
|
||||
docker-compose -f docker-compose.yml -f docker-compose.prod.yml restart
|
||||
fi
|
||||
wait_for_services
|
||||
;;
|
||||
status)
|
||||
if [ -x "$PROJECT_ROOT/docker/docker-compose-manager.sh" ]; then
|
||||
"$PROJECT_ROOT/docker/docker-compose-manager.sh" prod status
|
||||
else
|
||||
docker-compose -f docker-compose.yml -f docker-compose.prod.yml ps
|
||||
fi
|
||||
;;
|
||||
update)
|
||||
update_deployment
|
||||
;;
|
||||
cleanup)
|
||||
cleanup_deployment
|
||||
;;
|
||||
logs)
|
||||
if [ -x "$PROJECT_ROOT/docker/log-manager.sh" ]; then
|
||||
"$PROJECT_ROOT/docker/log-manager.sh" view "$@"
|
||||
else
|
||||
docker-compose -f docker-compose.yml -f docker-compose.prod.yml logs -f "$@"
|
||||
fi
|
||||
;;
|
||||
monitor)
|
||||
if [ -x "$PROJECT_ROOT/docker/monitor.sh" ]; then
|
||||
"$PROJECT_ROOT/docker/monitor.sh" watch
|
||||
else
|
||||
print_message $YELLOW "监控脚本不存在"
|
||||
fi
|
||||
;;
|
||||
help|--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
print_message $RED "未知命令: $command"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
60
deploy/docker-compose.dev.yml
Normal file
60
deploy/docker-compose.dev.yml
Normal file
@ -0,0 +1,60 @@
|
||||
version: '3.8'
|
||||
|
||||
# 开发环境配置覆盖
|
||||
services:
|
||||
qaup-postgres:
|
||||
# 开发环境暴露数据库端口便于调试
|
||||
ports:
|
||||
- "5432:5432"
|
||||
# 开发环境资源配置较低
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512M
|
||||
cpus: '1.0'
|
||||
reservations:
|
||||
memory: 256M
|
||||
cpus: '0.5'
|
||||
|
||||
qaup-redis:
|
||||
# 开发环境暴露 Redis 端口
|
||||
ports:
|
||||
- "6379:6379"
|
||||
# 开发环境不需要密码
|
||||
command: redis-server --appendonly yes
|
||||
|
||||
qaup-app:
|
||||
# 开发环境暴露应用端口
|
||||
ports:
|
||||
- "8080:8080"
|
||||
# 开发环境变量
|
||||
environment:
|
||||
SPRING_PROFILES_ACTIVE: dev
|
||||
# 开发环境日志级别
|
||||
LOGGING_LEVEL_COM_QAUP: debug
|
||||
LOGGING_LEVEL_ORG_SPRINGFRAMEWORK: info
|
||||
# 热重载配置
|
||||
SPRING_DEVTOOLS_RESTART_ENABLED: true
|
||||
# JVM 调试配置
|
||||
JVM_OPTS: -Dname=qaup-admin.jar -Duser.timezone=Asia/Shanghai -Xms256m -Xmx512m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
|
||||
# 开发环境挂载源码(如果需要热重载)
|
||||
volumes:
|
||||
- app_logs:/app/logs
|
||||
- app_uploads:/app/uploadPath
|
||||
- ./qaup-admin/target:/app/target:ro
|
||||
# 暴露调试端口
|
||||
ports:
|
||||
- "8080:8080"
|
||||
- "5005:5005" # JVM 调试端口
|
||||
|
||||
qaup-nginx:
|
||||
# 开发环境配置
|
||||
environment:
|
||||
NGINX_PORT: 80
|
||||
SERVER_NAME: localhost
|
||||
API_PREFIX: /api
|
||||
BACKEND_HOST: qaup-app
|
||||
BACKEND_PORT: 8080
|
||||
MAX_UPLOAD_SIZE: 10M
|
||||
ports:
|
||||
- "80:80"
|
||||
94
deploy/docker-compose.prod.yml
Normal file
94
deploy/docker-compose.prod.yml
Normal file
@ -0,0 +1,94 @@
|
||||
version: '3.8'
|
||||
|
||||
# 生产环境配置覆盖
|
||||
services:
|
||||
qaup-postgres:
|
||||
# 生产环境不暴露数据库端口
|
||||
ports: []
|
||||
# 增强资源配置
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 2G
|
||||
cpus: '2.0'
|
||||
reservations:
|
||||
memory: 1G
|
||||
cpus: '1.0'
|
||||
# 生产环境日志配置
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "100m"
|
||||
max-file: "5"
|
||||
|
||||
qaup-redis:
|
||||
# 生产环境不暴露 Redis 端口
|
||||
ports: []
|
||||
# 启用持久化
|
||||
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD} --maxmemory 256mb --maxmemory-policy allkeys-lru
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "50m"
|
||||
max-file: "3"
|
||||
|
||||
qaup-app:
|
||||
# 生产环境不直接暴露应用端口
|
||||
ports: []
|
||||
# 生产环境变量
|
||||
environment:
|
||||
SPRING_PROFILES_ACTIVE: prod
|
||||
# 安全配置
|
||||
MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: health,info,metrics
|
||||
MANAGEMENT_ENDPOINT_HEALTH_SHOW_DETAILS: when_authorized
|
||||
# 性能配置
|
||||
JVM_OPTS: -Dname=qaup-admin.jar -Duser.timezone=Asia/Shanghai -server -Xms1g -Xmx2g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:+UseStringDeduplication -XX:+OptimizeStringConcat
|
||||
# 增强资源配置
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 3G
|
||||
cpus: '2.0'
|
||||
reservations:
|
||||
memory: 2G
|
||||
cpus: '1.5'
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "200m"
|
||||
max-file: "10"
|
||||
|
||||
qaup-nginx:
|
||||
# 生产环境端口配置
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
# 生产环境配置
|
||||
environment:
|
||||
NGINX_PORT: 80
|
||||
SERVER_NAME: ${PRODUCTION_DOMAIN:-localhost}
|
||||
API_PREFIX: /prod-api
|
||||
BACKEND_HOST: qaup-app
|
||||
BACKEND_PORT: 8080
|
||||
MAX_UPLOAD_SIZE: 50M
|
||||
STATIC_CACHE_TIME: 30d
|
||||
# SSL 证书挂载(如果需要)
|
||||
volumes:
|
||||
- nginx_logs:/var/log/nginx
|
||||
- nginx_cache:/var/cache/nginx
|
||||
- ${SSL_CERT_DIR:-./ssl}:/etc/nginx/ssl:ro
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "100m"
|
||||
max-file: "5"
|
||||
|
||||
# 生产环境网络配置
|
||||
networks:
|
||||
qaup-network:
|
||||
driver: bridge
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.20.0.0/16
|
||||
driver_opts:
|
||||
com.docker.network.bridge.name: qaup-br0
|
||||
232
deploy/docker-compose.yml
Normal file
232
deploy/docker-compose.yml
Normal file
@ -0,0 +1,232 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# PostgreSQL 数据库服务
|
||||
qaup-postgres:
|
||||
image: postgis/postgis:15-3.3
|
||||
container_name: qaup-postgres
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: ${POSTGRES_DB:-qaup}
|
||||
POSTGRES_USER: ${POSTGRES_USER:-postgres}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-qaup123}
|
||||
APP_DB_USER: ${APP_DB_USER:-qaup_app}
|
||||
APP_DB_PASSWORD: ${APP_DB_PASSWORD:-qaup123}
|
||||
PGDATA: /var/lib/postgresql/data/pgdata
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- ./docker/postgres/init-db.sh:/docker-entrypoint-initdb.d/01-init-db.sh:ro
|
||||
- ./docker/postgres/qaup_database_schema.sql:/docker-entrypoint-initdb.d/02-schema.sql:ro
|
||||
- ./docker/postgres/postgresql.conf:/etc/postgresql/postgresql.conf:ro
|
||||
- postgres_backup:/backup
|
||||
- postgres_logs:/var/log/postgresql
|
||||
ports:
|
||||
- "${POSTGRES_PORT:-5432}:5432"
|
||||
networks:
|
||||
- qaup-network
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "/docker-entrypoint-initdb.d/01-init-db.sh || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 60s
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 1G
|
||||
cpus: '1.0'
|
||||
reservations:
|
||||
memory: 512M
|
||||
cpus: '0.5'
|
||||
|
||||
# Redis 缓存服务
|
||||
qaup-redis:
|
||||
image: redis:7-alpine
|
||||
container_name: qaup-redis
|
||||
restart: unless-stopped
|
||||
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-}
|
||||
environment:
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD:-}
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
- redis_logs:/var/log/redis
|
||||
ports:
|
||||
- "${REDIS_PORT:-6379}:6379"
|
||||
networks:
|
||||
- qaup-network
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512M
|
||||
cpus: '0.5'
|
||||
reservations:
|
||||
memory: 128M
|
||||
cpus: '0.1'
|
||||
|
||||
# QAUP 应用服务
|
||||
qaup-app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
image: qaup-app:latest
|
||||
container_name: qaup-app
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
# Spring 配置
|
||||
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-prod}
|
||||
|
||||
# 数据库配置
|
||||
DB_HOST: qaup-postgres
|
||||
DB_PORT: 5432
|
||||
DB_NAME: ${POSTGRES_DB:-qaup}
|
||||
DB_USERNAME: ${APP_DB_USER:-qaup_app}
|
||||
DB_PASSWORD: ${APP_DB_PASSWORD:-qaup123}
|
||||
|
||||
# Redis 配置
|
||||
REDIS_HOST: qaup-redis
|
||||
REDIS_PORT: 6379
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD:-}
|
||||
|
||||
# 应用配置
|
||||
SERVER_PORT: 8080
|
||||
UPLOAD_PATH: /app/uploadPath
|
||||
|
||||
# JVM 配置
|
||||
JVM_OPTS: ${JVM_OPTS:--Dname=qaup-admin.jar -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m}
|
||||
volumes:
|
||||
- app_logs:/app/logs
|
||||
- app_uploads:/app/uploadPath
|
||||
- app_temp:/tmp
|
||||
ports:
|
||||
- "${APP_PORT:-8080}:8080"
|
||||
networks:
|
||||
- qaup-network
|
||||
depends_on:
|
||||
qaup-postgres:
|
||||
condition: service_healthy
|
||||
qaup-redis:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 120s
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 2G
|
||||
cpus: '2.0'
|
||||
reservations:
|
||||
memory: 1G
|
||||
cpus: '1.0'
|
||||
|
||||
# Nginx 反向代理服务
|
||||
qaup-nginx:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/nginx/Dockerfile
|
||||
image: qaup-nginx:latest
|
||||
container_name: qaup-nginx
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
NGINX_PORT: 80
|
||||
SERVER_NAME: ${SERVER_NAME:-localhost}
|
||||
API_PREFIX: ${API_PREFIX:-/prod-api}
|
||||
BACKEND_HOST: qaup-app
|
||||
BACKEND_PORT: 8080
|
||||
MAX_UPLOAD_SIZE: ${MAX_UPLOAD_SIZE:-20M}
|
||||
volumes:
|
||||
- nginx_logs:/var/log/nginx
|
||||
- nginx_cache:/var/cache/nginx
|
||||
ports:
|
||||
- "${NGINX_PORT:-80}:80"
|
||||
- "${NGINX_HTTPS_PORT:-443}:443"
|
||||
networks:
|
||||
- qaup-network
|
||||
depends_on:
|
||||
qaup-app:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 256M
|
||||
cpus: '0.5'
|
||||
reservations:
|
||||
memory: 64M
|
||||
cpus: '0.1'
|
||||
|
||||
# 数据卷定义
|
||||
volumes:
|
||||
postgres_data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: ${DATA_DIR:-./data}/postgres
|
||||
postgres_backup:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: ${BACKUP_DIR:-./backup}/postgres
|
||||
postgres_logs:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: ${LOG_DIR:-./logs}/postgres
|
||||
redis_data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: ${DATA_DIR:-./data}/redis
|
||||
redis_logs:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: ${LOG_DIR:-./logs}/redis
|
||||
app_logs:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: ${LOG_DIR:-./logs}/app
|
||||
app_uploads:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: ${DATA_DIR:-./data}/uploads
|
||||
app_temp:
|
||||
driver: local
|
||||
nginx_logs:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: ${LOG_DIR:-./logs}/nginx
|
||||
nginx_cache:
|
||||
driver: local
|
||||
|
||||
# 网络定义
|
||||
networks:
|
||||
qaup-network:
|
||||
driver: bridge
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.20.0.0/16
|
||||
359
deploy/docker/alert-manager.sh
Executable file
359
deploy/docker/alert-manager.sh
Executable file
@ -0,0 +1,359 @@
|
||||
#!/bin/bash
|
||||
|
||||
# QAUP 告警管理脚本
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
ALERT_LOG="$PROJECT_ROOT/logs/alerts.log"
|
||||
ALERT_CONFIG="$SCRIPT_DIR/healthcheck.yml"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_message() {
|
||||
local color=$1
|
||||
local message=$2
|
||||
echo -e "${color}${message}${NC}"
|
||||
}
|
||||
|
||||
# 获取时间戳
|
||||
get_timestamp() {
|
||||
date '+%Y-%m-%d %H:%M:%S'
|
||||
}
|
||||
|
||||
# 记录告警
|
||||
log_alert() {
|
||||
local severity=$1
|
||||
local service=$2
|
||||
local message=$3
|
||||
local timestamp=$(get_timestamp)
|
||||
|
||||
# 确保日志目录存在
|
||||
mkdir -p "$(dirname "$ALERT_LOG")"
|
||||
|
||||
# 记录到日志文件
|
||||
echo "[$timestamp] [$severity] [$service] $message" >> "$ALERT_LOG"
|
||||
|
||||
# 输出到控制台
|
||||
case $severity in
|
||||
"CRITICAL")
|
||||
print_message $RED "🚨 CRITICAL [$service]: $message"
|
||||
;;
|
||||
"WARNING")
|
||||
print_message $YELLOW "⚠️ WARNING [$service]: $message"
|
||||
;;
|
||||
"INFO")
|
||||
print_message $BLUE "ℹ️ INFO [$service]: $message"
|
||||
;;
|
||||
*)
|
||||
print_message $GREEN "✓ [$service]: $message"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 检查容器状态
|
||||
check_container_alerts() {
|
||||
local containers=("qaup-postgres" "qaup-redis" "qaup-app" "qaup-nginx")
|
||||
|
||||
for container in "${containers[@]}"; do
|
||||
if ! docker ps --format "{{.Names}}" | grep -q "^${container}$"; then
|
||||
log_alert "CRITICAL" "$container" "容器已停止运行"
|
||||
elif docker ps --format "{{.Names}}\t{{.Status}}" | grep "$container" | grep -q "unhealthy"; then
|
||||
log_alert "WARNING" "$container" "容器健康检查失败"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# 检查资源使用率
|
||||
check_resource_alerts() {
|
||||
# 检查磁盘使用率
|
||||
local disk_usage=$(df . | awk 'NR==2 {print $5}' | sed 's/%//')
|
||||
if [ $disk_usage -gt 80 ]; then
|
||||
log_alert "WARNING" "SYSTEM" "磁盘使用率过高: ${disk_usage}%"
|
||||
fi
|
||||
|
||||
# 检查内存使用率
|
||||
local mem_usage=$(free | awk 'NR==2{printf "%.0f", $3*100/$2}')
|
||||
if [ $mem_usage -gt 85 ]; then
|
||||
log_alert "WARNING" "SYSTEM" "内存使用率过高: ${mem_usage}%"
|
||||
fi
|
||||
|
||||
# 检查容器资源使用
|
||||
while IFS= read -r line; do
|
||||
local container=$(echo "$line" | awk '{print $1}')
|
||||
local cpu_usage=$(echo "$line" | awk '{print $2}' | sed 's/%//')
|
||||
local mem_usage=$(echo "$line" | awk '{print $7}' | sed 's/%//')
|
||||
|
||||
if (( $(echo "$cpu_usage > 80" | bc -l) )); then
|
||||
log_alert "WARNING" "$container" "CPU使用率过高: ${cpu_usage}%"
|
||||
fi
|
||||
|
||||
if (( $(echo "$mem_usage > 85" | bc -l) )); then
|
||||
log_alert "WARNING" "$container" "内存使用率过高: ${mem_usage}%"
|
||||
fi
|
||||
done < <(docker stats --no-stream --format "{{.Container}} {{.CPUPerc}} {{.MemUsage}} {{.MemPerc}}" 2>/dev/null)
|
||||
}
|
||||
|
||||
# 检查服务响应时间
|
||||
check_response_time_alerts() {
|
||||
# 检查应用响应时间
|
||||
local app_response_time=$(curl -o /dev/null -s -w '%{time_total}' http://localhost:8080/actuator/health 2>/dev/null || echo "999")
|
||||
local app_time_ms=$(echo "$app_response_time * 1000" | bc)
|
||||
|
||||
if (( $(echo "$app_time_ms > 5000" | bc -l) )); then
|
||||
log_alert "WARNING" "qaup-app" "响应时间过长: ${app_time_ms}ms"
|
||||
fi
|
||||
|
||||
# 检查 Nginx 响应时间
|
||||
local nginx_response_time=$(curl -o /dev/null -s -w '%{time_total}' http://localhost/health 2>/dev/null || echo "999")
|
||||
local nginx_time_ms=$(echo "$nginx_response_time * 1000" | bc)
|
||||
|
||||
if (( $(echo "$nginx_time_ms > 2000" | bc -l) )); then
|
||||
log_alert "WARNING" "qaup-nginx" "响应时间过长: ${nginx_time_ms}ms"
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查错误日志
|
||||
check_error_log_alerts() {
|
||||
local error_patterns=("ERROR" "FATAL" "Exception" "failed" "timeout")
|
||||
local containers=("qaup-app" "qaup-nginx" "qaup-postgres" "qaup-redis")
|
||||
|
||||
for container in "${containers[@]}"; do
|
||||
# 检查最近5分钟的日志
|
||||
local recent_logs=$(docker logs "$container" --since 5m 2>&1)
|
||||
|
||||
for pattern in "${error_patterns[@]}"; do
|
||||
local error_count=$(echo "$recent_logs" | grep -c "$pattern" || echo "0")
|
||||
|
||||
if [ $error_count -gt 10 ]; then
|
||||
log_alert "WARNING" "$container" "检测到大量错误日志: $pattern ($error_count 次)"
|
||||
fi
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
# 检查数据库连接
|
||||
check_database_alerts() {
|
||||
# 检查数据库连接数
|
||||
local connection_count=$(docker exec qaup-postgres psql -U postgres -d qaup -t -c "SELECT count(*) FROM pg_stat_activity;" 2>/dev/null | xargs || echo "0")
|
||||
|
||||
if [ $connection_count -gt 150 ]; then
|
||||
log_alert "WARNING" "qaup-postgres" "数据库连接数过多: $connection_count"
|
||||
fi
|
||||
|
||||
# 检查数据库锁等待
|
||||
local lock_waits=$(docker exec qaup-postgres psql -U postgres -d qaup -t -c "SELECT count(*) FROM pg_stat_activity WHERE wait_event_type = 'Lock';" 2>/dev/null | xargs || echo "0")
|
||||
|
||||
if [ $lock_waits -gt 5 ]; then
|
||||
log_alert "WARNING" "qaup-postgres" "检测到数据库锁等待: $lock_waits 个连接"
|
||||
fi
|
||||
}
|
||||
|
||||
# 发送邮件告警
|
||||
send_email_alert() {
|
||||
local subject=$1
|
||||
local message=$2
|
||||
local recipients=${3:-"admin@example.com"}
|
||||
|
||||
# 这里可以集成实际的邮件发送服务
|
||||
# 例如使用 sendmail, postfix 或者第三方邮件服务
|
||||
|
||||
print_message $BLUE "邮件告警功能需要配置邮件服务器"
|
||||
print_message $YELLOW "主题: $subject"
|
||||
print_message $YELLOW "内容: $message"
|
||||
print_message $YELLOW "收件人: $recipients"
|
||||
}
|
||||
|
||||
# 发送钉钉/企业微信告警
|
||||
send_webhook_alert() {
|
||||
local webhook_url=$1
|
||||
local message=$2
|
||||
|
||||
if [ -n "$webhook_url" ]; then
|
||||
curl -X POST "$webhook_url" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"text\":\"$message\"}" \
|
||||
&>/dev/null
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
print_message $GREEN "Webhook 告警发送成功"
|
||||
else
|
||||
print_message $RED "Webhook 告警发送失败"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# 运行所有检查
|
||||
run_all_checks() {
|
||||
print_message $BLUE "开始系统告警检查..."
|
||||
|
||||
check_container_alerts
|
||||
check_resource_alerts
|
||||
check_response_time_alerts
|
||||
check_error_log_alerts
|
||||
check_database_alerts
|
||||
|
||||
print_message $GREEN "告警检查完成"
|
||||
}
|
||||
|
||||
# 持续监控模式
|
||||
continuous_monitoring() {
|
||||
local interval=${1:-300} # 默认5分钟间隔
|
||||
|
||||
print_message $GREEN "开始持续告警监控 (间隔: ${interval}秒)"
|
||||
print_message $YELLOW "按 Ctrl+C 停止监控"
|
||||
|
||||
while true; do
|
||||
run_all_checks
|
||||
sleep $interval
|
||||
done
|
||||
}
|
||||
|
||||
# 查看告警历史
|
||||
view_alert_history() {
|
||||
local lines=${1:-50}
|
||||
local filter=${2:-""}
|
||||
|
||||
if [ ! -f "$ALERT_LOG" ]; then
|
||||
print_message $YELLOW "告警日志文件不存在: $ALERT_LOG"
|
||||
return
|
||||
fi
|
||||
|
||||
print_message $BLUE "告警历史 (最近 $lines 条):"
|
||||
|
||||
if [ -n "$filter" ]; then
|
||||
tail -n $lines "$ALERT_LOG" | grep "$filter"
|
||||
else
|
||||
tail -n $lines "$ALERT_LOG"
|
||||
fi
|
||||
}
|
||||
|
||||
# 清理告警日志
|
||||
cleanup_alert_logs() {
|
||||
local days=${1:-30}
|
||||
|
||||
if [ -f "$ALERT_LOG" ]; then
|
||||
# 备份当前日志
|
||||
local backup_file="${ALERT_LOG}.backup.$(date +%Y%m%d)"
|
||||
cp "$ALERT_LOG" "$backup_file"
|
||||
|
||||
# 只保留最近指定天数的日志
|
||||
local cutoff_date=$(date -d "$days days ago" '+%Y-%m-%d')
|
||||
awk -v cutoff="$cutoff_date" '$0 >= "["cutoff {print}' "$ALERT_LOG" > "${ALERT_LOG}.tmp"
|
||||
mv "${ALERT_LOG}.tmp" "$ALERT_LOG"
|
||||
|
||||
print_message $GREEN "告警日志已清理,保留 $days 天内的记录"
|
||||
print_message $BLUE "备份文件: $backup_file"
|
||||
else
|
||||
print_message $YELLOW "告警日志文件不存在"
|
||||
fi
|
||||
}
|
||||
|
||||
# 生成告警统计报告
|
||||
generate_alert_stats() {
|
||||
if [ ! -f "$ALERT_LOG" ]; then
|
||||
print_message $YELLOW "告警日志文件不存在"
|
||||
return
|
||||
fi
|
||||
|
||||
print_message $BLUE "告警统计报告:"
|
||||
echo ""
|
||||
|
||||
# 按严重程度统计
|
||||
print_message $BLUE "按严重程度统计:"
|
||||
echo "CRITICAL: $(grep -c "CRITICAL" "$ALERT_LOG" || echo "0")"
|
||||
echo "WARNING: $(grep -c "WARNING" "$ALERT_LOG" || echo "0")"
|
||||
echo "INFO: $(grep -c "INFO" "$ALERT_LOG" || echo "0")"
|
||||
echo ""
|
||||
|
||||
# 按服务统计
|
||||
print_message $BLUE "按服务统计:"
|
||||
grep -o '\[.*\].*\[.*\]' "$ALERT_LOG" | awk -F']' '{print $2}' | sed 's/\[//' | sort | uniq -c | sort -nr
|
||||
echo ""
|
||||
|
||||
# 最近24小时统计
|
||||
local yesterday=$(date -d "1 day ago" '+%Y-%m-%d')
|
||||
local recent_alerts=$(grep "$yesterday\|$(date '+%Y-%m-%d')" "$ALERT_LOG" | wc -l)
|
||||
print_message $BLUE "最近24小时告警数: $recent_alerts"
|
||||
}
|
||||
|
||||
# 测试告警系统
|
||||
test_alerts() {
|
||||
print_message $BLUE "测试告警系统..."
|
||||
|
||||
log_alert "INFO" "SYSTEM" "告警系统测试开始"
|
||||
log_alert "WARNING" "TEST" "这是一个测试警告"
|
||||
log_alert "CRITICAL" "TEST" "这是一个测试严重告警"
|
||||
log_alert "INFO" "SYSTEM" "告警系统测试完成"
|
||||
|
||||
print_message $GREEN "告警系统测试完成,请检查日志文件: $ALERT_LOG"
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "QAUP 告警管理脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [命令] [选项]"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " check 运行所有告警检查"
|
||||
echo " monitor [interval] 持续监控模式 (默认300秒)"
|
||||
echo " history [lines] 查看告警历史"
|
||||
echo " cleanup [days] 清理告警日志 (默认30天)"
|
||||
echo " stats 生成告警统计报告"
|
||||
echo " test 测试告警系统"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 check # 运行一次检查"
|
||||
echo " $0 monitor 60 # 每60秒监控一次"
|
||||
echo " $0 history 100 # 查看最近100条告警"
|
||||
echo " $0 cleanup 7 # 清理7天前的告警日志"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
if [ $# -eq 0 ]; then
|
||||
show_help
|
||||
exit 0
|
||||
fi
|
||||
|
||||
local command=$1
|
||||
shift
|
||||
|
||||
case $command in
|
||||
check)
|
||||
run_all_checks
|
||||
;;
|
||||
monitor)
|
||||
continuous_monitoring "$@"
|
||||
;;
|
||||
history)
|
||||
view_alert_history "$@"
|
||||
;;
|
||||
cleanup)
|
||||
cleanup_alert_logs "$@"
|
||||
;;
|
||||
stats)
|
||||
generate_alert_stats
|
||||
;;
|
||||
test)
|
||||
test_alerts
|
||||
;;
|
||||
help|--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
print_message $RED "未知命令: $command"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
782
deploy/docker/backup-restore.sh
Executable file
782
deploy/docker/backup-restore.sh
Executable file
@ -0,0 +1,782 @@
|
||||
#!/bin/bash
|
||||
|
||||
# QAUP 综合备份和恢复脚本
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
BACKUP_ROOT="$PROJECT_ROOT/backup"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_message() {
|
||||
local color=$1
|
||||
local message=$2
|
||||
echo -e "${color}${message}${NC}"
|
||||
}
|
||||
|
||||
# 获取时间戳
|
||||
get_timestamp() {
|
||||
date '+%Y%m%d_%H%M%S'
|
||||
}
|
||||
|
||||
# 创建备份目录
|
||||
create_backup_dirs() {
|
||||
local timestamp=$1
|
||||
local backup_dir="$BACKUP_ROOT/$timestamp"
|
||||
|
||||
mkdir -p "$backup_dir"/{database,config,uploads,logs,system}
|
||||
echo "$backup_dir"
|
||||
}
|
||||
|
||||
# 数据库备份
|
||||
backup_database() {
|
||||
local backup_dir=$1
|
||||
local timestamp=$2
|
||||
|
||||
print_message $BLUE "备份数据库..."
|
||||
|
||||
# 检查数据库容器状态
|
||||
if ! docker ps --format "{{.Names}}" | grep -q "qaup-postgres"; then
|
||||
print_message $RED "数据库容器未运行"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 创建数据库备份
|
||||
local db_backup_file="$backup_dir/database/qaup_database_$timestamp.sql"
|
||||
|
||||
if docker exec qaup-postgres pg_dump -U postgres -d qaup --verbose --clean --if-exists --create > "$db_backup_file"; then
|
||||
# 压缩备份文件
|
||||
gzip "$db_backup_file"
|
||||
|
||||
# 创建数据库元信息
|
||||
cat > "$backup_dir/database/metadata.json" << EOF
|
||||
{
|
||||
"backup_time": "$(date -u '+%Y-%m-%dT%H:%M:%S.%3NZ')",
|
||||
"database_version": "$(docker exec qaup-postgres psql -U postgres -t -c 'SELECT version();' | xargs)",
|
||||
"database_size": "$(docker exec qaup-postgres psql -U postgres -d qaup -t -c "SELECT pg_size_pretty(pg_database_size('qaup'));" | xargs)",
|
||||
"table_count": "$(docker exec qaup-postgres psql -U postgres -d qaup -t -c "SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public';" | xargs)",
|
||||
"backup_file": "qaup_database_$timestamp.sql.gz",
|
||||
"backup_size": "$(du -h "$db_backup_file.gz" | cut -f1)"
|
||||
}
|
||||
EOF
|
||||
|
||||
print_message $GREEN "✓ 数据库备份完成: $(du -h "$db_backup_file.gz" | cut -f1)"
|
||||
return 0
|
||||
else
|
||||
print_message $RED "✗ 数据库备份失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 配置文件备份
|
||||
backup_config() {
|
||||
local backup_dir=$1
|
||||
local timestamp=$2
|
||||
|
||||
print_message $BLUE "备份配置文件..."
|
||||
|
||||
# 备份环境配置
|
||||
if [ -f "$PROJECT_ROOT/.env" ]; then
|
||||
cp "$PROJECT_ROOT/.env" "$backup_dir/config/env_$timestamp"
|
||||
print_message $GREEN "✓ 环境配置已备份"
|
||||
fi
|
||||
|
||||
# 备份 Docker Compose 配置
|
||||
cp "$PROJECT_ROOT"/docker-compose*.yml "$backup_dir/config/" 2>/dev/null || true
|
||||
|
||||
# 备份应用配置
|
||||
if [ -d "$PROJECT_ROOT/config" ]; then
|
||||
cp -r "$PROJECT_ROOT/config" "$backup_dir/config/app_config"
|
||||
print_message $GREEN "✓ 应用配置已备份"
|
||||
fi
|
||||
|
||||
# 备份 Docker 配置
|
||||
cp -r "$PROJECT_ROOT/docker" "$backup_dir/config/docker_config"
|
||||
|
||||
# 备份 SSL 证书
|
||||
if [ -d "$PROJECT_ROOT/ssl" ]; then
|
||||
cp -r "$PROJECT_ROOT/ssl" "$backup_dir/config/ssl_certs"
|
||||
print_message $GREEN "✓ SSL 证书已备份"
|
||||
fi
|
||||
|
||||
# 创建配置元信息
|
||||
cat > "$backup_dir/config/metadata.json" << EOF
|
||||
{
|
||||
"backup_time": "$(date -u '+%Y-%m-%dT%H:%M:%S.%3NZ')",
|
||||
"project_version": "$(grep version $PROJECT_ROOT/pom.xml | head -1 | sed 's/.*<version>\(.*\)<\/version>.*/\1/' | xargs)",
|
||||
"docker_compose_files": $(ls "$PROJECT_ROOT"/docker-compose*.yml | wc -l),
|
||||
"config_files_count": $(find "$backup_dir/config" -type f | wc -l)
|
||||
}
|
||||
EOF
|
||||
|
||||
print_message $GREEN "✓ 配置文件备份完成"
|
||||
}
|
||||
|
||||
# 上传文件备份
|
||||
backup_uploads() {
|
||||
local backup_dir=$1
|
||||
local timestamp=$2
|
||||
|
||||
print_message $BLUE "备份上传文件..."
|
||||
|
||||
local uploads_dir="$PROJECT_ROOT/data/uploads"
|
||||
|
||||
if [ -d "$uploads_dir" ] && [ "$(ls -A "$uploads_dir" 2>/dev/null)" ]; then
|
||||
# 创建上传文件备份
|
||||
tar -czf "$backup_dir/uploads/uploads_$timestamp.tar.gz" -C "$PROJECT_ROOT/data" uploads/
|
||||
|
||||
# 创建上传文件元信息
|
||||
cat > "$backup_dir/uploads/metadata.json" << EOF
|
||||
{
|
||||
"backup_time": "$(date -u '+%Y-%m-%dT%H:%M:%S.%3NZ')",
|
||||
"files_count": $(find "$uploads_dir" -type f | wc -l),
|
||||
"total_size": "$(du -sh "$uploads_dir" | cut -f1)",
|
||||
"backup_file": "uploads_$timestamp.tar.gz",
|
||||
"backup_size": "$(du -h "$backup_dir/uploads/uploads_$timestamp.tar.gz" | cut -f1)"
|
||||
}
|
||||
EOF
|
||||
|
||||
print_message $GREEN "✓ 上传文件备份完成: $(du -h "$backup_dir/uploads/uploads_$timestamp.tar.gz" | cut -f1)"
|
||||
else
|
||||
print_message $YELLOW "⚠ 上传目录为空,跳过备份"
|
||||
echo '{"backup_time": "'$(date -u '+%Y-%m-%dT%H:%M:%S.%3NZ')'", "status": "empty"}' > "$backup_dir/uploads/metadata.json"
|
||||
fi
|
||||
}
|
||||
|
||||
# 日志备份
|
||||
backup_logs() {
|
||||
local backup_dir=$1
|
||||
local timestamp=$2
|
||||
local days=${3:-7} # 默认备份最近7天的日志
|
||||
|
||||
print_message $BLUE "备份日志文件(最近 $days 天)..."
|
||||
|
||||
local logs_dir="$PROJECT_ROOT/logs"
|
||||
|
||||
if [ -d "$logs_dir" ]; then
|
||||
# 备份最近的日志文件
|
||||
find "$logs_dir" -name "*.log" -mtime -$days -exec cp {} "$backup_dir/logs/" \; 2>/dev/null || true
|
||||
|
||||
# 备份容器日志
|
||||
local containers=("qaup-app" "qaup-nginx" "qaup-postgres" "qaup-redis")
|
||||
for container in "${containers[@]}"; do
|
||||
if docker ps --format "{{.Names}}" | grep -q "$container"; then
|
||||
docker logs --since "${days}d" "$container" > "$backup_dir/logs/${container}_$timestamp.log" 2>&1 || true
|
||||
fi
|
||||
done
|
||||
|
||||
# 压缩日志备份
|
||||
if [ "$(ls -A "$backup_dir/logs" 2>/dev/null)" ]; then
|
||||
tar -czf "$backup_dir/logs/logs_$timestamp.tar.gz" -C "$backup_dir/logs" . --exclude="*.tar.gz"
|
||||
find "$backup_dir/logs" -name "*.log" -delete
|
||||
|
||||
# 创建日志元信息
|
||||
cat > "$backup_dir/logs/metadata.json" << EOF
|
||||
{
|
||||
"backup_time": "$(date -u '+%Y-%m-%dT%H:%M:%S.%3NZ')",
|
||||
"days_included": $days,
|
||||
"backup_file": "logs_$timestamp.tar.gz",
|
||||
"backup_size": "$(du -h "$backup_dir/logs/logs_$timestamp.tar.gz" | cut -f1)"
|
||||
}
|
||||
EOF
|
||||
|
||||
print_message $GREEN "✓ 日志备份完成: $(du -h "$backup_dir/logs/logs_$timestamp.tar.gz" | cut -f1)"
|
||||
else
|
||||
print_message $YELLOW "⚠ 未找到符合条件的日志文件"
|
||||
fi
|
||||
else
|
||||
print_message $YELLOW "⚠ 日志目录不存在"
|
||||
fi
|
||||
}
|
||||
|
||||
# 系统信息备份
|
||||
backup_system_info() {
|
||||
local backup_dir=$1
|
||||
local timestamp=$2
|
||||
|
||||
print_message $BLUE "备份系统信息..."
|
||||
|
||||
# 收集系统信息
|
||||
{
|
||||
echo "=== 系统信息备份 ==="
|
||||
echo "备份时间: $(date)"
|
||||
echo "主机名: $(hostname)"
|
||||
echo "操作系统: $(uname -a)"
|
||||
echo ""
|
||||
|
||||
echo "=== Docker 信息 ==="
|
||||
docker version
|
||||
echo ""
|
||||
docker-compose version
|
||||
echo ""
|
||||
|
||||
echo "=== 容器状态 ==="
|
||||
docker ps -a
|
||||
echo ""
|
||||
|
||||
echo "=== 镜像信息 ==="
|
||||
docker images
|
||||
echo ""
|
||||
|
||||
echo "=== 网络信息 ==="
|
||||
docker network ls
|
||||
echo ""
|
||||
|
||||
echo "=== 卷信息 ==="
|
||||
docker volume ls
|
||||
echo ""
|
||||
|
||||
echo "=== 系统资源 ==="
|
||||
free -h
|
||||
echo ""
|
||||
df -h
|
||||
echo ""
|
||||
|
||||
echo "=== 网络端口 ==="
|
||||
netstat -tuln | grep -E ":(80|443|8080|5432|6379) "
|
||||
echo ""
|
||||
|
||||
} > "$backup_dir/system/system_info_$timestamp.txt"
|
||||
|
||||
# 备份 Docker Compose 状态
|
||||
docker-compose ps > "$backup_dir/system/compose_status_$timestamp.txt" 2>/dev/null || true
|
||||
|
||||
# 创建系统元信息
|
||||
cat > "$backup_dir/system/metadata.json" << EOF
|
||||
{
|
||||
"backup_time": "$(date -u '+%Y-%m-%dT%H:%M:%S.%3NZ')",
|
||||
"hostname": "$(hostname)",
|
||||
"os_version": "$(uname -r)",
|
||||
"docker_version": "$(docker --version | cut -d' ' -f3 | sed 's/,//')",
|
||||
"compose_version": "$(docker-compose --version | cut -d' ' -f3 | sed 's/,//')",
|
||||
"running_containers": $(docker ps -q | wc -l),
|
||||
"total_containers": $(docker ps -aq | wc -l)
|
||||
}
|
||||
EOF
|
||||
|
||||
print_message $GREEN "✓ 系统信息备份完成"
|
||||
}
|
||||
|
||||
# 完整备份
|
||||
full_backup() {
|
||||
local timestamp=$(get_timestamp)
|
||||
local backup_dir=$(create_backup_dirs "$timestamp")
|
||||
|
||||
print_message $GREEN "========================================="
|
||||
print_message $GREEN "开始完整备份: $timestamp"
|
||||
print_message $GREEN "========================================="
|
||||
|
||||
local backup_success=true
|
||||
|
||||
# 执行各项备份
|
||||
backup_database "$backup_dir" "$timestamp" || backup_success=false
|
||||
backup_config "$backup_dir" "$timestamp" || backup_success=false
|
||||
backup_uploads "$backup_dir" "$timestamp" || backup_success=false
|
||||
backup_logs "$backup_dir" "$timestamp" || backup_success=false
|
||||
backup_system_info "$backup_dir" "$timestamp" || backup_success=false
|
||||
|
||||
# 创建备份清单
|
||||
create_backup_manifest "$backup_dir" "$timestamp"
|
||||
|
||||
# 压缩整个备份
|
||||
print_message $BLUE "压缩备份文件..."
|
||||
local backup_archive="$BACKUP_ROOT/qaup_full_backup_$timestamp.tar.gz"
|
||||
tar -czf "$backup_archive" -C "$BACKUP_ROOT" "$(basename "$backup_dir")"
|
||||
|
||||
# 计算校验和
|
||||
sha256sum "$backup_archive" > "$backup_archive.sha256"
|
||||
|
||||
# 清理临时目录
|
||||
rm -rf "$backup_dir"
|
||||
|
||||
if [ "$backup_success" = true ]; then
|
||||
print_message $GREEN "========================================="
|
||||
print_message $GREEN "完整备份完成!"
|
||||
print_message $GREEN "========================================="
|
||||
echo ""
|
||||
print_message $BLUE "备份信息:"
|
||||
echo " 备份文件: $backup_archive"
|
||||
echo " 文件大小: $(du -h "$backup_archive" | cut -f1)"
|
||||
echo " 校验文件: $backup_archive.sha256"
|
||||
echo ""
|
||||
|
||||
# 记录备份历史
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') FULL_BACKUP SUCCESS $backup_archive $(du -b "$backup_archive" | cut -f1)" >> "$BACKUP_ROOT/backup_history.log"
|
||||
|
||||
return 0
|
||||
else
|
||||
print_message $RED "备份过程中出现错误,请检查日志"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 创建备份清单
|
||||
create_backup_manifest() {
|
||||
local backup_dir=$1
|
||||
local timestamp=$2
|
||||
|
||||
cat > "$backup_dir/MANIFEST.json" << EOF
|
||||
{
|
||||
"backup_id": "$timestamp",
|
||||
"backup_time": "$(date -u '+%Y-%m-%dT%H:%M:%S.%3NZ')",
|
||||
"backup_type": "full",
|
||||
"version": "1.0",
|
||||
"components": {
|
||||
"database": {
|
||||
"included": $([ -f "$backup_dir/database/metadata.json" ] && echo "true" || echo "false"),
|
||||
"metadata_file": "database/metadata.json"
|
||||
},
|
||||
"config": {
|
||||
"included": $([ -f "$backup_dir/config/metadata.json" ] && echo "true" || echo "false"),
|
||||
"metadata_file": "config/metadata.json"
|
||||
},
|
||||
"uploads": {
|
||||
"included": $([ -f "$backup_dir/uploads/metadata.json" ] && echo "true" || echo "false"),
|
||||
"metadata_file": "uploads/metadata.json"
|
||||
},
|
||||
"logs": {
|
||||
"included": $([ -f "$backup_dir/logs/metadata.json" ] && echo "true" || echo "false"),
|
||||
"metadata_file": "logs/metadata.json"
|
||||
},
|
||||
"system": {
|
||||
"included": $([ -f "$backup_dir/system/metadata.json" ] && echo "true" || echo "false"),
|
||||
"metadata_file": "system/metadata.json"
|
||||
}
|
||||
},
|
||||
"total_size": "$(du -sh "$backup_dir" | cut -f1)",
|
||||
"file_count": $(find "$backup_dir" -type f | wc -l)
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
# 恢复数据库
|
||||
restore_database() {
|
||||
local backup_file=$1
|
||||
|
||||
print_message $BLUE "恢复数据库..."
|
||||
|
||||
if [ ! -f "$backup_file" ]; then
|
||||
print_message $RED "备份文件不存在: $backup_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查数据库容器状态
|
||||
if ! docker ps --format "{{.Names}}" | grep -q "qaup-postgres"; then
|
||||
print_message $RED "数据库容器未运行"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 确认恢复操作
|
||||
print_message $YELLOW "警告: 此操作将覆盖现有数据库数据"
|
||||
read -p "确定要继续吗? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
print_message $BLUE "恢复操作已取消"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 停止应用服务以避免数据冲突
|
||||
print_message $BLUE "停止应用服务..."
|
||||
docker stop qaup-app || true
|
||||
|
||||
# 恢复数据库
|
||||
if [[ "$backup_file" == *.gz ]]; then
|
||||
# 解压并恢复
|
||||
if gunzip -c "$backup_file" | docker exec -i qaup-postgres psql -U postgres; then
|
||||
print_message $GREEN "✓ 数据库恢复成功"
|
||||
else
|
||||
print_message $RED "✗ 数据库恢复失败"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
# 直接恢复
|
||||
if docker exec -i qaup-postgres psql -U postgres < "$backup_file"; then
|
||||
print_message $GREEN "✓ 数据库恢复成功"
|
||||
else
|
||||
print_message $RED "✗ 数据库恢复失败"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 重启应用服务
|
||||
print_message $BLUE "重启应用服务..."
|
||||
docker start qaup-app
|
||||
|
||||
# 等待服务就绪
|
||||
sleep 10
|
||||
if curl -f -s http://localhost:8080/actuator/health &>/dev/null; then
|
||||
print_message $GREEN "✓ 应用服务已恢复正常"
|
||||
else
|
||||
print_message $YELLOW "⚠ 应用服务可能需要更多时间启动"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# 恢复配置文件
|
||||
restore_config() {
|
||||
local backup_dir=$1
|
||||
|
||||
print_message $BLUE "恢复配置文件..."
|
||||
|
||||
if [ ! -d "$backup_dir" ]; then
|
||||
print_message $RED "备份目录不存在: $backup_dir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 备份当前配置
|
||||
local current_backup="$PROJECT_ROOT/config_backup_$(get_timestamp)"
|
||||
mkdir -p "$current_backup"
|
||||
|
||||
[ -f "$PROJECT_ROOT/.env" ] && cp "$PROJECT_ROOT/.env" "$current_backup/"
|
||||
[ -d "$PROJECT_ROOT/config" ] && cp -r "$PROJECT_ROOT/config" "$current_backup/"
|
||||
[ -d "$PROJECT_ROOT/docker" ] && cp -r "$PROJECT_ROOT/docker" "$current_backup/"
|
||||
|
||||
print_message $BLUE "当前配置已备份到: $current_backup"
|
||||
|
||||
# 恢复配置文件
|
||||
if [ -f "$backup_dir/env_"* ]; then
|
||||
cp "$backup_dir"/env_* "$PROJECT_ROOT/.env"
|
||||
print_message $GREEN "✓ 环境配置已恢复"
|
||||
fi
|
||||
|
||||
if [ -d "$backup_dir/app_config" ]; then
|
||||
rm -rf "$PROJECT_ROOT/config"
|
||||
cp -r "$backup_dir/app_config" "$PROJECT_ROOT/config"
|
||||
print_message $GREEN "✓ 应用配置已恢复"
|
||||
fi
|
||||
|
||||
if [ -d "$backup_dir/docker_config" ]; then
|
||||
rm -rf "$PROJECT_ROOT/docker"
|
||||
cp -r "$backup_dir/docker_config" "$PROJECT_ROOT/docker"
|
||||
# 恢复脚本执行权限
|
||||
find "$PROJECT_ROOT/docker" -name "*.sh" -exec chmod +x {} \;
|
||||
print_message $GREEN "✓ Docker 配置已恢复"
|
||||
fi
|
||||
|
||||
if [ -d "$backup_dir/ssl_certs" ]; then
|
||||
rm -rf "$PROJECT_ROOT/ssl"
|
||||
cp -r "$backup_dir/ssl_certs" "$PROJECT_ROOT/ssl"
|
||||
chmod 600 "$PROJECT_ROOT/ssl"/*.key 2>/dev/null || true
|
||||
print_message $GREEN "✓ SSL 证书已恢复"
|
||||
fi
|
||||
|
||||
print_message $GREEN "✓ 配置文件恢复完成"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 恢复上传文件
|
||||
restore_uploads() {
|
||||
local backup_file=$1
|
||||
|
||||
print_message $BLUE "恢复上传文件..."
|
||||
|
||||
if [ ! -f "$backup_file" ]; then
|
||||
print_message $RED "备份文件不存在: $backup_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 备份当前上传文件
|
||||
local uploads_dir="$PROJECT_ROOT/data/uploads"
|
||||
if [ -d "$uploads_dir" ] && [ "$(ls -A "$uploads_dir" 2>/dev/null)" ]; then
|
||||
local current_backup="$PROJECT_ROOT/uploads_backup_$(get_timestamp).tar.gz"
|
||||
tar -czf "$current_backup" -C "$PROJECT_ROOT/data" uploads/
|
||||
print_message $BLUE "当前上传文件已备份到: $current_backup"
|
||||
fi
|
||||
|
||||
# 恢复上传文件
|
||||
mkdir -p "$PROJECT_ROOT/data"
|
||||
if tar -xzf "$backup_file" -C "$PROJECT_ROOT/data"; then
|
||||
print_message $GREEN "✓ 上传文件恢复成功"
|
||||
return 0
|
||||
else
|
||||
print_message $RED "✗ 上传文件恢复失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 完整恢复
|
||||
full_restore() {
|
||||
local backup_archive=$1
|
||||
|
||||
if [ ! -f "$backup_archive" ]; then
|
||||
print_message $RED "备份文件不存在: $backup_archive"
|
||||
return 1
|
||||
fi
|
||||
|
||||
print_message $GREEN "========================================="
|
||||
print_message $GREEN "开始完整恢复"
|
||||
print_message $GREEN "========================================="
|
||||
|
||||
# 验证备份文件
|
||||
if [ -f "$backup_archive.sha256" ]; then
|
||||
print_message $BLUE "验证备份文件完整性..."
|
||||
if sha256sum -c "$backup_archive.sha256"; then
|
||||
print_message $GREEN "✓ 备份文件完整性验证通过"
|
||||
else
|
||||
print_message $RED "✗ 备份文件完整性验证失败"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 解压备份文件
|
||||
local temp_dir="/tmp/qaup_restore_$(get_timestamp)"
|
||||
mkdir -p "$temp_dir"
|
||||
|
||||
print_message $BLUE "解压备份文件..."
|
||||
if tar -xzf "$backup_archive" -C "$temp_dir"; then
|
||||
print_message $GREEN "✓ 备份文件解压成功"
|
||||
else
|
||||
print_message $RED "✗ 备份文件解压失败"
|
||||
rm -rf "$temp_dir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 查找备份目录
|
||||
local backup_dir=$(find "$temp_dir" -maxdepth 1 -type d -name "*" | grep -v "^$temp_dir$" | head -1)
|
||||
|
||||
if [ ! -d "$backup_dir" ]; then
|
||||
print_message $RED "未找到有效的备份目录"
|
||||
rm -rf "$temp_dir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 读取备份清单
|
||||
if [ -f "$backup_dir/MANIFEST.json" ]; then
|
||||
print_message $BLUE "备份清单信息:"
|
||||
cat "$backup_dir/MANIFEST.json" | grep -E '"backup_time"|"backup_type"|"total_size"' | sed 's/^ / /'
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# 确认恢复操作
|
||||
print_message $YELLOW "警告: 此操作将覆盖现有系统数据"
|
||||
read -p "确定要继续完整恢复吗? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
print_message $BLUE "恢复操作已取消"
|
||||
rm -rf "$temp_dir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local restore_success=true
|
||||
|
||||
# 恢复配置文件
|
||||
if [ -d "$backup_dir/config" ]; then
|
||||
restore_config "$backup_dir/config" || restore_success=false
|
||||
fi
|
||||
|
||||
# 恢复数据库
|
||||
if [ -f "$backup_dir/database"/*.sql.gz ]; then
|
||||
restore_database "$backup_dir/database"/*.sql.gz || restore_success=false
|
||||
elif [ -f "$backup_dir/database"/*.sql ]; then
|
||||
restore_database "$backup_dir/database"/*.sql || restore_success=false
|
||||
fi
|
||||
|
||||
# 恢复上传文件
|
||||
if [ -f "$backup_dir/uploads"/*.tar.gz ]; then
|
||||
restore_uploads "$backup_dir/uploads"/*.tar.gz || restore_success=false
|
||||
fi
|
||||
|
||||
# 清理临时文件
|
||||
rm -rf "$temp_dir"
|
||||
|
||||
if [ "$restore_success" = true ]; then
|
||||
print_message $GREEN "========================================="
|
||||
print_message $GREEN "完整恢复完成!"
|
||||
print_message $GREEN "========================================="
|
||||
echo ""
|
||||
print_message $BLUE "建议执行以下操作:"
|
||||
echo " 1. 重启所有服务: ./deploy.sh restart"
|
||||
echo " 2. 验证系统状态: ./docker/monitor.sh status"
|
||||
echo " 3. 检查应用功能: 访问前端页面测试"
|
||||
echo ""
|
||||
|
||||
# 记录恢复历史
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') FULL_RESTORE SUCCESS $backup_archive" >> "$BACKUP_ROOT/restore_history.log"
|
||||
|
||||
return 0
|
||||
else
|
||||
print_message $RED "恢复过程中出现错误,请检查系统状态"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 列出备份文件
|
||||
list_backups() {
|
||||
print_message $BLUE "可用的备份文件:"
|
||||
echo ""
|
||||
|
||||
if [ -d "$BACKUP_ROOT" ]; then
|
||||
local backup_files=($(find "$BACKUP_ROOT" -name "qaup_full_backup_*.tar.gz" -type f | sort -r))
|
||||
|
||||
if [ ${#backup_files[@]} -eq 0 ]; then
|
||||
print_message $YELLOW "未找到备份文件"
|
||||
return
|
||||
fi
|
||||
|
||||
local index=1
|
||||
for backup_file in "${backup_files[@]}"; do
|
||||
local filename=$(basename "$backup_file")
|
||||
local size=$(du -h "$backup_file" | cut -f1)
|
||||
local date=$(echo "$filename" | grep -o '[0-9]\{8\}_[0-9]\{6\}' | sed 's/_/ /')
|
||||
|
||||
echo "$index. $filename"
|
||||
echo " 大小: $size"
|
||||
echo " 时间: $date"
|
||||
|
||||
# 显示校验和状态
|
||||
if [ -f "$backup_file.sha256" ]; then
|
||||
echo " 校验: 可用"
|
||||
else
|
||||
echo " 校验: 不可用"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
index=$((index + 1))
|
||||
done
|
||||
else
|
||||
print_message $YELLOW "备份目录不存在: $BACKUP_ROOT"
|
||||
fi
|
||||
}
|
||||
|
||||
# 清理旧备份
|
||||
cleanup_old_backups() {
|
||||
local keep_days=${1:-30}
|
||||
|
||||
print_message $BLUE "清理 $keep_days 天前的备份文件..."
|
||||
|
||||
if [ ! -d "$BACKUP_ROOT" ]; then
|
||||
print_message $YELLOW "备份目录不存在"
|
||||
return
|
||||
fi
|
||||
|
||||
local deleted_count=0
|
||||
|
||||
# 清理备份文件
|
||||
while IFS= read -r -d '' file; do
|
||||
rm -f "$file" "$file.sha256"
|
||||
deleted_count=$((deleted_count + 1))
|
||||
print_message $GREEN "已删除: $(basename "$file")"
|
||||
done < <(find "$BACKUP_ROOT" -name "qaup_full_backup_*.tar.gz" -mtime +$keep_days -print0)
|
||||
|
||||
if [ $deleted_count -eq 0 ]; then
|
||||
print_message $GREEN "没有需要清理的备份文件"
|
||||
else
|
||||
print_message $GREEN "已清理 $deleted_count 个旧备份文件"
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "QAUP 备份和恢复脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [命令] [参数]"
|
||||
echo ""
|
||||
echo "备份命令:"
|
||||
echo " full 完整备份"
|
||||
echo " database 仅备份数据库"
|
||||
echo " config 仅备份配置文件"
|
||||
echo " uploads 仅备份上传文件"
|
||||
echo ""
|
||||
echo "恢复命令:"
|
||||
echo " restore <backup_file> 完整恢复"
|
||||
echo " restore-db <db_file> 仅恢复数据库"
|
||||
echo " restore-config <dir> 仅恢复配置"
|
||||
echo " restore-uploads <file> 仅恢复上传文件"
|
||||
echo ""
|
||||
echo "管理命令:"
|
||||
echo " list 列出备份文件"
|
||||
echo " cleanup [days] 清理旧备份 (默认30天)"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 full # 完整备份"
|
||||
echo " $0 restore backup/qaup_full_backup_*.tar.gz # 完整恢复"
|
||||
echo " $0 list # 列出备份"
|
||||
echo " $0 cleanup 7 # 清理7天前的备份"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
# 确保备份目录存在
|
||||
mkdir -p "$BACKUP_ROOT"
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
show_help
|
||||
exit 0
|
||||
fi
|
||||
|
||||
local command=$1
|
||||
shift
|
||||
|
||||
case $command in
|
||||
full)
|
||||
full_backup
|
||||
;;
|
||||
database)
|
||||
local timestamp=$(get_timestamp)
|
||||
local backup_dir=$(create_backup_dirs "$timestamp")
|
||||
backup_database "$backup_dir" "$timestamp"
|
||||
;;
|
||||
config)
|
||||
local timestamp=$(get_timestamp)
|
||||
local backup_dir=$(create_backup_dirs "$timestamp")
|
||||
backup_config "$backup_dir" "$timestamp"
|
||||
;;
|
||||
uploads)
|
||||
local timestamp=$(get_timestamp)
|
||||
local backup_dir=$(create_backup_dirs "$timestamp")
|
||||
backup_uploads "$backup_dir" "$timestamp"
|
||||
;;
|
||||
restore)
|
||||
if [ $# -eq 0 ]; then
|
||||
print_message $RED "请指定备份文件"
|
||||
exit 1
|
||||
fi
|
||||
full_restore "$1"
|
||||
;;
|
||||
restore-db)
|
||||
if [ $# -eq 0 ]; then
|
||||
print_message $RED "请指定数据库备份文件"
|
||||
exit 1
|
||||
fi
|
||||
restore_database "$1"
|
||||
;;
|
||||
restore-config)
|
||||
if [ $# -eq 0 ]; then
|
||||
print_message $RED "请指定配置备份目录"
|
||||
exit 1
|
||||
fi
|
||||
restore_config "$1"
|
||||
;;
|
||||
restore-uploads)
|
||||
if [ $# -eq 0 ]; then
|
||||
print_message $RED "请指定上传文件备份"
|
||||
exit 1
|
||||
fi
|
||||
restore_uploads "$1"
|
||||
;;
|
||||
list)
|
||||
list_backups
|
||||
;;
|
||||
cleanup)
|
||||
cleanup_old_backups "$@"
|
||||
;;
|
||||
help|--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
print_message $RED "未知命令: $command"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
66
deploy/docker/build-frontend.sh
Executable file
66
deploy/docker/build-frontend.sh
Executable file
@ -0,0 +1,66 @@
|
||||
#!/bin/bash
|
||||
|
||||
# QAUP 前端构建脚本
|
||||
# 用于 Docker 构建过程中的前端资源构建
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
FRONTEND_DIR="$PROJECT_ROOT/qaup-ui"
|
||||
BUILD_OUTPUT="$FRONTEND_DIR/dist"
|
||||
|
||||
echo "========================================="
|
||||
echo "QAUP 前端构建开始"
|
||||
echo "========================================="
|
||||
|
||||
# 检查前端目录
|
||||
if [ ! -d "$FRONTEND_DIR" ]; then
|
||||
echo "错误: 前端目录不存在: $FRONTEND_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$FRONTEND_DIR"
|
||||
|
||||
# 检查 package.json
|
||||
if [ ! -f "package.json" ]; then
|
||||
echo "错误: package.json 不存在"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 清理之前的构建
|
||||
echo "清理之前的构建文件..."
|
||||
rm -rf "$BUILD_OUTPUT"
|
||||
|
||||
# 安装依赖
|
||||
echo "安装前端依赖..."
|
||||
if [ -f "package-lock.json" ]; then
|
||||
npm ci --only=production
|
||||
else
|
||||
npm install --only=production
|
||||
fi
|
||||
|
||||
# 构建生产版本
|
||||
echo "构建生产版本..."
|
||||
npm run build:prod
|
||||
|
||||
# 验证构建结果
|
||||
if [ ! -d "$BUILD_OUTPUT" ]; then
|
||||
echo "错误: 构建失败,输出目录不存在: $BUILD_OUTPUT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 显示构建统计
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "前端构建完成统计:"
|
||||
echo "========================================="
|
||||
echo "构建输出目录: $BUILD_OUTPUT"
|
||||
echo "构建文件大小:"
|
||||
du -sh "$BUILD_OUTPUT"
|
||||
echo ""
|
||||
echo "主要文件列表:"
|
||||
find "$BUILD_OUTPUT" -name "*.html" -o -name "*.js" -o -name "*.css" | head -10
|
||||
|
||||
echo ""
|
||||
echo "前端构建完成!"
|
||||
335
deploy/docker/config-manager.sh
Executable file
335
deploy/docker/config-manager.sh
Executable file
@ -0,0 +1,335 @@
|
||||
#!/bin/bash
|
||||
|
||||
# QAUP 配置管理脚本
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
ENV_FILE="$PROJECT_ROOT/.env"
|
||||
ENV_TEMPLATE="$PROJECT_ROOT/.env.template"
|
||||
CONFIG_DIR="$PROJECT_ROOT/config"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_message() {
|
||||
local color=$1
|
||||
local message=$2
|
||||
echo -e "${color}${message}${NC}"
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "QAUP 配置管理脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [命令] [选项]"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " init 初始化配置文件"
|
||||
echo " validate 验证配置文件"
|
||||
echo " generate 生成配置文件"
|
||||
echo " backup 备份配置文件"
|
||||
echo " restore 恢复配置文件"
|
||||
echo " encrypt 加密敏感配置"
|
||||
echo " decrypt 解密敏感配置"
|
||||
echo " show 显示当前配置"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 init # 初始化配置"
|
||||
echo " $0 validate # 验证配置"
|
||||
echo " $0 generate prod # 生成生产环境配置"
|
||||
}
|
||||
|
||||
# 初始化配置
|
||||
init_config() {
|
||||
print_message $BLUE "初始化 QAUP 配置..."
|
||||
|
||||
# 创建配置目录
|
||||
mkdir -p "$CONFIG_DIR"
|
||||
|
||||
# 复制环境模板文件
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
if [ -f "$ENV_TEMPLATE" ]; then
|
||||
cp "$ENV_TEMPLATE" "$ENV_FILE"
|
||||
print_message $GREEN "环境配置文件已创建: $ENV_FILE"
|
||||
print_message $YELLOW "请编辑 $ENV_FILE 文件,修改相关配置"
|
||||
else
|
||||
print_message $RED "错误: 环境模板文件不存在: $ENV_TEMPLATE"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
print_message $YELLOW "环境配置文件已存在: $ENV_FILE"
|
||||
fi
|
||||
|
||||
# 创建必要的目录
|
||||
create_directories
|
||||
|
||||
print_message $GREEN "配置初始化完成!"
|
||||
}
|
||||
|
||||
# 创建必要的目录
|
||||
create_directories() {
|
||||
local data_dir="./data"
|
||||
local log_dir="./logs"
|
||||
local backup_dir="./backup"
|
||||
local ssl_dir="./ssl"
|
||||
|
||||
print_message $BLUE "创建必要的目录..."
|
||||
|
||||
mkdir -p "$data_dir"/{postgres,redis,uploads}
|
||||
mkdir -p "$log_dir"/{postgres,redis,app,nginx}
|
||||
mkdir -p "$backup_dir"/{postgres,config}
|
||||
mkdir -p "$ssl_dir"
|
||||
|
||||
# 设置目录权限
|
||||
chmod 755 "$data_dir" "$log_dir" "$backup_dir"
|
||||
chmod 700 "$data_dir"/postgres "$ssl_dir"
|
||||
|
||||
print_message $GREEN "目录创建完成"
|
||||
}
|
||||
|
||||
# 验证配置文件
|
||||
validate_config() {
|
||||
print_message $BLUE "验证配置文件..."
|
||||
|
||||
local errors=0
|
||||
|
||||
# 检查环境文件
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
print_message $RED "错误: 环境文件不存在: $ENV_FILE"
|
||||
errors=$((errors + 1))
|
||||
else
|
||||
# 检查必需的环境变量
|
||||
source "$ENV_FILE"
|
||||
|
||||
local required_vars=(
|
||||
"POSTGRES_DB"
|
||||
"POSTGRES_USER"
|
||||
"POSTGRES_PASSWORD"
|
||||
"APP_DB_USER"
|
||||
"APP_DB_PASSWORD"
|
||||
)
|
||||
|
||||
for var in "${required_vars[@]}"; do
|
||||
if [ -z "${!var}" ]; then
|
||||
print_message $RED "错误: 必需的环境变量未设置: $var"
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
# 检查密码强度
|
||||
if [ ${#POSTGRES_PASSWORD} -lt 8 ]; then
|
||||
print_message $YELLOW "警告: 数据库密码长度少于8位,建议使用更强的密码"
|
||||
fi
|
||||
|
||||
if [ ${#APP_DB_PASSWORD} -lt 8 ]; then
|
||||
print_message $YELLOW "警告: 应用数据库密码长度少于8位,建议使用更强的密码"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 检查配置文件
|
||||
local config_files=(
|
||||
"$CONFIG_DIR/application-prod.yml"
|
||||
)
|
||||
|
||||
for config_file in "${config_files[@]}"; do
|
||||
if [ ! -f "$config_file" ]; then
|
||||
print_message $YELLOW "警告: 配置文件不存在: $config_file"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $errors -eq 0 ]; then
|
||||
print_message $GREEN "配置验证通过!"
|
||||
return 0
|
||||
else
|
||||
print_message $RED "配置验证失败,发现 $errors 个错误"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 生成配置文件
|
||||
generate_config() {
|
||||
local env=${1:-prod}
|
||||
|
||||
print_message $BLUE "生成 $env 环境配置文件..."
|
||||
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
print_message $RED "错误: 环境文件不存在,请先运行 init 命令"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source "$ENV_FILE"
|
||||
|
||||
# 生成 Docker Compose 环境文件
|
||||
local compose_env_file="$PROJECT_ROOT/.env.compose"
|
||||
cat > "$compose_env_file" << EOF
|
||||
# Docker Compose 环境配置
|
||||
# 由配置管理脚本自动生成
|
||||
|
||||
# 数据库配置
|
||||
POSTGRES_DB=$POSTGRES_DB
|
||||
POSTGRES_USER=$POSTGRES_USER
|
||||
POSTGRES_PASSWORD=$POSTGRES_PASSWORD
|
||||
APP_DB_USER=$APP_DB_USER
|
||||
APP_DB_PASSWORD=$APP_DB_PASSWORD
|
||||
|
||||
# Redis 配置
|
||||
REDIS_PASSWORD=$REDIS_PASSWORD
|
||||
|
||||
# 应用配置
|
||||
SPRING_PROFILES_ACTIVE=$SPRING_PROFILES_ACTIVE
|
||||
APP_PORT=$APP_PORT
|
||||
|
||||
# Nginx 配置
|
||||
NGINX_PORT=$NGINX_PORT
|
||||
SERVER_NAME=$SERVER_NAME
|
||||
API_PREFIX=$API_PREFIX
|
||||
MAX_UPLOAD_SIZE=$MAX_UPLOAD_SIZE
|
||||
|
||||
# 目录配置
|
||||
DATA_DIR=$DATA_DIR
|
||||
LOG_DIR=$LOG_DIR
|
||||
BACKUP_DIR=$BACKUP_DIR
|
||||
|
||||
# JVM 配置
|
||||
JVM_OPTS=$JVM_OPTS
|
||||
EOF
|
||||
|
||||
print_message $GREEN "配置文件生成完成: $compose_env_file"
|
||||
}
|
||||
|
||||
# 备份配置文件
|
||||
backup_config() {
|
||||
local backup_dir="./backup/config"
|
||||
local timestamp=$(date +%Y%m%d_%H%M%S)
|
||||
local backup_file="config_backup_$timestamp.tar.gz"
|
||||
|
||||
print_message $BLUE "备份配置文件..."
|
||||
|
||||
mkdir -p "$backup_dir"
|
||||
|
||||
# 创建备份
|
||||
tar -czf "$backup_dir/$backup_file" \
|
||||
-C "$PROJECT_ROOT" \
|
||||
.env \
|
||||
config/ \
|
||||
docker-compose*.yml \
|
||||
2>/dev/null || true
|
||||
|
||||
print_message $GREEN "配置备份完成: $backup_dir/$backup_file"
|
||||
}
|
||||
|
||||
# 显示当前配置
|
||||
show_config() {
|
||||
print_message $BLUE "当前配置信息:"
|
||||
|
||||
if [ -f "$ENV_FILE" ]; then
|
||||
echo ""
|
||||
echo "环境变量配置:"
|
||||
echo "=============="
|
||||
# 隐藏敏感信息
|
||||
grep -v "PASSWORD\|SECRET" "$ENV_FILE" | grep -v "^#" | grep -v "^$"
|
||||
echo ""
|
||||
echo "敏感配置项:"
|
||||
echo "==========="
|
||||
grep "PASSWORD\|SECRET" "$ENV_FILE" | sed 's/=.*/=***/' || echo "无敏感配置项"
|
||||
else
|
||||
print_message $RED "环境配置文件不存在"
|
||||
fi
|
||||
}
|
||||
|
||||
# 加密敏感配置
|
||||
encrypt_config() {
|
||||
print_message $BLUE "加密敏感配置..."
|
||||
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
print_message $RED "错误: 环境文件不存在"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 创建加密版本
|
||||
local encrypted_file="$ENV_FILE.encrypted"
|
||||
|
||||
# 使用 openssl 加密(需要输入密码)
|
||||
openssl enc -aes-256-cbc -salt -in "$ENV_FILE" -out "$encrypted_file"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
print_message $GREEN "配置文件已加密: $encrypted_file"
|
||||
print_message $YELLOW "请妥善保管加密密码"
|
||||
else
|
||||
print_message $RED "配置文件加密失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 解密敏感配置
|
||||
decrypt_config() {
|
||||
print_message $BLUE "解密敏感配置..."
|
||||
|
||||
local encrypted_file="$ENV_FILE.encrypted"
|
||||
|
||||
if [ ! -f "$encrypted_file" ]; then
|
||||
print_message $RED "错误: 加密文件不存在: $encrypted_file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 使用 openssl 解密
|
||||
openssl enc -aes-256-cbc -d -in "$encrypted_file" -out "$ENV_FILE"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
print_message $GREEN "配置文件已解密: $ENV_FILE"
|
||||
else
|
||||
print_message $RED "配置文件解密失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
if [ $# -eq 0 ]; then
|
||||
show_help
|
||||
exit 0
|
||||
fi
|
||||
|
||||
local command=$1
|
||||
shift
|
||||
|
||||
case $command in
|
||||
init)
|
||||
init_config
|
||||
;;
|
||||
validate)
|
||||
validate_config
|
||||
;;
|
||||
generate)
|
||||
generate_config "$@"
|
||||
;;
|
||||
backup)
|
||||
backup_config
|
||||
;;
|
||||
show)
|
||||
show_config
|
||||
;;
|
||||
encrypt)
|
||||
encrypt_config
|
||||
;;
|
||||
decrypt)
|
||||
decrypt_config
|
||||
;;
|
||||
help|--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
print_message $RED "未知命令: $command"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
299
deploy/docker/docker-compose-manager.sh
Executable file
299
deploy/docker/docker-compose-manager.sh
Executable file
@ -0,0 +1,299 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Docker Compose 管理脚本
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
ENV_FILE="$PROJECT_ROOT/.env"
|
||||
|
||||
# 默认环境
|
||||
ENVIRONMENT=${ENVIRONMENT:-prod}
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 打印带颜色的消息
|
||||
print_message() {
|
||||
local color=$1
|
||||
local message=$2
|
||||
echo -e "${color}${message}${NC}"
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "QAUP Docker Compose 管理脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [环境] [命令] [选项]"
|
||||
echo ""
|
||||
echo "环境:"
|
||||
echo " dev 开发环境"
|
||||
echo " prod 生产环境(默认)"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " start 启动服务"
|
||||
echo " stop 停止服务"
|
||||
echo " restart 重启服务"
|
||||
echo " status 查看服务状态"
|
||||
echo " logs 查看服务日志"
|
||||
echo " build 构建镜像"
|
||||
echo " pull 拉取镜像"
|
||||
echo " clean 清理资源"
|
||||
echo " backup 备份数据"
|
||||
echo " restore 恢复数据"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 prod start # 启动生产环境"
|
||||
echo " $0 dev logs qaup-app # 查看开发环境应用日志"
|
||||
echo " $0 prod backup # 备份生产环境数据"
|
||||
}
|
||||
|
||||
# 检查环境文件
|
||||
check_env_file() {
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
print_message $YELLOW "警告: 环境文件不存在,创建默认配置..."
|
||||
create_default_env
|
||||
fi
|
||||
}
|
||||
|
||||
# 创建默认环境文件
|
||||
create_default_env() {
|
||||
cat > "$ENV_FILE" << EOF
|
||||
# QAUP Docker 部署环境配置
|
||||
|
||||
# 数据库配置
|
||||
POSTGRES_DB=qaup
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=qaup123
|
||||
APP_DB_USER=qaup_app
|
||||
APP_DB_PASSWORD=qaup123
|
||||
|
||||
# Redis 配置
|
||||
REDIS_PASSWORD=
|
||||
|
||||
# 应用配置
|
||||
SPRING_PROFILES_ACTIVE=prod
|
||||
APP_PORT=8080
|
||||
|
||||
# Nginx 配置
|
||||
NGINX_PORT=80
|
||||
NGINX_HTTPS_PORT=443
|
||||
SERVER_NAME=localhost
|
||||
API_PREFIX=/prod-api
|
||||
MAX_UPLOAD_SIZE=20M
|
||||
|
||||
# 目录配置
|
||||
DATA_DIR=./data
|
||||
LOG_DIR=./logs
|
||||
BACKUP_DIR=./backup
|
||||
|
||||
# JVM 配置
|
||||
JVM_OPTS=-Dname=qaup-admin.jar -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m
|
||||
EOF
|
||||
print_message $GREEN "默认环境文件已创建: $ENV_FILE"
|
||||
}
|
||||
|
||||
# 获取 compose 文件参数
|
||||
get_compose_files() {
|
||||
local env=$1
|
||||
local base_file="$PROJECT_ROOT/docker-compose.yml"
|
||||
local env_file="$PROJECT_ROOT/docker-compose.$env.yml"
|
||||
|
||||
if [ -f "$env_file" ]; then
|
||||
echo "-f $base_file -f $env_file"
|
||||
else
|
||||
echo "-f $base_file"
|
||||
fi
|
||||
}
|
||||
|
||||
# 创建必要的目录
|
||||
create_directories() {
|
||||
local data_dir=${DATA_DIR:-./data}
|
||||
local log_dir=${LOG_DIR:-./logs}
|
||||
local backup_dir=${BACKUP_DIR:-./backup}
|
||||
|
||||
print_message $BLUE "创建必要的目录..."
|
||||
mkdir -p "$data_dir"/{postgres,redis,uploads}
|
||||
mkdir -p "$log_dir"/{postgres,redis,app,nginx}
|
||||
mkdir -p "$backup_dir"/postgres
|
||||
|
||||
# 设置权限
|
||||
chmod 755 "$data_dir" "$log_dir" "$backup_dir"
|
||||
chmod 700 "$data_dir"/postgres
|
||||
}
|
||||
|
||||
# 启动服务
|
||||
start_services() {
|
||||
local env=$1
|
||||
shift
|
||||
local services="$@"
|
||||
|
||||
print_message $GREEN "启动 $env 环境服务..."
|
||||
check_env_file
|
||||
create_directories
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
docker-compose $(get_compose_files $env) --env-file "$ENV_FILE" up -d $services
|
||||
|
||||
print_message $GREEN "服务启动完成!"
|
||||
show_status $env
|
||||
}
|
||||
|
||||
# 停止服务
|
||||
stop_services() {
|
||||
local env=$1
|
||||
shift
|
||||
local services="$@"
|
||||
|
||||
print_message $YELLOW "停止 $env 环境服务..."
|
||||
cd "$PROJECT_ROOT"
|
||||
docker-compose $(get_compose_files $env) --env-file "$ENV_FILE" stop $services
|
||||
|
||||
print_message $GREEN "服务停止完成!"
|
||||
}
|
||||
|
||||
# 重启服务
|
||||
restart_services() {
|
||||
local env=$1
|
||||
shift
|
||||
local services="$@"
|
||||
|
||||
print_message $BLUE "重启 $env 环境服务..."
|
||||
stop_services $env $services
|
||||
sleep 2
|
||||
start_services $env $services
|
||||
}
|
||||
|
||||
# 查看服务状态
|
||||
show_status() {
|
||||
local env=$1
|
||||
|
||||
print_message $BLUE "查看 $env 环境服务状态..."
|
||||
cd "$PROJECT_ROOT"
|
||||
docker-compose $(get_compose_files $env) --env-file "$ENV_FILE" ps
|
||||
}
|
||||
|
||||
# 查看日志
|
||||
show_logs() {
|
||||
local env=$1
|
||||
shift
|
||||
local services="$@"
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
docker-compose $(get_compose_files $env) --env-file "$ENV_FILE" logs -f $services
|
||||
}
|
||||
|
||||
# 构建镜像
|
||||
build_images() {
|
||||
local env=$1
|
||||
|
||||
print_message $BLUE "构建 $env 环境镜像..."
|
||||
cd "$PROJECT_ROOT"
|
||||
docker-compose $(get_compose_files $env) --env-file "$ENV_FILE" build
|
||||
|
||||
print_message $GREEN "镜像构建完成!"
|
||||
}
|
||||
|
||||
# 清理资源
|
||||
clean_resources() {
|
||||
local env=$1
|
||||
|
||||
print_message $YELLOW "清理 $env 环境资源..."
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# 停止并删除容器
|
||||
docker-compose $(get_compose_files $env) --env-file "$ENV_FILE" down
|
||||
|
||||
# 清理未使用的镜像和网络
|
||||
docker system prune -f
|
||||
|
||||
print_message $GREEN "资源清理完成!"
|
||||
}
|
||||
|
||||
# 备份数据
|
||||
backup_data() {
|
||||
local env=$1
|
||||
|
||||
print_message $BLUE "备份 $env 环境数据..."
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# 执行数据库备份
|
||||
docker-compose $(get_compose_files $env) --env-file "$ENV_FILE" exec qaup-postgres /backup-db.sh
|
||||
|
||||
print_message $GREEN "数据备份完成!"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
if [ $# -eq 0 ]; then
|
||||
show_help
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 解析参数
|
||||
local env="prod"
|
||||
local command=""
|
||||
|
||||
# 检查第一个参数是否是环境
|
||||
if [[ "$1" =~ ^(dev|prod)$ ]]; then
|
||||
env=$1
|
||||
shift
|
||||
fi
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
show_help
|
||||
exit 0
|
||||
fi
|
||||
|
||||
command=$1
|
||||
shift
|
||||
|
||||
# 加载环境文件
|
||||
if [ -f "$ENV_FILE" ]; then
|
||||
source "$ENV_FILE"
|
||||
fi
|
||||
|
||||
# 执行命令
|
||||
case $command in
|
||||
start)
|
||||
start_services $env "$@"
|
||||
;;
|
||||
stop)
|
||||
stop_services $env "$@"
|
||||
;;
|
||||
restart)
|
||||
restart_services $env "$@"
|
||||
;;
|
||||
status)
|
||||
show_status $env
|
||||
;;
|
||||
logs)
|
||||
show_logs $env "$@"
|
||||
;;
|
||||
build)
|
||||
build_images $env
|
||||
;;
|
||||
clean)
|
||||
clean_resources $env
|
||||
;;
|
||||
backup)
|
||||
backup_data $env
|
||||
;;
|
||||
help|--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
print_message $RED "未知命令: $command"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 执行主函数
|
||||
main "$@"
|
||||
99
deploy/docker/healthcheck.yml
Normal file
99
deploy/docker/healthcheck.yml
Normal file
@ -0,0 +1,99 @@
|
||||
# QAUP 健康检查配置
|
||||
# 用于配置各服务的健康检查参数
|
||||
|
||||
healthchecks:
|
||||
qaup-app:
|
||||
endpoint: "http://localhost:8080/actuator/health"
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 120s
|
||||
expected_status: 200
|
||||
expected_content: '"status":"UP"'
|
||||
|
||||
qaup-nginx:
|
||||
endpoint: "http://localhost/health"
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
expected_status: 200
|
||||
expected_content: "healthy"
|
||||
|
||||
qaup-postgres:
|
||||
command: "pg_isready -h localhost -p 5432 -U postgres"
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 60s
|
||||
|
||||
qaup-redis:
|
||||
command: "redis-cli ping"
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
expected_output: "PONG"
|
||||
|
||||
# 监控阈值配置
|
||||
thresholds:
|
||||
cpu_usage: 80 # CPU使用率告警阈值 (%)
|
||||
memory_usage: 85 # 内存使用率告警阈值 (%)
|
||||
disk_usage: 80 # 磁盘使用率告警阈值 (%)
|
||||
response_time: 5000 # 响应时间告警阈值 (ms)
|
||||
error_rate: 5 # 错误率告警阈值 (%)
|
||||
|
||||
# 日志监控配置
|
||||
log_monitoring:
|
||||
error_patterns:
|
||||
- "ERROR"
|
||||
- "FATAL"
|
||||
- "Exception"
|
||||
- "failed"
|
||||
- "timeout"
|
||||
|
||||
warning_patterns:
|
||||
- "WARN"
|
||||
- "WARNING"
|
||||
- "deprecated"
|
||||
|
||||
ignore_patterns:
|
||||
- "health check"
|
||||
- "actuator"
|
||||
- "favicon.ico"
|
||||
|
||||
# 告警配置
|
||||
alerts:
|
||||
enabled: true
|
||||
channels:
|
||||
- type: "log"
|
||||
path: "./logs/alerts.log"
|
||||
- type: "email"
|
||||
enabled: false
|
||||
smtp_server: "smtp.example.com"
|
||||
smtp_port: 587
|
||||
username: "alerts@example.com"
|
||||
password: "password"
|
||||
recipients:
|
||||
- "admin@example.com"
|
||||
|
||||
rules:
|
||||
- name: "容器停止"
|
||||
condition: "container_stopped"
|
||||
severity: "critical"
|
||||
message: "容器 {{container_name}} 已停止运行"
|
||||
|
||||
- name: "健康检查失败"
|
||||
condition: "health_check_failed"
|
||||
severity: "warning"
|
||||
message: "服务 {{service_name}} 健康检查失败"
|
||||
|
||||
- name: "资源使用率过高"
|
||||
condition: "resource_usage_high"
|
||||
severity: "warning"
|
||||
message: "{{resource_type}} 使用率达到 {{usage}}%"
|
||||
|
||||
- name: "错误日志增加"
|
||||
condition: "error_log_increase"
|
||||
severity: "warning"
|
||||
message: "检测到错误日志数量异常增加"
|
||||
31
deploy/docker/image-versions.env
Normal file
31
deploy/docker/image-versions.env
Normal file
@ -0,0 +1,31 @@
|
||||
# QAUP Docker 镜像版本锁定配置
|
||||
# 用于确保离线部署的一致性
|
||||
|
||||
# 基础镜像版本
|
||||
OPENJDK_VERSION=17-jre-slim
|
||||
NGINX_VERSION=alpine
|
||||
POSTGRES_VERSION=15-3.3
|
||||
REDIS_VERSION=7-alpine
|
||||
NODE_VERSION=18-alpine
|
||||
MAVEN_VERSION=3.9.6-openjdk-17-slim
|
||||
|
||||
# 自定义镜像版本
|
||||
QAUP_APP_VERSION=latest
|
||||
QAUP_NGINX_VERSION=latest
|
||||
|
||||
# 镜像完整名称(用于脚本引用)
|
||||
OPENJDK_IMAGE=openjdk:${OPENJDK_VERSION}
|
||||
NGINX_IMAGE=nginx:${NGINX_VERSION}
|
||||
POSTGRES_IMAGE=postgis/postgis:${POSTGRES_VERSION}
|
||||
REDIS_IMAGE=redis:${REDIS_VERSION}
|
||||
NODE_IMAGE=node:${NODE_VERSION}
|
||||
MAVEN_IMAGE=maven:${MAVEN_VERSION}
|
||||
|
||||
QAUP_APP_IMAGE=qaup-app:${QAUP_APP_VERSION}
|
||||
QAUP_NGINX_IMAGE=qaup-nginx:${QAUP_NGINX_VERSION}
|
||||
|
||||
# 构建时间戳(自动生成)
|
||||
BUILD_TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
# 部署环境标识
|
||||
DEPLOY_ENV=production
|
||||
768
deploy/docker/integration-test.sh
Executable file
768
deploy/docker/integration-test.sh
Executable file
@ -0,0 +1,768 @@
|
||||
#!/bin/bash
|
||||
|
||||
# QAUP 集成测试脚本
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
TEST_LOG="$PROJECT_ROOT/logs/integration_test_$(date +%Y%m%d_%H%M%S).log"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_message() {
|
||||
local color=$1
|
||||
local message=$2
|
||||
echo -e "${color}${message}${NC}"
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') $message" >> "$TEST_LOG"
|
||||
}
|
||||
|
||||
# 测试结果统计
|
||||
TOTAL_TESTS=0
|
||||
PASSED_TESTS=0
|
||||
FAILED_TESTS=0
|
||||
FAILED_TEST_NAMES=()
|
||||
|
||||
# 记录测试结果
|
||||
record_test_result() {
|
||||
local test_name=$1
|
||||
local result=$2
|
||||
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
|
||||
if [ "$result" = "PASS" ]; then
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
print_message $GREEN "✓ $test_name: PASS"
|
||||
else
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
FAILED_TEST_NAMES+=("$test_name")
|
||||
print_message $RED "✗ $test_name: FAIL"
|
||||
fi
|
||||
}
|
||||
|
||||
# 等待服务就绪
|
||||
wait_for_service() {
|
||||
local service_name=$1
|
||||
local url=$2
|
||||
local max_attempts=${3:-30}
|
||||
local attempt=0
|
||||
|
||||
print_message $BLUE "等待 $service_name 服务就绪..."
|
||||
|
||||
while [ $attempt -lt $max_attempts ]; do
|
||||
if curl -f -s "$url" &>/dev/null; then
|
||||
print_message $GREEN "$service_name 服务已就绪"
|
||||
return 0
|
||||
fi
|
||||
|
||||
sleep 2
|
||||
attempt=$((attempt + 1))
|
||||
|
||||
if [ $((attempt % 10)) -eq 0 ]; then
|
||||
print_message $YELLOW "$service_name 等待中... ($attempt/$max_attempts)"
|
||||
fi
|
||||
done
|
||||
|
||||
print_message $RED "$service_name 服务启动超时"
|
||||
return 1
|
||||
}
|
||||
|
||||
# 测试 Docker 环境
|
||||
test_docker_environment() {
|
||||
print_message $BLUE "测试 Docker 环境..."
|
||||
|
||||
# 测试 Docker 是否运行
|
||||
if docker info &>/dev/null; then
|
||||
record_test_result "Docker服务运行" "PASS"
|
||||
else
|
||||
record_test_result "Docker服务运行" "FAIL"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 测试 Docker Compose 是否可用
|
||||
if docker-compose --version &>/dev/null; then
|
||||
record_test_result "Docker Compose可用" "PASS"
|
||||
else
|
||||
record_test_result "Docker Compose可用" "FAIL"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 测试网络连接
|
||||
if docker network ls | grep -q qaup; then
|
||||
record_test_result "Docker网络存在" "PASS"
|
||||
else
|
||||
record_test_result "Docker网络存在" "FAIL"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# 测试容器启动
|
||||
test_container_startup() {
|
||||
print_message $BLUE "测试容器启动..."
|
||||
|
||||
local containers=("qaup-postgres" "qaup-redis" "qaup-app" "qaup-nginx")
|
||||
|
||||
for container in "${containers[@]}"; do
|
||||
if docker ps --format "{{.Names}}" | grep -q "^${container}$"; then
|
||||
record_test_result "${container}容器运行" "PASS"
|
||||
else
|
||||
record_test_result "${container}容器运行" "FAIL"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# 测试服务健康检查
|
||||
test_service_health() {
|
||||
print_message $BLUE "测试服务健康检查..."
|
||||
|
||||
# 测试数据库健康检查
|
||||
if docker exec qaup-postgres pg_isready -h localhost -p 5432 -U postgres &>/dev/null; then
|
||||
record_test_result "PostgreSQL健康检查" "PASS"
|
||||
else
|
||||
record_test_result "PostgreSQL健康检查" "FAIL"
|
||||
fi
|
||||
|
||||
# 测试 Redis 健康检查
|
||||
if docker exec qaup-redis redis-cli ping | grep -q "PONG"; then
|
||||
record_test_result "Redis健康检查" "PASS"
|
||||
else
|
||||
record_test_result "Redis健康检查" "FAIL"
|
||||
fi
|
||||
|
||||
# 测试应用健康检查
|
||||
if wait_for_service "QAUP应用" "http://localhost:8080/actuator/health" 30; then
|
||||
record_test_result "应用健康检查" "PASS"
|
||||
else
|
||||
record_test_result "应用健康检查" "FAIL"
|
||||
fi
|
||||
|
||||
# 测试 Nginx 健康检查
|
||||
if wait_for_service "Nginx" "http://localhost/health" 10; then
|
||||
record_test_result "Nginx健康检查" "PASS"
|
||||
else
|
||||
record_test_result "Nginx健康检查" "FAIL"
|
||||
fi
|
||||
}
|
||||
|
||||
# 测试网络连通性
|
||||
test_network_connectivity() {
|
||||
print_message $BLUE "测试网络连通性..."
|
||||
|
||||
# 测试应用到数据库的连接
|
||||
if docker exec qaup-app nc -z qaup-postgres 5432 &>/dev/null; then
|
||||
record_test_result "应用到数据库连接" "PASS"
|
||||
else
|
||||
record_test_result "应用到数据库连接" "FAIL"
|
||||
fi
|
||||
|
||||
# 测试应用到 Redis 的连接
|
||||
if docker exec qaup-app nc -z qaup-redis 6379 &>/dev/null; then
|
||||
record_test_result "应用到Redis连接" "PASS"
|
||||
else
|
||||
record_test_result "应用到Redis连接" "FAIL"
|
||||
fi
|
||||
|
||||
# 测试 Nginx 到应用的连接
|
||||
if docker exec qaup-nginx nc -z qaup-app 8080 &>/dev/null; then
|
||||
record_test_result "Nginx到应用连接" "PASS"
|
||||
else
|
||||
record_test_result "Nginx到应用连接" "FAIL"
|
||||
fi
|
||||
}
|
||||
|
||||
# 测试数据库功能
|
||||
test_database_functionality() {
|
||||
print_message $BLUE "测试数据库功能..."
|
||||
|
||||
# 测试数据库连接
|
||||
if docker exec qaup-postgres psql -U postgres -d qaup -c "SELECT 1;" &>/dev/null; then
|
||||
record_test_result "数据库连接测试" "PASS"
|
||||
else
|
||||
record_test_result "数据库连接测试" "FAIL"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 测试 PostGIS 扩展
|
||||
if docker exec qaup-postgres psql -U postgres -d qaup -c "SELECT PostGIS_Version();" &>/dev/null; then
|
||||
record_test_result "PostGIS扩展测试" "PASS"
|
||||
else
|
||||
record_test_result "PostGIS扩展测试" "FAIL"
|
||||
fi
|
||||
|
||||
# 测试表结构
|
||||
local table_count=$(docker exec qaup-postgres psql -U postgres -d qaup -t -c "SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public';" | xargs)
|
||||
if [ "$table_count" -gt 0 ]; then
|
||||
record_test_result "数据库表结构测试" "PASS"
|
||||
print_message $BLUE "数据库包含 $table_count 个表"
|
||||
else
|
||||
record_test_result "数据库表结构测试" "FAIL"
|
||||
fi
|
||||
|
||||
# 测试数据库写入
|
||||
local test_table="integration_test_$(date +%s)"
|
||||
if docker exec qaup-postgres psql -U postgres -d qaup -c "CREATE TABLE $test_table (id SERIAL PRIMARY KEY, test_data TEXT); INSERT INTO $test_table (test_data) VALUES ('test'); DROP TABLE $test_table;" &>/dev/null; then
|
||||
record_test_result "数据库写入测试" "PASS"
|
||||
else
|
||||
record_test_result "数据库写入测试" "FAIL"
|
||||
fi
|
||||
}
|
||||
|
||||
# 测试 Redis 功能
|
||||
test_redis_functionality() {
|
||||
print_message $BLUE "测试 Redis 功能..."
|
||||
|
||||
# 测试 Redis 基本操作
|
||||
if docker exec qaup-redis redis-cli set test_key "test_value" | grep -q "OK"; then
|
||||
record_test_result "Redis写入测试" "PASS"
|
||||
else
|
||||
record_test_result "Redis写入测试" "FAIL"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 测试 Redis 读取
|
||||
if docker exec qaup-redis redis-cli get test_key | grep -q "test_value"; then
|
||||
record_test_result "Redis读取测试" "PASS"
|
||||
else
|
||||
record_test_result "Redis读取测试" "FAIL"
|
||||
fi
|
||||
|
||||
# 清理测试数据
|
||||
docker exec qaup-redis redis-cli del test_key &>/dev/null
|
||||
|
||||
# 测试 Redis 内存使用
|
||||
local memory_usage=$(docker exec qaup-redis redis-cli info memory | grep "used_memory_human" | cut -d: -f2 | tr -d '\r')
|
||||
if [ -n "$memory_usage" ]; then
|
||||
record_test_result "Redis内存信息测试" "PASS"
|
||||
print_message $BLUE "Redis 内存使用: $memory_usage"
|
||||
else
|
||||
record_test_result "Redis内存信息测试" "FAIL"
|
||||
fi
|
||||
}
|
||||
|
||||
# 测试应用 API
|
||||
test_application_api() {
|
||||
print_message $BLUE "测试应用 API..."
|
||||
|
||||
# 测试健康检查端点
|
||||
local health_response=$(curl -s http://localhost:8080/actuator/health)
|
||||
if echo "$health_response" | grep -q '"status":"UP"'; then
|
||||
record_test_result "应用健康端点测试" "PASS"
|
||||
else
|
||||
record_test_result "应用健康端点测试" "FAIL"
|
||||
fi
|
||||
|
||||
# 测试应用信息端点
|
||||
if curl -f -s http://localhost:8080/actuator/info &>/dev/null; then
|
||||
record_test_result "应用信息端点测试" "PASS"
|
||||
else
|
||||
record_test_result "应用信息端点测试" "FAIL"
|
||||
fi
|
||||
|
||||
# 测试应用指标端点
|
||||
if curl -f -s http://localhost:8080/actuator/metrics &>/dev/null; then
|
||||
record_test_result "应用指标端点测试" "PASS"
|
||||
else
|
||||
record_test_result "应用指标端点测试" "FAIL"
|
||||
fi
|
||||
|
||||
# 测试应用响应时间
|
||||
local response_time=$(curl -o /dev/null -s -w '%{time_total}' http://localhost:8080/actuator/health)
|
||||
if (( $(echo "$response_time < 5.0" | bc -l) )); then
|
||||
record_test_result "应用响应时间测试" "PASS"
|
||||
print_message $BLUE "应用响应时间: ${response_time}s"
|
||||
else
|
||||
record_test_result "应用响应时间测试" "FAIL"
|
||||
print_message $YELLOW "应用响应时间较慢: ${response_time}s"
|
||||
fi
|
||||
}
|
||||
|
||||
# 测试前端访问
|
||||
test_frontend_access() {
|
||||
print_message $BLUE "测试前端访问..."
|
||||
|
||||
# 测试 Nginx 健康检查
|
||||
if curl -f -s http://localhost/health | grep -q "healthy"; then
|
||||
record_test_result "前端健康检查测试" "PASS"
|
||||
else
|
||||
record_test_result "前端健康检查测试" "FAIL"
|
||||
fi
|
||||
|
||||
# 测试前端页面访问
|
||||
local http_status=$(curl -o /dev/null -s -w '%{http_code}' http://localhost/)
|
||||
if [ "$http_status" = "200" ]; then
|
||||
record_test_result "前端页面访问测试" "PASS"
|
||||
else
|
||||
record_test_result "前端页面访问测试" "FAIL"
|
||||
print_message $YELLOW "HTTP状态码: $http_status"
|
||||
fi
|
||||
|
||||
# 测试 API 代理
|
||||
local api_status=$(curl -o /dev/null -s -w '%{http_code}' http://localhost/prod-api/actuator/health)
|
||||
if [ "$api_status" = "200" ]; then
|
||||
record_test_result "API代理测试" "PASS"
|
||||
else
|
||||
record_test_result "API代理测试" "FAIL"
|
||||
print_message $YELLOW "API代理状态码: $api_status"
|
||||
fi
|
||||
|
||||
# 测试静态资源
|
||||
if curl -f -s http://localhost/favicon.ico &>/dev/null; then
|
||||
record_test_result "静态资源访问测试" "PASS"
|
||||
else
|
||||
record_test_result "静态资源访问测试" "FAIL"
|
||||
fi
|
||||
}
|
||||
|
||||
# 测试数据持久化
|
||||
test_data_persistence() {
|
||||
print_message $BLUE "测试数据持久化..."
|
||||
|
||||
# 测试数据库数据持久化
|
||||
local test_data="persistence_test_$(date +%s)"
|
||||
|
||||
# 创建测试数据
|
||||
if docker exec qaup-postgres psql -U postgres -d qaup -c "CREATE TABLE IF NOT EXISTS test_persistence (id SERIAL PRIMARY KEY, data TEXT); INSERT INTO test_persistence (data) VALUES ('$test_data');" &>/dev/null; then
|
||||
record_test_result "数据库数据创建测试" "PASS"
|
||||
else
|
||||
record_test_result "数据库数据创建测试" "FAIL"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 重启数据库容器
|
||||
print_message $BLUE "重启数据库容器测试持久化..."
|
||||
docker restart qaup-postgres &>/dev/null
|
||||
|
||||
# 等待数据库重新启动
|
||||
sleep 10
|
||||
|
||||
# 验证数据是否仍然存在
|
||||
if docker exec qaup-postgres psql -U postgres -d qaup -t -c "SELECT data FROM test_persistence WHERE data = '$test_data';" | grep -q "$test_data"; then
|
||||
record_test_result "数据库持久化测试" "PASS"
|
||||
else
|
||||
record_test_result "数据库持久化测试" "FAIL"
|
||||
fi
|
||||
|
||||
# 清理测试数据
|
||||
docker exec qaup-postgres psql -U postgres -d qaup -c "DELETE FROM test_persistence WHERE data = '$test_data';" &>/dev/null
|
||||
|
||||
# 测试 Redis 持久化
|
||||
docker exec qaup-redis redis-cli set persistence_test "$test_data" &>/dev/null
|
||||
docker restart qaup-redis &>/dev/null
|
||||
sleep 5
|
||||
|
||||
if docker exec qaup-redis redis-cli get persistence_test | grep -q "$test_data"; then
|
||||
record_test_result "Redis持久化测试" "PASS"
|
||||
else
|
||||
record_test_result "Redis持久化测试" "FAIL"
|
||||
fi
|
||||
|
||||
# 清理 Redis 测试数据
|
||||
docker exec qaup-redis redis-cli del persistence_test &>/dev/null
|
||||
}
|
||||
|
||||
# 测试文件上传功能
|
||||
test_file_upload() {
|
||||
print_message $BLUE "测试文件上传功能..."
|
||||
|
||||
# 创建测试文件
|
||||
local test_file="/tmp/test_upload_$(date +%s).txt"
|
||||
echo "This is a test file for upload functionality" > "$test_file"
|
||||
|
||||
# 检查上传目录是否存在
|
||||
if [ -d "$PROJECT_ROOT/data/uploads" ]; then
|
||||
# 模拟文件上传(复制到上传目录)
|
||||
cp "$test_file" "$PROJECT_ROOT/data/uploads/"
|
||||
|
||||
if [ -f "$PROJECT_ROOT/data/uploads/$(basename "$test_file")" ]; then
|
||||
record_test_result "文件上传测试" "PASS"
|
||||
|
||||
# 清理测试文件
|
||||
rm -f "$PROJECT_ROOT/data/uploads/$(basename "$test_file")"
|
||||
else
|
||||
record_test_result "文件上传测试" "FAIL"
|
||||
fi
|
||||
else
|
||||
record_test_result "文件上传目录检查" "FAIL"
|
||||
fi
|
||||
|
||||
# 清理临时文件
|
||||
rm -f "$test_file"
|
||||
}
|
||||
|
||||
# 测试日志功能
|
||||
test_logging() {
|
||||
print_message $BLUE "测试日志功能..."
|
||||
|
||||
# 检查应用日志
|
||||
if docker logs qaup-app --tail 10 | grep -q "Started"; then
|
||||
record_test_result "应用日志测试" "PASS"
|
||||
else
|
||||
record_test_result "应用日志测试" "FAIL"
|
||||
fi
|
||||
|
||||
# 检查 Nginx 访问日志
|
||||
if docker logs qaup-nginx --tail 10 &>/dev/null; then
|
||||
record_test_result "Nginx日志测试" "PASS"
|
||||
else
|
||||
record_test_result "Nginx日志测试" "FAIL"
|
||||
fi
|
||||
|
||||
# 检查日志目录
|
||||
if [ -d "$PROJECT_ROOT/logs" ]; then
|
||||
record_test_result "日志目录检查" "PASS"
|
||||
else
|
||||
record_test_result "日志目录检查" "FAIL"
|
||||
fi
|
||||
}
|
||||
|
||||
# 测试监控功能
|
||||
test_monitoring() {
|
||||
print_message $BLUE "测试监控功能..."
|
||||
|
||||
# 测试监控脚本
|
||||
if [ -x "$PROJECT_ROOT/docker/monitor.sh" ]; then
|
||||
if "$PROJECT_ROOT/docker/monitor.sh" status &>/dev/null; then
|
||||
record_test_result "监控脚本测试" "PASS"
|
||||
else
|
||||
record_test_result "监控脚本测试" "FAIL"
|
||||
fi
|
||||
else
|
||||
record_test_result "监控脚本存在性检查" "FAIL"
|
||||
fi
|
||||
|
||||
# 测试告警功能
|
||||
if [ -x "$PROJECT_ROOT/docker/alert-manager.sh" ]; then
|
||||
if "$PROJECT_ROOT/docker/alert-manager.sh" check &>/dev/null; then
|
||||
record_test_result "告警功能测试" "PASS"
|
||||
else
|
||||
record_test_result "告警功能测试" "FAIL"
|
||||
fi
|
||||
else
|
||||
record_test_result "告警脚本存在性检查" "FAIL"
|
||||
fi
|
||||
}
|
||||
|
||||
# 测试备份功能
|
||||
test_backup_functionality() {
|
||||
print_message $BLUE "测试备份功能..."
|
||||
|
||||
# 测试数据库备份
|
||||
if [ -x "$PROJECT_ROOT/docker/backup-restore.sh" ]; then
|
||||
if "$PROJECT_ROOT/docker/backup-restore.sh" database &>/dev/null; then
|
||||
record_test_result "数据库备份测试" "PASS"
|
||||
else
|
||||
record_test_result "数据库备份测试" "FAIL"
|
||||
fi
|
||||
else
|
||||
record_test_result "备份脚本存在性检查" "FAIL"
|
||||
fi
|
||||
|
||||
# 检查备份目录
|
||||
if [ -d "$PROJECT_ROOT/backup" ]; then
|
||||
record_test_result "备份目录检查" "PASS"
|
||||
else
|
||||
record_test_result "备份目录检查" "FAIL"
|
||||
fi
|
||||
}
|
||||
|
||||
# 性能测试
|
||||
test_performance() {
|
||||
print_message $BLUE "测试系统性能..."
|
||||
|
||||
# 测试并发请求
|
||||
local concurrent_requests=10
|
||||
local total_requests=100
|
||||
|
||||
print_message $BLUE "执行并发性能测试 ($concurrent_requests 并发, $total_requests 请求)..."
|
||||
|
||||
# 使用 curl 进行简单的并发测试
|
||||
local start_time=$(date +%s.%N)
|
||||
|
||||
for ((i=1; i<=concurrent_requests; i++)); do
|
||||
{
|
||||
for ((j=1; j<=10; j++)); do
|
||||
curl -s http://localhost/health &>/dev/null
|
||||
done
|
||||
} &
|
||||
done
|
||||
|
||||
wait
|
||||
|
||||
local end_time=$(date +%s.%N)
|
||||
local duration=$(echo "$end_time - $start_time" | bc)
|
||||
local rps=$(echo "scale=2; $total_requests / $duration" | bc)
|
||||
|
||||
print_message $BLUE "性能测试结果: ${rps} 请求/秒"
|
||||
|
||||
# 判断性能是否合格(简单阈值)
|
||||
if (( $(echo "$rps > 50" | bc -l) )); then
|
||||
record_test_result "并发性能测试" "PASS"
|
||||
else
|
||||
record_test_result "并发性能测试" "FAIL"
|
||||
fi
|
||||
|
||||
# 测试内存使用
|
||||
local memory_usage=$(docker stats --no-stream --format "{{.MemPerc}}" qaup-app | sed 's/%//')
|
||||
if (( $(echo "$memory_usage < 80" | bc -l) )); then
|
||||
record_test_result "内存使用测试" "PASS"
|
||||
print_message $BLUE "应用内存使用: ${memory_usage}%"
|
||||
else
|
||||
record_test_result "内存使用测试" "FAIL"
|
||||
print_message $YELLOW "应用内存使用较高: ${memory_usage}%"
|
||||
fi
|
||||
}
|
||||
|
||||
# 安全测试
|
||||
test_security() {
|
||||
print_message $BLUE "测试安全配置..."
|
||||
|
||||
# 测试端口访问限制
|
||||
local restricted_ports=("5432" "6379" "8080")
|
||||
|
||||
for port in "${restricted_ports[@]}"; do
|
||||
if ! nc -z localhost "$port" 2>/dev/null; then
|
||||
record_test_result "端口${port}访问限制测试" "PASS"
|
||||
else
|
||||
record_test_result "端口${port}访问限制测试" "FAIL"
|
||||
fi
|
||||
done
|
||||
|
||||
# 测试 HTTP 安全头
|
||||
local security_headers=("X-Frame-Options" "X-Content-Type-Options" "X-XSS-Protection")
|
||||
|
||||
for header in "${security_headers[@]}"; do
|
||||
if curl -I -s http://localhost/ | grep -q "$header"; then
|
||||
record_test_result "${header}安全头测试" "PASS"
|
||||
else
|
||||
record_test_result "${header}安全头测试" "FAIL"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# 运行完整集成测试
|
||||
run_full_integration_test() {
|
||||
print_message $GREEN "========================================="
|
||||
print_message $GREEN "开始 QAUP 系统集成测试"
|
||||
print_message $GREEN "========================================="
|
||||
|
||||
# 确保日志目录存在
|
||||
mkdir -p "$(dirname "$TEST_LOG")"
|
||||
|
||||
# 记录测试开始时间
|
||||
local start_time=$(date +%s)
|
||||
|
||||
# 执行各项测试
|
||||
test_docker_environment
|
||||
test_container_startup
|
||||
test_service_health
|
||||
test_network_connectivity
|
||||
test_database_functionality
|
||||
test_redis_functionality
|
||||
test_application_api
|
||||
test_frontend_access
|
||||
test_data_persistence
|
||||
test_file_upload
|
||||
test_logging
|
||||
test_monitoring
|
||||
test_backup_functionality
|
||||
test_performance
|
||||
test_security
|
||||
|
||||
# 计算测试时间
|
||||
local end_time=$(date +%s)
|
||||
local duration=$((end_time - start_time))
|
||||
|
||||
# 生成测试报告
|
||||
generate_test_report "$duration"
|
||||
}
|
||||
|
||||
# 生成测试报告
|
||||
generate_test_report() {
|
||||
local duration=$1
|
||||
|
||||
print_message $GREEN "========================================="
|
||||
print_message $GREEN "集成测试完成"
|
||||
print_message $GREEN "========================================="
|
||||
|
||||
echo ""
|
||||
print_message $BLUE "测试统计:"
|
||||
echo " 总测试数: $TOTAL_TESTS"
|
||||
echo " 通过测试: $PASSED_TESTS"
|
||||
echo " 失败测试: $FAILED_TESTS"
|
||||
echo " 成功率: $(echo "scale=2; $PASSED_TESTS * 100 / $TOTAL_TESTS" | bc)%"
|
||||
echo " 测试时间: ${duration}秒"
|
||||
echo ""
|
||||
|
||||
if [ $FAILED_TESTS -eq 0 ]; then
|
||||
print_message $GREEN "🎉 所有测试通过!系统部署成功!"
|
||||
echo ""
|
||||
print_message $BLUE "系统已准备就绪,可以投入使用:"
|
||||
echo " 前端访问: http://localhost"
|
||||
echo " API 文档: http://localhost/prod-api/swagger-ui.html"
|
||||
echo " 监控面板: ./docker/monitor.sh watch"
|
||||
echo ""
|
||||
else
|
||||
print_message $RED "❌ 部分测试失败,需要检查以下问题:"
|
||||
echo ""
|
||||
for failed_test in "${FAILED_TEST_NAMES[@]}"; do
|
||||
echo " - $failed_test"
|
||||
done
|
||||
echo ""
|
||||
print_message $YELLOW "建议操作:"
|
||||
echo " 1. 查看详细日志: $TEST_LOG"
|
||||
echo " 2. 检查服务状态: ./docker/monitor.sh status"
|
||||
echo " 3. 查看容器日志: docker logs <container_name>"
|
||||
echo " 4. 修复问题后重新运行测试"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# 创建 JSON 格式的测试报告
|
||||
local report_file="$PROJECT_ROOT/logs/integration_test_report_$(date +%Y%m%d_%H%M%S).json"
|
||||
|
||||
cat > "$report_file" << EOF
|
||||
{
|
||||
"test_run": {
|
||||
"timestamp": "$(date -u '+%Y-%m-%dT%H:%M:%S.%3NZ')",
|
||||
"duration_seconds": $duration,
|
||||
"total_tests": $TOTAL_TESTS,
|
||||
"passed_tests": $PASSED_TESTS,
|
||||
"failed_tests": $FAILED_TESTS,
|
||||
"success_rate": $(echo "scale=4; $PASSED_TESTS / $TOTAL_TESTS" | bc),
|
||||
"status": "$([ $FAILED_TESTS -eq 0 ] && echo "SUCCESS" || echo "FAILURE")"
|
||||
},
|
||||
"failed_tests": [
|
||||
$(IFS=','; printf ' "%s"' "${FAILED_TEST_NAMES[*]}" | sed 's/","/",\n "/g')
|
||||
],
|
||||
"log_file": "$TEST_LOG",
|
||||
"report_file": "$report_file"
|
||||
}
|
||||
EOF
|
||||
|
||||
print_message $BLUE "详细测试报告已生成:"
|
||||
echo " 日志文件: $TEST_LOG"
|
||||
echo " JSON报告: $report_file"
|
||||
|
||||
# 返回测试结果
|
||||
return $FAILED_TESTS
|
||||
}
|
||||
|
||||
# 快速健康检查
|
||||
quick_health_check() {
|
||||
print_message $BLUE "执行快速健康检查..."
|
||||
|
||||
test_container_startup
|
||||
test_service_health
|
||||
test_frontend_access
|
||||
|
||||
if [ $FAILED_TESTS -eq 0 ]; then
|
||||
print_message $GREEN "✓ 快速健康检查通过"
|
||||
return 0
|
||||
else
|
||||
print_message $RED "✗ 快速健康检查失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "QAUP 集成测试脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [命令]"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " full 运行完整集成测试"
|
||||
echo " quick 快速健康检查"
|
||||
echo " docker 测试 Docker 环境"
|
||||
echo " containers 测试容器状态"
|
||||
echo " health 测试服务健康状态"
|
||||
echo " network 测试网络连通性"
|
||||
echo " database 测试数据库功能"
|
||||
echo " redis 测试 Redis 功能"
|
||||
echo " api 测试应用 API"
|
||||
echo " frontend 测试前端访问"
|
||||
echo " performance 性能测试"
|
||||
echo " security 安全测试"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 full # 运行完整集成测试"
|
||||
echo " $0 quick # 快速健康检查"
|
||||
echo " $0 performance # 仅运行性能测试"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
if [ $# -eq 0 ]; then
|
||||
show_help
|
||||
exit 0
|
||||
fi
|
||||
|
||||
local command=$1
|
||||
|
||||
case $command in
|
||||
full)
|
||||
run_full_integration_test
|
||||
;;
|
||||
quick)
|
||||
quick_health_check
|
||||
;;
|
||||
docker)
|
||||
test_docker_environment
|
||||
generate_test_report 0
|
||||
;;
|
||||
containers)
|
||||
test_container_startup
|
||||
generate_test_report 0
|
||||
;;
|
||||
health)
|
||||
test_service_health
|
||||
generate_test_report 0
|
||||
;;
|
||||
network)
|
||||
test_network_connectivity
|
||||
generate_test_report 0
|
||||
;;
|
||||
database)
|
||||
test_database_functionality
|
||||
generate_test_report 0
|
||||
;;
|
||||
redis)
|
||||
test_redis_functionality
|
||||
generate_test_report 0
|
||||
;;
|
||||
api)
|
||||
test_application_api
|
||||
generate_test_report 0
|
||||
;;
|
||||
frontend)
|
||||
test_frontend_access
|
||||
generate_test_report 0
|
||||
;;
|
||||
performance)
|
||||
test_performance
|
||||
generate_test_report 0
|
||||
;;
|
||||
security)
|
||||
test_security
|
||||
generate_test_report 0
|
||||
;;
|
||||
help|--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
print_message $RED "未知命令: $command"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# 返回失败测试数量作为退出码
|
||||
exit $FAILED_TESTS
|
||||
}
|
||||
|
||||
main "$@"
|
||||
61
deploy/docker/load-images.sh
Executable file
61
deploy/docker/load-images.sh
Executable file
@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
|
||||
# QAUP 离线部署镜像导入脚本
|
||||
# 此脚本在客户内网环境中运行
|
||||
|
||||
set -e
|
||||
|
||||
# 配置变量
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
IMAGES_DIR="$SCRIPT_DIR/../offline-deploy/images"
|
||||
|
||||
echo "========================================="
|
||||
echo "QAUP 离线部署镜像导入开始"
|
||||
echo "========================================="
|
||||
|
||||
# 检查镜像目录是否存在
|
||||
if [ ! -d "$IMAGES_DIR" ]; then
|
||||
echo "错误: 镜像目录不存在: $IMAGES_DIR"
|
||||
echo "请确保离线部署包已正确解压"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$IMAGES_DIR"
|
||||
|
||||
# 验证校验和
|
||||
if [ -f "checksums.sha256" ]; then
|
||||
echo "验证镜像文件完整性..."
|
||||
if sha256sum -c checksums.sha256; then
|
||||
echo "✓ 所有镜像文件校验通过"
|
||||
else
|
||||
echo "✗ 镜像文件校验失败,请重新获取离线部署包"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "警告: 未找到校验和文件,跳过完整性验证"
|
||||
fi
|
||||
|
||||
# 导入镜像
|
||||
echo ""
|
||||
echo "开始导入 Docker 镜像..."
|
||||
|
||||
for image_file in *.tar.gz; do
|
||||
if [ -f "$image_file" ]; then
|
||||
echo "导入镜像: $image_file"
|
||||
gunzip -c "$image_file" | docker load
|
||||
echo "✓ $image_file 导入完成"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "验证导入的镜像:"
|
||||
echo "========================================="
|
||||
|
||||
# 验证镜像是否成功导入
|
||||
echo "已导入的镜像列表:"
|
||||
docker images --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}" | grep -E "(qaup|openjdk|nginx|postgis|redis)"
|
||||
|
||||
echo ""
|
||||
echo "镜像导入完成!"
|
||||
echo "现在可以运行 docker-compose up 启动服务"
|
||||
428
deploy/docker/log-manager.sh
Executable file
428
deploy/docker/log-manager.sh
Executable file
@ -0,0 +1,428 @@
|
||||
#!/bin/bash
|
||||
|
||||
# QAUP 日志管理脚本
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
LOG_DIR="$PROJECT_ROOT/logs"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_message() {
|
||||
local color=$1
|
||||
local message=$2
|
||||
echo -e "${color}${message}${NC}"
|
||||
}
|
||||
|
||||
# 配置日志轮转
|
||||
setup_log_rotation() {
|
||||
print_message $BLUE "配置日志轮转..."
|
||||
|
||||
local logrotate_config="/etc/logrotate.d/qaup"
|
||||
|
||||
# 创建 logrotate 配置
|
||||
sudo tee "$logrotate_config" > /dev/null << EOF
|
||||
# QAUP 日志轮转配置
|
||||
$LOG_DIR/app/*.log {
|
||||
daily
|
||||
rotate 30
|
||||
compress
|
||||
delaycompress
|
||||
missingok
|
||||
notifempty
|
||||
create 644 root root
|
||||
postrotate
|
||||
docker exec qaup-app pkill -USR1 java || true
|
||||
endscript
|
||||
}
|
||||
|
||||
$LOG_DIR/nginx/*.log {
|
||||
daily
|
||||
rotate 30
|
||||
compress
|
||||
delaycompress
|
||||
missingok
|
||||
notifempty
|
||||
create 644 root root
|
||||
postrotate
|
||||
docker exec qaup-nginx nginx -s reopen || true
|
||||
endscript
|
||||
}
|
||||
|
||||
$LOG_DIR/postgres/*.log {
|
||||
daily
|
||||
rotate 30
|
||||
compress
|
||||
delaycompress
|
||||
missingok
|
||||
notifempty
|
||||
create 644 root root
|
||||
}
|
||||
|
||||
$LOG_DIR/redis/*.log {
|
||||
daily
|
||||
rotate 30
|
||||
compress
|
||||
delaycompress
|
||||
missingok
|
||||
notifempty
|
||||
create 644 root root
|
||||
}
|
||||
EOF
|
||||
|
||||
print_message $GREEN "日志轮转配置已创建: $logrotate_config"
|
||||
|
||||
# 测试配置
|
||||
if sudo logrotate -d "$logrotate_config" &>/dev/null; then
|
||||
print_message $GREEN "✓ 日志轮转配置测试通过"
|
||||
else
|
||||
print_message $RED "✗ 日志轮转配置测试失败"
|
||||
fi
|
||||
}
|
||||
|
||||
# 查看日志
|
||||
view_logs() {
|
||||
local service=$1
|
||||
local lines=${2:-100}
|
||||
|
||||
case $service in
|
||||
app|qaup-app)
|
||||
print_message $BLUE "查看应用日志 (最近 $lines 行):"
|
||||
docker logs --tail $lines -f qaup-app
|
||||
;;
|
||||
nginx|qaup-nginx)
|
||||
print_message $BLUE "查看 Nginx 日志 (最近 $lines 行):"
|
||||
docker logs --tail $lines -f qaup-nginx
|
||||
;;
|
||||
postgres|qaup-postgres)
|
||||
print_message $BLUE "查看 PostgreSQL 日志 (最近 $lines 行):"
|
||||
docker logs --tail $lines -f qaup-postgres
|
||||
;;
|
||||
redis|qaup-redis)
|
||||
print_message $BLUE "查看 Redis 日志 (最近 $lines 行):"
|
||||
docker logs --tail $lines -f qaup-redis
|
||||
;;
|
||||
all)
|
||||
print_message $BLUE "查看所有服务日志 (最近 $lines 行):"
|
||||
docker-compose logs --tail $lines -f
|
||||
;;
|
||||
*)
|
||||
print_message $RED "未知服务: $service"
|
||||
print_message $YELLOW "可用服务: app, nginx, postgres, redis, all"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 分析错误日志
|
||||
analyze_error_logs() {
|
||||
print_message $BLUE "分析错误日志..."
|
||||
|
||||
local error_report="$LOG_DIR/error_analysis_$(date +%Y%m%d_%H%M%S).txt"
|
||||
|
||||
{
|
||||
echo "QAUP 错误日志分析报告"
|
||||
echo "生成时间: $(date)"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
# 分析应用错误
|
||||
echo "应用错误 (最近24小时):"
|
||||
echo "------------------------"
|
||||
docker logs qaup-app --since 24h 2>&1 | grep -i "error\|exception\|failed" | tail -20
|
||||
echo ""
|
||||
|
||||
# 分析 Nginx 错误
|
||||
echo "Nginx 错误 (最近24小时):"
|
||||
echo "------------------------"
|
||||
docker logs qaup-nginx --since 24h 2>&1 | grep -i "error\|failed" | tail -20
|
||||
echo ""
|
||||
|
||||
# 分析数据库错误
|
||||
echo "PostgreSQL 错误 (最近24小时):"
|
||||
echo "-----------------------------"
|
||||
docker logs qaup-postgres --since 24h 2>&1 | grep -i "error\|fatal\|panic" | tail -20
|
||||
echo ""
|
||||
|
||||
# 分析 Redis 错误
|
||||
echo "Redis 错误 (最近24小时):"
|
||||
echo "------------------------"
|
||||
docker logs qaup-redis --since 24h 2>&1 | grep -i "error\|warning" | tail -20
|
||||
echo ""
|
||||
|
||||
} > "$error_report"
|
||||
|
||||
print_message $GREEN "错误日志分析报告已生成: $error_report"
|
||||
|
||||
# 显示摘要
|
||||
local error_count=$(grep -c "error\|ERROR\|Error" "$error_report" || echo "0")
|
||||
if [ $error_count -gt 0 ]; then
|
||||
print_message $YELLOW "发现 $error_count 个错误条目"
|
||||
else
|
||||
print_message $GREEN "未发现明显错误"
|
||||
fi
|
||||
}
|
||||
|
||||
# 清理旧日志
|
||||
cleanup_old_logs() {
|
||||
local days=${1:-30}
|
||||
|
||||
print_message $BLUE "清理 $days 天前的日志文件..."
|
||||
|
||||
local cleaned_files=0
|
||||
|
||||
# 清理应用日志
|
||||
if [ -d "$LOG_DIR/app" ]; then
|
||||
local app_files=$(find "$LOG_DIR/app" -name "*.log*" -mtime +$days -type f | wc -l)
|
||||
find "$LOG_DIR/app" -name "*.log*" -mtime +$days -type f -delete
|
||||
cleaned_files=$((cleaned_files + app_files))
|
||||
fi
|
||||
|
||||
# 清理 Nginx 日志
|
||||
if [ -d "$LOG_DIR/nginx" ]; then
|
||||
local nginx_files=$(find "$LOG_DIR/nginx" -name "*.log*" -mtime +$days -type f | wc -l)
|
||||
find "$LOG_DIR/nginx" -name "*.log*" -mtime +$days -type f -delete
|
||||
cleaned_files=$((cleaned_files + nginx_files))
|
||||
fi
|
||||
|
||||
# 清理数据库日志
|
||||
if [ -d "$LOG_DIR/postgres" ]; then
|
||||
local postgres_files=$(find "$LOG_DIR/postgres" -name "*.log*" -mtime +$days -type f | wc -l)
|
||||
find "$LOG_DIR/postgres" -name "*.log*" -mtime +$days -type f -delete
|
||||
cleaned_files=$((cleaned_files + postgres_files))
|
||||
fi
|
||||
|
||||
# 清理 Redis 日志
|
||||
if [ -d "$LOG_DIR/redis" ]; then
|
||||
local redis_files=$(find "$LOG_DIR/redis" -name "*.log*" -mtime +$days -type f | wc -l)
|
||||
find "$LOG_DIR/redis" -name "*.log*" -mtime +$days -type f -delete
|
||||
cleaned_files=$((cleaned_files + redis_files))
|
||||
fi
|
||||
|
||||
print_message $GREEN "已清理 $cleaned_files 个旧日志文件"
|
||||
}
|
||||
|
||||
# 压缩日志文件
|
||||
compress_logs() {
|
||||
print_message $BLUE "压缩日志文件..."
|
||||
|
||||
local compressed_count=0
|
||||
|
||||
# 压缩7天前的日志文件
|
||||
for log_dir in "$LOG_DIR"/{app,nginx,postgres,redis}; do
|
||||
if [ -d "$log_dir" ]; then
|
||||
while IFS= read -r -d '' file; do
|
||||
if [[ "$file" != *.gz ]]; then
|
||||
gzip "$file"
|
||||
compressed_count=$((compressed_count + 1))
|
||||
fi
|
||||
done < <(find "$log_dir" -name "*.log" -mtime +7 -type f -print0)
|
||||
fi
|
||||
done
|
||||
|
||||
print_message $GREEN "已压缩 $compressed_count 个日志文件"
|
||||
}
|
||||
|
||||
# 导出日志
|
||||
export_logs() {
|
||||
local service=$1
|
||||
local start_date=$2
|
||||
local end_date=$3
|
||||
local output_file="logs_export_$(date +%Y%m%d_%H%M%S).tar.gz"
|
||||
|
||||
print_message $BLUE "导出日志..."
|
||||
|
||||
local temp_dir="/tmp/qaup_logs_export"
|
||||
mkdir -p "$temp_dir"
|
||||
|
||||
case $service in
|
||||
app|qaup-app)
|
||||
docker logs qaup-app --since "$start_date" --until "$end_date" > "$temp_dir/app.log"
|
||||
;;
|
||||
nginx|qaup-nginx)
|
||||
docker logs qaup-nginx --since "$start_date" --until "$end_date" > "$temp_dir/nginx.log"
|
||||
;;
|
||||
postgres|qaup-postgres)
|
||||
docker logs qaup-postgres --since "$start_date" --until "$end_date" > "$temp_dir/postgres.log"
|
||||
;;
|
||||
redis|qaup-redis)
|
||||
docker logs qaup-redis --since "$start_date" --until "$end_date" > "$temp_dir/redis.log"
|
||||
;;
|
||||
all)
|
||||
docker logs qaup-app --since "$start_date" --until "$end_date" > "$temp_dir/app.log"
|
||||
docker logs qaup-nginx --since "$start_date" --until "$end_date" > "$temp_dir/nginx.log"
|
||||
docker logs qaup-postgres --since "$start_date" --until "$end_date" > "$temp_dir/postgres.log"
|
||||
docker logs qaup-redis --since "$start_date" --until "$end_date" > "$temp_dir/redis.log"
|
||||
;;
|
||||
*)
|
||||
print_message $RED "未知服务: $service"
|
||||
rm -rf "$temp_dir"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# 创建压缩包
|
||||
tar -czf "$output_file" -C "$temp_dir" .
|
||||
rm -rf "$temp_dir"
|
||||
|
||||
print_message $GREEN "日志已导出到: $output_file"
|
||||
}
|
||||
|
||||
# 实时监控日志
|
||||
monitor_logs() {
|
||||
local service=${1:-all}
|
||||
local filter=${2:-""}
|
||||
|
||||
print_message $BLUE "实时监控日志 (服务: $service)"
|
||||
if [ -n "$filter" ]; then
|
||||
print_message $BLUE "过滤条件: $filter"
|
||||
fi
|
||||
print_message $YELLOW "按 Ctrl+C 停止监控"
|
||||
|
||||
case $service in
|
||||
app|qaup-app)
|
||||
if [ -n "$filter" ]; then
|
||||
docker logs -f qaup-app | grep --line-buffered "$filter"
|
||||
else
|
||||
docker logs -f qaup-app
|
||||
fi
|
||||
;;
|
||||
nginx|qaup-nginx)
|
||||
if [ -n "$filter" ]; then
|
||||
docker logs -f qaup-nginx | grep --line-buffered "$filter"
|
||||
else
|
||||
docker logs -f qaup-nginx
|
||||
fi
|
||||
;;
|
||||
postgres|qaup-postgres)
|
||||
if [ -n "$filter" ]; then
|
||||
docker logs -f qaup-postgres | grep --line-buffered "$filter"
|
||||
else
|
||||
docker logs -f qaup-postgres
|
||||
fi
|
||||
;;
|
||||
redis|qaup-redis)
|
||||
if [ -n "$filter" ]; then
|
||||
docker logs -f qaup-redis | grep --line-buffered "$filter"
|
||||
else
|
||||
docker logs -f qaup-redis
|
||||
fi
|
||||
;;
|
||||
all)
|
||||
if [ -n "$filter" ]; then
|
||||
docker-compose logs -f | grep --line-buffered "$filter"
|
||||
else
|
||||
docker-compose logs -f
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
print_message $RED "未知服务: $service"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 显示日志统计
|
||||
show_log_stats() {
|
||||
print_message $BLUE "日志统计信息:"
|
||||
|
||||
echo ""
|
||||
print_message $BLUE "日志目录大小:"
|
||||
if [ -d "$LOG_DIR" ]; then
|
||||
du -sh "$LOG_DIR"/* 2>/dev/null | sort -hr
|
||||
else
|
||||
print_message $YELLOW "日志目录不存在: $LOG_DIR"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
print_message $BLUE "容器日志大小:"
|
||||
docker system df --format "table {{.Type}}\t{{.TotalCount}}\t{{.Size}}\t{{.Reclaimable}}"
|
||||
|
||||
echo ""
|
||||
print_message $BLUE "最近24小时日志条目数:"
|
||||
local containers=("qaup-app" "qaup-nginx" "qaup-postgres" "qaup-redis")
|
||||
for container in "${containers[@]}"; do
|
||||
local count=$(docker logs "$container" --since 24h 2>/dev/null | wc -l)
|
||||
print_message $GREEN "$container: $count 条"
|
||||
done
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "QAUP 日志管理脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [命令] [选项]"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " setup 配置日志轮转"
|
||||
echo " view <service> 查看服务日志"
|
||||
echo " monitor <service> 实时监控日志"
|
||||
echo " analyze 分析错误日志"
|
||||
echo " cleanup [days] 清理旧日志 (默认30天)"
|
||||
echo " compress 压缩日志文件"
|
||||
echo " export <service> 导出日志"
|
||||
echo " stats 显示日志统计"
|
||||
echo ""
|
||||
echo "服务名称: app, nginx, postgres, redis, all"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 view app # 查看应用日志"
|
||||
echo " $0 monitor all # 监控所有服务日志"
|
||||
echo " $0 cleanup 7 # 清理7天前的日志"
|
||||
echo " $0 export all 2024-01-01 # 导出指定日期的日志"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
if [ $# -eq 0 ]; then
|
||||
show_help
|
||||
exit 0
|
||||
fi
|
||||
|
||||
local command=$1
|
||||
shift
|
||||
|
||||
case $command in
|
||||
setup)
|
||||
setup_log_rotation
|
||||
;;
|
||||
view)
|
||||
view_logs "$@"
|
||||
;;
|
||||
monitor)
|
||||
monitor_logs "$@"
|
||||
;;
|
||||
analyze)
|
||||
analyze_error_logs
|
||||
;;
|
||||
cleanup)
|
||||
cleanup_old_logs "$@"
|
||||
;;
|
||||
compress)
|
||||
compress_logs
|
||||
;;
|
||||
export)
|
||||
export_logs "$@"
|
||||
;;
|
||||
stats)
|
||||
show_log_stats
|
||||
;;
|
||||
help|--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
print_message $RED "未知命令: $command"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
348
deploy/docker/monitor.sh
Executable file
348
deploy/docker/monitor.sh
Executable file
@ -0,0 +1,348 @@
|
||||
#!/bin/bash
|
||||
|
||||
# QAUP 系统监控脚本
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
ENV_FILE="$PROJECT_ROOT/.env"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_message() {
|
||||
local color=$1
|
||||
local message=$2
|
||||
echo -e "${color}${message}${NC}"
|
||||
}
|
||||
|
||||
# 获取时间戳
|
||||
get_timestamp() {
|
||||
date '+%Y-%m-%d %H:%M:%S'
|
||||
}
|
||||
|
||||
# 检查容器状态
|
||||
check_container_status() {
|
||||
print_message $BLUE "检查容器状态..."
|
||||
|
||||
local containers=("qaup-postgres" "qaup-redis" "qaup-app" "qaup-nginx")
|
||||
local unhealthy_containers=()
|
||||
|
||||
for container in "${containers[@]}"; do
|
||||
if docker ps --format "table {{.Names}}\t{{.Status}}" | grep -q "$container"; then
|
||||
local status=$(docker ps --format "{{.Status}}" --filter "name=$container")
|
||||
if [[ "$status" == *"healthy"* ]] || [[ "$status" == *"Up"* ]]; then
|
||||
print_message $GREEN "✓ $container: 运行正常"
|
||||
else
|
||||
print_message $RED "✗ $container: $status"
|
||||
unhealthy_containers+=("$container")
|
||||
fi
|
||||
else
|
||||
print_message $RED "✗ $container: 未运行"
|
||||
unhealthy_containers+=("$container")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#unhealthy_containers[@]} -gt 0 ]; then
|
||||
print_message $YELLOW "异常容器: ${unhealthy_containers[*]}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# 检查服务健康状态
|
||||
check_service_health() {
|
||||
print_message $BLUE "检查服务健康状态..."
|
||||
|
||||
local errors=0
|
||||
|
||||
# 检查数据库连接
|
||||
if docker exec qaup-postgres pg_isready -h localhost -p 5432 -U postgres &>/dev/null; then
|
||||
print_message $GREEN "✓ PostgreSQL: 连接正常"
|
||||
else
|
||||
print_message $RED "✗ PostgreSQL: 连接失败"
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
|
||||
# 检查 Redis 连接
|
||||
if docker exec qaup-redis redis-cli ping &>/dev/null; then
|
||||
print_message $GREEN "✓ Redis: 连接正常"
|
||||
else
|
||||
print_message $RED "✗ Redis: 连接失败"
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
|
||||
# 检查应用健康端点
|
||||
if curl -f -s http://localhost:8080/actuator/health &>/dev/null; then
|
||||
print_message $GREEN "✓ QAUP 应用: 健康检查通过"
|
||||
else
|
||||
print_message $RED "✗ QAUP 应用: 健康检查失败"
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
|
||||
# 检查 Nginx
|
||||
if curl -f -s http://localhost/health &>/dev/null; then
|
||||
print_message $GREEN "✓ Nginx: 健康检查通过"
|
||||
else
|
||||
print_message $RED "✗ Nginx: 健康检查失败"
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
|
||||
return $errors
|
||||
}
|
||||
|
||||
# 检查资源使用情况
|
||||
check_resource_usage() {
|
||||
print_message $BLUE "检查资源使用情况..."
|
||||
|
||||
# 检查容器资源使用
|
||||
echo ""
|
||||
print_message $BLUE "容器资源使用:"
|
||||
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}"
|
||||
|
||||
echo ""
|
||||
print_message $BLUE "系统资源使用:"
|
||||
|
||||
# CPU 使用率
|
||||
local cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | sed 's/%us,//')
|
||||
print_message $GREEN "CPU 使用率: $cpu_usage"
|
||||
|
||||
# 内存使用情况
|
||||
local mem_info=$(free -h | awk 'NR==2{printf "已使用: %s/%s (%.2f%%)", $3,$2,$3*100/$2}')
|
||||
print_message $GREEN "内存使用: $mem_info"
|
||||
|
||||
# 磁盘使用情况
|
||||
local disk_usage=$(df -h . | awk 'NR==2{printf "已使用: %s/%s (%s)", $3,$2,$5}')
|
||||
print_message $GREEN "磁盘使用: $disk_usage"
|
||||
}
|
||||
|
||||
# 检查日志文件大小
|
||||
check_log_sizes() {
|
||||
print_message $BLUE "检查日志文件大小..."
|
||||
|
||||
local log_dirs=("./logs/postgres" "./logs/redis" "./logs/app" "./logs/nginx")
|
||||
local large_logs=()
|
||||
|
||||
for log_dir in "${log_dirs[@]}"; do
|
||||
if [ -d "$log_dir" ]; then
|
||||
local total_size=$(du -sh "$log_dir" 2>/dev/null | cut -f1)
|
||||
print_message $GREEN "✓ $log_dir: $total_size"
|
||||
|
||||
# 检查是否有大于 100MB 的日志文件
|
||||
while IFS= read -r -d '' file; do
|
||||
local file_size=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file" 2>/dev/null)
|
||||
if [ $file_size -gt 104857600 ]; then # 100MB
|
||||
large_logs+=("$file")
|
||||
fi
|
||||
done < <(find "$log_dir" -name "*.log" -print0 2>/dev/null)
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#large_logs[@]} -gt 0 ]; then
|
||||
print_message $YELLOW "发现大日志文件:"
|
||||
for log in "${large_logs[@]}"; do
|
||||
local size=$(du -sh "$log" | cut -f1)
|
||||
print_message $YELLOW " $log: $size"
|
||||
done
|
||||
print_message $YELLOW "建议配置日志轮转"
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查网络连接
|
||||
check_network_connectivity() {
|
||||
print_message $BLUE "检查网络连接..."
|
||||
|
||||
# 检查容器间网络连接
|
||||
if docker exec qaup-app nc -z qaup-postgres 5432 &>/dev/null; then
|
||||
print_message $GREEN "✓ 应用 -> 数据库: 连接正常"
|
||||
else
|
||||
print_message $RED "✗ 应用 -> 数据库: 连接失败"
|
||||
fi
|
||||
|
||||
if docker exec qaup-app nc -z qaup-redis 6379 &>/dev/null; then
|
||||
print_message $GREEN "✓ 应用 -> Redis: 连接正常"
|
||||
else
|
||||
print_message $RED "✗ 应用 -> Redis: 连接失败"
|
||||
fi
|
||||
|
||||
if docker exec qaup-nginx nc -z qaup-app 8080 &>/dev/null; then
|
||||
print_message $GREEN "✓ Nginx -> 应用: 连接正常"
|
||||
else
|
||||
print_message $RED "✗ Nginx -> 应用: 连接失败"
|
||||
fi
|
||||
}
|
||||
|
||||
# 生成监控报告
|
||||
generate_monitoring_report() {
|
||||
local timestamp=$(get_timestamp)
|
||||
local report_file="./logs/monitoring_report_$(date +%Y%m%d_%H%M%S).txt"
|
||||
|
||||
print_message $BLUE "生成监控报告: $report_file"
|
||||
|
||||
{
|
||||
echo "QAUP 系统监控报告"
|
||||
echo "生成时间: $timestamp"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
echo "容器状态:"
|
||||
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
|
||||
echo ""
|
||||
|
||||
echo "资源使用情况:"
|
||||
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}"
|
||||
echo ""
|
||||
|
||||
echo "系统资源:"
|
||||
free -h
|
||||
echo ""
|
||||
df -h
|
||||
echo ""
|
||||
|
||||
echo "网络状态:"
|
||||
netstat -tuln | grep -E ":(80|443|8080|5432|6379) "
|
||||
echo ""
|
||||
|
||||
} > "$report_file"
|
||||
|
||||
print_message $GREEN "监控报告已生成: $report_file"
|
||||
}
|
||||
|
||||
# 持续监控模式
|
||||
continuous_monitoring() {
|
||||
local interval=${1:-60} # 默认60秒间隔
|
||||
|
||||
print_message $GREEN "开始持续监控模式 (间隔: ${interval}秒)"
|
||||
print_message $YELLOW "按 Ctrl+C 停止监控"
|
||||
|
||||
while true; do
|
||||
clear
|
||||
print_message $BLUE "========================================="
|
||||
print_message $BLUE "QAUP 系统监控 - $(get_timestamp)"
|
||||
print_message $BLUE "========================================="
|
||||
|
||||
check_container_status
|
||||
echo ""
|
||||
check_service_health
|
||||
echo ""
|
||||
check_resource_usage
|
||||
|
||||
print_message $BLUE "========================================="
|
||||
print_message $YELLOW "下次检查: $(date -d "+${interval} seconds" '+%H:%M:%S')"
|
||||
|
||||
sleep $interval
|
||||
done
|
||||
}
|
||||
|
||||
# 告警检查
|
||||
check_alerts() {
|
||||
print_message $BLUE "检查系统告警..."
|
||||
|
||||
local alerts=()
|
||||
|
||||
# 检查容器状态
|
||||
local unhealthy_containers=$(docker ps -a --format "{{.Names}}" --filter "health=unhealthy")
|
||||
if [ -n "$unhealthy_containers" ]; then
|
||||
alerts+=("不健康的容器: $unhealthy_containers")
|
||||
fi
|
||||
|
||||
# 检查磁盘使用率
|
||||
local disk_usage=$(df . | awk 'NR==2 {print $5}' | sed 's/%//')
|
||||
if [ $disk_usage -gt 80 ]; then
|
||||
alerts+=("磁盘使用率过高: ${disk_usage}%")
|
||||
fi
|
||||
|
||||
# 检查内存使用率
|
||||
local mem_usage=$(free | awk 'NR==2{printf "%.0f", $3*100/$2}')
|
||||
if [ $mem_usage -gt 85 ]; then
|
||||
alerts+=("内存使用率过高: ${mem_usage}%")
|
||||
fi
|
||||
|
||||
# 输出告警
|
||||
if [ ${#alerts[@]} -gt 0 ]; then
|
||||
print_message $RED "发现告警:"
|
||||
for alert in "${alerts[@]}"; do
|
||||
print_message $RED " ⚠ $alert"
|
||||
done
|
||||
return 1
|
||||
else
|
||||
print_message $GREEN "✓ 无告警"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "QAUP 系统监控脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [命令] [选项]"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " status 检查系统状态"
|
||||
echo " health 检查服务健康状态"
|
||||
echo " resources 检查资源使用情况"
|
||||
echo " logs 检查日志文件"
|
||||
echo " network 检查网络连接"
|
||||
echo " alerts 检查系统告警"
|
||||
echo " report 生成监控报告"
|
||||
echo " watch 持续监控模式"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 status # 检查系统状态"
|
||||
echo " $0 watch 30 # 每30秒监控一次"
|
||||
echo " $0 report # 生成监控报告"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
if [ $# -eq 0 ]; then
|
||||
show_help
|
||||
exit 0
|
||||
fi
|
||||
|
||||
local command=$1
|
||||
shift
|
||||
|
||||
case $command in
|
||||
status)
|
||||
check_container_status
|
||||
;;
|
||||
health)
|
||||
check_service_health
|
||||
;;
|
||||
resources)
|
||||
check_resource_usage
|
||||
;;
|
||||
logs)
|
||||
check_log_sizes
|
||||
;;
|
||||
network)
|
||||
check_network_connectivity
|
||||
;;
|
||||
alerts)
|
||||
check_alerts
|
||||
;;
|
||||
report)
|
||||
generate_monitoring_report
|
||||
;;
|
||||
watch)
|
||||
continuous_monitoring "$@"
|
||||
;;
|
||||
help|--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
print_message $RED "未知命令: $command"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
45
deploy/docker/nginx/Dockerfile
Normal file
45
deploy/docker/nginx/Dockerfile
Normal file
@ -0,0 +1,45 @@
|
||||
# 多阶段构建 - 前端构建阶段
|
||||
FROM node:18-alpine AS frontend-builder
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 复制前端项目文件
|
||||
COPY qaup-ui/package*.json ./
|
||||
RUN npm ci --only=production
|
||||
|
||||
# 复制前端源码并构建
|
||||
COPY qaup-ui/ ./
|
||||
RUN npm run build:prod
|
||||
|
||||
# Nginx 运行阶段
|
||||
FROM nginx:alpine
|
||||
|
||||
# 安装必要工具
|
||||
RUN apk add --no-cache curl
|
||||
|
||||
# 创建 nginx 用户目录
|
||||
RUN mkdir -p /var/cache/nginx/client_temp && \
|
||||
chown -R nginx:nginx /var/cache/nginx
|
||||
|
||||
# 复制构建好的前端文件
|
||||
COPY --from=frontend-builder /app/dist /usr/share/nginx/html
|
||||
|
||||
# 复制 Nginx 配置文件
|
||||
COPY docker/nginx/nginx.conf /etc/nginx/nginx.conf
|
||||
COPY docker/nginx/default.conf /etc/nginx/conf.d/default.conf
|
||||
COPY docker/nginx/nginx-env.conf.template /etc/nginx/conf.d/nginx-env.conf.template
|
||||
COPY docker/nginx/start-nginx.sh /start-nginx.sh
|
||||
|
||||
# 设置执行权限
|
||||
RUN chmod +x /start-nginx.sh
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 80
|
||||
|
||||
# 健康检查
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
|
||||
CMD curl -f http://localhost/health || exit 1
|
||||
|
||||
# 启动 Nginx
|
||||
CMD ["/start-nginx.sh"]
|
||||
87
deploy/docker/nginx/default.conf
Normal file
87
deploy/docker/nginx/default.conf
Normal file
@ -0,0 +1,87 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# 安全头设置
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
|
||||
|
||||
# 健康检查端点
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "healthy\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
|
||||
# API 代理到后端 - 生产环境配置
|
||||
location /prod-api/ {
|
||||
proxy_pass http://qaup-app:8080/;
|
||||
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 30s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
|
||||
# 缓冲设置
|
||||
proxy_buffering on;
|
||||
proxy_buffer_size 4k;
|
||||
proxy_buffers 8 4k;
|
||||
proxy_busy_buffers_size 8k;
|
||||
|
||||
# 请求体大小限制(文件上传)
|
||||
client_max_body_size 20M;
|
||||
}
|
||||
|
||||
# 兼容开发环境 API 路径
|
||||
location /api/ {
|
||||
proxy_pass http://qaup-app:8080/;
|
||||
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 30s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
|
||||
proxy_buffering on;
|
||||
proxy_buffer_size 4k;
|
||||
proxy_buffers 8 4k;
|
||||
|
||||
client_max_body_size 20M;
|
||||
}
|
||||
|
||||
# 静态资源缓存
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
# 前端路由处理(SPA)
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
|
||||
# HTML 文件不缓存
|
||||
location ~* \.html$ {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
add_header Expires "0";
|
||||
}
|
||||
}
|
||||
|
||||
# 错误页面
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
71
deploy/docker/nginx/nginx-env.conf.template
Normal file
71
deploy/docker/nginx/nginx-env.conf.template
Normal file
@ -0,0 +1,71 @@
|
||||
# Nginx 环境配置模板
|
||||
# 使用环境变量进行配置替换
|
||||
|
||||
server {
|
||||
listen ${NGINX_PORT:-80};
|
||||
server_name ${SERVER_NAME:-localhost};
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# 安全头设置
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
|
||||
|
||||
# 健康检查端点
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "healthy\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
|
||||
# API 代理到后端
|
||||
location ${API_PREFIX:-/prod-api}/ {
|
||||
proxy_pass http://${BACKEND_HOST:-qaup-app}:${BACKEND_PORT:-8080}/;
|
||||
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 ${PROXY_CONNECT_TIMEOUT:-30s};
|
||||
proxy_send_timeout ${PROXY_SEND_TIMEOUT:-60s};
|
||||
proxy_read_timeout ${PROXY_READ_TIMEOUT:-60s};
|
||||
|
||||
# 缓冲设置
|
||||
proxy_buffering on;
|
||||
proxy_buffer_size 4k;
|
||||
proxy_buffers 8 4k;
|
||||
proxy_busy_buffers_size 8k;
|
||||
|
||||
# 请求体大小限制
|
||||
client_max_body_size ${MAX_UPLOAD_SIZE:-20M};
|
||||
}
|
||||
|
||||
# 静态资源缓存
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
expires ${STATIC_CACHE_TIME:-1y};
|
||||
add_header Cache-Control "public, immutable";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
# 前端路由处理(SPA)
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
|
||||
# HTML 文件不缓存
|
||||
location ~* \.html$ {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
add_header Expires "0";
|
||||
}
|
||||
}
|
||||
|
||||
# 错误页面
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
50
deploy/docker/nginx/nginx.conf
Normal file
50
deploy/docker/nginx/nginx.conf
Normal file
@ -0,0 +1,50 @@
|
||||
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"';
|
||||
|
||||
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 20M;
|
||||
|
||||
# Gzip 压缩
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_types
|
||||
text/plain
|
||||
text/css
|
||||
text/xml
|
||||
text/javascript
|
||||
application/json
|
||||
application/javascript
|
||||
application/xml+rss
|
||||
application/atom+xml
|
||||
image/svg+xml;
|
||||
|
||||
# 包含虚拟主机配置
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
||||
39
deploy/docker/nginx/start-nginx.sh
Executable file
39
deploy/docker/nginx/start-nginx.sh
Executable file
@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Nginx 启动脚本 - 支持环境变量配置
|
||||
|
||||
set -e
|
||||
|
||||
# 默认环境变量
|
||||
export NGINX_PORT=${NGINX_PORT:-80}
|
||||
export SERVER_NAME=${SERVER_NAME:-localhost}
|
||||
export API_PREFIX=${API_PREFIX:-/prod-api}
|
||||
export BACKEND_HOST=${BACKEND_HOST:-qaup-app}
|
||||
export BACKEND_PORT=${BACKEND_PORT:-8080}
|
||||
export PROXY_CONNECT_TIMEOUT=${PROXY_CONNECT_TIMEOUT:-30s}
|
||||
export PROXY_SEND_TIMEOUT=${PROXY_SEND_TIMEOUT:-60s}
|
||||
export PROXY_READ_TIMEOUT=${PROXY_READ_TIMEOUT:-60s}
|
||||
export MAX_UPLOAD_SIZE=${MAX_UPLOAD_SIZE:-20M}
|
||||
export STATIC_CACHE_TIME=${STATIC_CACHE_TIME:-1y}
|
||||
|
||||
echo "启动 Nginx 服务..."
|
||||
echo "配置参数:"
|
||||
echo " 端口: $NGINX_PORT"
|
||||
echo " 服务器名: $SERVER_NAME"
|
||||
echo " API 前缀: $API_PREFIX"
|
||||
echo " 后端地址: $BACKEND_HOST:$BACKEND_PORT"
|
||||
|
||||
# 如果存在环境配置模板,则使用 envsubst 替换变量
|
||||
if [ -f "/etc/nginx/conf.d/nginx-env.conf.template" ]; then
|
||||
echo "使用环境变量配置模板..."
|
||||
envsubst < /etc/nginx/conf.d/nginx-env.conf.template > /etc/nginx/conf.d/default.conf
|
||||
rm -f /etc/nginx/conf.d/nginx-env.conf.template
|
||||
fi
|
||||
|
||||
# 测试 Nginx 配置
|
||||
echo "测试 Nginx 配置..."
|
||||
nginx -t
|
||||
|
||||
# 启动 Nginx
|
||||
echo "启动 Nginx..."
|
||||
exec nginx -g "daemon off;"
|
||||
711
deploy/docker/ops-manager.sh
Executable file
711
deploy/docker/ops-manager.sh
Executable file
@ -0,0 +1,711 @@
|
||||
#!/bin/bash
|
||||
|
||||
# QAUP 运维管理脚本
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_message() {
|
||||
local color=$1
|
||||
local message=$2
|
||||
echo -e "${color}${message}${NC}"
|
||||
}
|
||||
|
||||
# 显示系统概览
|
||||
show_system_overview() {
|
||||
print_message $GREEN "========================================="
|
||||
print_message $GREEN "QAUP 系统概览"
|
||||
print_message $GREEN "========================================="
|
||||
|
||||
# 系统基本信息
|
||||
echo ""
|
||||
print_message $BLUE "系统信息:"
|
||||
echo " 主机名: $(hostname)"
|
||||
echo " 操作系统: $(uname -s) $(uname -r)"
|
||||
echo " 当前时间: $(date)"
|
||||
echo " 运行时间: $(uptime -p 2>/dev/null || uptime | awk '{print $3,$4}' | sed 's/,//')"
|
||||
|
||||
# 资源使用情况
|
||||
echo ""
|
||||
print_message $BLUE "资源使用:"
|
||||
echo " CPU 负载: $(uptime | awk -F'load average:' '{print $2}' | xargs)"
|
||||
echo " 内存使用: $(free -h | awk 'NR==2{printf "%s/%s (%.2f%%)", $3,$2,$3*100/$2}')"
|
||||
echo " 磁盘使用: $(df -h . | awk 'NR==2{printf "%s/%s (%s)", $3,$2,$5}')"
|
||||
|
||||
# Docker 信息
|
||||
echo ""
|
||||
print_message $BLUE "Docker 信息:"
|
||||
echo " Docker 版本: $(docker --version | cut -d' ' -f3 | sed 's/,//')"
|
||||
echo " 运行容器数: $(docker ps -q | wc -l)"
|
||||
echo " 总容器数: $(docker ps -aq | wc -l)"
|
||||
echo " 镜像数量: $(docker images -q | wc -l)"
|
||||
|
||||
# 服务状态
|
||||
echo ""
|
||||
print_message $BLUE "服务状态:"
|
||||
local containers=("qaup-postgres" "qaup-redis" "qaup-app" "qaup-nginx")
|
||||
|
||||
for container in "${containers[@]}"; do
|
||||
if docker ps --format "{{.Names}}" | grep -q "^${container}$"; then
|
||||
local status=$(docker ps --format "{{.Status}}" --filter "name=$container")
|
||||
print_message $GREEN " ✓ $container: $status"
|
||||
else
|
||||
print_message $RED " ✗ $container: 未运行"
|
||||
fi
|
||||
done
|
||||
|
||||
# 网络状态
|
||||
echo ""
|
||||
print_message $BLUE "网络状态:"
|
||||
local ports=("80" "443" "8080")
|
||||
for port in "${ports[@]}"; do
|
||||
if netstat -tuln 2>/dev/null | grep -q ":$port "; then
|
||||
print_message $GREEN " ✓ 端口 $port: 监听中"
|
||||
else
|
||||
print_message $YELLOW " ⚠ 端口 $port: 未监听"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 快速诊断
|
||||
quick_diagnosis() {
|
||||
print_message $BLUE "执行快速诊断..."
|
||||
|
||||
local issues=()
|
||||
|
||||
# 检查 Docker 服务
|
||||
if ! systemctl is-active --quiet docker 2>/dev/null; then
|
||||
issues+=("Docker 服务未运行")
|
||||
fi
|
||||
|
||||
# 检查容器状态
|
||||
local containers=("qaup-postgres" "qaup-redis" "qaup-app" "qaup-nginx")
|
||||
for container in "${containers[@]}"; do
|
||||
if ! docker ps --format "{{.Names}}" | grep -q "^${container}$"; then
|
||||
issues+=("容器 $container 未运行")
|
||||
fi
|
||||
done
|
||||
|
||||
# 检查磁盘空间
|
||||
local disk_usage=$(df . | awk 'NR==2 {print $5}' | sed 's/%//')
|
||||
if [ $disk_usage -gt 85 ]; then
|
||||
issues+=("磁盘使用率过高: ${disk_usage}%")
|
||||
fi
|
||||
|
||||
# 检查内存使用
|
||||
local mem_usage=$(free | awk 'NR==2{printf "%.0f", $3*100/$2}')
|
||||
if [ $mem_usage -gt 90 ]; then
|
||||
issues+=("内存使用率过高: ${mem_usage}%")
|
||||
fi
|
||||
|
||||
# 检查服务响应
|
||||
if ! curl -f -s http://localhost/health &>/dev/null; then
|
||||
issues+=("前端服务无响应")
|
||||
fi
|
||||
|
||||
if ! curl -f -s http://localhost:8080/actuator/health &>/dev/null; then
|
||||
issues+=("后端服务无响应")
|
||||
fi
|
||||
|
||||
# 显示诊断结果
|
||||
echo ""
|
||||
if [ ${#issues[@]} -eq 0 ]; then
|
||||
print_message $GREEN "✓ 系统状态正常,未发现问题"
|
||||
else
|
||||
print_message $YELLOW "发现以下问题:"
|
||||
for issue in "${issues[@]}"; do
|
||||
echo " - $issue"
|
||||
done
|
||||
|
||||
echo ""
|
||||
print_message $BLUE "建议操作:"
|
||||
echo " 1. 运行详细检查: $0 health-check"
|
||||
echo " 2. 查看系统日志: $0 logs"
|
||||
echo " 3. 重启服务: ./deploy.sh restart"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 健康检查
|
||||
health_check() {
|
||||
print_message $BLUE "执行系统健康检查..."
|
||||
|
||||
# 运行集成测试的健康检查部分
|
||||
if [ -x "$PROJECT_ROOT/docker/integration-test.sh" ]; then
|
||||
"$PROJECT_ROOT/docker/integration-test.sh" quick
|
||||
else
|
||||
print_message $YELLOW "集成测试脚本不存在,执行基本健康检查"
|
||||
|
||||
# 基本健康检查
|
||||
local health_ok=true
|
||||
|
||||
# 检查容器健康状态
|
||||
local containers=("qaup-postgres" "qaup-redis" "qaup-app" "qaup-nginx")
|
||||
for container in "${containers[@]}"; do
|
||||
if docker ps --format "{{.Names}}" | grep -q "^${container}$"; then
|
||||
print_message $GREEN "✓ $container 运行正常"
|
||||
else
|
||||
print_message $RED "✗ $container 未运行"
|
||||
health_ok=false
|
||||
fi
|
||||
done
|
||||
|
||||
# 检查服务响应
|
||||
if curl -f -s http://localhost/health &>/dev/null; then
|
||||
print_message $GREEN "✓ 前端服务响应正常"
|
||||
else
|
||||
print_message $RED "✗ 前端服务无响应"
|
||||
health_ok=false
|
||||
fi
|
||||
|
||||
if curl -f -s http://localhost:8080/actuator/health &>/dev/null; then
|
||||
print_message $GREEN "✓ 后端服务响应正常"
|
||||
else
|
||||
print_message $RED "✗ 后端服务无响应"
|
||||
health_ok=false
|
||||
fi
|
||||
|
||||
if [ "$health_ok" = true ]; then
|
||||
print_message $GREEN "✓ 系统健康检查通过"
|
||||
return 0
|
||||
else
|
||||
print_message $RED "✗ 系统健康检查失败"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# 查看系统日志
|
||||
view_logs() {
|
||||
local service=${1:-"all"}
|
||||
local lines=${2:-50}
|
||||
|
||||
print_message $BLUE "查看系统日志 (最近 $lines 行)..."
|
||||
|
||||
case $service in
|
||||
"all")
|
||||
echo ""
|
||||
print_message $BLUE "=== 应用日志 ==="
|
||||
docker logs --tail $lines qaup-app 2>/dev/null || echo "应用容器未运行"
|
||||
|
||||
echo ""
|
||||
print_message $BLUE "=== Nginx 日志 ==="
|
||||
docker logs --tail $lines qaup-nginx 2>/dev/null || echo "Nginx 容器未运行"
|
||||
|
||||
echo ""
|
||||
print_message $BLUE "=== 数据库日志 ==="
|
||||
docker logs --tail $lines qaup-postgres 2>/dev/null || echo "数据库容器未运行"
|
||||
|
||||
echo ""
|
||||
print_message $BLUE "=== Redis 日志 ==="
|
||||
docker logs --tail $lines qaup-redis 2>/dev/null || echo "Redis 容器未运行"
|
||||
;;
|
||||
"app"|"application")
|
||||
docker logs --tail $lines -f qaup-app
|
||||
;;
|
||||
"nginx"|"web")
|
||||
docker logs --tail $lines -f qaup-nginx
|
||||
;;
|
||||
"postgres"|"database"|"db")
|
||||
docker logs --tail $lines -f qaup-postgres
|
||||
;;
|
||||
"redis"|"cache")
|
||||
docker logs --tail $lines -f qaup-redis
|
||||
;;
|
||||
*)
|
||||
print_message $RED "未知服务: $service"
|
||||
print_message $BLUE "可用服务: all, app, nginx, postgres, redis"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 系统清理
|
||||
system_cleanup() {
|
||||
print_message $BLUE "执行系统清理..."
|
||||
|
||||
# 确认操作
|
||||
read -p "确定要执行系统清理吗? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
print_message $BLUE "清理操作已取消"
|
||||
return
|
||||
fi
|
||||
|
||||
local cleaned_items=0
|
||||
|
||||
# 清理 Docker 资源
|
||||
print_message $BLUE "清理 Docker 资源..."
|
||||
|
||||
# 清理停止的容器
|
||||
local stopped_containers=$(docker ps -aq --filter "status=exited")
|
||||
if [ -n "$stopped_containers" ]; then
|
||||
docker rm $stopped_containers
|
||||
cleaned_items=$((cleaned_items + $(echo $stopped_containers | wc -w)))
|
||||
print_message $GREEN "✓ 已清理停止的容器"
|
||||
fi
|
||||
|
||||
# 清理未使用的镜像
|
||||
local unused_images=$(docker images -f "dangling=true" -q)
|
||||
if [ -n "$unused_images" ]; then
|
||||
docker rmi $unused_images
|
||||
cleaned_items=$((cleaned_items + $(echo $unused_images | wc -w)))
|
||||
print_message $GREEN "✓ 已清理未使用的镜像"
|
||||
fi
|
||||
|
||||
# 清理未使用的网络
|
||||
docker network prune -f &>/dev/null
|
||||
print_message $GREEN "✓ 已清理未使用的网络"
|
||||
|
||||
# 清理未使用的卷
|
||||
docker volume prune -f &>/dev/null
|
||||
print_message $GREEN "✓ 已清理未使用的卷"
|
||||
|
||||
# 清理旧日志
|
||||
if [ -x "$PROJECT_ROOT/docker/log-manager.sh" ]; then
|
||||
"$PROJECT_ROOT/docker/log-manager.sh" cleanup 30
|
||||
print_message $GREEN "✓ 已清理旧日志文件"
|
||||
fi
|
||||
|
||||
# 清理临时文件
|
||||
find /tmp -name "*qaup*" -mtime +1 -delete 2>/dev/null || true
|
||||
print_message $GREEN "✓ 已清理临时文件"
|
||||
|
||||
print_message $GREEN "系统清理完成,共清理 $cleaned_items 个项目"
|
||||
}
|
||||
|
||||
# 性能分析
|
||||
performance_analysis() {
|
||||
print_message $BLUE "执行性能分析..."
|
||||
|
||||
echo ""
|
||||
print_message $BLUE "=== 系统资源使用 ==="
|
||||
|
||||
# CPU 使用率
|
||||
local cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | sed 's/%us,//')
|
||||
echo "CPU 使用率: $cpu_usage"
|
||||
|
||||
# 内存详细信息
|
||||
echo ""
|
||||
echo "内存使用详情:"
|
||||
free -h
|
||||
|
||||
# 磁盘 I/O
|
||||
echo ""
|
||||
echo "磁盘使用情况:"
|
||||
df -h
|
||||
|
||||
# 容器资源使用
|
||||
echo ""
|
||||
print_message $BLUE "=== 容器资源使用 ==="
|
||||
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}"
|
||||
|
||||
# 网络连接
|
||||
echo ""
|
||||
print_message $BLUE "=== 网络连接状态 ==="
|
||||
netstat -tuln | grep -E ":(80|443|8080|5432|6379) " | head -10
|
||||
|
||||
# 进程信息
|
||||
echo ""
|
||||
print_message $BLUE "=== 高资源使用进程 ==="
|
||||
ps aux --sort=-%cpu | head -10
|
||||
|
||||
# 数据库性能
|
||||
echo ""
|
||||
print_message $BLUE "=== 数据库性能 ==="
|
||||
if docker ps --format "{{.Names}}" | grep -q "qaup-postgres"; then
|
||||
local db_connections=$(docker exec qaup-postgres psql -U postgres -d qaup -t -c "SELECT count(*) FROM pg_stat_activity;" 2>/dev/null | xargs || echo "N/A")
|
||||
echo "数据库连接数: $db_connections"
|
||||
|
||||
local db_size=$(docker exec qaup-postgres psql -U postgres -d qaup -t -c "SELECT pg_size_pretty(pg_database_size('qaup'));" 2>/dev/null | xargs || echo "N/A")
|
||||
echo "数据库大小: $db_size"
|
||||
else
|
||||
echo "数据库容器未运行"
|
||||
fi
|
||||
|
||||
# Redis 性能
|
||||
echo ""
|
||||
print_message $BLUE "=== Redis 性能 ==="
|
||||
if docker ps --format "{{.Names}}" | grep -q "qaup-redis"; then
|
||||
local redis_memory=$(docker exec qaup-redis redis-cli info memory | grep "used_memory_human" | cut -d: -f2 | tr -d '\r' || echo "N/A")
|
||||
echo "Redis 内存使用: $redis_memory"
|
||||
|
||||
local redis_keys=$(docker exec qaup-redis redis-cli dbsize 2>/dev/null || echo "N/A")
|
||||
echo "Redis 键数量: $redis_keys"
|
||||
else
|
||||
echo "Redis 容器未运行"
|
||||
fi
|
||||
}
|
||||
|
||||
# 安全检查
|
||||
security_check() {
|
||||
print_message $BLUE "执行安全检查..."
|
||||
|
||||
local security_issues=()
|
||||
|
||||
# 检查文件权限
|
||||
echo ""
|
||||
print_message $BLUE "=== 文件权限检查 ==="
|
||||
|
||||
if [ -f "$PROJECT_ROOT/.env" ]; then
|
||||
local env_perms=$(stat -c "%a" "$PROJECT_ROOT/.env")
|
||||
if [ "$env_perms" = "600" ]; then
|
||||
print_message $GREEN "✓ .env 文件权限正确 ($env_perms)"
|
||||
else
|
||||
print_message $YELLOW "⚠ .env 文件权限不安全 ($env_perms),建议设置为 600"
|
||||
security_issues+=("环境文件权限不安全")
|
||||
fi
|
||||
fi
|
||||
|
||||
# 检查容器安全
|
||||
echo ""
|
||||
print_message $BLUE "=== 容器安全检查 ==="
|
||||
|
||||
local containers=("qaup-postgres" "qaup-redis" "qaup-app" "qaup-nginx")
|
||||
for container in "${containers[@]}"; do
|
||||
if docker ps --format "{{.Names}}" | grep -q "^${container}$"; then
|
||||
# 检查是否以 root 用户运行
|
||||
local user=$(docker exec "$container" whoami 2>/dev/null || echo "unknown")
|
||||
if [ "$user" = "root" ]; then
|
||||
print_message $YELLOW "⚠ $container 以 root 用户运行"
|
||||
security_issues+=("$container 以 root 用户运行")
|
||||
else
|
||||
print_message $GREEN "✓ $container 使用非 root 用户: $user"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# 检查网络安全
|
||||
echo ""
|
||||
print_message $BLUE "=== 网络安全检查 ==="
|
||||
|
||||
# 检查不应该暴露的端口
|
||||
local sensitive_ports=("5432" "6379")
|
||||
for port in "${sensitive_ports[@]}"; do
|
||||
if netstat -tuln 2>/dev/null | grep -q "0.0.0.0:$port "; then
|
||||
print_message $YELLOW "⚠ 敏感端口 $port 对外暴露"
|
||||
security_issues+=("端口 $port 对外暴露")
|
||||
else
|
||||
print_message $GREEN "✓ 端口 $port 未对外暴露"
|
||||
fi
|
||||
done
|
||||
|
||||
# 检查 SSL/TLS
|
||||
if [ -d "$PROJECT_ROOT/ssl" ]; then
|
||||
print_message $GREEN "✓ SSL 证书目录存在"
|
||||
|
||||
# 检查证书文件权限
|
||||
if [ -f "$PROJECT_ROOT/ssl/server.key" ]; then
|
||||
local key_perms=$(stat -c "%a" "$PROJECT_ROOT/ssl/server.key")
|
||||
if [ "$key_perms" = "600" ]; then
|
||||
print_message $GREEN "✓ SSL 私钥权限正确"
|
||||
else
|
||||
print_message $YELLOW "⚠ SSL 私钥权限不安全"
|
||||
security_issues+=("SSL 私钥权限不安全")
|
||||
fi
|
||||
fi
|
||||
else
|
||||
print_message $YELLOW "⚠ 未配置 SSL 证书"
|
||||
security_issues+=("未配置 SSL 证书")
|
||||
fi
|
||||
|
||||
# 显示安全检查结果
|
||||
echo ""
|
||||
if [ ${#security_issues[@]} -eq 0 ]; then
|
||||
print_message $GREEN "✓ 安全检查通过,未发现安全问题"
|
||||
else
|
||||
print_message $YELLOW "发现以下安全问题:"
|
||||
for issue in "${security_issues[@]}"; do
|
||||
echo " - $issue"
|
||||
done
|
||||
|
||||
echo ""
|
||||
print_message $BLUE "建议操作:"
|
||||
echo " 1. 运行安全配置脚本: ./docker/security-setup.sh all"
|
||||
echo " 2. 检查防火墙设置: sudo ufw status"
|
||||
echo " 3. 更新系统和软件包"
|
||||
fi
|
||||
}
|
||||
|
||||
# 备份管理
|
||||
backup_management() {
|
||||
local action=${1:-"status"}
|
||||
|
||||
case $action in
|
||||
"status")
|
||||
print_message $BLUE "备份状态:"
|
||||
|
||||
if [ -d "$PROJECT_ROOT/backup" ]; then
|
||||
local backup_count=$(find "$PROJECT_ROOT/backup" -name "qaup_full_backup_*.tar.gz" | wc -l)
|
||||
echo " 备份文件数量: $backup_count"
|
||||
|
||||
if [ $backup_count -gt 0 ]; then
|
||||
local latest_backup=$(find "$PROJECT_ROOT/backup" -name "qaup_full_backup_*.tar.gz" -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -d' ' -f2-)
|
||||
local backup_date=$(echo "$latest_backup" | grep -o '[0-9]\{8\}_[0-9]\{6\}' | sed 's/_/ /')
|
||||
local backup_size=$(du -h "$latest_backup" | cut -f1)
|
||||
|
||||
echo " 最新备份: $(basename "$latest_backup")"
|
||||
echo " 备份时间: $backup_date"
|
||||
echo " 备份大小: $backup_size"
|
||||
else
|
||||
print_message $YELLOW " 未找到备份文件"
|
||||
fi
|
||||
else
|
||||
print_message $YELLOW " 备份目录不存在"
|
||||
fi
|
||||
;;
|
||||
"create")
|
||||
print_message $BLUE "创建系统备份..."
|
||||
if [ -x "$PROJECT_ROOT/docker/backup-restore.sh" ]; then
|
||||
"$PROJECT_ROOT/docker/backup-restore.sh" full
|
||||
else
|
||||
print_message $RED "备份脚本不存在"
|
||||
fi
|
||||
;;
|
||||
"list")
|
||||
if [ -x "$PROJECT_ROOT/docker/backup-restore.sh" ]; then
|
||||
"$PROJECT_ROOT/docker/backup-restore.sh" list
|
||||
else
|
||||
print_message $RED "备份脚本不存在"
|
||||
fi
|
||||
;;
|
||||
"cleanup")
|
||||
print_message $BLUE "清理旧备份..."
|
||||
if [ -x "$PROJECT_ROOT/docker/backup-restore.sh" ]; then
|
||||
"$PROJECT_ROOT/docker/backup-restore.sh" cleanup 30
|
||||
else
|
||||
print_message $RED "备份脚本不存在"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
print_message $RED "未知备份操作: $action"
|
||||
echo "可用操作: status, create, list, cleanup"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 服务管理
|
||||
service_management() {
|
||||
local action=${1:-"status"}
|
||||
local service=${2:-"all"}
|
||||
|
||||
case $action in
|
||||
"start")
|
||||
print_message $BLUE "启动服务: $service"
|
||||
if [ "$service" = "all" ]; then
|
||||
"$PROJECT_ROOT/deploy.sh" start
|
||||
else
|
||||
docker start "qaup-$service"
|
||||
fi
|
||||
;;
|
||||
"stop")
|
||||
print_message $BLUE "停止服务: $service"
|
||||
if [ "$service" = "all" ]; then
|
||||
"$PROJECT_ROOT/deploy.sh" stop
|
||||
else
|
||||
docker stop "qaup-$service"
|
||||
fi
|
||||
;;
|
||||
"restart")
|
||||
print_message $BLUE "重启服务: $service"
|
||||
if [ "$service" = "all" ]; then
|
||||
"$PROJECT_ROOT/deploy.sh" restart
|
||||
else
|
||||
docker restart "qaup-$service"
|
||||
fi
|
||||
;;
|
||||
"status")
|
||||
print_message $BLUE "服务状态:"
|
||||
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" --filter "name=qaup-"
|
||||
;;
|
||||
*)
|
||||
print_message $RED "未知服务操作: $action"
|
||||
echo "可用操作: start, stop, restart, status"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 生成运维报告
|
||||
generate_ops_report() {
|
||||
local report_file="$PROJECT_ROOT/logs/ops_report_$(date +%Y%m%d_%H%M%S).md"
|
||||
|
||||
print_message $BLUE "生成运维报告: $report_file"
|
||||
|
||||
{
|
||||
echo "# QAUP 系统运维报告"
|
||||
echo ""
|
||||
echo "生成时间: $(date)"
|
||||
echo "报告类型: 系统状态报告"
|
||||
echo ""
|
||||
|
||||
echo "## 系统概览"
|
||||
echo ""
|
||||
echo "- 主机名: $(hostname)"
|
||||
echo "- 操作系统: $(uname -s) $(uname -r)"
|
||||
echo "- 运行时间: $(uptime -p 2>/dev/null || uptime | awk '{print $3,$4}' | sed 's/,//')"
|
||||
echo "- Docker 版本: $(docker --version | cut -d' ' -f3 | sed 's/,//')"
|
||||
echo ""
|
||||
|
||||
echo "## 资源使用"
|
||||
echo ""
|
||||
echo "- CPU 负载: $(uptime | awk -F'load average:' '{print $2}' | xargs)"
|
||||
echo "- 内存使用: $(free -h | awk 'NR==2{printf "%s/%s (%.2f%%)", $3,$2,$3*100/$2}')"
|
||||
echo "- 磁盘使用: $(df -h . | awk 'NR==2{printf "%s/%s (%s)", $3,$2,$5}')"
|
||||
echo ""
|
||||
|
||||
echo "## 服务状态"
|
||||
echo ""
|
||||
echo "```"
|
||||
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" --filter "name=qaup-"
|
||||
echo "```"
|
||||
echo ""
|
||||
|
||||
echo "## 容器资源使用"
|
||||
echo ""
|
||||
echo "```"
|
||||
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}"
|
||||
echo "```"
|
||||
echo ""
|
||||
|
||||
echo "## 网络状态"
|
||||
echo ""
|
||||
echo "```"
|
||||
netstat -tuln | grep -E ":(80|443|8080|5432|6379) " | head -10
|
||||
echo "```"
|
||||
echo ""
|
||||
|
||||
echo "## 备份状态"
|
||||
echo ""
|
||||
if [ -d "$PROJECT_ROOT/backup" ]; then
|
||||
local backup_count=$(find "$PROJECT_ROOT/backup" -name "qaup_full_backup_*.tar.gz" | wc -l)
|
||||
echo "- 备份文件数量: $backup_count"
|
||||
|
||||
if [ $backup_count -gt 0 ]; then
|
||||
local latest_backup=$(find "$PROJECT_ROOT/backup" -name "qaup_full_backup_*.tar.gz" -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -d' ' -f2-)
|
||||
local backup_date=$(echo "$latest_backup" | grep -o '[0-9]\{8\}_[0-9]\{6\}' | sed 's/_/ /')
|
||||
local backup_size=$(du -h "$latest_backup" | cut -f1)
|
||||
|
||||
echo "- 最新备份: $(basename "$latest_backup")"
|
||||
echo "- 备份时间: $backup_date"
|
||||
echo "- 备份大小: $backup_size"
|
||||
fi
|
||||
else
|
||||
echo "- 备份目录不存在"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo "## 建议操作"
|
||||
echo ""
|
||||
echo "- 定期检查系统资源使用情况"
|
||||
echo "- 定期执行系统备份"
|
||||
echo "- 监控服务健康状态"
|
||||
echo "- 清理旧日志和临时文件"
|
||||
echo "- 更新系统和软件包"
|
||||
echo ""
|
||||
|
||||
} > "$report_file"
|
||||
|
||||
print_message $GREEN "运维报告已生成: $report_file"
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "QAUP 运维管理脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [命令] [参数]"
|
||||
echo ""
|
||||
echo "系统管理命令:"
|
||||
echo " overview 显示系统概览"
|
||||
echo " diagnosis 快速诊断"
|
||||
echo " health-check 健康检查"
|
||||
echo " performance 性能分析"
|
||||
echo " security 安全检查"
|
||||
echo " cleanup 系统清理"
|
||||
echo " report 生成运维报告"
|
||||
echo ""
|
||||
echo "服务管理命令:"
|
||||
echo " service <action> [service] 服务管理"
|
||||
echo " actions: start, stop, restart, status"
|
||||
echo " services: all, app, nginx, postgres, redis"
|
||||
echo ""
|
||||
echo "日志管理命令:"
|
||||
echo " logs [service] [lines] 查看日志"
|
||||
echo " services: all, app, nginx, postgres, redis"
|
||||
echo ""
|
||||
echo "备份管理命令:"
|
||||
echo " backup <action> 备份管理"
|
||||
echo " actions: status, create, list, cleanup"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 overview # 显示系统概览"
|
||||
echo " $0 diagnosis # 快速诊断"
|
||||
echo " $0 service restart app # 重启应用服务"
|
||||
echo " $0 logs app 100 # 查看应用最近100行日志"
|
||||
echo " $0 backup create # 创建系统备份"
|
||||
echo " $0 cleanup # 系统清理"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
if [ $# -eq 0 ]; then
|
||||
show_system_overview
|
||||
exit 0
|
||||
fi
|
||||
|
||||
local command=$1
|
||||
shift
|
||||
|
||||
case $command in
|
||||
overview)
|
||||
show_system_overview
|
||||
;;
|
||||
diagnosis)
|
||||
quick_diagnosis
|
||||
;;
|
||||
health-check|health)
|
||||
health_check
|
||||
;;
|
||||
performance|perf)
|
||||
performance_analysis
|
||||
;;
|
||||
security|sec)
|
||||
security_check
|
||||
;;
|
||||
cleanup|clean)
|
||||
system_cleanup
|
||||
;;
|
||||
report)
|
||||
generate_ops_report
|
||||
;;
|
||||
service|svc)
|
||||
service_management "$@"
|
||||
;;
|
||||
logs|log)
|
||||
view_logs "$@"
|
||||
;;
|
||||
backup|bak)
|
||||
backup_management "$@"
|
||||
;;
|
||||
help|--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
print_message $RED "未知命令: $command"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
622
deploy/docker/performance-tuning.sh
Executable file
622
deploy/docker/performance-tuning.sh
Executable file
@ -0,0 +1,622 @@
|
||||
#!/bin/bash
|
||||
|
||||
# QAUP 性能优化脚本
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_message() {
|
||||
local color=$1
|
||||
local message=$2
|
||||
echo -e "${color}${message}${NC}"
|
||||
}
|
||||
|
||||
# 获取系统信息
|
||||
get_system_info() {
|
||||
local total_mem=$(free -m | awk 'NR==2{print $2}')
|
||||
local cpu_cores=$(nproc)
|
||||
local total_disk=$(df -BG . | awk 'NR==2 {print $2}' | sed 's/G//')
|
||||
|
||||
echo "系统信息:"
|
||||
echo " CPU 核心数: $cpu_cores"
|
||||
echo " 总内存: ${total_mem}MB"
|
||||
echo " 可用磁盘: ${total_disk}GB"
|
||||
echo ""
|
||||
|
||||
# 导出变量供其他函数使用
|
||||
export TOTAL_MEM=$total_mem
|
||||
export CPU_CORES=$cpu_cores
|
||||
export TOTAL_DISK=$total_disk
|
||||
}
|
||||
|
||||
# 优化 JVM 参数
|
||||
optimize_jvm() {
|
||||
print_message $BLUE "优化 JVM 参数..."
|
||||
|
||||
local heap_size
|
||||
local metaspace_size
|
||||
|
||||
# 根据系统内存计算 JVM 参数
|
||||
if [ $TOTAL_MEM -ge 8192 ]; then
|
||||
# 8GB+ 内存
|
||||
heap_size="4g"
|
||||
metaspace_size="512m"
|
||||
elif [ $TOTAL_MEM -ge 4096 ]; then
|
||||
# 4-8GB 内存
|
||||
heap_size="2g"
|
||||
metaspace_size="256m"
|
||||
else
|
||||
# 4GB 以下内存
|
||||
heap_size="1g"
|
||||
metaspace_size="128m"
|
||||
fi
|
||||
|
||||
# 生成优化的 JVM 参数
|
||||
local jvm_opts="-Dname=qaup-admin.jar"
|
||||
jvm_opts="$jvm_opts -Duser.timezone=Asia/Shanghai"
|
||||
jvm_opts="$jvm_opts -server"
|
||||
jvm_opts="$jvm_opts -Xms${heap_size} -Xmx${heap_size}"
|
||||
jvm_opts="$jvm_opts -XX:MetaspaceSize=${metaspace_size} -XX:MaxMetaspaceSize=${metaspace_size}"
|
||||
|
||||
# GC 优化
|
||||
if [ $CPU_CORES -ge 4 ]; then
|
||||
# 多核使用 G1GC
|
||||
jvm_opts="$jvm_opts -XX:+UseG1GC"
|
||||
jvm_opts="$jvm_opts -XX:G1HeapRegionSize=16m"
|
||||
jvm_opts="$jvm_opts -XX:MaxGCPauseMillis=200"
|
||||
jvm_opts="$jvm_opts -XX:+UseStringDeduplication"
|
||||
else
|
||||
# 少核使用 Parallel GC
|
||||
jvm_opts="$jvm_opts -XX:+UseParallelGC"
|
||||
jvm_opts="$jvm_opts -XX:+UseParallelOldGC"
|
||||
fi
|
||||
|
||||
# 性能监控和调试
|
||||
jvm_opts="$jvm_opts -XX:+HeapDumpOnOutOfMemoryError"
|
||||
jvm_opts="$jvm_opts -XX:HeapDumpPath=/app/logs/"
|
||||
jvm_opts="$jvm_opts -XX:+PrintGCDetails"
|
||||
jvm_opts="$jvm_opts -XX:+PrintGCTimeStamps"
|
||||
jvm_opts="$jvm_opts -Xloggc:/app/logs/gc.log"
|
||||
|
||||
# 更新环境文件
|
||||
if [ -f "$PROJECT_ROOT/.env" ]; then
|
||||
# 备份原文件
|
||||
cp "$PROJECT_ROOT/.env" "$PROJECT_ROOT/.env.backup.$(date +%Y%m%d_%H%M%S)"
|
||||
|
||||
# 更新 JVM_OPTS
|
||||
if grep -q "^JVM_OPTS=" "$PROJECT_ROOT/.env"; then
|
||||
sed -i "s|^JVM_OPTS=.*|JVM_OPTS=$jvm_opts|" "$PROJECT_ROOT/.env"
|
||||
else
|
||||
echo "JVM_OPTS=$jvm_opts" >> "$PROJECT_ROOT/.env"
|
||||
fi
|
||||
|
||||
print_message $GREEN "✓ JVM 参数已优化"
|
||||
echo " 堆内存: $heap_size"
|
||||
echo " 元空间: $metaspace_size"
|
||||
echo " GC算法: $(echo $jvm_opts | grep -o 'XX:+Use[A-Za-z0-9]*GC')"
|
||||
else
|
||||
print_message $RED "✗ 环境文件不存在: $PROJECT_ROOT/.env"
|
||||
fi
|
||||
}
|
||||
|
||||
# 优化数据库配置
|
||||
optimize_database() {
|
||||
print_message $BLUE "优化数据库配置..."
|
||||
|
||||
local shared_buffers
|
||||
local effective_cache_size
|
||||
local work_mem
|
||||
local maintenance_work_mem
|
||||
local max_connections
|
||||
|
||||
# 根据系统内存计算数据库参数
|
||||
if [ $TOTAL_MEM -ge 8192 ]; then
|
||||
# 8GB+ 内存
|
||||
shared_buffers="2GB"
|
||||
effective_cache_size="6GB"
|
||||
work_mem="16MB"
|
||||
maintenance_work_mem="256MB"
|
||||
max_connections="200"
|
||||
elif [ $TOTAL_MEM -ge 4096 ]; then
|
||||
# 4-8GB 内存
|
||||
shared_buffers="1GB"
|
||||
effective_cache_size="3GB"
|
||||
work_mem="8MB"
|
||||
maintenance_work_mem="128MB"
|
||||
max_connections="150"
|
||||
else
|
||||
# 4GB 以下内存
|
||||
shared_buffers="512MB"
|
||||
effective_cache_size="1536MB"
|
||||
work_mem="4MB"
|
||||
maintenance_work_mem="64MB"
|
||||
max_connections="100"
|
||||
fi
|
||||
|
||||
# 生成优化的 PostgreSQL 配置
|
||||
cat > "$PROJECT_ROOT/docker/postgres/postgresql-optimized.conf" << EOF
|
||||
# PostgreSQL 性能优化配置
|
||||
# 自动生成于: $(date)
|
||||
|
||||
# 连接设置
|
||||
listen_addresses = '*'
|
||||
port = 5432
|
||||
max_connections = $max_connections
|
||||
superuser_reserved_connections = 3
|
||||
|
||||
# 内存设置
|
||||
shared_buffers = $shared_buffers
|
||||
effective_cache_size = $effective_cache_size
|
||||
work_mem = $work_mem
|
||||
maintenance_work_mem = $maintenance_work_mem
|
||||
|
||||
# 检查点设置
|
||||
checkpoint_completion_target = 0.9
|
||||
wal_buffers = 16MB
|
||||
default_statistics_target = 100
|
||||
|
||||
# 查询规划器设置
|
||||
random_page_cost = 1.1
|
||||
effective_io_concurrency = $([ $TOTAL_DISK -gt 100 ] && echo "200" || echo "100")
|
||||
|
||||
# 写前日志设置
|
||||
wal_level = replica
|
||||
max_wal_size = 2GB
|
||||
min_wal_size = 80MB
|
||||
|
||||
# 自动清理设置
|
||||
autovacuum = on
|
||||
autovacuum_max_workers = 3
|
||||
autovacuum_naptime = 1min
|
||||
autovacuum_vacuum_threshold = 50
|
||||
autovacuum_analyze_threshold = 50
|
||||
|
||||
# 日志设置
|
||||
logging_collector = on
|
||||
log_directory = 'pg_log'
|
||||
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
|
||||
log_rotation_age = 1d
|
||||
log_rotation_size = 100MB
|
||||
log_min_duration_statement = 1000
|
||||
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '
|
||||
log_checkpoints = on
|
||||
log_connections = on
|
||||
log_disconnections = on
|
||||
log_lock_waits = on
|
||||
|
||||
# 时区设置
|
||||
timezone = 'Asia/Shanghai'
|
||||
log_timezone = 'Asia/Shanghai'
|
||||
|
||||
# 字符集设置
|
||||
lc_messages = 'en_US.UTF-8'
|
||||
lc_monetary = 'en_US.UTF-8'
|
||||
lc_numeric = 'en_US.UTF-8'
|
||||
lc_time = 'en_US.UTF-8'
|
||||
|
||||
# 默认文本搜索配置
|
||||
default_text_search_config = 'pg_catalog.english'
|
||||
|
||||
# 并发设置
|
||||
max_worker_processes = $CPU_CORES
|
||||
max_parallel_workers_per_gather = $([ $CPU_CORES -gt 2 ] && echo "2" || echo "1")
|
||||
max_parallel_workers = $CPU_CORES
|
||||
EOF
|
||||
|
||||
print_message $GREEN "✓ 数据库配置已优化"
|
||||
echo " 共享缓冲区: $shared_buffers"
|
||||
echo " 有效缓存: $effective_cache_size"
|
||||
echo " 工作内存: $work_mem"
|
||||
echo " 最大连接数: $max_connections"
|
||||
}
|
||||
|
||||
# 优化 Nginx 配置
|
||||
optimize_nginx() {
|
||||
print_message $BLUE "优化 Nginx 配置..."
|
||||
|
||||
# 生成优化的 Nginx 配置
|
||||
cat > "$PROJECT_ROOT/docker/nginx/nginx-optimized.conf" << EOF
|
||||
# Nginx 性能优化配置
|
||||
# 自动生成于: $(date)
|
||||
|
||||
user nginx;
|
||||
worker_processes $CPU_CORES;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
# 优化事件处理
|
||||
events {
|
||||
worker_connections $([ $TOTAL_MEM -gt 4096 ] && echo "2048" || echo "1024");
|
||||
use epoll;
|
||||
multi_accept on;
|
||||
worker_rlimit_nofile 65535;
|
||||
}
|
||||
|
||||
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" '
|
||||
'rt=\$request_time uct="\$upstream_connect_time" '
|
||||
'uht="\$upstream_header_time" urt="\$upstream_response_time"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
# 基本设置
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 65;
|
||||
keepalive_requests 1000;
|
||||
types_hash_max_size 2048;
|
||||
server_tokens off;
|
||||
|
||||
# 缓冲区设置
|
||||
client_body_buffer_size 128k;
|
||||
client_max_body_size 50m;
|
||||
client_header_buffer_size 1k;
|
||||
large_client_header_buffers 4 4k;
|
||||
output_buffers 1 32k;
|
||||
postpone_output 1460;
|
||||
|
||||
# 超时设置
|
||||
client_header_timeout 3m;
|
||||
client_body_timeout 3m;
|
||||
send_timeout 3m;
|
||||
|
||||
# Gzip 压缩
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_types
|
||||
text/plain
|
||||
text/css
|
||||
text/xml
|
||||
text/javascript
|
||||
application/json
|
||||
application/javascript
|
||||
application/xml+rss
|
||||
application/atom+xml
|
||||
image/svg+xml;
|
||||
|
||||
# 缓存设置
|
||||
open_file_cache max=1000 inactive=20s;
|
||||
open_file_cache_valid 30s;
|
||||
open_file_cache_min_uses 2;
|
||||
open_file_cache_errors on;
|
||||
|
||||
# 包含虚拟主机配置
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
||||
EOF
|
||||
|
||||
print_message $GREEN "✓ Nginx 配置已优化"
|
||||
echo " 工作进程数: $CPU_CORES"
|
||||
echo " 连接数: $([ $TOTAL_MEM -gt 4096 ] && echo "2048" || echo "1024")"
|
||||
echo " 缓冲区: 优化完成"
|
||||
}
|
||||
|
||||
# 优化 Redis 配置
|
||||
optimize_redis() {
|
||||
print_message $BLUE "优化 Redis 配置..."
|
||||
|
||||
local maxmemory
|
||||
|
||||
# 根据系统内存计算 Redis 内存限制
|
||||
if [ $TOTAL_MEM -ge 8192 ]; then
|
||||
maxmemory="1gb"
|
||||
elif [ $TOTAL_MEM -ge 4096 ]; then
|
||||
maxmemory="512mb"
|
||||
else
|
||||
maxmemory="256mb"
|
||||
fi
|
||||
|
||||
# 生成优化的 Redis 配置
|
||||
cat > "$PROJECT_ROOT/docker/redis/redis-optimized.conf" << EOF
|
||||
# Redis 性能优化配置
|
||||
# 自动生成于: $(date)
|
||||
|
||||
# 内存设置
|
||||
maxmemory $maxmemory
|
||||
maxmemory-policy allkeys-lru
|
||||
|
||||
# 持久化设置
|
||||
save 900 1
|
||||
save 300 10
|
||||
save 60 10000
|
||||
|
||||
# AOF 设置
|
||||
appendonly yes
|
||||
appendfsync everysec
|
||||
no-appendfsync-on-rewrite no
|
||||
auto-aof-rewrite-percentage 100
|
||||
auto-aof-rewrite-min-size 64mb
|
||||
|
||||
# 网络设置
|
||||
tcp-keepalive 300
|
||||
timeout 0
|
||||
|
||||
# 客户端设置
|
||||
maxclients 10000
|
||||
|
||||
# 慢日志设置
|
||||
slowlog-log-slower-than 10000
|
||||
slowlog-max-len 128
|
||||
|
||||
# 其他优化
|
||||
hash-max-ziplist-entries 512
|
||||
hash-max-ziplist-value 64
|
||||
list-max-ziplist-size -2
|
||||
list-compress-depth 0
|
||||
set-max-intset-entries 512
|
||||
zset-max-ziplist-entries 128
|
||||
zset-max-ziplist-value 64
|
||||
EOF
|
||||
|
||||
print_message $GREEN "✓ Redis 配置已优化"
|
||||
echo " 最大内存: $maxmemory"
|
||||
echo " 淘汰策略: allkeys-lru"
|
||||
echo " 持久化: AOF + RDB"
|
||||
}
|
||||
|
||||
# 优化系统参数
|
||||
optimize_system() {
|
||||
print_message $BLUE "优化系统参数..."
|
||||
|
||||
# 检查是否有 root 权限
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
print_message $YELLOW "⚠ 需要 root 权限来优化系统参数"
|
||||
print_message $YELLOW "请手动执行以下命令或使用 sudo 运行此脚本:"
|
||||
echo ""
|
||||
echo "# 网络优化"
|
||||
echo "echo 'net.core.somaxconn = 65535' >> /etc/sysctl.conf"
|
||||
echo "echo 'net.core.netdev_max_backlog = 5000' >> /etc/sysctl.conf"
|
||||
echo "echo 'net.ipv4.tcp_max_syn_backlog = 65535' >> /etc/sysctl.conf"
|
||||
echo "echo 'net.ipv4.tcp_fin_timeout = 10' >> /etc/sysctl.conf"
|
||||
echo "echo 'net.ipv4.tcp_tw_reuse = 1' >> /etc/sysctl.conf"
|
||||
echo "echo 'net.ipv4.tcp_keepalive_time = 600' >> /etc/sysctl.conf"
|
||||
echo ""
|
||||
echo "# 文件描述符限制"
|
||||
echo "echo '* soft nofile 65535' >> /etc/security/limits.conf"
|
||||
echo "echo '* hard nofile 65535' >> /etc/security/limits.conf"
|
||||
echo ""
|
||||
echo "# 应用生效"
|
||||
echo "sysctl -p"
|
||||
return
|
||||
fi
|
||||
|
||||
# 网络参数优化
|
||||
cat >> /etc/sysctl.conf << EOF
|
||||
|
||||
# QAUP 系统优化参数
|
||||
# 添加时间: $(date)
|
||||
|
||||
# 网络优化
|
||||
net.core.somaxconn = 65535
|
||||
net.core.netdev_max_backlog = 5000
|
||||
net.core.rmem_default = 262144
|
||||
net.core.rmem_max = 16777216
|
||||
net.core.wmem_default = 262144
|
||||
net.core.wmem_max = 16777216
|
||||
net.ipv4.tcp_rmem = 4096 65536 16777216
|
||||
net.ipv4.tcp_wmem = 4096 65536 16777216
|
||||
net.ipv4.tcp_max_syn_backlog = 65535
|
||||
net.ipv4.tcp_fin_timeout = 10
|
||||
net.ipv4.tcp_tw_reuse = 1
|
||||
net.ipv4.tcp_keepalive_time = 600
|
||||
net.ipv4.tcp_keepalive_probes = 3
|
||||
net.ipv4.tcp_keepalive_intvl = 15
|
||||
|
||||
# 虚拟内存优化
|
||||
vm.swappiness = 10
|
||||
vm.dirty_ratio = 15
|
||||
vm.dirty_background_ratio = 5
|
||||
|
||||
EOF
|
||||
|
||||
# 文件描述符限制
|
||||
cat >> /etc/security/limits.conf << EOF
|
||||
|
||||
# QAUP 系统文件描述符限制
|
||||
# 添加时间: $(date)
|
||||
* soft nofile 65535
|
||||
* hard nofile 65535
|
||||
* soft nproc 65535
|
||||
* hard nproc 65535
|
||||
|
||||
EOF
|
||||
|
||||
# 应用系统参数
|
||||
sysctl -p
|
||||
|
||||
print_message $GREEN "✓ 系统参数已优化"
|
||||
}
|
||||
|
||||
# 创建性能监控脚本
|
||||
create_performance_monitor() {
|
||||
print_message $BLUE "创建性能监控脚本..."
|
||||
|
||||
cat > "$PROJECT_ROOT/docker/performance-monitor.sh" << 'EOF'
|
||||
#!/bin/bash
|
||||
|
||||
# QAUP 性能监控脚本
|
||||
|
||||
MONITOR_LOG="./logs/performance-$(date +%Y%m%d).log"
|
||||
|
||||
# 记录性能数据
|
||||
log_performance() {
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
|
||||
{
|
||||
echo "[$timestamp] 性能监控数据"
|
||||
echo "================================"
|
||||
|
||||
# 系统资源
|
||||
echo "系统负载: $(uptime | awk -F'load average:' '{print $2}')"
|
||||
echo "内存使用: $(free | awk 'NR==2{printf "%.2f%%", $3*100/$2}')"
|
||||
echo "磁盘使用: $(df -h . | awk 'NR==2{print $5}')"
|
||||
|
||||
# 容器资源
|
||||
echo ""
|
||||
echo "容器资源使用:"
|
||||
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}"
|
||||
|
||||
# 应用性能
|
||||
echo ""
|
||||
echo "应用响应时间:"
|
||||
local app_time=$(curl -o /dev/null -s -w '%{time_total}' http://localhost:8080/actuator/health 2>/dev/null || echo "N/A")
|
||||
local nginx_time=$(curl -o /dev/null -s -w '%{time_total}' http://localhost/health 2>/dev/null || echo "N/A")
|
||||
echo "应用健康检查: ${app_time}s"
|
||||
echo "Nginx健康检查: ${nginx_time}s"
|
||||
|
||||
# 数据库性能
|
||||
echo ""
|
||||
echo "数据库连接数:"
|
||||
local db_connections=$(docker exec qaup-postgres psql -U postgres -d qaup -t -c "SELECT count(*) FROM pg_stat_activity;" 2>/dev/null | xargs || echo "N/A")
|
||||
echo "活跃连接: $db_connections"
|
||||
|
||||
echo "================================"
|
||||
echo ""
|
||||
|
||||
} >> "$MONITOR_LOG"
|
||||
}
|
||||
|
||||
# 主循环
|
||||
while true; do
|
||||
log_performance
|
||||
sleep 300 # 每5分钟记录一次
|
||||
done
|
||||
EOF
|
||||
|
||||
chmod +x "$PROJECT_ROOT/docker/performance-monitor.sh"
|
||||
|
||||
print_message $GREEN "✓ 性能监控脚本已创建"
|
||||
}
|
||||
|
||||
# 应用所有优化
|
||||
apply_all_optimizations() {
|
||||
print_message $GREEN "开始应用所有性能优化..."
|
||||
echo ""
|
||||
|
||||
get_system_info
|
||||
|
||||
optimize_jvm
|
||||
echo ""
|
||||
|
||||
optimize_database
|
||||
echo ""
|
||||
|
||||
optimize_nginx
|
||||
echo ""
|
||||
|
||||
optimize_redis
|
||||
echo ""
|
||||
|
||||
optimize_system
|
||||
echo ""
|
||||
|
||||
create_performance_monitor
|
||||
echo ""
|
||||
|
||||
print_message $GREEN "========================================="
|
||||
print_message $GREEN "性能优化完成!"
|
||||
print_message $GREEN "========================================="
|
||||
echo ""
|
||||
|
||||
print_message $BLUE "优化摘要:"
|
||||
echo " ✓ JVM 参数已根据系统配置优化"
|
||||
echo " ✓ 数据库配置已优化"
|
||||
echo " ✓ Nginx 配置已优化"
|
||||
echo " ✓ Redis 配置已优化"
|
||||
echo " ✓ 系统参数已优化(如有权限)"
|
||||
echo " ✓ 性能监控脚本已创建"
|
||||
echo ""
|
||||
|
||||
print_message $YELLOW "下一步操作:"
|
||||
echo " 1. 重启服务以应用配置: ./deploy.sh restart"
|
||||
echo " 2. 启动性能监控: ./docker/performance-monitor.sh &"
|
||||
echo " 3. 验证优化效果: ./docker/monitor.sh resources"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "QAUP 性能优化脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [命令]"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " all 应用所有优化"
|
||||
echo " jvm 优化 JVM 参数"
|
||||
echo " database 优化数据库配置"
|
||||
echo " nginx 优化 Nginx 配置"
|
||||
echo " redis 优化 Redis 配置"
|
||||
echo " system 优化系统参数"
|
||||
echo " monitor 创建性能监控脚本"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 all # 应用所有优化"
|
||||
echo " $0 jvm # 仅优化 JVM"
|
||||
echo " sudo $0 system # 优化系统参数(需要 root)"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
if [ $# -eq 0 ]; then
|
||||
show_help
|
||||
exit 0
|
||||
fi
|
||||
|
||||
local command=$1
|
||||
|
||||
# 获取系统信息
|
||||
get_system_info
|
||||
|
||||
case $command in
|
||||
all)
|
||||
apply_all_optimizations
|
||||
;;
|
||||
jvm)
|
||||
optimize_jvm
|
||||
;;
|
||||
database)
|
||||
optimize_database
|
||||
;;
|
||||
nginx)
|
||||
optimize_nginx
|
||||
;;
|
||||
redis)
|
||||
optimize_redis
|
||||
;;
|
||||
system)
|
||||
optimize_system
|
||||
;;
|
||||
monitor)
|
||||
create_performance_monitor
|
||||
;;
|
||||
help|--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
print_message $RED "未知命令: $command"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
56
deploy/docker/postgres/backup-db.sh
Executable file
56
deploy/docker/postgres/backup-db.sh
Executable file
@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
|
||||
# QAUP 数据库备份脚本
|
||||
|
||||
set -e
|
||||
|
||||
# 配置变量
|
||||
BACKUP_DIR="/backup"
|
||||
DB_NAME="${POSTGRES_DB:-qaup}"
|
||||
DB_USER="${POSTGRES_USER:-postgres}"
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
BACKUP_FILE="qaup_backup_${TIMESTAMP}.sql"
|
||||
BACKUP_PATH="$BACKUP_DIR/$BACKUP_FILE"
|
||||
|
||||
echo "========================================="
|
||||
echo "QAUP 数据库备份开始"
|
||||
echo "========================================="
|
||||
|
||||
# 创建备份目录
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# 执行备份
|
||||
echo "备份数据库: $DB_NAME"
|
||||
echo "备份文件: $BACKUP_PATH"
|
||||
|
||||
pg_dump -h localhost -U "$DB_USER" -d "$DB_NAME" \
|
||||
--verbose \
|
||||
--clean \
|
||||
--if-exists \
|
||||
--create \
|
||||
--format=plain \
|
||||
--encoding=UTF8 \
|
||||
> "$BACKUP_PATH"
|
||||
|
||||
# 压缩备份文件
|
||||
echo "压缩备份文件..."
|
||||
gzip "$BACKUP_PATH"
|
||||
COMPRESSED_FILE="${BACKUP_PATH}.gz"
|
||||
|
||||
# 显示备份信息
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "备份完成信息:"
|
||||
echo "========================================="
|
||||
echo "备份文件: $COMPRESSED_FILE"
|
||||
echo "文件大小: $(du -h "$COMPRESSED_FILE" | cut -f1)"
|
||||
echo "备份时间: $(date)"
|
||||
|
||||
# 清理旧备份(保留最近7天)
|
||||
echo ""
|
||||
echo "清理旧备份文件(保留7天)..."
|
||||
find "$BACKUP_DIR" -name "qaup_backup_*.sql.gz" -mtime +7 -delete
|
||||
echo "清理完成"
|
||||
|
||||
echo ""
|
||||
echo "数据库备份完成!"
|
||||
29
deploy/docker/postgres/health-check.sh
Executable file
29
deploy/docker/postgres/health-check.sh
Executable file
@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
# PostgreSQL 健康检查脚本
|
||||
|
||||
set -e
|
||||
|
||||
DB_NAME="${POSTGRES_DB:-qaup}"
|
||||
DB_USER="${POSTGRES_USER:-postgres}"
|
||||
|
||||
# 检查 PostgreSQL 服务是否运行
|
||||
if ! pg_isready -h localhost -p 5432 -U "$DB_USER" -d "$DB_NAME" -q; then
|
||||
echo "PostgreSQL 服务未就绪"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查数据库连接
|
||||
if ! psql -h localhost -U "$DB_USER" -d "$DB_NAME" -c "SELECT 1;" > /dev/null 2>&1; then
|
||||
echo "数据库连接失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查 PostGIS 扩展
|
||||
if ! psql -h localhost -U "$DB_USER" -d "$DB_NAME" -c "SELECT PostGIS_Version();" > /dev/null 2>&1; then
|
||||
echo "PostGIS 扩展不可用"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "数据库健康检查通过"
|
||||
exit 0
|
||||
86
deploy/docker/postgres/init-db.sh
Executable file
86
deploy/docker/postgres/init-db.sh
Executable file
@ -0,0 +1,86 @@
|
||||
#!/bin/bash
|
||||
|
||||
# PostgreSQL 数据库初始化脚本
|
||||
# 此脚本在 PostgreSQL 容器首次启动时自动执行
|
||||
|
||||
set -e
|
||||
|
||||
echo "========================================="
|
||||
echo "QAUP 数据库初始化开始"
|
||||
echo "========================================="
|
||||
|
||||
# 创建数据库(如果不存在)
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||
-- 创建扩展
|
||||
CREATE EXTENSION IF NOT EXISTS postgis;
|
||||
CREATE EXTENSION IF NOT EXISTS postgis_topology;
|
||||
|
||||
-- 显示扩展信息
|
||||
SELECT name, default_version, installed_version
|
||||
FROM pg_available_extensions
|
||||
WHERE name IN ('postgis', 'postgis_topology');
|
||||
EOSQL
|
||||
|
||||
echo "✓ PostGIS 扩展安装完成"
|
||||
|
||||
# 导入数据库架构
|
||||
if [ -f "/docker-entrypoint-initdb.d/qaup_database_schema.sql" ]; then
|
||||
echo "导入数据库架构..."
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" -f /docker-entrypoint-initdb.d/qaup_database_schema.sql
|
||||
echo "✓ 数据库架构导入完成"
|
||||
else
|
||||
echo "警告: 未找到数据库架构文件"
|
||||
fi
|
||||
|
||||
# 导入初始数据(如果存在)
|
||||
if [ -f "/docker-entrypoint-initdb.d/initial_data.sql" ]; then
|
||||
echo "导入初始数据..."
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" -f /docker-entrypoint-initdb.d/initial_data.sql
|
||||
echo "✓ 初始数据导入完成"
|
||||
fi
|
||||
|
||||
# 创建数据库用户和权限设置
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||
-- 创建应用用户(如果不存在)
|
||||
DO \$\$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = '${APP_DB_USER:-qaup_app}') THEN
|
||||
CREATE USER ${APP_DB_USER:-qaup_app} WITH PASSWORD '${APP_DB_PASSWORD:-qaup123}';
|
||||
END IF;
|
||||
END
|
||||
\$\$;
|
||||
|
||||
-- 授予权限
|
||||
GRANT CONNECT ON DATABASE ${POSTGRES_DB} TO ${APP_DB_USER:-qaup_app};
|
||||
GRANT USAGE ON SCHEMA public TO ${APP_DB_USER:-qaup_app};
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO ${APP_DB_USER:-qaup_app};
|
||||
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO ${APP_DB_USER:-qaup_app};
|
||||
|
||||
-- 为未来创建的表设置默认权限
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO ${APP_DB_USER:-qaup_app};
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT ON SEQUENCES TO ${APP_DB_USER:-qaup_app};
|
||||
EOSQL
|
||||
|
||||
echo "✓ 数据库用户和权限设置完成"
|
||||
|
||||
# 显示数据库信息
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||
-- 显示数据库基本信息
|
||||
SELECT
|
||||
current_database() as database_name,
|
||||
current_user as current_user,
|
||||
version() as postgresql_version;
|
||||
|
||||
-- 显示表数量
|
||||
SELECT
|
||||
schemaname,
|
||||
COUNT(*) as table_count
|
||||
FROM pg_tables
|
||||
WHERE schemaname = 'public'
|
||||
GROUP BY schemaname;
|
||||
EOSQL
|
||||
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "QAUP 数据库初始化完成"
|
||||
echo "========================================="
|
||||
54
deploy/docker/postgres/postgresql.conf
Normal file
54
deploy/docker/postgres/postgresql.conf
Normal file
@ -0,0 +1,54 @@
|
||||
# PostgreSQL 生产环境配置
|
||||
# 针对 QAUP 应用优化
|
||||
|
||||
# 连接设置
|
||||
listen_addresses = '*'
|
||||
port = 5432
|
||||
max_connections = 200
|
||||
superuser_reserved_connections = 3
|
||||
|
||||
# 内存设置
|
||||
shared_buffers = 256MB
|
||||
effective_cache_size = 1GB
|
||||
work_mem = 4MB
|
||||
maintenance_work_mem = 64MB
|
||||
|
||||
# 检查点设置
|
||||
checkpoint_completion_target = 0.9
|
||||
wal_buffers = 16MB
|
||||
default_statistics_target = 100
|
||||
|
||||
# 日志设置
|
||||
logging_collector = on
|
||||
log_directory = 'pg_log'
|
||||
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
|
||||
log_rotation_age = 1d
|
||||
log_rotation_size = 100MB
|
||||
log_min_duration_statement = 1000
|
||||
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '
|
||||
log_checkpoints = on
|
||||
log_connections = on
|
||||
log_disconnections = on
|
||||
log_lock_waits = on
|
||||
|
||||
# 性能优化
|
||||
random_page_cost = 1.1
|
||||
effective_io_concurrency = 200
|
||||
|
||||
# 自动清理设置
|
||||
autovacuum = on
|
||||
autovacuum_max_workers = 3
|
||||
autovacuum_naptime = 1min
|
||||
|
||||
# 时区设置
|
||||
timezone = 'Asia/Shanghai'
|
||||
log_timezone = 'Asia/Shanghai'
|
||||
|
||||
# 字符集设置
|
||||
lc_messages = 'en_US.UTF-8'
|
||||
lc_monetary = 'en_US.UTF-8'
|
||||
lc_numeric = 'en_US.UTF-8'
|
||||
lc_time = 'en_US.UTF-8'
|
||||
|
||||
# 默认文本搜索配置
|
||||
default_text_search_config = 'pg_catalog.english'
|
||||
5112
deploy/docker/postgres/qaup_database_schema.sql
Normal file
5112
deploy/docker/postgres/qaup_database_schema.sql
Normal file
File diff suppressed because it is too large
Load Diff
72
deploy/docker/postgres/restore-db.sh
Executable file
72
deploy/docker/postgres/restore-db.sh
Executable file
@ -0,0 +1,72 @@
|
||||
#!/bin/bash
|
||||
|
||||
# QAUP 数据库恢复脚本
|
||||
|
||||
set -e
|
||||
|
||||
# 检查参数
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "用法: $0 <备份文件路径>"
|
||||
echo "示例: $0 /backup/qaup_backup_20241231_120000.sql.gz"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BACKUP_FILE="$1"
|
||||
DB_NAME="${POSTGRES_DB:-qaup}"
|
||||
DB_USER="${POSTGRES_USER:-postgres}"
|
||||
|
||||
echo "========================================="
|
||||
echo "QAUP 数据库恢复开始"
|
||||
echo "========================================="
|
||||
|
||||
# 检查备份文件是否存在
|
||||
if [ ! -f "$BACKUP_FILE" ]; then
|
||||
echo "错误: 备份文件不存在: $BACKUP_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "恢复文件: $BACKUP_FILE"
|
||||
echo "目标数据库: $DB_NAME"
|
||||
|
||||
# 确认操作
|
||||
read -p "警告: 此操作将覆盖现有数据库。是否继续? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo "操作已取消"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 停止应用连接(可选)
|
||||
echo "建议在恢复前停止应用服务以避免数据冲突"
|
||||
|
||||
# 解压备份文件(如果是压缩的)
|
||||
TEMP_FILE=""
|
||||
if [[ "$BACKUP_FILE" == *.gz ]]; then
|
||||
echo "解压备份文件..."
|
||||
TEMP_FILE="/tmp/restore_$(basename "$BACKUP_FILE" .gz)"
|
||||
gunzip -c "$BACKUP_FILE" > "$TEMP_FILE"
|
||||
RESTORE_FILE="$TEMP_FILE"
|
||||
else
|
||||
RESTORE_FILE="$BACKUP_FILE"
|
||||
fi
|
||||
|
||||
# 执行恢复
|
||||
echo "开始恢复数据库..."
|
||||
psql -h localhost -U "$DB_USER" -d postgres -f "$RESTORE_FILE"
|
||||
|
||||
# 清理临时文件
|
||||
if [ -n "$TEMP_FILE" ] && [ -f "$TEMP_FILE" ]; then
|
||||
rm -f "$TEMP_FILE"
|
||||
fi
|
||||
|
||||
# 验证恢复结果
|
||||
echo ""
|
||||
echo "验证恢复结果..."
|
||||
TABLE_COUNT=$(psql -h localhost -U "$DB_USER" -d "$DB_NAME" -t -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public';")
|
||||
echo "恢复后表数量: $TABLE_COUNT"
|
||||
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "数据库恢复完成!"
|
||||
echo "========================================="
|
||||
echo "请重启应用服务以确保正常连接"
|
||||
97
deploy/docker/prepare-offline-images.sh
Executable file
97
deploy/docker/prepare-offline-images.sh
Executable file
@ -0,0 +1,97 @@
|
||||
#!/bin/bash
|
||||
|
||||
# QAUP 离线部署镜像准备脚本
|
||||
# 此脚本需要在有网络连接的环境中运行
|
||||
|
||||
set -e
|
||||
|
||||
# 配置变量
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
IMAGES_DIR="$PROJECT_ROOT/offline-deploy/images"
|
||||
CONFIG_DIR="$PROJECT_ROOT/offline-deploy/config"
|
||||
SCRIPTS_DIR="$PROJECT_ROOT/offline-deploy/scripts"
|
||||
|
||||
# 镜像版本配置
|
||||
OPENJDK_IMAGE="openjdk:17-jre-slim"
|
||||
NGINX_IMAGE="nginx:alpine"
|
||||
POSTGRES_IMAGE="postgis/postgis:15-3.3"
|
||||
REDIS_IMAGE="redis:7-alpine"
|
||||
NODE_IMAGE="node:18-alpine"
|
||||
MAVEN_IMAGE="maven:3.9.6-openjdk-17-slim"
|
||||
|
||||
# 自定义镜像名称
|
||||
QAUP_APP_IMAGE="qaup-app:latest"
|
||||
QAUP_NGINX_IMAGE="qaup-nginx:latest"
|
||||
|
||||
echo "========================================="
|
||||
echo "QAUP 离线部署镜像准备开始"
|
||||
echo "========================================="
|
||||
|
||||
# 创建目录结构
|
||||
echo "创建离线部署目录结构..."
|
||||
mkdir -p "$IMAGES_DIR" "$CONFIG_DIR" "$SCRIPTS_DIR"
|
||||
|
||||
# 拉取基础镜像
|
||||
echo "拉取基础镜像..."
|
||||
docker pull $OPENJDK_IMAGE
|
||||
docker pull $NGINX_IMAGE
|
||||
docker pull $POSTGRES_IMAGE
|
||||
docker pull $REDIS_IMAGE
|
||||
docker pull $NODE_IMAGE
|
||||
docker pull $MAVEN_IMAGE
|
||||
|
||||
# 构建自定义镜像
|
||||
echo "构建 QAUP 应用镜像..."
|
||||
cd "$PROJECT_ROOT"
|
||||
docker build -t $QAUP_APP_IMAGE .
|
||||
|
||||
echo "构建 QAUP Nginx 镜像..."
|
||||
docker build -f docker/nginx/Dockerfile -t $QAUP_NGINX_IMAGE .
|
||||
|
||||
# 导出镜像
|
||||
echo "导出镜像文件..."
|
||||
docker save $OPENJDK_IMAGE | gzip > "$IMAGES_DIR/openjdk-17-jre-slim.tar.gz"
|
||||
docker save $NGINX_IMAGE | gzip > "$IMAGES_DIR/nginx-alpine.tar.gz"
|
||||
docker save $POSTGRES_IMAGE | gzip > "$IMAGES_DIR/postgis-15-3.3.tar.gz"
|
||||
docker save $REDIS_IMAGE | gzip > "$IMAGES_DIR/redis-7-alpine.tar.gz"
|
||||
docker save $QAUP_APP_IMAGE | gzip > "$IMAGES_DIR/qaup-app.tar.gz"
|
||||
docker save $QAUP_NGINX_IMAGE | gzip > "$IMAGES_DIR/qaup-nginx.tar.gz"
|
||||
|
||||
# 创建镜像清单文件
|
||||
echo "创建镜像清单..."
|
||||
cat > "$IMAGES_DIR/images-list.txt" << EOF
|
||||
# QAUP 离线部署镜像清单
|
||||
# 生成时间: $(date)
|
||||
|
||||
# 基础镜像
|
||||
openjdk-17-jre-slim.tar.gz -> $OPENJDK_IMAGE
|
||||
nginx-alpine.tar.gz -> $NGINX_IMAGE
|
||||
postgis-15-3.3.tar.gz -> $POSTGRES_IMAGE
|
||||
redis-7-alpine.tar.gz -> $REDIS_IMAGE
|
||||
|
||||
# 自定义镜像
|
||||
qaup-app.tar.gz -> $QAUP_APP_IMAGE
|
||||
qaup-nginx.tar.gz -> $QAUP_NGINX_IMAGE
|
||||
EOF
|
||||
|
||||
# 计算文件大小和校验和
|
||||
echo "计算文件校验和..."
|
||||
cd "$IMAGES_DIR"
|
||||
sha256sum *.tar.gz > checksums.sha256
|
||||
|
||||
# 显示统计信息
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "镜像准备完成统计信息:"
|
||||
echo "========================================="
|
||||
echo "镜像文件数量: $(ls -1 *.tar.gz | wc -l)"
|
||||
echo "总大小: $(du -sh . | cut -f1)"
|
||||
echo ""
|
||||
echo "镜像文件列表:"
|
||||
ls -lh *.tar.gz
|
||||
echo ""
|
||||
echo "校验和文件已生成: checksums.sha256"
|
||||
echo ""
|
||||
echo "离线部署镜像准备完成!"
|
||||
echo "镜像文件位置: $IMAGES_DIR"
|
||||
318
deploy/docker/secrets-manager.sh
Executable file
318
deploy/docker/secrets-manager.sh
Executable file
@ -0,0 +1,318 @@
|
||||
#!/bin/bash
|
||||
|
||||
# QAUP 敏感信息管理脚本
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
SECRETS_DIR="$PROJECT_ROOT/secrets"
|
||||
ENV_FILE="$PROJECT_ROOT/.env"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_message() {
|
||||
local color=$1
|
||||
local message=$2
|
||||
echo -e "${color}${message}${NC}"
|
||||
}
|
||||
|
||||
# 生成随机密码
|
||||
generate_password() {
|
||||
local length=${1:-16}
|
||||
openssl rand -base64 $length | tr -d "=+/" | cut -c1-$length
|
||||
}
|
||||
|
||||
# 生成强密码
|
||||
generate_strong_password() {
|
||||
local length=${1:-20}
|
||||
# 包含大小写字母、数字和特殊字符
|
||||
LC_ALL=C tr -dc 'A-Za-z0-9!@#$%^&*' < /dev/urandom | head -c $length
|
||||
}
|
||||
|
||||
# 初始化密钥管理
|
||||
init_secrets() {
|
||||
print_message $BLUE "初始化密钥管理..."
|
||||
|
||||
mkdir -p "$SECRETS_DIR"
|
||||
chmod 700 "$SECRETS_DIR"
|
||||
|
||||
# 生成主密钥
|
||||
local master_key_file="$SECRETS_DIR/master.key"
|
||||
if [ ! -f "$master_key_file" ]; then
|
||||
openssl rand -hex 32 > "$master_key_file"
|
||||
chmod 600 "$master_key_file"
|
||||
print_message $GREEN "主密钥已生成: $master_key_file"
|
||||
fi
|
||||
|
||||
# 创建密钥配置文件
|
||||
local secrets_config="$SECRETS_DIR/secrets.conf"
|
||||
if [ ! -f "$secrets_config" ]; then
|
||||
cat > "$secrets_config" << EOF
|
||||
# QAUP 密钥配置文件
|
||||
# 此文件包含加密后的敏感信息
|
||||
|
||||
# 数据库密码
|
||||
POSTGRES_PASSWORD_ENCRYPTED=
|
||||
APP_DB_PASSWORD_ENCRYPTED=
|
||||
|
||||
# Redis 密码
|
||||
REDIS_PASSWORD_ENCRYPTED=
|
||||
|
||||
# Token 密钥
|
||||
TOKEN_SECRET_ENCRYPTED=
|
||||
|
||||
# API 密钥
|
||||
AIRPORT_API_PASSWORD_ENCRYPTED=
|
||||
EOF
|
||||
chmod 600 "$secrets_config"
|
||||
print_message $GREEN "密钥配置文件已创建: $secrets_config"
|
||||
fi
|
||||
|
||||
print_message $GREEN "密钥管理初始化完成"
|
||||
}
|
||||
|
||||
# 加密字符串
|
||||
encrypt_string() {
|
||||
local plaintext="$1"
|
||||
local master_key_file="$SECRETS_DIR/master.key"
|
||||
|
||||
if [ ! -f "$master_key_file" ]; then
|
||||
print_message $RED "错误: 主密钥文件不存在,请先运行 init 命令"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local master_key=$(cat "$master_key_file")
|
||||
echo -n "$plaintext" | openssl enc -aes-256-cbc -a -salt -pass pass:"$master_key"
|
||||
}
|
||||
|
||||
# 解密字符串
|
||||
decrypt_string() {
|
||||
local encrypted="$1"
|
||||
local master_key_file="$SECRETS_DIR/master.key"
|
||||
|
||||
if [ ! -f "$master_key_file" ]; then
|
||||
print_message $RED "错误: 主密钥文件不存在"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local master_key=$(cat "$master_key_file")
|
||||
echo -n "$encrypted" | openssl enc -aes-256-cbc -d -a -pass pass:"$master_key"
|
||||
}
|
||||
|
||||
# 生成所有密码
|
||||
generate_all_passwords() {
|
||||
print_message $BLUE "生成所有密码..."
|
||||
|
||||
# 生成数据库密码
|
||||
local postgres_password=$(generate_strong_password 20)
|
||||
local app_db_password=$(generate_strong_password 20)
|
||||
|
||||
# 生成 Redis 密码
|
||||
local redis_password=$(generate_strong_password 16)
|
||||
|
||||
# 生成 Token 密钥
|
||||
local token_secret=$(generate_password 32)
|
||||
|
||||
# 生成 API 密码
|
||||
local airport_api_password=$(generate_strong_password 16)
|
||||
|
||||
print_message $GREEN "密码生成完成:"
|
||||
echo "PostgreSQL 管理员密码: $postgres_password"
|
||||
echo "应用数据库密码: $app_db_password"
|
||||
echo "Redis 密码: $redis_password"
|
||||
echo "Token 密钥: $token_secret"
|
||||
echo "机场 API 密码: $airport_api_password"
|
||||
|
||||
# 询问是否保存到配置文件
|
||||
read -p "是否将密码保存到环境配置文件? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
save_passwords_to_env "$postgres_password" "$app_db_password" "$redis_password" "$token_secret" "$airport_api_password"
|
||||
fi
|
||||
}
|
||||
|
||||
# 保存密码到环境文件
|
||||
save_passwords_to_env() {
|
||||
local postgres_password="$1"
|
||||
local app_db_password="$2"
|
||||
local redis_password="$3"
|
||||
local token_secret="$4"
|
||||
local airport_api_password="$5"
|
||||
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
print_message $RED "错误: 环境文件不存在: $ENV_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 备份原文件
|
||||
cp "$ENV_FILE" "$ENV_FILE.backup.$(date +%Y%m%d_%H%M%S)"
|
||||
|
||||
# 更新密码
|
||||
sed -i.tmp "s/^POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=$postgres_password/" "$ENV_FILE"
|
||||
sed -i.tmp "s/^APP_DB_PASSWORD=.*/APP_DB_PASSWORD=$app_db_password/" "$ENV_FILE"
|
||||
sed -i.tmp "s/^REDIS_PASSWORD=.*/REDIS_PASSWORD=$redis_password/" "$ENV_FILE"
|
||||
sed -i.tmp "s/^TOKEN_SECRET=.*/TOKEN_SECRET=$token_secret/" "$ENV_FILE"
|
||||
sed -i.tmp "s/^AIRPORT_API_PASSWORD=.*/AIRPORT_API_PASSWORD=$airport_api_password/" "$ENV_FILE"
|
||||
|
||||
rm -f "$ENV_FILE.tmp"
|
||||
|
||||
print_message $GREEN "密码已保存到环境文件"
|
||||
}
|
||||
|
||||
# 验证密码强度
|
||||
validate_password_strength() {
|
||||
local password="$1"
|
||||
local min_length=8
|
||||
local score=0
|
||||
|
||||
# 检查长度
|
||||
if [ ${#password} -ge $min_length ]; then
|
||||
score=$((score + 1))
|
||||
fi
|
||||
|
||||
# 检查是否包含小写字母
|
||||
if [[ "$password" =~ [a-z] ]]; then
|
||||
score=$((score + 1))
|
||||
fi
|
||||
|
||||
# 检查是否包含大写字母
|
||||
if [[ "$password" =~ [A-Z] ]]; then
|
||||
score=$((score + 1))
|
||||
fi
|
||||
|
||||
# 检查是否包含数字
|
||||
if [[ "$password" =~ [0-9] ]]; then
|
||||
score=$((score + 1))
|
||||
fi
|
||||
|
||||
# 检查是否包含特殊字符
|
||||
if [[ "$password" =~ [^a-zA-Z0-9] ]]; then
|
||||
score=$((score + 1))
|
||||
fi
|
||||
|
||||
case $score in
|
||||
0-2)
|
||||
print_message $RED "密码强度: 弱"
|
||||
return 1
|
||||
;;
|
||||
3)
|
||||
print_message $YELLOW "密码强度: 中等"
|
||||
return 0
|
||||
;;
|
||||
4-5)
|
||||
print_message $GREEN "密码强度: 强"
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 检查环境文件中的密码
|
||||
check_env_passwords() {
|
||||
print_message $BLUE "检查环境文件中的密码强度..."
|
||||
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
print_message $RED "错误: 环境文件不存在: $ENV_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source "$ENV_FILE"
|
||||
|
||||
local weak_passwords=0
|
||||
|
||||
# 检查各种密码
|
||||
echo "检查 PostgreSQL 密码..."
|
||||
if ! validate_password_strength "$POSTGRES_PASSWORD"; then
|
||||
weak_passwords=$((weak_passwords + 1))
|
||||
fi
|
||||
|
||||
echo "检查应用数据库密码..."
|
||||
if ! validate_password_strength "$APP_DB_PASSWORD"; then
|
||||
weak_passwords=$((weak_passwords + 1))
|
||||
fi
|
||||
|
||||
if [ -n "$REDIS_PASSWORD" ]; then
|
||||
echo "检查 Redis 密码..."
|
||||
if ! validate_password_strength "$REDIS_PASSWORD"; then
|
||||
weak_passwords=$((weak_passwords + 1))
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $weak_passwords -eq 0 ]; then
|
||||
print_message $GREEN "所有密码强度检查通过"
|
||||
else
|
||||
print_message $YELLOW "发现 $weak_passwords 个弱密码,建议重新生成"
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "QAUP 敏感信息管理脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [命令] [选项]"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " init 初始化密钥管理"
|
||||
echo " generate 生成所有密码"
|
||||
echo " check 检查密码强度"
|
||||
echo " encrypt <text> 加密文本"
|
||||
echo " decrypt <text> 解密文本"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 init # 初始化密钥管理"
|
||||
echo " $0 generate # 生成所有密码"
|
||||
echo " $0 check # 检查密码强度"
|
||||
echo " $0 encrypt 'mypassword' # 加密密码"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
if [ $# -eq 0 ]; then
|
||||
show_help
|
||||
exit 0
|
||||
fi
|
||||
|
||||
local command=$1
|
||||
shift
|
||||
|
||||
case $command in
|
||||
init)
|
||||
init_secrets
|
||||
;;
|
||||
generate)
|
||||
generate_all_passwords
|
||||
;;
|
||||
check)
|
||||
check_env_passwords
|
||||
;;
|
||||
encrypt)
|
||||
if [ $# -eq 0 ]; then
|
||||
print_message $RED "错误: 请提供要加密的文本"
|
||||
exit 1
|
||||
fi
|
||||
encrypt_string "$1"
|
||||
;;
|
||||
decrypt)
|
||||
if [ $# -eq 0 ]; then
|
||||
print_message $RED "错误: 请提供要解密的文本"
|
||||
exit 1
|
||||
fi
|
||||
decrypt_string "$1"
|
||||
;;
|
||||
help|--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
print_message $RED "未知命令: $command"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
738
deploy/docker/security-setup.sh
Executable file
738
deploy/docker/security-setup.sh
Executable file
@ -0,0 +1,738 @@
|
||||
#!/bin/bash
|
||||
|
||||
# QAUP 安全配置脚本
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_message() {
|
||||
local color=$1
|
||||
local message=$2
|
||||
echo -e "${color}${message}${NC}"
|
||||
}
|
||||
|
||||
# 创建非 root 用户配置
|
||||
setup_non_root_users() {
|
||||
print_message $BLUE "配置容器非 root 用户..."
|
||||
|
||||
# 更新应用 Dockerfile 以使用非 root 用户
|
||||
cat > "$PROJECT_ROOT/docker/app-security.Dockerfile" << 'EOF'
|
||||
# 安全版本的应用 Dockerfile
|
||||
FROM openjdk:17-jre-slim
|
||||
|
||||
# 创建应用用户和组
|
||||
RUN groupadd -r qaup && useradd -r -g qaup -d /app -s /bin/bash qaup
|
||||
|
||||
# 安装必要工具
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl \
|
||||
netcat-openbsd \
|
||||
fontconfig \
|
||||
fonts-dejavu-core \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& apt-get clean
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 创建必要目录并设置权限
|
||||
RUN mkdir -p /app/logs /app/uploadPath /app/temp \
|
||||
&& chown -R qaup:qaup /app \
|
||||
&& chmod 755 /app \
|
||||
&& chmod 750 /app/logs /app/uploadPath \
|
||||
&& chmod 700 /app/temp
|
||||
|
||||
# 复制应用文件
|
||||
COPY --chown=qaup:qaup qaup-admin/target/qaup-admin.jar /app/app.jar
|
||||
COPY --chown=qaup:qaup docker/start.sh /app/start.sh
|
||||
|
||||
# 设置文件权限
|
||||
RUN chmod 644 /app/app.jar \
|
||||
&& chmod 750 /app/start.sh
|
||||
|
||||
# 切换到非 root 用户
|
||||
USER qaup
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 8080
|
||||
|
||||
# 健康检查
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
||||
CMD curl -f http://localhost:8080/actuator/health || exit 1
|
||||
|
||||
# 启动应用
|
||||
ENTRYPOINT ["/app/start.sh"]
|
||||
EOF
|
||||
|
||||
print_message $GREEN "✓ 应用容器非 root 用户配置完成"
|
||||
}
|
||||
|
||||
# 配置网络安全
|
||||
setup_network_security() {
|
||||
print_message $BLUE "配置网络安全..."
|
||||
|
||||
# 创建安全的 Docker Compose 网络配置
|
||||
cat > "$PROJECT_ROOT/docker-compose.security.yml" << 'EOF'
|
||||
version: '3.8'
|
||||
|
||||
# 安全网络配置覆盖
|
||||
services:
|
||||
qaup-postgres:
|
||||
networks:
|
||||
- db-network
|
||||
# 不暴露数据库端口到主机
|
||||
ports: []
|
||||
|
||||
qaup-redis:
|
||||
networks:
|
||||
- cache-network
|
||||
# 不暴露 Redis 端口到主机
|
||||
ports: []
|
||||
|
||||
qaup-app:
|
||||
networks:
|
||||
- app-network
|
||||
- db-network
|
||||
- cache-network
|
||||
# 不暴露应用端口到主机
|
||||
ports: []
|
||||
|
||||
qaup-nginx:
|
||||
networks:
|
||||
- app-network
|
||||
# 只暴露必要的 HTTP/HTTPS 端口
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
|
||||
# 网络隔离配置
|
||||
networks:
|
||||
app-network:
|
||||
driver: bridge
|
||||
internal: false
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.20.1.0/24
|
||||
|
||||
db-network:
|
||||
driver: bridge
|
||||
internal: true # 内部网络,不能访问外网
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.20.2.0/24
|
||||
|
||||
cache-network:
|
||||
driver: bridge
|
||||
internal: true # 内部网络,不能访问外网
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.20.3.0/24
|
||||
EOF
|
||||
|
||||
print_message $GREEN "✓ 网络安全配置完成"
|
||||
}
|
||||
|
||||
# 配置文件权限
|
||||
setup_file_permissions() {
|
||||
print_message $BLUE "配置文件权限..."
|
||||
|
||||
# 设置配置文件权限
|
||||
if [ -f "$PROJECT_ROOT/.env" ]; then
|
||||
chmod 600 "$PROJECT_ROOT/.env"
|
||||
print_message $GREEN "✓ 环境文件权限已设置为 600"
|
||||
fi
|
||||
|
||||
# 设置数据目录权限
|
||||
if [ -d "$PROJECT_ROOT/data" ]; then
|
||||
chmod 750 "$PROJECT_ROOT/data"
|
||||
chmod 700 "$PROJECT_ROOT/data/postgres" 2>/dev/null || true
|
||||
chmod 755 "$PROJECT_ROOT/data/redis" 2>/dev/null || true
|
||||
chmod 755 "$PROJECT_ROOT/data/uploads" 2>/dev/null || true
|
||||
print_message $GREEN "✓ 数据目录权限已设置"
|
||||
fi
|
||||
|
||||
# 设置日志目录权限
|
||||
if [ -d "$PROJECT_ROOT/logs" ]; then
|
||||
chmod 755 "$PROJECT_ROOT/logs"
|
||||
chmod 644 "$PROJECT_ROOT/logs"/*.log 2>/dev/null || true
|
||||
print_message $GREEN "✓ 日志目录权限已设置"
|
||||
fi
|
||||
|
||||
# 设置脚本权限
|
||||
find "$PROJECT_ROOT/docker" -name "*.sh" -exec chmod 750 {} \;
|
||||
chmod 750 "$PROJECT_ROOT/deploy.sh" 2>/dev/null || true
|
||||
print_message $GREEN "✓ 脚本文件权限已设置"
|
||||
|
||||
# 设置配置文件权限
|
||||
find "$PROJECT_ROOT/config" -type f -exec chmod 644 {} \; 2>/dev/null || true
|
||||
find "$PROJECT_ROOT/docker" -name "*.conf" -exec chmod 644 {} \; 2>/dev/null || true
|
||||
print_message $GREEN "✓ 配置文件权限已设置"
|
||||
}
|
||||
|
||||
# 配置 SSL/TLS
|
||||
setup_ssl_tls() {
|
||||
print_message $BLUE "配置 SSL/TLS..."
|
||||
|
||||
# 创建 SSL 目录
|
||||
mkdir -p "$PROJECT_ROOT/ssl"
|
||||
chmod 700 "$PROJECT_ROOT/ssl"
|
||||
|
||||
# 创建自签名证书(用于测试)
|
||||
if [ ! -f "$PROJECT_ROOT/ssl/server.crt" ]; then
|
||||
print_message $YELLOW "生成自签名 SSL 证书(仅用于测试)..."
|
||||
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout "$PROJECT_ROOT/ssl/server.key" \
|
||||
-out "$PROJECT_ROOT/ssl/server.crt" \
|
||||
-subj "/C=CN/ST=State/L=City/O=Organization/CN=localhost" \
|
||||
2>/dev/null
|
||||
|
||||
chmod 600 "$PROJECT_ROOT/ssl/server.key"
|
||||
chmod 644 "$PROJECT_ROOT/ssl/server.crt"
|
||||
|
||||
print_message $YELLOW "⚠ 生产环境请使用正式的 SSL 证书"
|
||||
fi
|
||||
|
||||
# 创建支持 HTTPS 的 Nginx 配置
|
||||
cat > "$PROJECT_ROOT/docker/nginx/https.conf" << 'EOF'
|
||||
# HTTPS 配置
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name localhost;
|
||||
|
||||
# SSL 证书配置
|
||||
ssl_certificate /etc/nginx/ssl/server.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/server.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;
|
||||
|
||||
# 安全头
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header X-Frame-Options "SAMEORIGIN" 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;
|
||||
|
||||
# 其他配置与 HTTP 相同
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "healthy\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
|
||||
location /prod-api/ {
|
||||
proxy_pass http://qaup-app:8080/;
|
||||
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 30s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
|
||||
proxy_buffering on;
|
||||
proxy_buffer_size 4k;
|
||||
proxy_buffers 8 4k;
|
||||
|
||||
client_max_body_size 20M;
|
||||
}
|
||||
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
|
||||
location ~* \.html$ {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
add_header Expires "0";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# HTTP 重定向到 HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
EOF
|
||||
|
||||
print_message $GREEN "✓ SSL/TLS 配置完成"
|
||||
}
|
||||
|
||||
# 配置防火墙规则
|
||||
setup_firewall() {
|
||||
print_message $BLUE "配置防火墙规则..."
|
||||
|
||||
# 检查是否有 root 权限
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
print_message $YELLOW "⚠ 需要 root 权限来配置防火墙"
|
||||
print_message $YELLOW "请手动执行以下命令:"
|
||||
echo ""
|
||||
echo "# 启用 UFW 防火墙"
|
||||
echo "sudo ufw --force enable"
|
||||
echo ""
|
||||
echo "# 允许 SSH 连接"
|
||||
echo "sudo ufw allow 22/tcp"
|
||||
echo ""
|
||||
echo "# 允许 HTTP 和 HTTPS"
|
||||
echo "sudo ufw allow 80/tcp"
|
||||
echo "sudo ufw allow 443/tcp"
|
||||
echo ""
|
||||
echo "# 拒绝其他端口的外部访问"
|
||||
echo "sudo ufw deny 8080/tcp"
|
||||
echo "sudo ufw deny 5432/tcp"
|
||||
echo "sudo ufw deny 6379/tcp"
|
||||
echo ""
|
||||
echo "# 查看防火墙状态"
|
||||
echo "sudo ufw status verbose"
|
||||
return
|
||||
fi
|
||||
|
||||
# 配置 UFW 防火墙
|
||||
ufw --force enable
|
||||
|
||||
# 允许必要的端口
|
||||
ufw allow 22/tcp comment 'SSH'
|
||||
ufw allow 80/tcp comment 'HTTP'
|
||||
ufw allow 443/tcp comment 'HTTPS'
|
||||
|
||||
# 拒绝直接访问内部服务端口
|
||||
ufw deny 8080/tcp comment 'Block direct app access'
|
||||
ufw deny 5432/tcp comment 'Block direct database access'
|
||||
ufw deny 6379/tcp comment 'Block direct Redis access'
|
||||
|
||||
# 显示防火墙状态
|
||||
ufw status verbose
|
||||
|
||||
print_message $GREEN "✓ 防火墙配置完成"
|
||||
}
|
||||
|
||||
# 配置容器安全选项
|
||||
setup_container_security() {
|
||||
print_message $BLUE "配置容器安全选项..."
|
||||
|
||||
# 创建安全的 Docker Compose 配置
|
||||
cat > "$PROJECT_ROOT/docker-compose.security-options.yml" << 'EOF'
|
||||
version: '3.8'
|
||||
|
||||
# 容器安全选项配置
|
||||
services:
|
||||
qaup-postgres:
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
cap_drop:
|
||||
- ALL
|
||||
cap_add:
|
||||
- CHOWN
|
||||
- DAC_OVERRIDE
|
||||
- FOWNER
|
||||
- SETGID
|
||||
- SETUID
|
||||
read_only: false
|
||||
tmpfs:
|
||||
- /tmp:noexec,nosuid,size=100m
|
||||
|
||||
qaup-redis:
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
cap_drop:
|
||||
- ALL
|
||||
cap_add:
|
||||
- SETGID
|
||||
- SETUID
|
||||
read_only: false
|
||||
tmpfs:
|
||||
- /tmp:noexec,nosuid,size=50m
|
||||
|
||||
qaup-app:
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
cap_drop:
|
||||
- ALL
|
||||
read_only: false
|
||||
tmpfs:
|
||||
- /tmp:noexec,nosuid,size=200m
|
||||
|
||||
qaup-nginx:
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
cap_drop:
|
||||
- ALL
|
||||
cap_add:
|
||||
- CHOWN
|
||||
- DAC_OVERRIDE
|
||||
- SETGID
|
||||
- SETUID
|
||||
read_only: false
|
||||
tmpfs:
|
||||
- /tmp:noexec,nosuid,size=50m
|
||||
- /var/cache/nginx:noexec,nosuid,size=100m
|
||||
EOF
|
||||
|
||||
print_message $GREEN "✓ 容器安全选项配置完成"
|
||||
}
|
||||
|
||||
# 配置日志审计
|
||||
setup_audit_logging() {
|
||||
print_message $BLUE "配置审计日志..."
|
||||
|
||||
# 创建审计日志配置
|
||||
cat > "$PROJECT_ROOT/docker/audit-config.yml" << 'EOF'
|
||||
# 审计日志配置
|
||||
audit:
|
||||
enabled: true
|
||||
log_file: "./logs/audit.log"
|
||||
|
||||
# 审计事件类型
|
||||
events:
|
||||
- login_attempts
|
||||
- admin_operations
|
||||
- data_access
|
||||
- configuration_changes
|
||||
- security_events
|
||||
|
||||
# 日志格式
|
||||
format: json
|
||||
|
||||
# 日志轮转
|
||||
rotation:
|
||||
max_size: 100MB
|
||||
max_files: 10
|
||||
compress: true
|
||||
EOF
|
||||
|
||||
# 创建审计日志脚本
|
||||
cat > "$PROJECT_ROOT/docker/audit-logger.sh" << 'AUDIT_EOF'
|
||||
#!/bin/bash
|
||||
|
||||
# 审计日志记录脚本
|
||||
|
||||
AUDIT_LOG="./logs/audit.log"
|
||||
|
||||
# 记录审计事件
|
||||
log_audit_event() {
|
||||
local event_type=$1
|
||||
local user=${2:-"system"}
|
||||
local action=$3
|
||||
local resource=${4:-""}
|
||||
local result=${5:-"success"}
|
||||
local details=${6:-""}
|
||||
|
||||
local timestamp=$(date -u '+%Y-%m-%dT%H:%M:%S.%3NZ')
|
||||
local event_id=$(uuidgen 2>/dev/null || echo "$(date +%s)-$$")
|
||||
|
||||
# 创建 JSON 格式的审计日志
|
||||
cat >> "$AUDIT_LOG" << EOF
|
||||
{
|
||||
"timestamp": "$timestamp",
|
||||
"event_id": "$event_id",
|
||||
"event_type": "$event_type",
|
||||
"user": "$user",
|
||||
"action": "$action",
|
||||
"resource": "$resource",
|
||||
"result": "$result",
|
||||
"details": "$details",
|
||||
"source_ip": "${SSH_CLIENT%% *}",
|
||||
"session_id": "${SSH_TTY:-console}"
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
# 监控 Docker 事件
|
||||
monitor_docker_events() {
|
||||
docker events --format '{{.Time}} {{.Type}} {{.Action}} {{.Actor.Attributes.name}}' | while read timestamp type action container; do
|
||||
if [[ "$type" == "container" && "$action" =~ ^(start|stop|restart|kill|die)$ ]]; then
|
||||
log_audit_event "container_event" "docker" "$action" "$container" "success" "Container $action event"
|
||||
fi
|
||||
done &
|
||||
}
|
||||
|
||||
# 启动审计监控
|
||||
if [ "$1" = "start" ]; then
|
||||
echo "启动审计日志监控..."
|
||||
monitor_docker_events
|
||||
echo "审计日志监控已启动"
|
||||
fi
|
||||
|
||||
# 导出函数供其他脚本使用
|
||||
if [ "$1" != "start" ]; then
|
||||
log_audit_event "$@"
|
||||
fi
|
||||
AUDIT_EOF
|
||||
|
||||
chmod +x "$PROJECT_ROOT/docker/audit-logger.sh"
|
||||
|
||||
print_message $GREEN "✓ 审计日志配置完成"
|
||||
}
|
||||
|
||||
# 配置密码策略
|
||||
setup_password_policy() {
|
||||
print_message $BLUE "配置密码策略..."
|
||||
|
||||
# 创建密码策略检查脚本
|
||||
cat > "$PROJECT_ROOT/docker/password-policy.sh" << 'POLICY_EOF'
|
||||
#!/bin/bash
|
||||
|
||||
# 密码策略检查脚本
|
||||
|
||||
# 密码策略配置
|
||||
MIN_LENGTH=12
|
||||
REQUIRE_UPPERCASE=true
|
||||
REQUIRE_LOWERCASE=true
|
||||
REQUIRE_NUMBERS=true
|
||||
REQUIRE_SPECIAL=true
|
||||
FORBIDDEN_PATTERNS=("password" "123456" "admin" "qaup")
|
||||
|
||||
# 检查密码强度
|
||||
check_password_strength() {
|
||||
local password=$1
|
||||
local errors=()
|
||||
|
||||
# 检查长度
|
||||
if [ ${#password} -lt $MIN_LENGTH ]; then
|
||||
errors+=("密码长度至少需要 $MIN_LENGTH 位")
|
||||
fi
|
||||
|
||||
# 检查大写字母
|
||||
if [ "$REQUIRE_UPPERCASE" = true ] && ! [[ "$password" =~ [A-Z] ]]; then
|
||||
errors+=("密码必须包含大写字母")
|
||||
fi
|
||||
|
||||
# 检查小写字母
|
||||
if [ "$REQUIRE_LOWERCASE" = true ] && ! [[ "$password" =~ [a-z] ]]; then
|
||||
errors+=("密码必须包含小写字母")
|
||||
fi
|
||||
|
||||
# 检查数字
|
||||
if [ "$REQUIRE_NUMBERS" = true ] && ! [[ "$password" =~ [0-9] ]]; then
|
||||
errors+=("密码必须包含数字")
|
||||
fi
|
||||
|
||||
# 检查特殊字符
|
||||
if [ "$REQUIRE_SPECIAL" = true ] && ! [[ "$password" =~ [^a-zA-Z0-9] ]]; then
|
||||
errors+=("密码必须包含特殊字符")
|
||||
fi
|
||||
|
||||
# 检查禁用模式
|
||||
for pattern in "${FORBIDDEN_PATTERNS[@]}"; do
|
||||
if [[ "${password,,}" == *"${pattern,,}"* ]]; then
|
||||
errors+=("密码不能包含常见词汇: $pattern")
|
||||
fi
|
||||
done
|
||||
|
||||
# 返回结果
|
||||
if [ ${#errors[@]} -eq 0 ]; then
|
||||
echo "密码强度检查通过"
|
||||
return 0
|
||||
else
|
||||
echo "密码强度检查失败:"
|
||||
for error in "${errors[@]}"; do
|
||||
echo " - $error"
|
||||
done
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 生成强密码
|
||||
generate_strong_password() {
|
||||
local length=${1:-16}
|
||||
|
||||
# 确保包含所有必需的字符类型
|
||||
local uppercase="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
local lowercase="abcdefghijklmnopqrstuvwxyz"
|
||||
local numbers="0123456789"
|
||||
local special="!@#$%^&*()_+-=[]{}|;:,.<>?"
|
||||
|
||||
local password=""
|
||||
|
||||
# 至少包含一个每种类型的字符
|
||||
password+=$(echo $uppercase | fold -w1 | shuf | head -1)
|
||||
password+=$(echo $lowercase | fold -w1 | shuf | head -1)
|
||||
password+=$(echo $numbers | fold -w1 | shuf | head -1)
|
||||
password+=$(echo $special | fold -w1 | shuf | head -1)
|
||||
|
||||
# 填充剩余长度
|
||||
local all_chars="$uppercase$lowercase$numbers$special"
|
||||
for ((i=4; i<length; i++)); do
|
||||
password+=$(echo $all_chars | fold -w1 | shuf | head -1)
|
||||
done
|
||||
|
||||
# 打乱字符顺序
|
||||
echo $password | fold -w1 | shuf | tr -d '\n'
|
||||
}
|
||||
|
||||
# 主函数
|
||||
case "$1" in
|
||||
check)
|
||||
if [ -z "$2" ]; then
|
||||
echo "用法: $0 check <password>"
|
||||
exit 1
|
||||
fi
|
||||
check_password_strength "$2"
|
||||
;;
|
||||
generate)
|
||||
generate_strong_password "$2"
|
||||
;;
|
||||
*)
|
||||
echo "用法: $0 {check|generate} [参数]"
|
||||
echo " check <password> - 检查密码强度"
|
||||
echo " generate [length] - 生成强密码"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
POLICY_EOF
|
||||
|
||||
chmod +x "$PROJECT_ROOT/docker/password-policy.sh"
|
||||
|
||||
print_message $GREEN "✓ 密码策略配置完成"
|
||||
}
|
||||
|
||||
# 应用所有安全配置
|
||||
apply_all_security() {
|
||||
print_message $GREEN "开始应用所有安全配置..."
|
||||
echo ""
|
||||
|
||||
setup_non_root_users
|
||||
echo ""
|
||||
|
||||
setup_network_security
|
||||
echo ""
|
||||
|
||||
setup_file_permissions
|
||||
echo ""
|
||||
|
||||
setup_ssl_tls
|
||||
echo ""
|
||||
|
||||
setup_firewall
|
||||
echo ""
|
||||
|
||||
setup_container_security
|
||||
echo ""
|
||||
|
||||
setup_audit_logging
|
||||
echo ""
|
||||
|
||||
setup_password_policy
|
||||
echo ""
|
||||
|
||||
print_message $GREEN "========================================="
|
||||
print_message $GREEN "安全配置完成!"
|
||||
print_message $GREEN "========================================="
|
||||
echo ""
|
||||
|
||||
print_message $BLUE "安全配置摘要:"
|
||||
echo " ✓ 容器非 root 用户配置"
|
||||
echo " ✓ 网络隔离和安全配置"
|
||||
echo " ✓ 文件权限安全设置"
|
||||
echo " ✓ SSL/TLS 加密配置"
|
||||
echo " ✓ 防火墙规则配置"
|
||||
echo " ✓ 容器安全选项配置"
|
||||
echo " ✓ 审计日志配置"
|
||||
echo " ✓ 密码策略配置"
|
||||
echo ""
|
||||
|
||||
print_message $YELLOW "重要提醒:"
|
||||
echo " 1. 生产环境请使用正式的 SSL 证书"
|
||||
echo " 2. 定期更新密码和证书"
|
||||
echo " 3. 监控审计日志"
|
||||
echo " 4. 定期进行安全扫描"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "QAUP 安全配置脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [命令]"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " all 应用所有安全配置"
|
||||
echo " users 配置非 root 用户"
|
||||
echo " network 配置网络安全"
|
||||
echo " permissions 配置文件权限"
|
||||
echo " ssl 配置 SSL/TLS"
|
||||
echo " firewall 配置防火墙"
|
||||
echo " containers 配置容器安全选项"
|
||||
echo " audit 配置审计日志"
|
||||
echo " password 配置密码策略"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 all # 应用所有安全配置"
|
||||
echo " sudo $0 firewall # 配置防火墙(需要 root)"
|
||||
echo " $0 ssl # 配置 SSL/TLS"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
if [ $# -eq 0 ]; then
|
||||
show_help
|
||||
exit 0
|
||||
fi
|
||||
|
||||
local command=$1
|
||||
|
||||
case $command in
|
||||
all)
|
||||
apply_all_security
|
||||
;;
|
||||
users)
|
||||
setup_non_root_users
|
||||
;;
|
||||
network)
|
||||
setup_network_security
|
||||
;;
|
||||
permissions)
|
||||
setup_file_permissions
|
||||
;;
|
||||
ssl)
|
||||
setup_ssl_tls
|
||||
;;
|
||||
firewall)
|
||||
setup_firewall
|
||||
;;
|
||||
containers)
|
||||
setup_container_security
|
||||
;;
|
||||
audit)
|
||||
setup_audit_logging
|
||||
;;
|
||||
password)
|
||||
setup_password_policy
|
||||
;;
|
||||
help|--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
print_message $RED "未知命令: $command"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
24
deploy/docker/start.sh
Executable file
24
deploy/docker/start.sh
Executable file
@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 设置 JVM 参数
|
||||
JVM_OPTS="${JVM_OPTS:--Dname=qaup-admin.jar -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC}"
|
||||
|
||||
# 等待数据库就绪
|
||||
echo "等待数据库连接..."
|
||||
while ! nc -z ${DB_HOST:-qaup-postgres} ${DB_PORT:-5432}; do
|
||||
echo "数据库未就绪,等待 5 秒..."
|
||||
sleep 5
|
||||
done
|
||||
echo "数据库连接成功"
|
||||
|
||||
# 等待 Redis 就绪
|
||||
echo "等待 Redis 连接..."
|
||||
while ! nc -z ${REDIS_HOST:-qaup-redis} ${REDIS_PORT:-6379}; do
|
||||
echo "Redis 未就绪,等待 3 秒..."
|
||||
sleep 3
|
||||
done
|
||||
echo "Redis 连接成功"
|
||||
|
||||
# 启动应用
|
||||
echo "启动 QAUP 应用..."
|
||||
exec java $JVM_OPTS -jar app.jar
|
||||
338
deploy/docker/validate-environment.sh
Executable file
338
deploy/docker/validate-environment.sh
Executable file
@ -0,0 +1,338 @@
|
||||
#!/bin/bash
|
||||
|
||||
# QAUP 环境验证脚本
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
ENV_FILE="$PROJECT_ROOT/.env"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_message() {
|
||||
local color=$1
|
||||
local message=$2
|
||||
echo -e "${color}${message}${NC}"
|
||||
}
|
||||
|
||||
# 检查系统要求
|
||||
check_system_requirements() {
|
||||
print_message $BLUE "检查系统要求..."
|
||||
|
||||
local errors=0
|
||||
|
||||
# 检查操作系统
|
||||
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||
print_message $GREEN "✓ 操作系统: Linux"
|
||||
else
|
||||
print_message $YELLOW "⚠ 操作系统: $OSTYPE (建议使用 Linux)"
|
||||
fi
|
||||
|
||||
# 检查 Docker
|
||||
if command -v docker &> /dev/null; then
|
||||
local docker_version=$(docker --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
|
||||
print_message $GREEN "✓ Docker: $docker_version"
|
||||
|
||||
# 检查 Docker 版本
|
||||
local major_version=$(echo $docker_version | cut -d. -f1)
|
||||
local minor_version=$(echo $docker_version | cut -d. -f2)
|
||||
|
||||
if [ $major_version -lt 20 ] || ([ $major_version -eq 20 ] && [ $minor_version -lt 10 ]); then
|
||||
print_message $YELLOW "⚠ Docker 版本较低,建议升级到 20.10+"
|
||||
fi
|
||||
else
|
||||
print_message $RED "✗ Docker 未安装"
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
|
||||
# 检查 Docker Compose
|
||||
if command -v docker-compose &> /dev/null; then
|
||||
local compose_version=$(docker-compose --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
|
||||
print_message $GREEN "✓ Docker Compose: $compose_version"
|
||||
else
|
||||
print_message $RED "✗ Docker Compose 未安装"
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
|
||||
# 检查内存
|
||||
local total_mem=$(free -m | awk 'NR==2{printf "%.0f", $2/1024}')
|
||||
if [ $total_mem -ge 4 ]; then
|
||||
print_message $GREEN "✓ 内存: ${total_mem}GB"
|
||||
else
|
||||
print_message $YELLOW "⚠ 内存: ${total_mem}GB (建议至少 4GB)"
|
||||
fi
|
||||
|
||||
# 检查磁盘空间
|
||||
local available_space=$(df -BG . | awk 'NR==2 {print $4}' | sed 's/G//')
|
||||
if [ $available_space -ge 20 ]; then
|
||||
print_message $GREEN "✓ 可用磁盘空间: ${available_space}GB"
|
||||
else
|
||||
print_message $YELLOW "⚠ 可用磁盘空间: ${available_space}GB (建议至少 20GB)"
|
||||
fi
|
||||
|
||||
return $errors
|
||||
}
|
||||
|
||||
# 检查端口占用
|
||||
check_ports() {
|
||||
print_message $BLUE "检查端口占用..."
|
||||
|
||||
local ports=(80 443 8080 5432 6379)
|
||||
local occupied_ports=()
|
||||
|
||||
for port in "${ports[@]}"; do
|
||||
if netstat -tuln 2>/dev/null | grep -q ":$port "; then
|
||||
occupied_ports+=($port)
|
||||
print_message $YELLOW "⚠ 端口 $port 已被占用"
|
||||
else
|
||||
print_message $GREEN "✓ 端口 $port 可用"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#occupied_ports[@]} -gt 0 ]; then
|
||||
print_message $YELLOW "警告: 以下端口被占用: ${occupied_ports[*]}"
|
||||
print_message $YELLOW "请在 .env 文件中修改端口配置或停止占用端口的服务"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# 检查 Docker 服务状态
|
||||
check_docker_service() {
|
||||
print_message $BLUE "检查 Docker 服务状态..."
|
||||
|
||||
if systemctl is-active --quiet docker; then
|
||||
print_message $GREEN "✓ Docker 服务正在运行"
|
||||
else
|
||||
print_message $RED "✗ Docker 服务未运行"
|
||||
print_message $YELLOW "请运行: sudo systemctl start docker"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查当前用户是否在 docker 组中
|
||||
if groups | grep -q docker; then
|
||||
print_message $GREEN "✓ 当前用户在 docker 组中"
|
||||
else
|
||||
print_message $YELLOW "⚠ 当前用户不在 docker 组中"
|
||||
print_message $YELLOW "请运行: sudo usermod -aG docker $USER"
|
||||
print_message $YELLOW "然后重新登录或运行: newgrp docker"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# 检查环境配置
|
||||
check_environment_config() {
|
||||
print_message $BLUE "检查环境配置..."
|
||||
|
||||
local errors=0
|
||||
|
||||
# 检查环境文件
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
print_message $RED "✗ 环境文件不存在: $ENV_FILE"
|
||||
print_message $YELLOW "请运行: docker/config-manager.sh init"
|
||||
return 1
|
||||
fi
|
||||
|
||||
source "$ENV_FILE"
|
||||
|
||||
# 检查必需的环境变量
|
||||
local required_vars=(
|
||||
"POSTGRES_DB"
|
||||
"POSTGRES_USER"
|
||||
"POSTGRES_PASSWORD"
|
||||
"APP_DB_USER"
|
||||
"APP_DB_PASSWORD"
|
||||
)
|
||||
|
||||
for var in "${required_vars[@]}"; do
|
||||
if [ -z "${!var}" ]; then
|
||||
print_message $RED "✗ 必需的环境变量未设置: $var"
|
||||
errors=$((errors + 1))
|
||||
else
|
||||
print_message $GREEN "✓ $var 已设置"
|
||||
fi
|
||||
done
|
||||
|
||||
# 检查密码强度
|
||||
if [ ${#POSTGRES_PASSWORD} -lt 8 ]; then
|
||||
print_message $YELLOW "⚠ PostgreSQL 密码长度少于8位"
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
|
||||
if [ ${#APP_DB_PASSWORD} -lt 8 ]; then
|
||||
print_message $YELLOW "⚠ 应用数据库密码长度少于8位"
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
|
||||
return $errors
|
||||
}
|
||||
|
||||
# 检查文件权限
|
||||
check_file_permissions() {
|
||||
print_message $BLUE "检查文件权限..."
|
||||
|
||||
local errors=0
|
||||
|
||||
# 检查脚本文件权限
|
||||
local script_files=(
|
||||
"docker/start.sh"
|
||||
"docker/build-frontend.sh"
|
||||
"docker/nginx/start-nginx.sh"
|
||||
"docker/postgres/init-db.sh"
|
||||
"docker/postgres/backup-db.sh"
|
||||
"docker/postgres/restore-db.sh"
|
||||
"docker/postgres/health-check.sh"
|
||||
"docker/prepare-offline-images.sh"
|
||||
"docker/load-images.sh"
|
||||
"docker/docker-compose-manager.sh"
|
||||
"docker/config-manager.sh"
|
||||
"docker/secrets-manager.sh"
|
||||
)
|
||||
|
||||
for script in "${script_files[@]}"; do
|
||||
local script_path="$PROJECT_ROOT/$script"
|
||||
if [ -f "$script_path" ]; then
|
||||
if [ -x "$script_path" ]; then
|
||||
print_message $GREEN "✓ $script 可执行"
|
||||
else
|
||||
print_message $YELLOW "⚠ $script 不可执行"
|
||||
chmod +x "$script_path"
|
||||
print_message $GREEN "✓ 已修复 $script 权限"
|
||||
fi
|
||||
else
|
||||
print_message $RED "✗ 脚本文件不存在: $script"
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
return $errors
|
||||
}
|
||||
|
||||
# 检查目录结构
|
||||
check_directory_structure() {
|
||||
print_message $BLUE "检查目录结构..."
|
||||
|
||||
local required_dirs=(
|
||||
"docker"
|
||||
"docker/nginx"
|
||||
"docker/postgres"
|
||||
"config"
|
||||
)
|
||||
|
||||
local errors=0
|
||||
|
||||
for dir in "${required_dirs[@]}"; do
|
||||
local dir_path="$PROJECT_ROOT/$dir"
|
||||
if [ -d "$dir_path" ]; then
|
||||
print_message $GREEN "✓ 目录存在: $dir"
|
||||
else
|
||||
print_message $RED "✗ 目录不存在: $dir"
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
return $errors
|
||||
}
|
||||
|
||||
# 检查 Docker 镜像
|
||||
check_docker_images() {
|
||||
print_message $BLUE "检查 Docker 镜像..."
|
||||
|
||||
local base_images=(
|
||||
"openjdk:17-jre-slim"
|
||||
"nginx:alpine"
|
||||
"postgis/postgis:15-3.3"
|
||||
"redis:7-alpine"
|
||||
)
|
||||
|
||||
local missing_images=()
|
||||
|
||||
for image in "${base_images[@]}"; do
|
||||
if docker image inspect "$image" &> /dev/null; then
|
||||
print_message $GREEN "✓ 镜像存在: $image"
|
||||
else
|
||||
missing_images+=("$image")
|
||||
print_message $YELLOW "⚠ 镜像不存在: $image"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#missing_images[@]} -gt 0 ]; then
|
||||
print_message $YELLOW "缺失的镜像: ${missing_images[*]}"
|
||||
print_message $YELLOW "请运行: docker/prepare-offline-images.sh 或手动拉取镜像"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# 生成验证报告
|
||||
generate_report() {
|
||||
local total_errors=$1
|
||||
|
||||
print_message $BLUE "========================================="
|
||||
print_message $BLUE "环境验证报告"
|
||||
print_message $BLUE "========================================="
|
||||
|
||||
if [ $total_errors -eq 0 ]; then
|
||||
print_message $GREEN "✓ 环境验证通过,可以开始部署"
|
||||
print_message $GREEN "建议的下一步操作:"
|
||||
echo " 1. 运行: docker/docker-compose-manager.sh prod build"
|
||||
echo " 2. 运行: docker/docker-compose-manager.sh prod start"
|
||||
else
|
||||
print_message $RED "✗ 环境验证失败,发现 $total_errors 个问题"
|
||||
print_message $YELLOW "请修复上述问题后重新运行验证"
|
||||
fi
|
||||
|
||||
print_message $BLUE "========================================="
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
print_message $GREEN "开始 QAUP 环境验证..."
|
||||
print_message $BLUE "========================================="
|
||||
|
||||
local total_errors=0
|
||||
|
||||
# 执行各项检查
|
||||
check_system_requirements
|
||||
total_errors=$((total_errors + $?))
|
||||
|
||||
echo ""
|
||||
check_docker_service
|
||||
total_errors=$((total_errors + $?))
|
||||
|
||||
echo ""
|
||||
check_ports
|
||||
total_errors=$((total_errors + $?))
|
||||
|
||||
echo ""
|
||||
check_directory_structure
|
||||
total_errors=$((total_errors + $?))
|
||||
|
||||
echo ""
|
||||
check_file_permissions
|
||||
total_errors=$((total_errors + $?))
|
||||
|
||||
echo ""
|
||||
check_environment_config
|
||||
total_errors=$((total_errors + $?))
|
||||
|
||||
echo ""
|
||||
check_docker_images
|
||||
total_errors=$((total_errors + $?))
|
||||
|
||||
echo ""
|
||||
generate_report $total_errors
|
||||
|
||||
exit $total_errors
|
||||
}
|
||||
|
||||
main "$@"
|
||||
501
deploy/docker/zero-downtime-update.sh
Executable file
501
deploy/docker/zero-downtime-update.sh
Executable file
@ -0,0 +1,501 @@
|
||||
#!/bin/bash
|
||||
|
||||
# QAUP 零停机更新脚本
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_message() {
|
||||
local color=$1
|
||||
local message=$2
|
||||
echo -e "${color}${message}${NC}"
|
||||
}
|
||||
|
||||
# 获取时间戳
|
||||
get_timestamp() {
|
||||
date '+%Y-%m-%d %H:%M:%S'
|
||||
}
|
||||
|
||||
# 记录更新日志
|
||||
log_update() {
|
||||
local message=$1
|
||||
local timestamp=$(get_timestamp)
|
||||
echo "[$timestamp] $message" >> "./logs/update.log"
|
||||
print_message $BLUE "[$timestamp] $message"
|
||||
}
|
||||
|
||||
# 检查服务健康状态
|
||||
check_service_health() {
|
||||
local service=$1
|
||||
local max_attempts=${2:-30}
|
||||
local attempt=0
|
||||
|
||||
while [ $attempt -lt $max_attempts ]; do
|
||||
case $service in
|
||||
"app")
|
||||
if curl -f -s http://localhost:8080/actuator/health &>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
"nginx")
|
||||
if curl -f -s http://localhost/health &>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
"postgres")
|
||||
if docker exec qaup-postgres pg_isready -h localhost -p 5432 -U postgres &>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
"redis")
|
||||
if docker exec qaup-redis redis-cli ping &>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
sleep 2
|
||||
attempt=$((attempt + 1))
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# 等待服务就绪
|
||||
wait_for_service() {
|
||||
local service=$1
|
||||
local timeout=${2:-60}
|
||||
|
||||
log_update "等待 $service 服务就绪..."
|
||||
|
||||
if check_service_health "$service" $((timeout / 2)); then
|
||||
log_update "$service 服务已就绪"
|
||||
return 0
|
||||
else
|
||||
log_update "$service 服务启动超时"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 创建服务备份
|
||||
create_service_backup() {
|
||||
local service=$1
|
||||
local backup_name="${service}_backup_$(date +%Y%m%d_%H%M%S)"
|
||||
|
||||
log_update "创建 $service 服务备份: $backup_name"
|
||||
|
||||
# 导出当前运行的容器为镜像
|
||||
if docker ps --format "{{.Names}}" | grep -q "qaup-$service"; then
|
||||
docker commit "qaup-$service" "$backup_name"
|
||||
log_update "$service 服务备份已创建: $backup_name"
|
||||
echo "$backup_name" > "./logs/${service}_backup.txt"
|
||||
return 0
|
||||
else
|
||||
log_update "警告: $service 容器未运行,跳过备份"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 恢复服务备份
|
||||
restore_service_backup() {
|
||||
local service=$1
|
||||
local backup_file="./logs/${service}_backup.txt"
|
||||
|
||||
if [ -f "$backup_file" ]; then
|
||||
local backup_name=$(cat "$backup_file")
|
||||
log_update "恢复 $service 服务备份: $backup_name"
|
||||
|
||||
# 停止当前容器
|
||||
docker stop "qaup-$service" || true
|
||||
docker rm "qaup-$service" || true
|
||||
|
||||
# 从备份镜像启动容器
|
||||
docker run -d --name "qaup-$service" \
|
||||
--network qaup_qaup-network \
|
||||
"$backup_name"
|
||||
|
||||
if wait_for_service "$service"; then
|
||||
log_update "$service 服务备份恢复成功"
|
||||
return 0
|
||||
else
|
||||
log_update "$service 服务备份恢复失败"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
log_update "错误: 未找到 $service 服务备份信息"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 滚动更新应用服务
|
||||
rolling_update_app() {
|
||||
log_update "开始应用服务滚动更新..."
|
||||
|
||||
# 1. 创建备份
|
||||
create_service_backup "app"
|
||||
|
||||
# 2. 构建新镜像
|
||||
log_update "构建新的应用镜像..."
|
||||
cd "$PROJECT_ROOT"
|
||||
docker build -t qaup-app:new .
|
||||
|
||||
# 3. 启动新容器(临时端口)
|
||||
log_update "启动新应用容器..."
|
||||
docker run -d --name qaup-app-new \
|
||||
--network qaup_qaup-network \
|
||||
-p 8081:8080 \
|
||||
--env-file .env \
|
||||
-v qaup_app_logs:/app/logs \
|
||||
-v qaup_app_uploads:/app/uploadPath \
|
||||
qaup-app:new
|
||||
|
||||
# 4. 等待新容器就绪
|
||||
if ! check_service_health "app" 30; then
|
||||
log_update "新应用容器启动失败,回滚..."
|
||||
docker stop qaup-app-new || true
|
||||
docker rm qaup-app-new || true
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 5. 更新 Nginx 配置指向新容器
|
||||
log_update "更新负载均衡配置..."
|
||||
docker exec qaup-nginx sh -c "
|
||||
sed -i 's/qaup-app:8080/qaup-app-new:8080/g' /etc/nginx/conf.d/default.conf
|
||||
nginx -s reload
|
||||
"
|
||||
|
||||
# 6. 验证新服务
|
||||
sleep 10
|
||||
if curl -f -s http://localhost/prod-api/actuator/health &>/dev/null; then
|
||||
log_update "新应用服务验证成功"
|
||||
|
||||
# 7. 停止旧容器
|
||||
log_update "停止旧应用容器..."
|
||||
docker stop qaup-app || true
|
||||
docker rm qaup-app || true
|
||||
|
||||
# 8. 重命名新容器
|
||||
docker rename qaup-app-new qaup-app
|
||||
|
||||
# 9. 更新镜像标签
|
||||
docker tag qaup-app:new qaup-app:latest
|
||||
docker rmi qaup-app:new || true
|
||||
|
||||
log_update "应用服务滚动更新完成"
|
||||
return 0
|
||||
else
|
||||
log_update "新应用服务验证失败,回滚..."
|
||||
|
||||
# 回滚 Nginx 配置
|
||||
docker exec qaup-nginx sh -c "
|
||||
sed -i 's/qaup-app-new:8080/qaup-app:8080/g' /etc/nginx/conf.d/default.conf
|
||||
nginx -s reload
|
||||
"
|
||||
|
||||
# 清理新容器
|
||||
docker stop qaup-app-new || true
|
||||
docker rm qaup-app-new || true
|
||||
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 蓝绿部署
|
||||
blue_green_deployment() {
|
||||
log_update "开始蓝绿部署..."
|
||||
|
||||
local current_env="blue"
|
||||
local new_env="green"
|
||||
|
||||
# 检查当前环境
|
||||
if docker ps --format "{{.Names}}" | grep -q "qaup-app-green"; then
|
||||
current_env="green"
|
||||
new_env="blue"
|
||||
fi
|
||||
|
||||
log_update "当前环境: $current_env, 目标环境: $new_env"
|
||||
|
||||
# 1. 构建新镜像
|
||||
log_update "构建新镜像..."
|
||||
cd "$PROJECT_ROOT"
|
||||
docker build -t "qaup-app:$new_env" .
|
||||
|
||||
# 2. 启动新环境
|
||||
log_update "启动 $new_env 环境..."
|
||||
docker run -d --name "qaup-app-$new_env" \
|
||||
--network qaup_qaup-network \
|
||||
--env-file .env \
|
||||
-v qaup_app_logs:/app/logs \
|
||||
-v qaup_app_uploads:/app/uploadPath \
|
||||
"qaup-app:$new_env"
|
||||
|
||||
# 3. 等待新环境就绪
|
||||
if ! wait_for_service "app"; then
|
||||
log_update "$new_env 环境启动失败"
|
||||
docker stop "qaup-app-$new_env" || true
|
||||
docker rm "qaup-app-$new_env" || true
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 4. 切换流量
|
||||
log_update "切换流量到 $new_env 环境..."
|
||||
docker exec qaup-nginx sh -c "
|
||||
sed -i 's/qaup-app-$current_env:8080/qaup-app-$new_env:8080/g' /etc/nginx/conf.d/default.conf
|
||||
nginx -s reload
|
||||
"
|
||||
|
||||
# 5. 验证新环境
|
||||
sleep 10
|
||||
if curl -f -s http://localhost/prod-api/actuator/health &>/dev/null; then
|
||||
log_update "$new_env 环境验证成功"
|
||||
|
||||
# 6. 停止旧环境
|
||||
log_update "停止 $current_env 环境..."
|
||||
docker stop "qaup-app-$current_env" || true
|
||||
docker rm "qaup-app-$current_env" || true
|
||||
|
||||
# 7. 重命名新环境容器
|
||||
docker rename "qaup-app-$new_env" qaup-app
|
||||
|
||||
log_update "蓝绿部署完成"
|
||||
return 0
|
||||
else
|
||||
log_update "$new_env 环境验证失败,回滚..."
|
||||
|
||||
# 回滚流量
|
||||
docker exec qaup-nginx sh -c "
|
||||
sed -i 's/qaup-app-$new_env:8080/qaup-app-$current_env:8080/g' /etc/nginx/conf.d/default.conf
|
||||
nginx -s reload
|
||||
"
|
||||
|
||||
# 清理失败的环境
|
||||
docker stop "qaup-app-$new_env" || true
|
||||
docker rm "qaup-app-$new_env" || true
|
||||
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 数据库在线迁移
|
||||
database_online_migration() {
|
||||
log_update "开始数据库在线迁移..."
|
||||
|
||||
# 1. 创建数据库备份
|
||||
log_update "创建数据库备份..."
|
||||
docker exec qaup-postgres /backup-db.sh
|
||||
|
||||
# 2. 检查迁移脚本
|
||||
local migration_dir="$PROJECT_ROOT/sql/migrations"
|
||||
if [ ! -d "$migration_dir" ]; then
|
||||
log_update "未找到数据库迁移脚本目录: $migration_dir"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 3. 执行迁移脚本
|
||||
for migration_file in "$migration_dir"/*.sql; do
|
||||
if [ -f "$migration_file" ]; then
|
||||
local filename=$(basename "$migration_file")
|
||||
log_update "执行迁移脚本: $filename"
|
||||
|
||||
if docker exec qaup-postgres psql -U postgres -d qaup -f "/migrations/$filename"; then
|
||||
log_update "迁移脚本 $filename 执行成功"
|
||||
else
|
||||
log_update "迁移脚本 $filename 执行失败,停止迁移"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
log_update "数据库在线迁移完成"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 健康检查和验证
|
||||
post_update_verification() {
|
||||
log_update "开始更新后验证..."
|
||||
|
||||
local checks_passed=0
|
||||
local total_checks=5
|
||||
|
||||
# 1. 检查所有容器状态
|
||||
log_update "检查容器状态..."
|
||||
if docker ps --format "{{.Names}}" | grep -q "qaup-"; then
|
||||
checks_passed=$((checks_passed + 1))
|
||||
log_update "✓ 容器状态检查通过"
|
||||
else
|
||||
log_update "✗ 容器状态检查失败"
|
||||
fi
|
||||
|
||||
# 2. 检查应用健康状态
|
||||
log_update "检查应用健康状态..."
|
||||
if check_service_health "app"; then
|
||||
checks_passed=$((checks_passed + 1))
|
||||
log_update "✓ 应用健康检查通过"
|
||||
else
|
||||
log_update "✗ 应用健康检查失败"
|
||||
fi
|
||||
|
||||
# 3. 检查前端访问
|
||||
log_update "检查前端访问..."
|
||||
if curl -f -s http://localhost/health &>/dev/null; then
|
||||
checks_passed=$((checks_passed + 1))
|
||||
log_update "✓ 前端访问检查通过"
|
||||
else
|
||||
log_update "✗ 前端访问检查失败"
|
||||
fi
|
||||
|
||||
# 4. 检查数据库连接
|
||||
log_update "检查数据库连接..."
|
||||
if check_service_health "postgres"; then
|
||||
checks_passed=$((checks_passed + 1))
|
||||
log_update "✓ 数据库连接检查通过"
|
||||
else
|
||||
log_update "✗ 数据库连接检查失败"
|
||||
fi
|
||||
|
||||
# 5. 检查 Redis 连接
|
||||
log_update "检查 Redis 连接..."
|
||||
if check_service_health "redis"; then
|
||||
checks_passed=$((checks_passed + 1))
|
||||
log_update "✓ Redis 连接检查通过"
|
||||
else
|
||||
log_update "✗ Redis 连接检查失败"
|
||||
fi
|
||||
|
||||
# 验证结果
|
||||
log_update "验证完成: $checks_passed/$total_checks 项检查通过"
|
||||
|
||||
if [ $checks_passed -eq $total_checks ]; then
|
||||
log_update "✓ 所有验证检查通过,更新成功"
|
||||
return 0
|
||||
else
|
||||
log_update "✗ 部分验证检查失败,建议检查系统状态"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 完整的零停机更新流程
|
||||
full_zero_downtime_update() {
|
||||
log_update "========================================="
|
||||
log_update "开始零停机更新流程"
|
||||
log_update "========================================="
|
||||
|
||||
# 1. 预检查
|
||||
log_update "执行更新前检查..."
|
||||
if ! post_update_verification; then
|
||||
log_update "更新前检查失败,取消更新"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 2. 数据库迁移
|
||||
if ! database_online_migration; then
|
||||
log_update "数据库迁移失败,取消更新"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 3. 应用更新
|
||||
if ! rolling_update_app; then
|
||||
log_update "应用更新失败,尝试回滚..."
|
||||
restore_service_backup "app"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 4. 更新后验证
|
||||
if ! post_update_verification; then
|
||||
log_update "更新后验证失败,建议检查系统状态"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_update "========================================="
|
||||
log_update "零停机更新完成"
|
||||
log_update "========================================="
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "QAUP 零停机更新脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [命令]"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " full 完整的零停机更新流程"
|
||||
echo " rolling 滚动更新应用服务"
|
||||
echo " blue-green 蓝绿部署"
|
||||
echo " db-migration 数据库在线迁移"
|
||||
echo " verify 更新后验证"
|
||||
echo " backup 创建服务备份"
|
||||
echo " restore 恢复服务备份"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 full # 执行完整更新流程"
|
||||
echo " $0 rolling # 仅滚动更新应用"
|
||||
echo " $0 backup app # 备份应用服务"
|
||||
echo " $0 restore app # 恢复应用服务"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
# 确保日志目录存在
|
||||
mkdir -p "./logs"
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
show_help
|
||||
exit 0
|
||||
fi
|
||||
|
||||
local command=$1
|
||||
shift
|
||||
|
||||
case $command in
|
||||
full)
|
||||
full_zero_downtime_update
|
||||
;;
|
||||
rolling)
|
||||
rolling_update_app
|
||||
;;
|
||||
blue-green)
|
||||
blue_green_deployment
|
||||
;;
|
||||
db-migration)
|
||||
database_online_migration
|
||||
;;
|
||||
verify)
|
||||
post_update_verification
|
||||
;;
|
||||
backup)
|
||||
if [ $# -eq 0 ]; then
|
||||
print_message $RED "请指定要备份的服务名称"
|
||||
exit 1
|
||||
fi
|
||||
create_service_backup "$1"
|
||||
;;
|
||||
restore)
|
||||
if [ $# -eq 0 ]; then
|
||||
print_message $RED "请指定要恢复的服务名称"
|
||||
exit 1
|
||||
fi
|
||||
restore_service_backup "$1"
|
||||
;;
|
||||
help|--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
print_message $RED "未知命令: $command"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
601
deploy/docs/故障排除.md
Normal file
601
deploy/docs/故障排除.md
Normal file
@ -0,0 +1,601 @@
|
||||
# QAUP 系统故障排除指南
|
||||
|
||||
## 概述
|
||||
|
||||
本文档提供了 QAUP 系统常见问题的诊断和解决方案。按照问题类型分类,提供详细的排查步骤和解决方法。
|
||||
|
||||
## 快速诊断
|
||||
|
||||
### 系统状态检查
|
||||
|
||||
```bash
|
||||
# 快速检查所有服务状态
|
||||
./deploy.sh status
|
||||
|
||||
# 运行完整的系统检查
|
||||
./docker/monitor.sh status
|
||||
|
||||
# 检查系统告警
|
||||
./docker/alert-manager.sh check
|
||||
```
|
||||
|
||||
### 日志快速查看
|
||||
|
||||
```bash
|
||||
# 查看所有服务的最新日志
|
||||
./deploy.sh logs all
|
||||
|
||||
# 查看错误日志分析
|
||||
./docker/log-manager.sh analyze
|
||||
```
|
||||
|
||||
## 容器相关问题
|
||||
|
||||
### 问题 1: 容器无法启动
|
||||
|
||||
**症状**: 容器状态显示 `Exited` 或 `Restarting`
|
||||
|
||||
**诊断步骤**:
|
||||
```bash
|
||||
# 查看容器状态
|
||||
docker ps -a
|
||||
|
||||
# 查看容器启动日志
|
||||
docker logs <container_name>
|
||||
|
||||
# 检查容器配置
|
||||
docker inspect <container_name>
|
||||
```
|
||||
|
||||
**常见原因和解决方案**:
|
||||
|
||||
1. **端口冲突**
|
||||
```bash
|
||||
# 检查端口占用
|
||||
netstat -tuln | grep <port>
|
||||
|
||||
# 解决方案:修改 .env 文件中的端口配置
|
||||
nano .env
|
||||
```
|
||||
|
||||
2. **资源不足**
|
||||
```bash
|
||||
# 检查系统资源
|
||||
free -h
|
||||
df -h
|
||||
|
||||
# 解决方案:释放资源或增加服务器配置
|
||||
```
|
||||
|
||||
3. **配置文件错误**
|
||||
```bash
|
||||
# 验证配置文件
|
||||
./docker/config-manager.sh validate
|
||||
|
||||
# 重新初始化配置
|
||||
./docker/config-manager.sh init
|
||||
```
|
||||
|
||||
### 问题 2: 容器健康检查失败
|
||||
|
||||
**症状**: 容器状态显示 `unhealthy`
|
||||
|
||||
**诊断步骤**:
|
||||
```bash
|
||||
# 查看健康检查日志
|
||||
docker inspect <container_name> | grep -A 10 "Health"
|
||||
|
||||
# 手动执行健康检查命令
|
||||
docker exec <container_name> <health_check_command>
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 重启不健康的容器
|
||||
docker restart <container_name>
|
||||
|
||||
# 如果持续失败,检查应用日志
|
||||
docker logs <container_name> | tail -100
|
||||
```
|
||||
|
||||
## 数据库相关问题
|
||||
|
||||
### 问题 3: 数据库连接失败
|
||||
|
||||
**症状**: 应用日志显示数据库连接错误
|
||||
|
||||
**诊断步骤**:
|
||||
```bash
|
||||
# 检查数据库容器状态
|
||||
docker ps | grep qaup-postgres
|
||||
|
||||
# 测试数据库连接
|
||||
docker exec qaup-postgres pg_isready -h localhost -p 5432 -U postgres
|
||||
|
||||
# 检查数据库日志
|
||||
docker logs qaup-postgres | tail -50
|
||||
```
|
||||
|
||||
**常见原因和解决方案**:
|
||||
|
||||
1. **数据库未完全启动**
|
||||
```bash
|
||||
# 等待数据库完全启动
|
||||
sleep 30
|
||||
|
||||
# 重新测试连接
|
||||
docker exec qaup-postgres pg_isready -h localhost -p 5432 -U postgres
|
||||
```
|
||||
|
||||
2. **密码错误**
|
||||
```bash
|
||||
# 检查环境变量
|
||||
grep POSTGRES .env
|
||||
|
||||
# 重置数据库密码(需要重新创建容器)
|
||||
docker-compose down
|
||||
docker volume rm qaup_postgres_data
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
3. **数据库损坏**
|
||||
```bash
|
||||
# 检查数据库完整性
|
||||
docker exec qaup-postgres psql -U postgres -d qaup -c "SELECT version();"
|
||||
|
||||
# 如果损坏,从备份恢复
|
||||
docker exec qaup-postgres /restore-db.sh /backup/latest_backup.sql.gz
|
||||
```
|
||||
|
||||
### 问题 4: 数据库性能问题
|
||||
|
||||
**症状**: 查询响应慢,应用超时
|
||||
|
||||
**诊断步骤**:
|
||||
```bash
|
||||
# 检查数据库连接数
|
||||
docker exec qaup-postgres psql -U postgres -d qaup -c "SELECT count(*) FROM pg_stat_activity;"
|
||||
|
||||
# 检查慢查询
|
||||
docker exec qaup-postgres psql -U postgres -d qaup -c "SELECT query, query_start, state FROM pg_stat_activity WHERE state = 'active';"
|
||||
|
||||
# 检查数据库锁
|
||||
docker exec qaup-postgres psql -U postgres -d qaup -c "SELECT * FROM pg_locks WHERE NOT granted;"
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 终止长时间运行的查询
|
||||
docker exec qaup-postgres psql -U postgres -d qaup -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'active' AND query_start < now() - interval '5 minutes';"
|
||||
|
||||
# 重启数据库容器
|
||||
docker restart qaup-postgres
|
||||
|
||||
# 优化数据库配置
|
||||
nano docker/postgres/postgresql.conf
|
||||
```
|
||||
|
||||
## 应用相关问题
|
||||
|
||||
### 问题 5: 应用启动失败
|
||||
|
||||
**症状**: qaup-app 容器无法启动或立即退出
|
||||
|
||||
**诊断步骤**:
|
||||
```bash
|
||||
# 查看应用启动日志
|
||||
docker logs qaup-app
|
||||
|
||||
# 检查 JVM 参数
|
||||
grep JVM_OPTS .env
|
||||
|
||||
# 检查应用配置
|
||||
docker exec qaup-app cat /app/config/application-prod.yml
|
||||
```
|
||||
|
||||
**常见原因和解决方案**:
|
||||
|
||||
1. **内存不足**
|
||||
```bash
|
||||
# 检查系统内存
|
||||
free -h
|
||||
|
||||
# 调整 JVM 内存参数
|
||||
nano .env
|
||||
# 修改 JVM_OPTS,减少 -Xmx 值
|
||||
```
|
||||
|
||||
2. **配置文件错误**
|
||||
```bash
|
||||
# 验证配置文件语法
|
||||
docker run --rm -v $(pwd)/config:/config mikefarah/yq eval /config/application-prod.yml
|
||||
|
||||
# 重新生成配置
|
||||
./docker/config-manager.sh generate prod
|
||||
```
|
||||
|
||||
3. **依赖服务未就绪**
|
||||
```bash
|
||||
# 检查数据库和 Redis 状态
|
||||
docker exec qaup-postgres pg_isready
|
||||
docker exec qaup-redis redis-cli ping
|
||||
|
||||
# 重启应用容器
|
||||
docker restart qaup-app
|
||||
```
|
||||
|
||||
### 问题 6: 应用内存泄漏
|
||||
|
||||
**症状**: 应用内存使用持续增长,最终 OOM
|
||||
|
||||
**诊断步骤**:
|
||||
```bash
|
||||
# 监控内存使用
|
||||
docker stats qaup-app
|
||||
|
||||
# 检查 JVM 内存使用
|
||||
docker exec qaup-app jstat -gc 1
|
||||
|
||||
# 生成堆转储
|
||||
docker exec qaup-app jcmd 1 GC.run_finalization
|
||||
docker exec qaup-app jcmd 1 VM.gc
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 调整 JVM 参数
|
||||
nano .env
|
||||
# 添加内存分析参数
|
||||
JVM_OPTS="... -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/app/logs/"
|
||||
|
||||
# 重启应用
|
||||
docker restart qaup-app
|
||||
|
||||
# 定期重启应用(临时方案)
|
||||
echo "0 2 * * * docker restart qaup-app" | crontab -
|
||||
```
|
||||
|
||||
## 网络相关问题
|
||||
|
||||
### 问题 7: 前端无法访问
|
||||
|
||||
**症状**: 浏览器无法打开前端页面
|
||||
|
||||
**诊断步骤**:
|
||||
```bash
|
||||
# 检查 Nginx 容器状态
|
||||
docker ps | grep qaup-nginx
|
||||
|
||||
# 测试 Nginx 健康检查
|
||||
curl -I http://localhost/health
|
||||
|
||||
# 检查端口监听
|
||||
netstat -tuln | grep :80
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 重启 Nginx 容器
|
||||
docker restart qaup-nginx
|
||||
|
||||
# 检查 Nginx 配置
|
||||
docker exec qaup-nginx nginx -t
|
||||
|
||||
# 查看 Nginx 错误日志
|
||||
docker logs qaup-nginx | grep error
|
||||
```
|
||||
|
||||
### 问题 8: API 请求失败
|
||||
|
||||
**症状**: 前端可以访问,但 API 请求返回 502/504 错误
|
||||
|
||||
**诊断步骤**:
|
||||
```bash
|
||||
# 测试后端应用健康检查
|
||||
curl http://localhost:8080/actuator/health
|
||||
|
||||
# 检查 Nginx 到应用的连接
|
||||
docker exec qaup-nginx nc -z qaup-app 8080
|
||||
|
||||
# 查看 Nginx 访问日志
|
||||
docker logs qaup-nginx | grep "prod-api"
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 检查应用容器状态
|
||||
docker ps | grep qaup-app
|
||||
|
||||
# 重启应用容器
|
||||
docker restart qaup-app
|
||||
|
||||
# 检查网络连接
|
||||
docker network ls
|
||||
docker network inspect qaup_qaup-network
|
||||
```
|
||||
|
||||
## 性能相关问题
|
||||
|
||||
### 问题 9: 系统响应慢
|
||||
|
||||
**症状**: 页面加载慢,API 响应时间长
|
||||
|
||||
**诊断步骤**:
|
||||
```bash
|
||||
# 检查系统资源使用
|
||||
./docker/monitor.sh resources
|
||||
|
||||
# 检查容器资源使用
|
||||
docker stats
|
||||
|
||||
# 测试响应时间
|
||||
curl -w "@curl-format.txt" -o /dev/null -s http://localhost/health
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 优化数据库连接池
|
||||
nano config/application-prod.yml
|
||||
# 调整 maxActive, minIdle 等参数
|
||||
|
||||
# 增加 JVM 内存
|
||||
nano .env
|
||||
# 增加 -Xmx 值
|
||||
|
||||
# 启用 Nginx 缓存
|
||||
nano docker/nginx/default.conf
|
||||
# 添加缓存配置
|
||||
```
|
||||
|
||||
### 问题 10: 磁盘空间不足
|
||||
|
||||
**症状**: 系统告警磁盘使用率过高
|
||||
|
||||
**诊断步骤**:
|
||||
```bash
|
||||
# 检查磁盘使用
|
||||
df -h
|
||||
|
||||
# 检查日志文件大小
|
||||
du -sh logs/
|
||||
|
||||
# 检查 Docker 空间使用
|
||||
docker system df
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 清理旧日志
|
||||
./docker/log-manager.sh cleanup 7
|
||||
|
||||
# 清理 Docker 资源
|
||||
docker system prune -f
|
||||
|
||||
# 压缩日志文件
|
||||
./docker/log-manager.sh compress
|
||||
|
||||
# 设置日志轮转
|
||||
./docker/log-manager.sh setup
|
||||
```
|
||||
|
||||
## 数据相关问题
|
||||
|
||||
### 问题 11: 数据丢失
|
||||
|
||||
**症状**: 数据库中的数据消失或损坏
|
||||
|
||||
**诊断步骤**:
|
||||
```bash
|
||||
# 检查数据卷
|
||||
docker volume ls | grep qaup
|
||||
|
||||
# 检查数据库表
|
||||
docker exec qaup-postgres psql -U postgres -d qaup -c "\dt"
|
||||
|
||||
# 检查备份文件
|
||||
ls -la backup/postgres/
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 从最近的备份恢复
|
||||
docker exec qaup-postgres /restore-db.sh /backup/qaup_backup_latest.sql.gz
|
||||
|
||||
# 如果没有备份,检查数据卷
|
||||
docker volume inspect qaup_postgres_data
|
||||
|
||||
# 重新初始化数据库(最后手段)
|
||||
docker-compose down
|
||||
docker volume rm qaup_postgres_data
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### 问题 12: 备份失败
|
||||
|
||||
**症状**: 数据库备份脚本执行失败
|
||||
|
||||
**诊断步骤**:
|
||||
```bash
|
||||
# 手动执行备份
|
||||
docker exec qaup-postgres /backup-db.sh
|
||||
|
||||
# 检查备份目录权限
|
||||
ls -la backup/postgres/
|
||||
|
||||
# 检查磁盘空间
|
||||
df -h backup/
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 修复目录权限
|
||||
sudo chown -R $USER:$USER backup/
|
||||
chmod 755 backup/postgres/
|
||||
|
||||
# 清理旧备份释放空间
|
||||
find backup/postgres/ -name "*.sql.gz" -mtime +30 -delete
|
||||
|
||||
# 重新执行备份
|
||||
docker exec qaup-postgres /backup-db.sh
|
||||
```
|
||||
|
||||
## 监控和告警问题
|
||||
|
||||
### 问题 13: 监控脚本无法运行
|
||||
|
||||
**症状**: 监控脚本报错或无输出
|
||||
|
||||
**诊断步骤**:
|
||||
```bash
|
||||
# 检查脚本权限
|
||||
ls -la docker/monitor.sh
|
||||
|
||||
# 手动运行脚本
|
||||
bash -x docker/monitor.sh status
|
||||
|
||||
# 检查依赖工具
|
||||
which curl nc
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 修复脚本权限
|
||||
chmod +x docker/monitor.sh
|
||||
|
||||
# 安装缺失的工具
|
||||
sudo apt install curl netcat-openbsd
|
||||
|
||||
# 重新运行监控
|
||||
./docker/monitor.sh status
|
||||
```
|
||||
|
||||
### 问题 14: 告警不工作
|
||||
|
||||
**症状**: 系统异常但没有收到告警
|
||||
|
||||
**诊断步骤**:
|
||||
```bash
|
||||
# 测试告警系统
|
||||
./docker/alert-manager.sh test
|
||||
|
||||
# 检查告警配置
|
||||
cat docker/healthcheck.yml
|
||||
|
||||
# 查看告警日志
|
||||
tail -f logs/alerts.log
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 重新配置告警
|
||||
nano docker/healthcheck.yml
|
||||
|
||||
# 启动持续监控
|
||||
./docker/alert-manager.sh monitor 60 &
|
||||
|
||||
# 检查告警历史
|
||||
./docker/alert-manager.sh history 50
|
||||
```
|
||||
|
||||
## 紧急恢复程序
|
||||
|
||||
### 完全系统故障
|
||||
|
||||
如果系统完全无法访问:
|
||||
|
||||
```bash
|
||||
# 1. 停止所有服务
|
||||
docker-compose down
|
||||
|
||||
# 2. 检查系统资源
|
||||
free -h
|
||||
df -h
|
||||
|
||||
# 3. 清理 Docker 资源
|
||||
docker system prune -f
|
||||
|
||||
# 4. 从备份恢复配置
|
||||
cp backup/config/config_backup_latest.tar.gz .
|
||||
tar -xzf config_backup_latest.tar.gz
|
||||
|
||||
# 5. 重新启动服务
|
||||
./deploy.sh start
|
||||
|
||||
# 6. 验证服务状态
|
||||
./deploy.sh status
|
||||
```
|
||||
|
||||
### 数据库完全损坏
|
||||
|
||||
```bash
|
||||
# 1. 停止应用服务
|
||||
docker stop qaup-app
|
||||
|
||||
# 2. 备份当前数据(如果可能)
|
||||
docker exec qaup-postgres pg_dumpall -U postgres > emergency_backup.sql
|
||||
|
||||
# 3. 删除损坏的数据卷
|
||||
docker-compose down
|
||||
docker volume rm qaup_postgres_data
|
||||
|
||||
# 4. 重新创建数据库
|
||||
docker-compose up -d qaup-postgres
|
||||
|
||||
# 5. 等待数据库启动
|
||||
sleep 60
|
||||
|
||||
# 6. 从备份恢复
|
||||
docker exec qaup-postgres /restore-db.sh /backup/qaup_backup_latest.sql.gz
|
||||
|
||||
# 7. 启动应用服务
|
||||
docker start qaup-app
|
||||
```
|
||||
|
||||
## 预防措施
|
||||
|
||||
### 定期维护
|
||||
|
||||
```bash
|
||||
# 创建维护脚本
|
||||
cat > maintenance.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
# 每日维护脚本
|
||||
|
||||
# 检查系统状态
|
||||
./docker/monitor.sh status
|
||||
|
||||
# 检查告警
|
||||
./docker/alert-manager.sh check
|
||||
|
||||
# 备份数据库
|
||||
docker exec qaup-postgres /backup-db.sh
|
||||
|
||||
# 清理旧日志
|
||||
./docker/log-manager.sh cleanup 30
|
||||
|
||||
# 生成监控报告
|
||||
./docker/monitor.sh report
|
||||
EOF
|
||||
|
||||
chmod +x maintenance.sh
|
||||
|
||||
# 设置定时任务
|
||||
echo "0 2 * * * /path/to/qaup/maintenance.sh" | crontab -
|
||||
```
|
||||
|
||||
### 监控设置
|
||||
|
||||
```bash
|
||||
# 启动持续监控
|
||||
./docker/alert-manager.sh monitor 300 &
|
||||
|
||||
# 设置日志轮转
|
||||
./docker/log-manager.sh setup
|
||||
|
||||
# 配置系统监控
|
||||
./docker/monitor.sh watch 60 &
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**注意**: 如果问题仍然无法解决,请收集相关日志和系统信息,联系技术支持团队。
|
||||
535
deploy/docs/离线部署指南.md
Normal file
535
deploy/docs/离线部署指南.md
Normal file
@ -0,0 +1,535 @@
|
||||
# QAUP 系统离线部署指南
|
||||
|
||||
## 概述
|
||||
|
||||
本文档详细介绍了如何在无互联网连接的内网环境中部署 QAUP 系统。离线部署包含所有必需的 Docker 镜像、配置文件和脚本,确保在完全隔离的网络环境中也能成功部署。
|
||||
|
||||
## 离线部署流程
|
||||
|
||||
### 阶段一:准备离线部署包(在有网络环境中进行)
|
||||
|
||||
#### 1. 环境要求
|
||||
|
||||
**准备环境**:
|
||||
- 有互联网连接的 Linux/macOS 系统
|
||||
- Docker 20.10+ 和 Docker Compose 1.29+
|
||||
- 至少 10GB 可用磁盘空间
|
||||
|
||||
#### 2. 获取项目代码
|
||||
|
||||
```bash
|
||||
# 克隆或下载项目代码
|
||||
git clone <项目地址>
|
||||
cd QAUP-Management
|
||||
|
||||
# 或者解压项目源码包
|
||||
tar -xzf qaup-source-code.tar.gz
|
||||
cd QAUP-Management
|
||||
```
|
||||
|
||||
#### 3. 准备 Docker 镜像
|
||||
|
||||
```bash
|
||||
# 运行镜像准备脚本
|
||||
./docker/prepare-offline-images.sh
|
||||
```
|
||||
|
||||
该脚本将执行以下操作:
|
||||
- 拉取所有必需的基础镜像
|
||||
- 构建自定义应用镜像
|
||||
- 导出所有镜像为 tar.gz 文件
|
||||
- 生成镜像清单和校验和
|
||||
|
||||
#### 4. 创建离线部署包
|
||||
|
||||
```bash
|
||||
# 创建离线部署包制作脚本
|
||||
cat > create-offline-package.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
PACKAGE_NAME="qaup-offline-deploy-$(date +%Y%m%d)"
|
||||
PACKAGE_DIR="./$PACKAGE_NAME"
|
||||
|
||||
echo "创建离线部署包: $PACKAGE_NAME"
|
||||
|
||||
# 创建包目录结构
|
||||
mkdir -p "$PACKAGE_DIR"/{images,config,scripts,docs}
|
||||
|
||||
# 复制 Docker 镜像
|
||||
echo "复制 Docker 镜像..."
|
||||
cp -r offline-deploy/images/* "$PACKAGE_DIR/images/"
|
||||
|
||||
# 复制配置文件
|
||||
echo "复制配置文件..."
|
||||
cp docker-compose*.yml "$PACKAGE_DIR/"
|
||||
cp .env.template "$PACKAGE_DIR/"
|
||||
cp -r config "$PACKAGE_DIR/"
|
||||
cp -r docker "$PACKAGE_DIR/"
|
||||
|
||||
# 复制脚本文件
|
||||
echo "复制脚本文件..."
|
||||
cp deploy.sh "$PACKAGE_DIR/"
|
||||
cp -r docker "$PACKAGE_DIR/"
|
||||
|
||||
# 复制文档
|
||||
echo "复制文档..."
|
||||
cp -r docs "$PACKAGE_DIR/"
|
||||
cp README.md "$PACKAGE_DIR/" 2>/dev/null || true
|
||||
|
||||
# 复制数据库架构
|
||||
echo "复制数据库文件..."
|
||||
mkdir -p "$PACKAGE_DIR/sql"
|
||||
cp sql/qaup_database_schema.sql "$PACKAGE_DIR/sql/"
|
||||
|
||||
# 创建安装脚本
|
||||
cat > "$PACKAGE_DIR/install.sh" << 'INSTALL_EOF'
|
||||
#!/bin/bash
|
||||
|
||||
# QAUP 离线安装脚本
|
||||
|
||||
set -e
|
||||
|
||||
echo "========================================="
|
||||
echo " QAUP 系统离线安装"
|
||||
echo "========================================="
|
||||
|
||||
# 检查 Docker
|
||||
if ! command -v docker &> /dev/null; then
|
||||
echo "错误: Docker 未安装,请先安装 Docker"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v docker-compose &> /dev/null; then
|
||||
echo "错误: Docker Compose 未安装,请先安装 Docker Compose"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 导入镜像
|
||||
echo "导入 Docker 镜像..."
|
||||
./docker/load-images.sh
|
||||
|
||||
# 初始化配置
|
||||
echo "初始化配置..."
|
||||
if [ ! -f .env ]; then
|
||||
cp .env.template .env
|
||||
echo "请编辑 .env 文件配置系统参数"
|
||||
fi
|
||||
|
||||
# 设置脚本权限
|
||||
echo "设置脚本权限..."
|
||||
find . -name "*.sh" -exec chmod +x {} \;
|
||||
|
||||
# 创建必要目录
|
||||
echo "创建数据目录..."
|
||||
mkdir -p data/{postgres,redis,uploads}
|
||||
mkdir -p logs/{postgres,redis,app,nginx}
|
||||
mkdir -p backup/postgres
|
||||
|
||||
echo ""
|
||||
echo "离线安装准备完成!"
|
||||
echo ""
|
||||
echo "下一步操作:"
|
||||
echo "1. 编辑 .env 文件配置系统参数"
|
||||
echo "2. 运行: ./deploy.sh install"
|
||||
echo ""
|
||||
|
||||
INSTALL_EOF
|
||||
|
||||
chmod +x "$PACKAGE_DIR/install.sh"
|
||||
|
||||
# 创建 README
|
||||
cat > "$PACKAGE_DIR/README.md" << 'README_EOF'
|
||||
# QAUP 系统离线部署包
|
||||
|
||||
## 快速开始
|
||||
|
||||
1. 解压部署包到目标服务器
|
||||
2. 运行安装脚本: `./install.sh`
|
||||
3. 编辑配置文件: `nano .env`
|
||||
4. 启动系统: `./deploy.sh install`
|
||||
|
||||
## 文档
|
||||
|
||||
- 部署指南: `docs/部署指南.md`
|
||||
- 离线部署指南: `docs/离线部署指南.md`
|
||||
- 故障排除: `docs/故障排除.md`
|
||||
|
||||
## 支持
|
||||
|
||||
如有问题,请参考故障排除文档或联系技术支持。
|
||||
README_EOF
|
||||
|
||||
# 创建版本信息
|
||||
cat > "$PACKAGE_DIR/VERSION" << 'VERSION_EOF'
|
||||
QAUP 系统离线部署包
|
||||
版本: 1.0.1
|
||||
构建时间: $(date)
|
||||
包含组件:
|
||||
- QAUP 应用 (Spring Boot)
|
||||
- PostgreSQL 数据库 (PostGIS)
|
||||
- Redis 缓存
|
||||
- Nginx 反向代理
|
||||
VERSION_EOF
|
||||
|
||||
# 打包
|
||||
echo "打包部署包..."
|
||||
tar -czf "${PACKAGE_NAME}.tar.gz" "$PACKAGE_DIR"
|
||||
|
||||
# 生成校验和
|
||||
echo "生成校验和..."
|
||||
sha256sum "${PACKAGE_NAME}.tar.gz" > "${PACKAGE_NAME}.tar.gz.sha256"
|
||||
|
||||
# 清理临时目录
|
||||
rm -rf "$PACKAGE_DIR"
|
||||
|
||||
echo ""
|
||||
echo "离线部署包创建完成:"
|
||||
echo " 文件: ${PACKAGE_NAME}.tar.gz"
|
||||
echo " 大小: $(du -sh ${PACKAGE_NAME}.tar.gz | cut -f1)"
|
||||
echo " 校验: ${PACKAGE_NAME}.tar.gz.sha256"
|
||||
echo ""
|
||||
|
||||
EOF
|
||||
|
||||
chmod +x create-offline-package.sh
|
||||
|
||||
# 运行打包脚本
|
||||
./create-offline-package.sh
|
||||
```
|
||||
|
||||
### 阶段二:离线部署(在客户内网环境中进行)
|
||||
|
||||
#### 1. 传输部署包
|
||||
|
||||
将离线部署包传输到目标服务器:
|
||||
|
||||
```bash
|
||||
# 通过 U盘、移动硬盘等方式传输文件
|
||||
# 或通过内网文件传输
|
||||
scp qaup-offline-deploy-*.tar.gz user@target-server:/opt/
|
||||
```
|
||||
|
||||
#### 2. 解压和验证
|
||||
|
||||
```bash
|
||||
# 进入目标目录
|
||||
cd /opt
|
||||
|
||||
# 验证文件完整性
|
||||
sha256sum -c qaup-offline-deploy-*.tar.gz.sha256
|
||||
|
||||
# 解压部署包
|
||||
tar -xzf qaup-offline-deploy-*.tar.gz
|
||||
cd qaup-offline-deploy-*
|
||||
```
|
||||
|
||||
#### 3. 运行离线安装
|
||||
|
||||
```bash
|
||||
# 运行安装脚本
|
||||
./install.sh
|
||||
```
|
||||
|
||||
安装脚本将:
|
||||
- 检查 Docker 环境
|
||||
- 导入所有 Docker 镜像
|
||||
- 初始化配置文件
|
||||
- 设置脚本权限
|
||||
- 创建必要目录
|
||||
|
||||
#### 4. 配置系统
|
||||
|
||||
```bash
|
||||
# 编辑环境配置
|
||||
nano .env
|
||||
```
|
||||
|
||||
**重要配置项**:
|
||||
```bash
|
||||
# 数据库配置
|
||||
POSTGRES_PASSWORD=设置强密码
|
||||
APP_DB_PASSWORD=设置强密码
|
||||
|
||||
# 服务器配置
|
||||
SERVER_NAME=内网IP或域名
|
||||
NGINX_PORT=80
|
||||
|
||||
# 其他配置根据实际环境调整
|
||||
```
|
||||
|
||||
#### 5. 启动系统
|
||||
|
||||
```bash
|
||||
# 一键部署
|
||||
./deploy.sh install
|
||||
```
|
||||
|
||||
## 离线环境特殊配置
|
||||
|
||||
### 1. 时间同步
|
||||
|
||||
内网环境可能无法自动同步时间:
|
||||
|
||||
```bash
|
||||
# 手动设置系统时间
|
||||
sudo date -s "2024-01-01 12:00:00"
|
||||
|
||||
# 或配置内网 NTP 服务器
|
||||
sudo nano /etc/systemd/timesyncd.conf
|
||||
# 添加: NTP=内网NTP服务器IP
|
||||
```
|
||||
|
||||
### 2. DNS 配置
|
||||
|
||||
```bash
|
||||
# 配置内网 DNS
|
||||
sudo nano /etc/resolv.conf
|
||||
# 添加: nameserver 内网DNS服务器IP
|
||||
|
||||
# 或在 hosts 文件中添加必要的域名解析
|
||||
sudo nano /etc/hosts
|
||||
```
|
||||
|
||||
### 3. 防火墙配置
|
||||
|
||||
```bash
|
||||
# 配置防火墙规则
|
||||
sudo ufw allow from 内网网段 to any port 80
|
||||
sudo ufw allow from 内网网段 to any port 443
|
||||
sudo ufw enable
|
||||
```
|
||||
|
||||
## 离线更新
|
||||
|
||||
### 1. 准备更新包
|
||||
|
||||
在有网络的环境中:
|
||||
|
||||
```bash
|
||||
# 构建新版本镜像
|
||||
./docker/prepare-offline-images.sh
|
||||
|
||||
# 创建更新包
|
||||
tar -czf qaup-update-$(date +%Y%m%d).tar.gz \
|
||||
offline-deploy/images/ \
|
||||
docker-compose*.yml \
|
||||
config/ \
|
||||
docker/
|
||||
```
|
||||
|
||||
### 2. 应用更新
|
||||
|
||||
在离线环境中:
|
||||
|
||||
```bash
|
||||
# 备份当前配置
|
||||
./docker/config-manager.sh backup
|
||||
|
||||
# 停止服务
|
||||
./deploy.sh stop
|
||||
|
||||
# 解压更新包
|
||||
tar -xzf qaup-update-*.tar.gz
|
||||
|
||||
# 导入新镜像
|
||||
./docker/load-images.sh
|
||||
|
||||
# 启动服务
|
||||
./deploy.sh start
|
||||
```
|
||||
|
||||
## 离线监控
|
||||
|
||||
### 1. 本地监控
|
||||
|
||||
```bash
|
||||
# 启动本地监控
|
||||
./docker/monitor.sh watch 60
|
||||
|
||||
# 设置告警监控
|
||||
./docker/alert-manager.sh monitor 300 &
|
||||
```
|
||||
|
||||
### 2. 日志管理
|
||||
|
||||
```bash
|
||||
# 配置日志轮转
|
||||
./docker/log-manager.sh setup
|
||||
|
||||
# 定期清理日志
|
||||
echo "0 2 * * * /opt/qaup/docker/log-manager.sh cleanup 30" | crontab -
|
||||
```
|
||||
|
||||
## 离线备份
|
||||
|
||||
### 1. 数据备份
|
||||
|
||||
```bash
|
||||
# 创建备份脚本
|
||||
cat > backup-offline.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
|
||||
BACKUP_DIR="/backup/qaup/$(date +%Y%m%d)"
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# 备份数据库
|
||||
docker exec qaup-postgres pg_dump -U postgres qaup | gzip > "$BACKUP_DIR/database.sql.gz"
|
||||
|
||||
# 备份配置文件
|
||||
tar -czf "$BACKUP_DIR/config.tar.gz" .env config/ docker/
|
||||
|
||||
# 备份上传文件
|
||||
tar -czf "$BACKUP_DIR/uploads.tar.gz" data/uploads/
|
||||
|
||||
echo "备份完成: $BACKUP_DIR"
|
||||
EOF
|
||||
|
||||
chmod +x backup-offline.sh
|
||||
|
||||
# 设置定时备份
|
||||
echo "0 1 * * * /opt/qaup/backup-offline.sh" | crontab -
|
||||
```
|
||||
|
||||
### 2. 系统备份
|
||||
|
||||
```bash
|
||||
# 完整系统备份
|
||||
tar -czf qaup-system-backup-$(date +%Y%m%d).tar.gz \
|
||||
--exclude='data/postgres' \
|
||||
--exclude='logs' \
|
||||
.
|
||||
```
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 1. 镜像导入失败
|
||||
|
||||
```bash
|
||||
# 检查镜像文件
|
||||
ls -la images/
|
||||
|
||||
# 验证镜像文件完整性
|
||||
cd images && sha256sum -c checksums.sha256
|
||||
|
||||
# 手动导入单个镜像
|
||||
gunzip -c qaup-app.tar.gz | docker load
|
||||
```
|
||||
|
||||
### 2. 服务无法启动
|
||||
|
||||
```bash
|
||||
# 检查 Docker 服务
|
||||
sudo systemctl status docker
|
||||
|
||||
# 检查容器日志
|
||||
docker logs qaup-app
|
||||
docker logs qaup-postgres
|
||||
|
||||
# 检查网络连接
|
||||
docker network ls
|
||||
```
|
||||
|
||||
### 3. 配置问题
|
||||
|
||||
```bash
|
||||
# 验证配置文件
|
||||
./docker/config-manager.sh validate
|
||||
|
||||
# 重新初始化配置
|
||||
./docker/config-manager.sh init
|
||||
```
|
||||
|
||||
## 安全考虑
|
||||
|
||||
### 1. 网络隔离
|
||||
|
||||
```bash
|
||||
# 确保容器网络隔离
|
||||
docker network inspect qaup_qaup-network
|
||||
|
||||
# 配置防火墙规则
|
||||
sudo ufw deny out 53
|
||||
sudo ufw deny out 80
|
||||
sudo ufw deny out 443
|
||||
```
|
||||
|
||||
### 2. 访问控制
|
||||
|
||||
```bash
|
||||
# 设置文件权限
|
||||
chmod 600 .env
|
||||
chmod 700 data/
|
||||
chmod 755 logs/
|
||||
|
||||
# 限制用户访问
|
||||
sudo chown -R qaup:qaup /opt/qaup
|
||||
```
|
||||
|
||||
### 3. 审计日志
|
||||
|
||||
```bash
|
||||
# 启用系统审计
|
||||
sudo apt install auditd
|
||||
sudo systemctl enable auditd
|
||||
|
||||
# 配置审计规则
|
||||
echo "-w /opt/qaup -p wa -k qaup-access" | sudo tee -a /etc/audit/rules.d/qaup.rules
|
||||
```
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 1. 内网优化
|
||||
|
||||
```bash
|
||||
# 调整网络参数
|
||||
echo 'net.core.rmem_max = 16777216' | sudo tee -a /etc/sysctl.conf
|
||||
echo 'net.core.wmem_max = 16777216' | sudo tee -a /etc/sysctl.conf
|
||||
sudo sysctl -p
|
||||
```
|
||||
|
||||
### 2. 存储优化
|
||||
|
||||
```bash
|
||||
# 使用本地存储
|
||||
# 修改 docker-compose.yml 中的卷配置
|
||||
# 使用高性能磁盘存储数据库文件
|
||||
```
|
||||
|
||||
## 维护计划
|
||||
|
||||
### 日常维护
|
||||
|
||||
```bash
|
||||
# 创建维护脚本
|
||||
cat > daily-maintenance.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
|
||||
# 检查系统状态
|
||||
./docker/monitor.sh status
|
||||
|
||||
# 检查磁盘空间
|
||||
df -h
|
||||
|
||||
# 清理临时文件
|
||||
docker system prune -f
|
||||
|
||||
# 备份数据
|
||||
./backup-offline.sh
|
||||
EOF
|
||||
|
||||
chmod +x daily-maintenance.sh
|
||||
echo "0 6 * * * /opt/qaup/daily-maintenance.sh" | crontab -
|
||||
```
|
||||
|
||||
### 定期检查
|
||||
|
||||
- 每日:系统状态检查、日志查看
|
||||
- 每周:性能监控、备份验证
|
||||
- 每月:安全检查、配置审核
|
||||
- 每季度:系统更新、容量规划
|
||||
|
||||
---
|
||||
|
||||
**注意**: 离线环境部署需要特别注意安全和稳定性,建议在部署前进行充分测试。
|
||||
428
deploy/docs/部署指南.md
Normal file
428
deploy/docs/部署指南.md
Normal file
@ -0,0 +1,428 @@
|
||||
# QAUP 系统 Docker 部署指南
|
||||
|
||||
## 概述
|
||||
|
||||
本文档详细介绍了如何在 Ubuntu 系统上使用 Docker 部署 QAUP 管理系统。该部署方案支持生产环境使用,包含完整的监控、日志管理和备份功能。
|
||||
|
||||
## 系统要求
|
||||
|
||||
### 硬件要求
|
||||
- **CPU**: 最少 2 核,推荐 4 核或以上
|
||||
- **内存**: 最少 4GB,推荐 8GB 或以上
|
||||
- **磁盘**: 最少 20GB 可用空间,推荐 50GB 或以上
|
||||
- **网络**: 稳定的网络连接(内网部署无需外网)
|
||||
|
||||
### 软件要求
|
||||
- **操作系统**: Ubuntu 18.04 LTS 或更高版本
|
||||
- **Docker**: 20.10 或更高版本
|
||||
- **Docker Compose**: 1.29 或更高版本
|
||||
- **其他工具**: curl, wget, unzip
|
||||
|
||||
## 安装 Docker 和 Docker Compose
|
||||
|
||||
### 安装 Docker
|
||||
|
||||
```bash
|
||||
# 更新包索引
|
||||
sudo apt update
|
||||
|
||||
# 安装必要的包
|
||||
sudo apt install apt-transport-https ca-certificates curl gnupg lsb-release
|
||||
|
||||
# 添加 Docker 官方 GPG 密钥
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
||||
|
||||
# 设置稳定版仓库
|
||||
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
|
||||
# 安装 Docker Engine
|
||||
sudo apt update
|
||||
sudo apt install docker-ce docker-ce-cli containerd.io
|
||||
|
||||
# 将当前用户添加到 docker 组
|
||||
sudo usermod -aG docker $USER
|
||||
|
||||
# 重新登录或运行以下命令
|
||||
newgrp docker
|
||||
```
|
||||
|
||||
### 安装 Docker Compose
|
||||
|
||||
```bash
|
||||
# 下载 Docker Compose
|
||||
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||
|
||||
# 添加执行权限
|
||||
sudo chmod +x /usr/local/bin/docker-compose
|
||||
|
||||
# 验证安装
|
||||
docker-compose --version
|
||||
```
|
||||
|
||||
## 部署步骤
|
||||
|
||||
### 1. 获取部署包
|
||||
|
||||
#### 在线部署
|
||||
```bash
|
||||
# 克隆项目(如果有 Git 访问权限)
|
||||
git clone <项目地址>
|
||||
cd QAUP-Management
|
||||
```
|
||||
|
||||
#### 离线部署
|
||||
```bash
|
||||
# 解压离线部署包
|
||||
tar -xzf qaup-offline-deploy.tar.gz
|
||||
cd QAUP-Management
|
||||
|
||||
# 导入 Docker 镜像
|
||||
./docker/load-images.sh
|
||||
```
|
||||
|
||||
### 2. 环境验证
|
||||
|
||||
```bash
|
||||
# 运行环境验证脚本
|
||||
./docker/validate-environment.sh
|
||||
```
|
||||
|
||||
如果验证失败,请根据提示修复问题。
|
||||
|
||||
### 3. 配置系统
|
||||
|
||||
```bash
|
||||
# 初始化配置
|
||||
./docker/config-manager.sh init
|
||||
|
||||
# 编辑环境配置文件
|
||||
nano .env
|
||||
```
|
||||
|
||||
**重要配置项说明:**
|
||||
|
||||
```bash
|
||||
# 数据库配置
|
||||
POSTGRES_DB=qaup
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=请设置强密码
|
||||
APP_DB_USER=qaup_app
|
||||
APP_DB_PASSWORD=请设置强密码
|
||||
|
||||
# Redis 配置
|
||||
REDIS_PASSWORD=请设置密码或留空
|
||||
|
||||
# 应用配置
|
||||
SPRING_PROFILES_ACTIVE=prod
|
||||
APP_PORT=8080
|
||||
|
||||
# Nginx 配置
|
||||
NGINX_PORT=80
|
||||
SERVER_NAME=your-domain.com # 替换为实际域名或IP
|
||||
API_PREFIX=/prod-api
|
||||
|
||||
# 目录配置
|
||||
DATA_DIR=./data
|
||||
LOG_DIR=./logs
|
||||
BACKUP_DIR=./backup
|
||||
```
|
||||
|
||||
### 4. 一键部署
|
||||
|
||||
```bash
|
||||
# 运行一键部署脚本
|
||||
./deploy.sh install
|
||||
```
|
||||
|
||||
部署脚本将自动执行以下步骤:
|
||||
1. 检查系统要求
|
||||
2. 初始化配置
|
||||
3. 验证环境
|
||||
4. 构建 Docker 镜像
|
||||
5. 启动服务
|
||||
6. 等待服务就绪
|
||||
7. 运行健康检查
|
||||
|
||||
### 5. 验证部署
|
||||
|
||||
部署完成后,访问以下地址验证:
|
||||
|
||||
- **前端界面**: http://your-server-ip
|
||||
- **API 健康检查**: http://your-server-ip/health
|
||||
- **应用健康检查**: http://your-server-ip/prod-api/actuator/health
|
||||
|
||||
## 服务管理
|
||||
|
||||
### 基本操作
|
||||
|
||||
```bash
|
||||
# 查看服务状态
|
||||
./deploy.sh status
|
||||
|
||||
# 启动服务
|
||||
./deploy.sh start
|
||||
|
||||
# 停止服务
|
||||
./deploy.sh stop
|
||||
|
||||
# 重启服务
|
||||
./deploy.sh restart
|
||||
|
||||
# 查看日志
|
||||
./deploy.sh logs all # 所有服务日志
|
||||
./deploy.sh logs app # 应用日志
|
||||
./deploy.sh logs nginx # Nginx 日志
|
||||
```
|
||||
|
||||
### 高级管理
|
||||
|
||||
```bash
|
||||
# 使用 Docker Compose 管理器
|
||||
./docker/docker-compose-manager.sh prod start
|
||||
./docker/docker-compose-manager.sh prod stop
|
||||
./docker/docker-compose-manager.sh prod restart
|
||||
./docker/docker-compose-manager.sh prod status
|
||||
|
||||
# 日志管理
|
||||
./docker/log-manager.sh view app 100 # 查看应用最近100行日志
|
||||
./docker/log-manager.sh monitor all # 实时监控所有日志
|
||||
./docker/log-manager.sh analyze # 分析错误日志
|
||||
./docker/log-manager.sh cleanup 30 # 清理30天前的日志
|
||||
|
||||
# 系统监控
|
||||
./docker/monitor.sh status # 检查系统状态
|
||||
./docker/monitor.sh watch 60 # 每60秒监控一次
|
||||
./docker/monitor.sh resources # 查看资源使用
|
||||
```
|
||||
|
||||
## 监控和告警
|
||||
|
||||
### 系统监控
|
||||
|
||||
```bash
|
||||
# 实时监控
|
||||
./docker/monitor.sh watch
|
||||
|
||||
# 生成监控报告
|
||||
./docker/monitor.sh report
|
||||
|
||||
# 检查告警
|
||||
./docker/alert-manager.sh check
|
||||
|
||||
# 持续告警监控
|
||||
./docker/alert-manager.sh monitor 300 # 每5分钟检查一次
|
||||
```
|
||||
|
||||
### 健康检查
|
||||
|
||||
系统提供多层次的健康检查:
|
||||
|
||||
1. **容器级健康检查**: Docker 内置健康检查
|
||||
2. **服务级健康检查**: 应用程序健康端点
|
||||
3. **系统级健康检查**: 资源使用监控
|
||||
|
||||
## 数据备份和恢复
|
||||
|
||||
### 数据库备份
|
||||
|
||||
```bash
|
||||
# 手动备份
|
||||
docker exec qaup-postgres /backup-db.sh
|
||||
|
||||
# 查看备份文件
|
||||
ls -la ./backup/postgres/
|
||||
|
||||
# 恢复数据库
|
||||
docker exec qaup-postgres /restore-db.sh /backup/qaup_backup_20241231_120000.sql.gz
|
||||
```
|
||||
|
||||
### 配置备份
|
||||
|
||||
```bash
|
||||
# 备份配置文件
|
||||
./docker/config-manager.sh backup
|
||||
|
||||
# 查看备份
|
||||
ls -la ./backup/config/
|
||||
```
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
#### 1. 容器启动失败
|
||||
|
||||
```bash
|
||||
# 查看容器状态
|
||||
docker ps -a
|
||||
|
||||
# 查看容器日志
|
||||
docker logs qaup-app
|
||||
docker logs qaup-postgres
|
||||
docker logs qaup-redis
|
||||
docker logs qaup-nginx
|
||||
|
||||
# 检查资源使用
|
||||
docker stats
|
||||
```
|
||||
|
||||
#### 2. 数据库连接失败
|
||||
|
||||
```bash
|
||||
# 检查数据库容器状态
|
||||
docker exec qaup-postgres pg_isready -h localhost -p 5432 -U postgres
|
||||
|
||||
# 检查数据库日志
|
||||
docker logs qaup-postgres
|
||||
|
||||
# 测试数据库连接
|
||||
docker exec qaup-postgres psql -U postgres -d qaup -c "SELECT version();"
|
||||
```
|
||||
|
||||
#### 3. 前端无法访问
|
||||
|
||||
```bash
|
||||
# 检查 Nginx 状态
|
||||
curl -I http://localhost/health
|
||||
|
||||
# 检查 Nginx 配置
|
||||
docker exec qaup-nginx nginx -t
|
||||
|
||||
# 查看 Nginx 日志
|
||||
docker logs qaup-nginx
|
||||
```
|
||||
|
||||
#### 4. 应用响应慢
|
||||
|
||||
```bash
|
||||
# 检查应用健康状态
|
||||
curl http://localhost:8080/actuator/health
|
||||
|
||||
# 查看应用日志
|
||||
docker logs qaup-app | grep -i "error\|exception"
|
||||
|
||||
# 检查资源使用
|
||||
docker stats qaup-app
|
||||
```
|
||||
|
||||
### 日志分析
|
||||
|
||||
```bash
|
||||
# 分析错误日志
|
||||
./docker/log-manager.sh analyze
|
||||
|
||||
# 查看特定时间段的日志
|
||||
docker logs qaup-app --since "2024-01-01T00:00:00" --until "2024-01-01T23:59:59"
|
||||
|
||||
# 搜索特定错误
|
||||
docker logs qaup-app | grep -i "OutOfMemoryError"
|
||||
```
|
||||
|
||||
## 性能优化
|
||||
|
||||
### JVM 调优
|
||||
|
||||
编辑 `.env` 文件中的 JVM 参数:
|
||||
|
||||
```bash
|
||||
# 生产环境 JVM 配置
|
||||
JVM_OPTS=-Dname=qaup-admin.jar -Duser.timezone=Asia/Shanghai -server -Xms2g -Xmx4g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -XX:+UseG1GC -XX:G1HeapRegionSize=16m
|
||||
```
|
||||
|
||||
### 数据库优化
|
||||
|
||||
编辑 `docker/postgres/postgresql.conf`:
|
||||
|
||||
```bash
|
||||
# 根据服务器配置调整
|
||||
shared_buffers = 512MB # 25% of RAM
|
||||
effective_cache_size = 2GB # 75% of RAM
|
||||
work_mem = 8MB
|
||||
maintenance_work_mem = 128MB
|
||||
```
|
||||
|
||||
### Nginx 优化
|
||||
|
||||
编辑 `docker/nginx/nginx.conf`:
|
||||
|
||||
```bash
|
||||
worker_processes auto;
|
||||
worker_connections 2048;
|
||||
keepalive_timeout 65;
|
||||
client_max_body_size 50M;
|
||||
```
|
||||
|
||||
## 安全配置
|
||||
|
||||
### 防火墙设置
|
||||
|
||||
```bash
|
||||
# 只开放必要端口
|
||||
sudo ufw allow 22/tcp # SSH
|
||||
sudo ufw allow 80/tcp # HTTP
|
||||
sudo ufw allow 443/tcp # HTTPS
|
||||
sudo ufw enable
|
||||
```
|
||||
|
||||
### SSL/TLS 配置
|
||||
|
||||
1. 获取 SSL 证书(Let's Encrypt 或商业证书)
|
||||
2. 将证书文件放置在 `./ssl/` 目录
|
||||
3. 更新 Nginx 配置启用 HTTPS
|
||||
|
||||
### 密码安全
|
||||
|
||||
```bash
|
||||
# 生成强密码
|
||||
./docker/secrets-manager.sh generate
|
||||
|
||||
# 检查密码强度
|
||||
./docker/secrets-manager.sh check
|
||||
```
|
||||
|
||||
## 更新和维护
|
||||
|
||||
### 系统更新
|
||||
|
||||
```bash
|
||||
# 备份当前部署
|
||||
./docker/config-manager.sh backup
|
||||
|
||||
# 更新部署
|
||||
./deploy.sh update
|
||||
|
||||
# 验证更新
|
||||
./deploy.sh status
|
||||
```
|
||||
|
||||
### 定期维护
|
||||
|
||||
建议定期执行以下维护任务:
|
||||
|
||||
```bash
|
||||
# 每日
|
||||
./docker/alert-manager.sh check
|
||||
./docker/monitor.sh status
|
||||
|
||||
# 每周
|
||||
./docker/log-manager.sh cleanup 30
|
||||
./docker/log-manager.sh compress
|
||||
|
||||
# 每月
|
||||
docker system prune -f
|
||||
./docker/config-manager.sh backup
|
||||
```
|
||||
|
||||
## 联系支持
|
||||
|
||||
如果遇到无法解决的问题,请提供以下信息:
|
||||
|
||||
1. 系统信息:`uname -a`
|
||||
2. Docker 版本:`docker --version`
|
||||
3. 容器状态:`docker ps -a`
|
||||
4. 错误日志:相关服务的日志文件
|
||||
5. 配置文件:`.env` 文件(隐藏敏感信息)
|
||||
|
||||
---
|
||||
|
||||
**注意**: 本文档会根据系统更新持续维护,请定期查看最新版本。
|
||||
653
doc/design/universal_autonomous_vehicle_api.md
Normal file
653
doc/design/universal_autonomous_vehicle_api.md
Normal file
@ -0,0 +1,653 @@
|
||||
# 通用无人车运行状态API设计方案
|
||||
|
||||
## 1. 设计原则
|
||||
|
||||
### 1.1 行业标准参考
|
||||
- **ISO 26262**: 汽车功能安全标准
|
||||
- **SAE J3016**: 自动驾驶分级标准
|
||||
- **ISO 21448**: 预期功能安全标准(SOTIF)
|
||||
- **IEEE 2857**: 自动驾驶系统隐私工程标准
|
||||
|
||||
### 1.2 API设计原则
|
||||
- **RESTful设计**: 遵循REST架构风格
|
||||
- **版本控制**: 支持API版本管理
|
||||
- **统一响应格式**: 标准化的响应结构
|
||||
- **错误处理**: 完善的错误码和错误信息
|
||||
- **安全性**: 认证授权机制
|
||||
- **可扩展性**: 支持未来功能扩展
|
||||
|
||||
## 2. API接口设计
|
||||
|
||||
### 2.1 基础信息
|
||||
|
||||
**接口地址**: `GET /api/v1/vehicles/{vehicleId}/status`
|
||||
|
||||
**认证方式**: Bearer Token (JWT)
|
||||
|
||||
**内容类型**: `application/json`
|
||||
|
||||
### 2.2 请求参数
|
||||
|
||||
#### 路径参数
|
||||
| 参数名 | 类型 | 必填 | 描述 |
|
||||
|--------|------|------|------|
|
||||
| vehicleId | string | 是 | 车辆唯一标识符 |
|
||||
|
||||
#### 查询参数
|
||||
| 参数名 | 类型 | 必填 | 描述 | 默认值 |
|
||||
|--------|------|------|------|-------|
|
||||
| fields | string | 否 | 指定返回字段,逗号分隔 | 全部字段 |
|
||||
| format | string | 否 | 响应格式 (json/xml) | json |
|
||||
|
||||
### 2.3 请求示例
|
||||
|
||||
#### 基本请求
|
||||
```bash
|
||||
GET /api/v1/vehicles/AV-001/status
|
||||
Host: api.example.com
|
||||
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
#### 指定字段请求
|
||||
```bash
|
||||
GET /api/v1/vehicles/AV-001/status?fields=vehicleInfo,operationalStatus,motionStatus
|
||||
Host: api.example.com
|
||||
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
#### cURL 请求示例
|
||||
```bash
|
||||
# 获取完整状态信息
|
||||
curl -X GET "https://api.example.com/api/v1/vehicles/AV-001/status" \
|
||||
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
|
||||
-H "Content-Type: application/json"
|
||||
|
||||
# 只获取核心状态信息
|
||||
curl -X GET "https://api.example.com/api/v1/vehicles/AV-001/status?fields=vehicleInfo,operationalStatus,controlStatus,motionStatus" \
|
||||
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
|
||||
-H "Content-Type: application/json"
|
||||
|
||||
# 获取电池和诊断信息
|
||||
curl -X GET "https://api.example.com/api/v1/vehicles/AV-001/status?fields=vehicleInfo,batteryStatus,diagnostics" \
|
||||
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
|
||||
-H "Content-Type: application/json"
|
||||
```
|
||||
|
||||
### 2.4 响应数据结构
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"timestamp": 1736175610000,
|
||||
"data": {
|
||||
"vehicleInfo": {
|
||||
"vehicleId": "AV-001",
|
||||
"vehicleType": "GROUND_SUPPORT_EQUIPMENT",
|
||||
"manufacturer": "厂商名称",
|
||||
"model": "车型",
|
||||
"serialNumber": "序列号",
|
||||
"firmwareVersion": "1.2.3"
|
||||
},
|
||||
"operationalStatus": {
|
||||
"powerStatus": "ON",
|
||||
"systemHealth": "HEALTHY",
|
||||
"operationalMode": "AUTONOMOUS",
|
||||
"missionStatus": "IN_PROGRESS",
|
||||
"emergencyStatus": "NORMAL",
|
||||
"lastHeartbeat": 1736175610000
|
||||
},
|
||||
"controlStatus": {
|
||||
"controlMode": "AUTONOMOUS",
|
||||
"controlAuthority": "SYSTEM",
|
||||
"remoteControlActive": false,
|
||||
"manualOverrideActive": false,
|
||||
"safetyDriverPresent": false
|
||||
},
|
||||
"motionStatus": {
|
||||
"position": {
|
||||
"latitude": 36.354068,
|
||||
"longitude": 120.083410,
|
||||
"altitude": 45.2,
|
||||
"coordinateSystem": "WGS84"
|
||||
},
|
||||
"velocity": {
|
||||
"speed": 3.2,
|
||||
"direction": 1.57,
|
||||
"verticalSpeed": 0.0,
|
||||
"speedUnit": "m/s",
|
||||
"directionUnit": "radians"
|
||||
},
|
||||
"acceleration": {
|
||||
"longitudinal": 0.5,
|
||||
"lateral": 0.1,
|
||||
"vertical": 0.0,
|
||||
"unit": "m/s²"
|
||||
}
|
||||
},
|
||||
"vehicleState": {
|
||||
"engineStatus": "RUNNING",
|
||||
"transmissionState": "DRIVE",
|
||||
"brakingSystem": "NORMAL",
|
||||
"steeringSystem": "NORMAL",
|
||||
"fuelLevel": 75.0
|
||||
},
|
||||
"batteryStatus": {
|
||||
"mainBattery": {
|
||||
"chargeLevel": 85.5,
|
||||
"voltage": 48.2,
|
||||
"current": -15.3,
|
||||
"temperature": 35.2,
|
||||
"health": "GOOD",
|
||||
"cycleCount": 1250,
|
||||
"capacity": {
|
||||
"current": 95.2,
|
||||
"design": 100.0,
|
||||
"unit": "kWh"
|
||||
},
|
||||
"chargingStatus": "DISCHARGING",
|
||||
"estimatedRange": 120.5,
|
||||
"timeToEmpty": 480,
|
||||
"timeToFull": null,
|
||||
"cellVoltages": [3.85, 3.87, 3.86, 3.88],
|
||||
"balancingActive": false
|
||||
},
|
||||
"auxiliaryBattery": {
|
||||
"chargeLevel": 92.0,
|
||||
"voltage": 12.6,
|
||||
"current": -2.1,
|
||||
"temperature": 28.5,
|
||||
"health": "GOOD",
|
||||
"chargingStatus": "DISCHARGING"
|
||||
},
|
||||
"backupBattery": {
|
||||
"chargeLevel": 100.0,
|
||||
"voltage": 24.0,
|
||||
"health": "GOOD",
|
||||
"lastMaintenance": 1735571200000
|
||||
}
|
||||
},
|
||||
"safetyStatus": {
|
||||
"collisionAvoidanceActive": true,
|
||||
"emergencyBrakingReady": true,
|
||||
"pathPlanningStatus": "ACTIVE",
|
||||
"obstacleDetectionStatus": "ACTIVE",
|
||||
"minimumRiskManeuverTriggered": false
|
||||
},
|
||||
"autonomyLevel": {
|
||||
"currentLevel": 4,
|
||||
"availableLevels": [2, 3, 4],
|
||||
"fallbackLevel": 2
|
||||
},
|
||||
"sensorStatus": {
|
||||
"cameras": [
|
||||
{
|
||||
"sensorId": "CAM_FRONT",
|
||||
"status": "ACTIVE",
|
||||
"health": "GOOD",
|
||||
"lastUpdate": 1736175610000
|
||||
}
|
||||
],
|
||||
"lidars": [
|
||||
{
|
||||
"sensorId": "LIDAR_360",
|
||||
"status": "ACTIVE",
|
||||
"health": "GOOD",
|
||||
"lastUpdate": 1736175610000
|
||||
}
|
||||
],
|
||||
"radars": [
|
||||
{
|
||||
"sensorId": "RADAR_FRONT",
|
||||
"status": "ACTIVE",
|
||||
"health": "GOOD",
|
||||
"lastUpdate": 1736175610000
|
||||
}
|
||||
],
|
||||
"gps": {
|
||||
"status": "ACTIVE",
|
||||
"accuracy": 0.5,
|
||||
"satelliteCount": 12,
|
||||
"lastUpdate": 1736175610000
|
||||
},
|
||||
"imu": {
|
||||
"status": "ACTIVE",
|
||||
"health": "GOOD",
|
||||
"lastUpdate": 1736175610000
|
||||
}
|
||||
},
|
||||
"communicationStatus": {
|
||||
"v2xStatus": "CONNECTED",
|
||||
"cellularSignalStrength": -65,
|
||||
"wifiStatus": "CONNECTED",
|
||||
"cloudConnectivity": "ONLINE"
|
||||
},
|
||||
"diagnostics": {
|
||||
"faultCodes": [
|
||||
{
|
||||
"code": "P0001",
|
||||
"severity": "CRITICAL",
|
||||
"description": "燃油系统压力过低",
|
||||
"component": "燃油泵",
|
||||
"timestamp": 1736175610000,
|
||||
"status": "ACTIVE"
|
||||
}
|
||||
],
|
||||
"warnings": [
|
||||
{
|
||||
"severity": "WARNING",
|
||||
"description": "电池温度偏高",
|
||||
"component": "主电池组",
|
||||
"timestamp": 1736175610000
|
||||
}
|
||||
],
|
||||
"maintenanceAlerts": [
|
||||
{
|
||||
"type": "SCHEDULED_MAINTENANCE",
|
||||
"description": "定期保养到期",
|
||||
"dueDate": 1736262000000,
|
||||
"priority": "HIGH"
|
||||
}
|
||||
]
|
||||
},
|
||||
"environmentalContext": {
|
||||
"weatherCondition": "CLEAR",
|
||||
"roadCondition": "DRY",
|
||||
"trafficDensity": "LOW",
|
||||
"lightingCondition": "DAYLIGHT"
|
||||
},
|
||||
"missionContext": {
|
||||
"currentMission": {
|
||||
"missionId": "MISSION_001",
|
||||
"missionType": "CARGO_TRANSPORT",
|
||||
"startTime": 1736175000000,
|
||||
"estimatedEndTime": 1736178600000,
|
||||
"progress": 65.5
|
||||
},
|
||||
"waypoints": [
|
||||
{
|
||||
"waypointId": "WP_001",
|
||||
"latitude": 36.354068,
|
||||
"longitude": 120.083410,
|
||||
"status": "COMPLETED"
|
||||
}
|
||||
]
|
||||
},
|
||||
"complianceStatus": {
|
||||
"regulatoryCompliance": "COMPLIANT",
|
||||
"certificationStatus": "VALID",
|
||||
"auditTrail": "ENABLED"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 3. 数据字段详细说明
|
||||
|
||||
### 3.1 车辆基础信息 (vehicleInfo) - 必填
|
||||
- **vehicleId**: 车辆唯一标识符 [必填]
|
||||
- **vehicleType**: 车辆类型 (PASSENGER_CAR, TRUCK, BUS, GROUND_SUPPORT_EQUIPMENT等) [可选]
|
||||
- **manufacturer**: 制造商 [可选]
|
||||
- **model**: 车型 [可选]
|
||||
- **serialNumber**: 序列号 [可选]
|
||||
- **firmwareVersion**: 固件版本 [可选]
|
||||
|
||||
### 3.2 运行状态 (operationalStatus) - 必填
|
||||
- **powerStatus**: 电源状态 (ON, OFF, STANDBY) [必填]
|
||||
- **systemHealth**: 系统健康状态 (HEALTHY, DEGRADED, CRITICAL, FAULT) [必填]
|
||||
- **operationalMode**: 运行模式 (MANUAL, ASSISTED, AUTONOMOUS, REMOTE) [必填]
|
||||
- **missionStatus**: 任务状态 (IDLE, IN_PROGRESS, COMPLETED, PAUSED, ABORTED) [可选]
|
||||
- **emergencyStatus**: 紧急状态 (NORMAL, WARNING, EMERGENCY, CRITICAL) [必填]
|
||||
- **lastHeartbeat**: 最后心跳时间戳 [必填]
|
||||
|
||||
### 3.3 控制状态 (controlStatus) - 必填
|
||||
- **controlMode**: 控制模式 (MANUAL, AUTONOMOUS, REMOTE, HYBRID) [必填]
|
||||
- **controlAuthority**: 控制权限 (DRIVER, SYSTEM, REMOTE_OPERATOR) [必填]
|
||||
- **remoteControlActive**: 远程控制是否激活 [必填]
|
||||
- **manualOverrideActive**: 手动接管是否激活 [可选]
|
||||
- **safetyDriverPresent**: 安全员是否在场 [可选]
|
||||
|
||||
### 3.4 运动状态 (motionStatus) - 必填
|
||||
#### 位置信息 (position) [必填]
|
||||
- **latitude**: 纬度 [必填]
|
||||
- **longitude**: 经度 [必填]
|
||||
- **altitude**: 海拔高度 [可选]
|
||||
- **coordinateSystem**: 坐标系统 [可选,默认WGS84]
|
||||
|
||||
#### 速度信息 (velocity) [必填]
|
||||
- **speed**: 速度值 [必填]
|
||||
- **direction**: 方向角 [必填]
|
||||
- **verticalSpeed**: 垂直速度 [可选]
|
||||
- **speedUnit**: 速度单位 [可选,默认m/s]
|
||||
- **directionUnit**: 方向单位 [可选,默认radians]
|
||||
|
||||
#### 加速度信息 (acceleration) [可选]
|
||||
- **longitudinal**: 纵向加速度 [可选]
|
||||
- **lateral**: 横向加速度 [可选]
|
||||
- **vertical**: 垂直加速度 [可选]
|
||||
- **unit**: 加速度单位 [可选,默认m/s²]
|
||||
|
||||
### 3.5 车辆状态 (vehicleState) - 可选
|
||||
- **engineStatus**: 发动机状态 (RUNNING, STOPPED, IDLE, FAULT) [可选]
|
||||
- **transmissionState**: 变速箱状态 (PARK, DRIVE, REVERSE, NEUTRAL) [可选]
|
||||
- **brakingSystem**: 制动系统状态 (NORMAL, WARNING, FAULT) [可选]
|
||||
- **steeringSystem**: 转向系统状态 (NORMAL, WARNING, FAULT) [可选]
|
||||
- **fuelLevel**: 燃油液位百分比 [可选]
|
||||
|
||||
### 3.6 电池状态 (batteryStatus) - 可选
|
||||
#### 主电池 (mainBattery) [可选]
|
||||
- **chargeLevel**: 电量百分比 (0-100) [可选]
|
||||
- **voltage**: 电压值 (V) [可选]
|
||||
- **current**: 电流值 (A,正值充电,负值放电) [可选]
|
||||
- **temperature**: 电池温度 (°C) [可选]
|
||||
- **health**: 电池健康状态 (GOOD, FAIR, POOR, CRITICAL) [可选]
|
||||
- **cycleCount**: 充放电循环次数 [可选]
|
||||
- **capacity**: 电池容量信息 [可选]
|
||||
- **current**: 当前容量 [可选]
|
||||
- **design**: 设计容量 [可选]
|
||||
- **unit**: 容量单位 (kWh, Ah) [可选]
|
||||
- **chargingStatus**: 充电状态 (CHARGING, DISCHARGING, IDLE, FAULT) [可选]
|
||||
- **estimatedRange**: 预估续航里程 (km) [可选]
|
||||
- **timeToEmpty**: 预估放电时间 (分钟) [可选]
|
||||
- **timeToFull**: 预估充满时间 (分钟) [可选]
|
||||
- **cellVoltages**: 单体电池电压数组 [可选]
|
||||
- **balancingActive**: 电池均衡是否激活 [可选]
|
||||
|
||||
#### 辅助电池 (auxiliaryBattery) [可选]
|
||||
- **chargeLevel**: 电量百分比 [可选]
|
||||
- **voltage**: 电压值 [可选]
|
||||
- **current**: 电流值 [可选]
|
||||
- **temperature**: 温度 [可选]
|
||||
- **health**: 健康状态 [可选]
|
||||
- **chargingStatus**: 充电状态 [可选]
|
||||
|
||||
#### 备用电池 (backupBattery) [可选]
|
||||
- **chargeLevel**: 电量百分比 [可选]
|
||||
- **voltage**: 电压值 [可选]
|
||||
- **health**: 健康状态 [可选]
|
||||
- **lastMaintenance**: 最后维护时间戳 [可选]
|
||||
|
||||
### 3.7 安全状态 (safetyStatus) - 可选
|
||||
- **collisionAvoidanceActive**: 碰撞避免系统是否激活 [可选]
|
||||
- **emergencyBrakingReady**: 紧急制动系统是否就绪 [可选]
|
||||
- **pathPlanningStatus**: 路径规划状态 (ACTIVE, INACTIVE, FAULT) [可选]
|
||||
- **obstacleDetectionStatus**: 障碍物检测状态 (ACTIVE, INACTIVE, FAULT) [可选]
|
||||
- **minimumRiskManeuverTriggered**: 最小风险策略是否触发 [可选]
|
||||
|
||||
### 3.8 自动驾驶等级 (autonomyLevel) - 可选
|
||||
- **currentLevel**: 当前自动驾驶等级 (0-5, 基于SAE J3016标准) [可选]
|
||||
- **availableLevels**: 可用的自动驾驶等级数组 [可选]
|
||||
- **fallbackLevel**: 降级后的自动驾驶等级 [可选]
|
||||
|
||||
### 3.9 传感器状态 (sensorStatus) - 可选
|
||||
#### 摄像头 (cameras) [可选]
|
||||
- **sensorId**: 传感器ID [可选]
|
||||
- **status**: 状态 (ACTIVE, INACTIVE, FAULT) [可选]
|
||||
- **health**: 健康状态 (GOOD, FAIR, POOR) [可选]
|
||||
- **lastUpdate**: 最后更新时间戳 [可选]
|
||||
|
||||
#### 激光雷达 (lidars) [可选]
|
||||
- **sensorId**: 传感器ID [可选]
|
||||
- **status**: 状态 [可选]
|
||||
- **health**: 健康状态 [可选]
|
||||
- **lastUpdate**: 最后更新时间戳 [可选]
|
||||
|
||||
#### 毫米波雷达 (radars) [可选]
|
||||
- **sensorId**: 传感器ID [可选]
|
||||
- **status**: 状态 [可选]
|
||||
- **health**: 健康状态 [可选]
|
||||
- **lastUpdate**: 最后更新时间戳 [可选]
|
||||
|
||||
#### GPS (gps) [可选]
|
||||
- **status**: GPS状态 [可选]
|
||||
- **accuracy**: 精度 (米) [可选]
|
||||
- **satelliteCount**: 卫星数量 [可选]
|
||||
- **lastUpdate**: 最后更新时间戳 [可选]
|
||||
|
||||
#### 惯性测量单元 (imu) [可选]
|
||||
- **status**: IMU状态 [可选]
|
||||
- **health**: 健康状态 [可选]
|
||||
- **lastUpdate**: 最后更新时间戳 [可选]
|
||||
|
||||
### 3.10 通信状态 (communicationStatus) - 可选
|
||||
- **v2xStatus**: V2X通信状态 (CONNECTED, DISCONNECTED, FAULT) [可选]
|
||||
- **cellularSignalStrength**: 蜂窝信号强度 (dBm) [可选]
|
||||
- **wifiStatus**: WiFi状态 (CONNECTED, DISCONNECTED, FAULT) [可选]
|
||||
- **cloudConnectivity**: 云端连接状态 (ONLINE, OFFLINE, FAULT) [可选]
|
||||
|
||||
### 3.11 诊断信息 (diagnostics) - 可选
|
||||
#### 故障代码 (faultCodes) [可选]
|
||||
- **code**: 故障代码 (如P0001, B0002等) [可选]
|
||||
- **severity**: 严重程度 (INFO, WARNING, CRITICAL, FATAL) [可选]
|
||||
- **description**: 故障描述 [可选]
|
||||
- **component**: 故障组件 [可选]
|
||||
- **timestamp**: 故障发生时间戳 [可选]
|
||||
- **status**: 故障状态 (ACTIVE, RESOLVED) [可选]
|
||||
|
||||
#### 警告信息 (warnings) [可选]
|
||||
- **severity**: 严重程度 (LOW, MEDIUM, HIGH) [可选]
|
||||
- **description**: 警告描述 [可选]
|
||||
- **component**: 相关组件 [可选]
|
||||
- **timestamp**: 警告时间戳 [可选]
|
||||
|
||||
#### 维护提醒 (maintenanceAlerts) [可选]
|
||||
- **type**: 维护类型 (SCHEDULED_MAINTENANCE, COMPONENT_REPLACEMENT, INSPECTION等) [可选]
|
||||
- **description**: 维护描述 [可选]
|
||||
- **dueDate**: 到期日期时间戳 [可选]
|
||||
- **priority**: 优先级 (LOW, MEDIUM, HIGH, URGENT) [可选]
|
||||
|
||||
### 3.12 环境上下文 (environmentalContext) - 可选
|
||||
- **weatherCondition**: 天气状况 (CLEAR, RAIN, SNOW, FOG等) [可选]
|
||||
- **roadCondition**: 路面状况 (DRY, WET, ICY, SNOW等) [可选]
|
||||
- **trafficDensity**: 交通密度 (LOW, MEDIUM, HIGH) [可选]
|
||||
- **lightingCondition**: 光照条件 (DAYLIGHT, DUSK, NIGHT, TUNNEL等) [可选]
|
||||
|
||||
### 3.13 任务上下文 (missionContext) - 可选
|
||||
#### 当前任务 (currentMission) [可选]
|
||||
- **missionId**: 任务ID [可选]
|
||||
- **missionType**: 任务类型 [可选]
|
||||
- **startTime**: 开始时间戳 [可选]
|
||||
- **estimatedEndTime**: 预计结束时间戳 [可选]
|
||||
- **progress**: 任务进度百分比 [可选]
|
||||
|
||||
#### 路径点 (waypoints) [可选]
|
||||
- **waypointId**: 路径点ID [可选]
|
||||
- **latitude**: 纬度 [可选]
|
||||
- **longitude**: 经度 [可选]
|
||||
- **status**: 状态 (PENDING, COMPLETED, SKIPPED) [可选]
|
||||
|
||||
### 3.14 合规状态 (complianceStatus) - 可选
|
||||
- **regulatoryCompliance**: 法规合规状态 (COMPLIANT, NON_COMPLIANT, UNKNOWN) [可选]
|
||||
- **certificationStatus**: 认证状态 (VALID, EXPIRED, PENDING) [可选]
|
||||
- **auditTrail**: 审计跟踪状态 (ENABLED, DISABLED) [可选]
|
||||
|
||||
## 4. 错误处理
|
||||
|
||||
### 4.1 HTTP状态码
|
||||
- **200**: 成功
|
||||
- **400**: 请求参数错误
|
||||
- **401**: 未授权
|
||||
- **403**: 禁止访问
|
||||
- **404**: 车辆不存在
|
||||
- **429**: 请求频率过高
|
||||
- **500**: 服务器内部错误
|
||||
- **503**: 服务不可用
|
||||
|
||||
### 4.2 错误响应格式
|
||||
```json
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Invalid vehicle ID format",
|
||||
"timestamp": 1736175610000,
|
||||
"error": {
|
||||
"type": "VALIDATION_ERROR",
|
||||
"details": "Vehicle ID must be alphanumeric and 3-20 characters long",
|
||||
"field": "vehicleId"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 5. 安全考虑
|
||||
|
||||
### 5.1 认证授权
|
||||
- 使用JWT Token进行身份认证
|
||||
- 基于角色的访问控制(RBAC)
|
||||
- API密钥管理
|
||||
|
||||
### 5.2 数据保护
|
||||
- HTTPS加密传输
|
||||
- 敏感数据脱敏
|
||||
- 数据访问日志记录
|
||||
|
||||
### 5.3 隐私保护
|
||||
- 位置数据匿名化选项
|
||||
- 数据保留策略
|
||||
- 用户同意管理
|
||||
|
||||
## 6. 性能优化
|
||||
|
||||
### 6.1 缓存策略
|
||||
- Redis缓存热点数据
|
||||
- CDN加速静态资源
|
||||
- 数据库查询优化
|
||||
|
||||
### 6.2 限流控制
|
||||
- 基于IP的限流
|
||||
- 基于用户的限流
|
||||
- 基于API的限流
|
||||
|
||||
## 7. 监控和日志
|
||||
|
||||
### 7.1 API监控
|
||||
- 响应时间监控
|
||||
- 错误率监控
|
||||
- 吞吐量监控
|
||||
|
||||
### 7.2 日志记录
|
||||
- 访问日志
|
||||
- 错误日志
|
||||
- 审计日志
|
||||
|
||||
## 8. 版本管理
|
||||
|
||||
### 8.1 版本策略
|
||||
- 语义化版本控制
|
||||
- 向后兼容性保证
|
||||
- 废弃通知机制
|
||||
|
||||
### 8.2 版本迁移
|
||||
- 渐进式迁移
|
||||
- 并行版本支持
|
||||
- 迁移工具提供
|
||||
|
||||
## 9. 扩展性设计
|
||||
|
||||
### 9.1 插件机制
|
||||
- 自定义字段支持
|
||||
- 厂商特定扩展
|
||||
- 行业特定适配
|
||||
|
||||
### 9.2 集成能力
|
||||
- Webhook支持
|
||||
- 消息队列集成
|
||||
- 第三方系统对接
|
||||
|
||||
## 10. 测试策略
|
||||
|
||||
### 10.1 单元测试
|
||||
- API接口测试
|
||||
- 数据验证测试
|
||||
- 错误处理测试
|
||||
|
||||
### 10.2 集成测试
|
||||
- 端到端测试
|
||||
- 性能测试
|
||||
- 安全测试
|
||||
|
||||
## 11. 字段可选性说明
|
||||
|
||||
### 11.1 必填字段组
|
||||
以下字段组是API响应的核心,必须包含:
|
||||
- **vehicleInfo.vehicleId**: 车辆唯一标识
|
||||
- **operationalStatus**: 基本运行状态
|
||||
- **controlStatus**: 控制状态
|
||||
- **motionStatus.position**: 位置信息
|
||||
- **motionStatus.velocity**: 速度信息
|
||||
|
||||
### 11.2 可选字段组
|
||||
其他所有字段组都是可选的,厂商可根据实际情况选择性实现:
|
||||
- **batteryStatus**: 电池相关信息(电动车必需,燃油车可选)
|
||||
- **sensorStatus**: 传感器状态(根据车辆配置)
|
||||
- **safetyStatus**: 安全系统状态
|
||||
- **autonomyLevel**: 自动驾驶等级信息
|
||||
- **communicationStatus**: 通信状态
|
||||
- **diagnostics**: 诊断信息
|
||||
- **environmentalContext**: 环境信息
|
||||
- **missionContext**: 任务信息
|
||||
- **complianceStatus**: 合规信息
|
||||
|
||||
### 11.3 实现建议
|
||||
1. **渐进式实现**: 厂商可先实现必填字段,再逐步添加可选字段
|
||||
2. **配置驱动**: 通过配置文件控制哪些字段组需要返回
|
||||
3. **字段过滤**: 支持通过fields参数指定返回的字段组
|
||||
4. **版本兼容**: 新增字段不影响现有客户端的兼容性
|
||||
|
||||
## 12. 电池状态详细说明
|
||||
|
||||
### 12.1 电池类型分类
|
||||
- **主电池 (mainBattery)**: 车辆主要动力电池
|
||||
- **辅助电池 (auxiliaryBattery)**: 12V/24V辅助系统电池
|
||||
- **备用电池 (backupBattery)**: 紧急情况下的备用电源
|
||||
|
||||
### 12.2 电池健康状态定义
|
||||
- **GOOD**: 电池健康,容量>80%设计容量
|
||||
- **FAIR**: 电池轻微老化,容量60-80%设计容量
|
||||
- **POOR**: 电池明显老化,容量40-60%设计容量
|
||||
- **CRITICAL**: 电池严重老化,容量<40%设计容量
|
||||
|
||||
### 12.3 充电状态定义
|
||||
- **CHARGING**: 正在充电
|
||||
- **DISCHARGING**: 正在放电
|
||||
- **IDLE**: 空闲状态(既不充电也不放电)
|
||||
- **FAULT**: 充电系统故障
|
||||
|
||||
### 12.4 电池监控重点参数
|
||||
- **温度监控**: 防止过热和过冷
|
||||
- **电压监控**: 防止过充和过放
|
||||
- **电流监控**: 监控充放电电流
|
||||
- **容量衰减**: 跟踪电池老化程度
|
||||
- **循环次数**: 评估电池寿命
|
||||
|
||||
## 13. 故障诊断说明
|
||||
|
||||
### 13.1 故障严重程度定义
|
||||
- **INFO**: 信息性消息,不影响正常运行
|
||||
- **WARNING**: 警告级别,需要关注但可继续运行
|
||||
- **CRITICAL**: 严重故障,影响部分功能
|
||||
- **FATAL**: 致命故障,必须立即停车处理
|
||||
|
||||
### 13.2 故障状态定义
|
||||
- **ACTIVE**: 故障当前存在
|
||||
- **RESOLVED**: 故障已解决
|
||||
|
||||
### 13.3 维护类型定义
|
||||
- **SCHEDULED_MAINTENANCE**: 定期保养
|
||||
- **COMPONENT_REPLACEMENT**: 组件更换
|
||||
- **INSPECTION**: 检查维护
|
||||
|
||||
### 13.4 优先级定义
|
||||
- **LOW**: 低优先级,可延后处理
|
||||
- **MEDIUM**: 中等优先级,建议及时处理
|
||||
- **HIGH**: 高优先级,需要尽快处理
|
||||
- **URGENT**: 紧急,必须立即处理
|
||||
|
||||
这个优化后的设计方案具有以下特点:
|
||||
|
||||
1. **灵活性**: 大部分字段设为可选,厂商可根据实际情况实现
|
||||
2. **实用性**: 突出了核心必需字段,确保基本功能
|
||||
3. **详细的电池信息**: 提供了完整的电池状态监控能力
|
||||
4. **简化的故障信息**: 保持核心故障诊断功能,结构简洁
|
||||
5. **渐进式实现**: 支持分阶段实现,降低开发成本
|
||||
6. **向后兼容**: 新增字段不影响现有系统
|
||||
@ -1,89 +0,0 @@
|
||||
package com.qaup.collision.common.model.spatial;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.locationtech.jts.geom.Point;
|
||||
import org.locationtech.jts.geom.Polygon;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* PostGIS空间数据示例模型
|
||||
*
|
||||
* 演示如何在JPA实体中使用PostGIS空间数据类型:
|
||||
* - Point: 表示位置点(车辆、航空器位置)
|
||||
* - Polygon: 表示区域(机场区域、电子围栏)
|
||||
*
|
||||
* 支持的空间查询示例:
|
||||
* - ST_Contains: 检查点是否在多边形内
|
||||
* - ST_DWithin: 检查两点间距离是否小于指定值
|
||||
* - ST_Distance: 计算两点间距离
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "spatial_example")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SpatialExample {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 位置点 - 使用PostGIS POINT类型
|
||||
* SRID 4326表示WGS84坐标系统(GPS坐标)
|
||||
*/
|
||||
@Column(name = "location", columnDefinition = "geometry(Point,4326)")
|
||||
private Point location;
|
||||
|
||||
/**
|
||||
* 区域边界 - 使用PostGIS POLYGON类型
|
||||
* 可用于表示机场区域、电子围栏等
|
||||
*/
|
||||
@Column(name = "boundary", columnDefinition = "geometry(Polygon,4326)")
|
||||
private Polygon boundary;
|
||||
|
||||
/**
|
||||
* 描述信息
|
||||
*/
|
||||
@Column(name = "description")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@Column(name = "created_at")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 预设置创建时间
|
||||
*/
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
createdAt = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
PostGIS查询示例(原生SQL):
|
||||
|
||||
-- 1. 查找在指定区域内的所有点
|
||||
SELECT * FROM spatial_example
|
||||
WHERE ST_Contains(boundary, location);
|
||||
|
||||
-- 2. 查找距离指定点100米内的所有记录
|
||||
SELECT *, ST_Distance(location, ST_SetSRID(ST_MakePoint(120.0834, 36.3541), 4326)) as distance
|
||||
FROM spatial_example
|
||||
WHERE ST_DWithin(
|
||||
location,
|
||||
ST_SetSRID(ST_MakePoint(120.0834, 36.3541), 4326),
|
||||
0.001 -- 约100米(度数)
|
||||
);
|
||||
|
||||
-- 3. 创建空间索引(提升查询性能)
|
||||
CREATE INDEX idx_spatial_example_location ON spatial_example USING GIST (location);
|
||||
CREATE INDEX idx_spatial_example_boundary ON spatial_example USING GIST (boundary);
|
||||
*/
|
||||
5112
sql/qaup_database_schema.sql
Normal file
5112
sql/qaup_database_schema.sql
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user