feat: 添加 Jenkins CI/CD 配置和部署脚本
- 添加 Jenkinsfile 支持智能增量构建 - 添加 Docker 多阶段构建模板 - 添加 Docker Compose 配置(连接 1Panel 中间件) - 添加部署、回滚、清理脚本 - 添加环境变量配置模板
This commit is contained in:
15
.dockerignore
Normal file
15
.dockerignore
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
target/
|
||||||
|
.m2/
|
||||||
|
*.log
|
||||||
|
*.tmp
|
||||||
|
.DS_Store
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
.vscode/
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
*.class
|
||||||
|
*.jar
|
||||||
|
!gradle-wrapper.jar
|
||||||
|
.gradle/
|
||||||
126
.env.example
Normal file
126
.env.example
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
# ============================================
|
||||||
|
# 环境变量配置文件
|
||||||
|
# 复制此文件为 .env 并根据实际环境修改
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# ============ 通用配置 ============
|
||||||
|
COMPOSE_PROJECT_NAME=aiot-platform
|
||||||
|
TZ=Asia/Shanghai
|
||||||
|
|
||||||
|
# ============ Docker Registry ============
|
||||||
|
REGISTRY_HOST=localhost:5000
|
||||||
|
IMAGE_TAG=latest
|
||||||
|
|
||||||
|
# ============ 数据库配置 ============
|
||||||
|
# 1Panel 安装的 MySQL 配置
|
||||||
|
MYSQL_HOST=127.0.0.1
|
||||||
|
MYSQL_PORT=3306
|
||||||
|
MYSQL_ROOT_PASSWORD=your_1panel_mysql_root_password
|
||||||
|
MYSQL_DATABASE=aiot_platform
|
||||||
|
MYSQL_USER=aiot
|
||||||
|
MYSQL_PASSWORD=aiot_password
|
||||||
|
|
||||||
|
# MySQL 内存配置(1Panel 管理,此处配置不生效)
|
||||||
|
MYSQL_MEMORY_LIMIT=2048m
|
||||||
|
MYSQL_MEMORY_RESERVATION=1024m
|
||||||
|
|
||||||
|
# ============ Redis 配置 ============
|
||||||
|
# 1Panel 安装的 Redis 配置
|
||||||
|
REDIS_HOST=127.0.0.1
|
||||||
|
REDIS_PORT=6379
|
||||||
|
REDIS_PASSWORD=your_1panel_redis_password
|
||||||
|
|
||||||
|
# Redis 内存配置(1Panel 管理,此处配置不生效)
|
||||||
|
REDIS_MEMORY_LIMIT=512m
|
||||||
|
REDIS_MEMORY_RESERVATION=256m
|
||||||
|
|
||||||
|
# ============ Nacos 配置 ============
|
||||||
|
# 1Panel 安装的 Nacos 配置
|
||||||
|
NACOS_HOST=127.0.0.1
|
||||||
|
NACOS_PORT=8848
|
||||||
|
NACOS_NAMESPACE=aiot-platform
|
||||||
|
NACOS_USERNAME=nacos
|
||||||
|
NACOS_PASSWORD=nacos
|
||||||
|
|
||||||
|
# Nacos 内存配置(1Panel 管理,此处配置不生效)
|
||||||
|
NACOS_MEMORY_LIMIT=1024m
|
||||||
|
NACOS_MEMORY_RESERVATION=768m
|
||||||
|
|
||||||
|
# ============ RocketMQ 配置 ============
|
||||||
|
# 1Panel 安装的 RocketMQ 配置
|
||||||
|
ROCKETMQ_NAMESRV_HOST=127.0.0.1
|
||||||
|
ROCKETMQ_NAMESRV_PORT=9876
|
||||||
|
ROCKETMQ_BROKER_HOST=127.0.0.1
|
||||||
|
|
||||||
|
# RocketMQ 内存配置(1Panel 管理,此处配置不生效)
|
||||||
|
ROCKETMQ_NAMESRV_MEMORY_LIMIT=512m
|
||||||
|
ROCKETMQ_BROKER_MEMORY_LIMIT=1536m
|
||||||
|
|
||||||
|
# ============ 应用服务内存配置 ============
|
||||||
|
# 格式: 服务名_MEMORY_LIMIT / 服务名_JVM_XMS / 服务名_JVM_XMX
|
||||||
|
|
||||||
|
# API 网关(标准配置)
|
||||||
|
GATEWAY_MEMORY_LIMIT=768m
|
||||||
|
GATEWAY_JVM_XMS=512m
|
||||||
|
GATEWAY_JVM_XMX=768m
|
||||||
|
|
||||||
|
# 系统服务(标准配置)
|
||||||
|
SYSTEM_MEMORY_LIMIT=768m
|
||||||
|
SYSTEM_JVM_XMS=512m
|
||||||
|
SYSTEM_JVM_XMX=768m
|
||||||
|
|
||||||
|
# 基础设施服务(标准配置)
|
||||||
|
INFRA_MEMORY_LIMIT=768m
|
||||||
|
INFRA_JVM_XMS=512m
|
||||||
|
INFRA_JVM_XMX=768m
|
||||||
|
|
||||||
|
# IoT 业务服务(核心服务,较大内存)
|
||||||
|
IOT_SERVER_MEMORY_LIMIT=1024m
|
||||||
|
IOT_SERVER_JVM_XMS=768m
|
||||||
|
IOT_SERVER_JVM_XMX=1024m
|
||||||
|
|
||||||
|
# IoT 设备网关(核心服务,较大内存)
|
||||||
|
IOT_GATEWAY_MEMORY_LIMIT=1024m
|
||||||
|
IOT_GATEWAY_JVM_XMS=768m
|
||||||
|
IOT_GATEWAY_JVM_XMX=1024m
|
||||||
|
|
||||||
|
# 运维服务(轻量配置)
|
||||||
|
OPS_MEMORY_LIMIT=384m
|
||||||
|
OPS_JVM_XMS=256m
|
||||||
|
OPS_JVM_XMX=384m
|
||||||
|
|
||||||
|
# ============ JVM 通用参数 ============
|
||||||
|
JVM_COMMON_OPTS=-XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+HeapDumpOnOutOfMemoryError
|
||||||
|
|
||||||
|
# ============ Spring Boot 配置 ============
|
||||||
|
SPRING_PROFILES_ACTIVE=prod
|
||||||
|
|
||||||
|
# ============ 日志配置 ============
|
||||||
|
LOG_LEVEL=INFO
|
||||||
|
LOG_PATH=/app/logs
|
||||||
|
|
||||||
|
# ============ 端口映射 ============
|
||||||
|
GATEWAY_PORT=48080
|
||||||
|
SYSTEM_PORT=48081
|
||||||
|
INFRA_PORT=48082
|
||||||
|
IOT_SERVER_PORT=48083
|
||||||
|
IOT_GATEWAY_PORT=48084
|
||||||
|
OPS_PORT=48085
|
||||||
|
|
||||||
|
# ============ 健康检查配置 ============
|
||||||
|
HEALTH_CHECK_INTERVAL=30s
|
||||||
|
HEALTH_CHECK_TIMEOUT=10s
|
||||||
|
HEALTH_CHECK_RETRIES=3
|
||||||
|
HEALTH_CHECK_START_PERIOD=60s
|
||||||
|
|
||||||
|
# ============ 资源限制说明 ============
|
||||||
|
# 当前配置预计总内存占用:
|
||||||
|
# - 应用服务: ~4.5GB (Docker 容器)
|
||||||
|
# - 中间件: 由 1Panel 管理(不在 Docker Compose 中)
|
||||||
|
# - 总计: ~4.5GB (仅应用服务,为系统预留 11.5GB)
|
||||||
|
#
|
||||||
|
# 调整建议:
|
||||||
|
# 1. 核心服务(IOT_SERVER, IOT_GATEWAY)已配置较大内存 (1GB)
|
||||||
|
# 2. 如需增加某服务内存,修改对应的 MEMORY_LIMIT 和 JVM_XMX
|
||||||
|
# 3. 中间件内存配置请在 1Panel 面板中调整
|
||||||
|
# 4. 确保所有服务总内存不超过 14GB,为系统预留至少 2GB
|
||||||
282
Jenkinsfile
vendored
Normal file
282
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
// ============================================
|
||||||
|
// AIOT Platform - Jenkins Pipeline
|
||||||
|
// 智能增量构建 + Docker 容器化部署
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
pipeline {
|
||||||
|
agent any
|
||||||
|
|
||||||
|
options {
|
||||||
|
// 保留最近 10 次构建
|
||||||
|
buildDiscarder(logRotator(numToKeepStr: '10'))
|
||||||
|
// 禁止并发构建
|
||||||
|
disableConcurrentBuilds()
|
||||||
|
// 超时设置
|
||||||
|
timeout(time: 60, unit: 'MINUTES')
|
||||||
|
}
|
||||||
|
|
||||||
|
environment {
|
||||||
|
// Gitea 仓库配置
|
||||||
|
GIT_REPO = 'http://124.221.55.225:3000/XW-AIOT/aiot-platform-cloud.git'
|
||||||
|
|
||||||
|
// Docker Registry 配置
|
||||||
|
REGISTRY = 'localhost:5000'
|
||||||
|
|
||||||
|
// Maven 配置
|
||||||
|
MAVEN_OPTS = '-Xmx2048m -Dmaven.repo.local=.m2/repository'
|
||||||
|
|
||||||
|
// 镜像标签
|
||||||
|
IMAGE_TAG = "${env.BRANCH_NAME}-${env.BUILD_NUMBER}-${env.GIT_COMMIT?.take(8)}"
|
||||||
|
|
||||||
|
// 服务列表(核心服务)
|
||||||
|
CORE_SERVICES = 'viewsh-gateway,viewsh-module-system-server,viewsh-module-infra-server,viewsh-module-iot-server,viewsh-module-iot-gateway,viewsh-module-ops-server'
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Checkout') {
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
echo "=== 检出代码 ==="
|
||||||
|
checkout scm
|
||||||
|
|
||||||
|
// 获取 Git 提交信息
|
||||||
|
env.GIT_COMMIT_MSG = sh(
|
||||||
|
script: 'git log -1 --pretty=%B',
|
||||||
|
returnStdout: true
|
||||||
|
).trim()
|
||||||
|
|
||||||
|
echo "Commit: ${env.GIT_COMMIT}"
|
||||||
|
echo "Message: ${env.GIT_COMMIT_MSG}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Detect Changes') {
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
echo "=== 检测变更的服务 ==="
|
||||||
|
|
||||||
|
// 获取变更的文件列表
|
||||||
|
def changedFiles = sh(
|
||||||
|
script: '''
|
||||||
|
if [ "${GIT_PREVIOUS_COMMIT}" = "" ]; then
|
||||||
|
# 首次构建,构建所有核心服务
|
||||||
|
echo "all"
|
||||||
|
else
|
||||||
|
# 检测变更的文件
|
||||||
|
git diff --name-only ${GIT_PREVIOUS_COMMIT} ${GIT_COMMIT}
|
||||||
|
fi
|
||||||
|
''',
|
||||||
|
returnStdout: true
|
||||||
|
).trim()
|
||||||
|
|
||||||
|
echo "Changed files:\n${changedFiles}"
|
||||||
|
|
||||||
|
// 分析需要构建的服务
|
||||||
|
def servicesToBuild = []
|
||||||
|
|
||||||
|
if (changedFiles == 'all') {
|
||||||
|
// 首次构建或强制全量构建
|
||||||
|
servicesToBuild = CORE_SERVICES.split(',')
|
||||||
|
} else {
|
||||||
|
// 检测每个服务是否有变更
|
||||||
|
CORE_SERVICES.split(',').each { service ->
|
||||||
|
def modulePath = service.replace('-server', '').replace('viewsh-module-', 'viewsh-module-').replace('viewsh-', '')
|
||||||
|
|
||||||
|
if (changedFiles.contains(modulePath) ||
|
||||||
|
changedFiles.contains('pom.xml') ||
|
||||||
|
changedFiles.contains('viewsh-framework') ||
|
||||||
|
changedFiles.contains('viewsh-dependencies')) {
|
||||||
|
servicesToBuild.add(service)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有检测到变更,但有代码提交,构建所有服务
|
||||||
|
if (servicesToBuild.isEmpty() && !changedFiles.isEmpty()) {
|
||||||
|
servicesToBuild = CORE_SERVICES.split(',')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
env.SERVICES_TO_BUILD = servicesToBuild.join(',')
|
||||||
|
echo "Services to build: ${env.SERVICES_TO_BUILD}"
|
||||||
|
|
||||||
|
if (servicesToBuild.isEmpty()) {
|
||||||
|
echo "No services need to be built. Skipping build."
|
||||||
|
currentBuild.result = 'SUCCESS'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Maven Build') {
|
||||||
|
when {
|
||||||
|
expression { env.SERVICES_TO_BUILD != '' }
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
echo "=== Maven 构建 ==="
|
||||||
|
|
||||||
|
def services = env.SERVICES_TO_BUILD.split(',')
|
||||||
|
|
||||||
|
// 并行构建(最多 3 个并行,避免内存溢出)
|
||||||
|
def parallelBuilds = [:]
|
||||||
|
def batchSize = 3
|
||||||
|
|
||||||
|
for (int i = 0; i < services.size(); i += batchSize) {
|
||||||
|
def batch = services[i..Math.min(i + batchSize - 1, services.size() - 1)]
|
||||||
|
|
||||||
|
batch.each { service ->
|
||||||
|
def modulePath = getModulePath(service)
|
||||||
|
|
||||||
|
parallelBuilds["Build ${service}"] = {
|
||||||
|
stage("Build ${service}") {
|
||||||
|
echo "Building ${service}..."
|
||||||
|
sh """
|
||||||
|
mvn clean package -pl ${modulePath} -am -DskipTests -B
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行当前批次
|
||||||
|
parallel parallelBuilds
|
||||||
|
parallelBuilds = [:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Docker Build & Push') {
|
||||||
|
when {
|
||||||
|
expression { env.SERVICES_TO_BUILD != '' }
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
echo "=== Docker 镜像构建与推送 ==="
|
||||||
|
|
||||||
|
def services = env.SERVICES_TO_BUILD.split(',')
|
||||||
|
def parallelBuilds = [:]
|
||||||
|
|
||||||
|
services.each { service ->
|
||||||
|
parallelBuilds["Docker ${service}"] = {
|
||||||
|
stage("Docker ${service}") {
|
||||||
|
def modulePath = getModulePath(service)
|
||||||
|
def jarName = service
|
||||||
|
|
||||||
|
echo "Building Docker image for ${service}..."
|
||||||
|
|
||||||
|
sh """
|
||||||
|
docker build \
|
||||||
|
-f docker/Dockerfile.template \
|
||||||
|
--build-arg MODULE_NAME=${modulePath} \
|
||||||
|
--build-arg JAR_NAME=${jarName} \
|
||||||
|
--build-arg SKIP_TESTS=true \
|
||||||
|
-t ${REGISTRY}/${service}:${IMAGE_TAG} \
|
||||||
|
-t ${REGISTRY}/${service}:latest \
|
||||||
|
.
|
||||||
|
"""
|
||||||
|
|
||||||
|
echo "Pushing Docker image for ${service}..."
|
||||||
|
sh """
|
||||||
|
docker push ${REGISTRY}/${service}:${IMAGE_TAG}
|
||||||
|
docker push ${REGISTRY}/${service}:latest
|
||||||
|
"""
|
||||||
|
|
||||||
|
echo "Cleaning up old images for ${service}..."
|
||||||
|
sh """
|
||||||
|
docker images ${REGISTRY}/${service} --format '{{.ID}} {{.Tag}}' | \
|
||||||
|
grep -v '${IMAGE_TAG}' | grep -v 'latest' | \
|
||||||
|
awk '{print \$1}' | head -n -2 | xargs -r docker rmi -f || true
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 并行构建镜像(最多 3 个并行)
|
||||||
|
def batchSize = 3
|
||||||
|
for (int i = 0; i < services.size(); i += batchSize) {
|
||||||
|
def batch = services[i..Math.min(i + batchSize - 1, services.size() - 1)]
|
||||||
|
def batchBuilds = [:]
|
||||||
|
|
||||||
|
batch.each { service ->
|
||||||
|
batchBuilds["Docker ${service}"] = parallelBuilds["Docker ${service}"]
|
||||||
|
}
|
||||||
|
|
||||||
|
parallel batchBuilds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Deploy') {
|
||||||
|
when {
|
||||||
|
expression { env.SERVICES_TO_BUILD != '' && env.BRANCH_NAME == 'master' }
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
echo "=== 部署到生产环境 ==="
|
||||||
|
|
||||||
|
def services = env.SERVICES_TO_BUILD.split(',')
|
||||||
|
|
||||||
|
services.each { service ->
|
||||||
|
echo "Deploying ${service}..."
|
||||||
|
|
||||||
|
sh """
|
||||||
|
docker-compose -f docker-compose.core.yml pull ${service}
|
||||||
|
docker-compose -f docker-compose.core.yml up -d ${service}
|
||||||
|
"""
|
||||||
|
|
||||||
|
// 等待服务健康检查
|
||||||
|
echo "Waiting for ${service} to be healthy..."
|
||||||
|
sh """
|
||||||
|
timeout 120 sh -c 'until docker inspect --format="{{.State.Health.Status}}" aiot-${service} 2>/dev/null | grep -q healthy; do sleep 5; done' || true
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Deployment completed!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
success {
|
||||||
|
echo "=== 构建成功 ==="
|
||||||
|
echo "Built services: ${env.SERVICES_TO_BUILD}"
|
||||||
|
echo "Image tag: ${IMAGE_TAG}"
|
||||||
|
}
|
||||||
|
|
||||||
|
failure {
|
||||||
|
echo "=== 构建失败 ==="
|
||||||
|
echo "Please check the logs for details."
|
||||||
|
}
|
||||||
|
|
||||||
|
always {
|
||||||
|
// 清理工作空间(可选)
|
||||||
|
// cleanWs()
|
||||||
|
|
||||||
|
// 显示磁盘使用情况
|
||||||
|
sh 'df -h'
|
||||||
|
sh 'docker system df'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 辅助函数
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
def getModulePath(String service) {
|
||||||
|
// 根据服务名获取 Maven 模块路径
|
||||||
|
def moduleMap = [
|
||||||
|
'viewsh-gateway': 'viewsh-gateway',
|
||||||
|
'viewsh-module-system-server': 'viewsh-module-system/viewsh-module-system-server',
|
||||||
|
'viewsh-module-infra-server': 'viewsh-module-infra/viewsh-module-infra-server',
|
||||||
|
'viewsh-module-iot-server': 'viewsh-module-iot/viewsh-module-iot-server',
|
||||||
|
'viewsh-module-iot-gateway': 'viewsh-module-iot/viewsh-module-iot-gateway',
|
||||||
|
'viewsh-module-ops-server': 'viewsh-module-ops/viewsh-module-ops-server'
|
||||||
|
]
|
||||||
|
|
||||||
|
return moduleMap[service] ?: service
|
||||||
|
}
|
||||||
203
docker-compose.core.yml
Normal file
203
docker-compose.core.yml
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# AIOT Platform - 核心服务部署配置 (方案 A)
|
||||||
|
# 连接到 1Panel 已安装的中间件
|
||||||
|
# 总内存占用: ~4.5GB (仅应用服务)
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
networks:
|
||||||
|
aiot-network:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
app-logs:
|
||||||
|
|
||||||
|
services:
|
||||||
|
# ============ 应用服务 ============
|
||||||
|
# 注意: 中间件(MySQL, Redis, Nacos, RocketMQ)已通过 1Panel 安装
|
||||||
|
# 应用服务通过环境变量配置连接到宿主机的中间件
|
||||||
|
|
||||||
|
|
||||||
|
viewsh-gateway:
|
||||||
|
image: ${REGISTRY_HOST}/viewsh-gateway:${IMAGE_TAG}
|
||||||
|
container_name: aiot-gateway
|
||||||
|
restart: unless-stopped
|
||||||
|
network_mode: host # 使用宿主机网络,直接访问 1Panel 中间件
|
||||||
|
environment:
|
||||||
|
JAVA_OPTS: "-Xms${GATEWAY_JVM_XMS} -Xmx${GATEWAY_JVM_XMX} ${JVM_COMMON_OPTS}"
|
||||||
|
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE}
|
||||||
|
NACOS_SERVER_ADDR: ${NACOS_HOST}:${NACOS_PORT}
|
||||||
|
REDIS_HOST: ${REDIS_HOST}
|
||||||
|
REDIS_PORT: ${REDIS_PORT}
|
||||||
|
REDIS_PASSWORD: ${REDIS_PASSWORD}
|
||||||
|
TZ: ${TZ}
|
||||||
|
volumes:
|
||||||
|
- app-logs:/app/logs
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: ${GATEWAY_MEMORY_LIMIT}
|
||||||
|
cpus: '1.0'
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:48080/actuator/health"]
|
||||||
|
interval: ${HEALTH_CHECK_INTERVAL}
|
||||||
|
timeout: ${HEALTH_CHECK_TIMEOUT}
|
||||||
|
retries: ${HEALTH_CHECK_RETRIES}
|
||||||
|
start_period: ${HEALTH_CHECK_START_PERIOD}
|
||||||
|
|
||||||
|
viewsh-module-system-server:
|
||||||
|
image: ${REGISTRY_HOST}/viewsh-module-system-server:${IMAGE_TAG}
|
||||||
|
container_name: aiot-system-server
|
||||||
|
restart: unless-stopped
|
||||||
|
network_mode: host
|
||||||
|
environment:
|
||||||
|
JAVA_OPTS: "-Xms${SYSTEM_JVM_XMS} -Xmx${SYSTEM_JVM_XMX} ${JVM_COMMON_OPTS}"
|
||||||
|
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE}
|
||||||
|
NACOS_SERVER_ADDR: ${NACOS_HOST}:${NACOS_PORT}
|
||||||
|
MYSQL_HOST: ${MYSQL_HOST}
|
||||||
|
MYSQL_PORT: ${MYSQL_PORT}
|
||||||
|
MYSQL_DATABASE: ${MYSQL_DATABASE}
|
||||||
|
MYSQL_USER: ${MYSQL_USER}
|
||||||
|
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
|
||||||
|
REDIS_HOST: ${REDIS_HOST}
|
||||||
|
REDIS_PORT: ${REDIS_PORT}
|
||||||
|
REDIS_PASSWORD: ${REDIS_PASSWORD}
|
||||||
|
TZ: ${TZ}
|
||||||
|
volumes:
|
||||||
|
- app-logs:/app/logs
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: ${SYSTEM_MEMORY_LIMIT}
|
||||||
|
cpus: '1.0'
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:48081/actuator/health"]
|
||||||
|
interval: ${HEALTH_CHECK_INTERVAL}
|
||||||
|
timeout: ${HEALTH_CHECK_TIMEOUT}
|
||||||
|
retries: ${HEALTH_CHECK_RETRIES}
|
||||||
|
start_period: ${HEALTH_CHECK_START_PERIOD}
|
||||||
|
|
||||||
|
viewsh-module-infra-server:
|
||||||
|
image: ${REGISTRY_HOST}/viewsh-module-infra-server:${IMAGE_TAG}
|
||||||
|
container_name: aiot-infra-server
|
||||||
|
restart: unless-stopped
|
||||||
|
network_mode: host
|
||||||
|
environment:
|
||||||
|
JAVA_OPTS: "-Xms${INFRA_JVM_XMS} -Xmx${INFRA_JVM_XMX} ${JVM_COMMON_OPTS}"
|
||||||
|
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE}
|
||||||
|
NACOS_SERVER_ADDR: ${NACOS_HOST}:${NACOS_PORT}
|
||||||
|
MYSQL_HOST: ${MYSQL_HOST}
|
||||||
|
MYSQL_PORT: ${MYSQL_PORT}
|
||||||
|
MYSQL_DATABASE: ${MYSQL_DATABASE}
|
||||||
|
MYSQL_USER: ${MYSQL_USER}
|
||||||
|
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
|
||||||
|
REDIS_HOST: ${REDIS_HOST}
|
||||||
|
REDIS_PORT: ${REDIS_PORT}
|
||||||
|
REDIS_PASSWORD: ${REDIS_PASSWORD}
|
||||||
|
TZ: ${TZ}
|
||||||
|
volumes:
|
||||||
|
- app-logs:/app/logs
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: ${INFRA_MEMORY_LIMIT}
|
||||||
|
cpus: '1.0'
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:48082/actuator/health"]
|
||||||
|
interval: ${HEALTH_CHECK_INTERVAL}
|
||||||
|
timeout: ${HEALTH_CHECK_TIMEOUT}
|
||||||
|
retries: ${HEALTH_CHECK_RETRIES}
|
||||||
|
start_period: ${HEALTH_CHECK_START_PERIOD}
|
||||||
|
|
||||||
|
viewsh-module-iot-server:
|
||||||
|
image: ${REGISTRY_HOST}/viewsh-module-iot-server:${IMAGE_TAG}
|
||||||
|
container_name: aiot-iot-server
|
||||||
|
restart: unless-stopped
|
||||||
|
network_mode: host
|
||||||
|
environment:
|
||||||
|
JAVA_OPTS: "-Xms${IOT_SERVER_JVM_XMS} -Xmx${IOT_SERVER_JVM_XMX} ${JVM_COMMON_OPTS}"
|
||||||
|
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE}
|
||||||
|
NACOS_SERVER_ADDR: ${NACOS_HOST}:${NACOS_PORT}
|
||||||
|
MYSQL_HOST: ${MYSQL_HOST}
|
||||||
|
MYSQL_PORT: ${MYSQL_PORT}
|
||||||
|
MYSQL_DATABASE: ${MYSQL_DATABASE}
|
||||||
|
MYSQL_USER: ${MYSQL_USER}
|
||||||
|
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
|
||||||
|
REDIS_HOST: ${REDIS_HOST}
|
||||||
|
REDIS_PORT: ${REDIS_PORT}
|
||||||
|
REDIS_PASSWORD: ${REDIS_PASSWORD}
|
||||||
|
ROCKETMQ_NAMESRV_ADDR: ${ROCKETMQ_NAMESRV_HOST}:${ROCKETMQ_NAMESRV_PORT}
|
||||||
|
TZ: ${TZ}
|
||||||
|
volumes:
|
||||||
|
- app-logs:/app/logs
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: ${IOT_SERVER_MEMORY_LIMIT}
|
||||||
|
cpus: '1.5'
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:48083/actuator/health"]
|
||||||
|
interval: ${HEALTH_CHECK_INTERVAL}
|
||||||
|
timeout: ${HEALTH_CHECK_TIMEOUT}
|
||||||
|
retries: ${HEALTH_CHECK_RETRIES}
|
||||||
|
start_period: ${HEALTH_CHECK_START_PERIOD}
|
||||||
|
|
||||||
|
viewsh-module-iot-gateway:
|
||||||
|
image: ${REGISTRY_HOST}/viewsh-module-iot-gateway:${IMAGE_TAG}
|
||||||
|
container_name: aiot-iot-gateway
|
||||||
|
restart: unless-stopped
|
||||||
|
network_mode: host
|
||||||
|
environment:
|
||||||
|
JAVA_OPTS: "-Xms${IOT_GATEWAY_JVM_XMS} -Xmx${IOT_GATEWAY_JVM_XMX} ${JVM_COMMON_OPTS}"
|
||||||
|
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE}
|
||||||
|
REDIS_HOST: ${REDIS_HOST}
|
||||||
|
REDIS_PORT: ${REDIS_PORT}
|
||||||
|
REDIS_PASSWORD: ${REDIS_PASSWORD}
|
||||||
|
ROCKETMQ_NAMESRV_ADDR: ${ROCKETMQ_NAMESRV_HOST}:${ROCKETMQ_NAMESRV_PORT}
|
||||||
|
TZ: ${TZ}
|
||||||
|
volumes:
|
||||||
|
- app-logs:/app/logs
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: ${IOT_GATEWAY_MEMORY_LIMIT}
|
||||||
|
cpus: '1.5'
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:48084/actuator/health"]
|
||||||
|
interval: ${HEALTH_CHECK_INTERVAL}
|
||||||
|
timeout: ${HEALTH_CHECK_TIMEOUT}
|
||||||
|
retries: ${HEALTH_CHECK_RETRIES}
|
||||||
|
start_period: ${HEALTH_CHECK_START_PERIOD}
|
||||||
|
|
||||||
|
viewsh-module-ops-server:
|
||||||
|
image: ${REGISTRY_HOST}/viewsh-module-ops-server:${IMAGE_TAG}
|
||||||
|
container_name: aiot-ops-server
|
||||||
|
restart: unless-stopped
|
||||||
|
network_mode: host
|
||||||
|
environment:
|
||||||
|
JAVA_OPTS: "-Xms${OPS_JVM_XMS} -Xmx${OPS_JVM_XMX} ${JVM_COMMON_OPTS}"
|
||||||
|
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE}
|
||||||
|
NACOS_SERVER_ADDR: ${NACOS_HOST}:${NACOS_PORT}
|
||||||
|
MYSQL_HOST: ${MYSQL_HOST}
|
||||||
|
MYSQL_PORT: ${MYSQL_PORT}
|
||||||
|
MYSQL_DATABASE: ${MYSQL_DATABASE}
|
||||||
|
MYSQL_USER: ${MYSQL_USER}
|
||||||
|
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
|
||||||
|
REDIS_HOST: ${REDIS_HOST}
|
||||||
|
REDIS_PORT: ${REDIS_PORT}
|
||||||
|
REDIS_PASSWORD: ${REDIS_PASSWORD}
|
||||||
|
TZ: ${TZ}
|
||||||
|
volumes:
|
||||||
|
- app-logs:/app/logs
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: ${OPS_MEMORY_LIMIT}
|
||||||
|
cpus: '0.5'
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:48085/actuator/health"]
|
||||||
|
interval: ${HEALTH_CHECK_INTERVAL}
|
||||||
|
timeout: ${HEALTH_CHECK_TIMEOUT}
|
||||||
|
retries: ${HEALTH_CHECK_RETRIES}
|
||||||
|
start_period: ${HEALTH_CHECK_START_PERIOD}
|
||||||
83
docker/Dockerfile.template
Normal file
83
docker/Dockerfile.template
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
# 多阶段构建 Dockerfile 模板
|
||||||
|
# 适用于所有 Spring Boot 服务
|
||||||
|
|
||||||
|
# ============ 构建阶段 ============
|
||||||
|
FROM eclipse-temurin:17-jdk-alpine AS builder
|
||||||
|
|
||||||
|
# 安装必要工具
|
||||||
|
RUN apk add --no-cache maven
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
# 复制 Maven 配置文件(利用 Docker 缓存)
|
||||||
|
COPY pom.xml .
|
||||||
|
COPY viewsh-dependencies/pom.xml viewsh-dependencies/
|
||||||
|
COPY viewsh-framework/pom.xml viewsh-framework/
|
||||||
|
COPY viewsh-gateway/pom.xml viewsh-gateway/
|
||||||
|
COPY viewsh-module-system/pom.xml viewsh-module-system/
|
||||||
|
COPY viewsh-module-infra/pom.xml viewsh-module-infra/
|
||||||
|
COPY viewsh-module-iot/pom.xml viewsh-module-iot/
|
||||||
|
COPY viewsh-module-ops/pom.xml viewsh-module-ops/
|
||||||
|
COPY viewsh-server/pom.xml viewsh-server/
|
||||||
|
|
||||||
|
# 下载依赖(利用缓存层)
|
||||||
|
RUN mvn dependency:go-offline -B || true
|
||||||
|
|
||||||
|
# 复制源代码
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 构建参数:指定要构建的模块
|
||||||
|
ARG MODULE_NAME
|
||||||
|
ARG SKIP_TESTS=true
|
||||||
|
|
||||||
|
# 编译打包
|
||||||
|
RUN if [ "$SKIP_TESTS" = "true" ]; then \
|
||||||
|
mvn clean package -pl ${MODULE_NAME} -am -DskipTests -B; \
|
||||||
|
else \
|
||||||
|
mvn clean package -pl ${MODULE_NAME} -am -B; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============ 运行阶段 ============
|
||||||
|
FROM eclipse-temurin:17-jre-alpine
|
||||||
|
|
||||||
|
# 构建参数
|
||||||
|
ARG MODULE_NAME
|
||||||
|
ARG JAR_NAME
|
||||||
|
ARG APP_PORT=48080
|
||||||
|
|
||||||
|
# 元数据标签
|
||||||
|
LABEL maintainer="XW-AIOT Team"
|
||||||
|
LABEL service="${MODULE_NAME}"
|
||||||
|
|
||||||
|
# 创建非 root 用户
|
||||||
|
RUN addgroup -g 1000 appuser && \
|
||||||
|
adduser -D -u 1000 -G appuser appuser
|
||||||
|
|
||||||
|
# 创建应用目录
|
||||||
|
RUN mkdir -p /app/logs /app/config && \
|
||||||
|
chown -R appuser:appuser /app
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 从构建阶段复制 JAR 文件
|
||||||
|
COPY --from=builder --chown=appuser:appuser /build/${MODULE_NAME}/target/${JAR_NAME}.jar app.jar
|
||||||
|
|
||||||
|
# 切换到非 root 用户
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
# 环境变量
|
||||||
|
ENV TZ=Asia/Shanghai \
|
||||||
|
JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/app/logs/heap-dump.hprof" \
|
||||||
|
SPRING_PROFILES_ACTIVE=prod \
|
||||||
|
APP_ARGS=""
|
||||||
|
|
||||||
|
# 暴露端口
|
||||||
|
EXPOSE ${APP_PORT}
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
||||||
|
CMD wget --no-verbose --tries=1 --spider http://localhost:${APP_PORT}/actuator/health || exit 1
|
||||||
|
|
||||||
|
# 启动应用
|
||||||
|
ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar ${APP_ARGS}"]
|
||||||
165
docker/services-config.json
Normal file
165
docker/services-config.json
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
{
|
||||||
|
"services": {
|
||||||
|
"viewsh-gateway": {
|
||||||
|
"module": "viewsh-gateway",
|
||||||
|
"jarName": "viewsh-gateway",
|
||||||
|
"port": 48080,
|
||||||
|
"memory": {
|
||||||
|
"limit": "768m",
|
||||||
|
"reservation": "512m",
|
||||||
|
"jvm": {
|
||||||
|
"xms": "512m",
|
||||||
|
"xmx": "768m"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cpu": "1.0",
|
||||||
|
"priority": "core",
|
||||||
|
"healthCheck": "/actuator/health",
|
||||||
|
"dependencies": ["nacos", "redis"]
|
||||||
|
},
|
||||||
|
"viewsh-module-system-server": {
|
||||||
|
"module": "viewsh-module-system/viewsh-module-system-server",
|
||||||
|
"jarName": "viewsh-module-system-server",
|
||||||
|
"port": 48081,
|
||||||
|
"memory": {
|
||||||
|
"limit": "768m",
|
||||||
|
"reservation": "512m",
|
||||||
|
"jvm": {
|
||||||
|
"xms": "512m",
|
||||||
|
"xmx": "768m"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cpu": "1.0",
|
||||||
|
"priority": "core",
|
||||||
|
"healthCheck": "/actuator/health",
|
||||||
|
"dependencies": ["mysql", "redis", "nacos"]
|
||||||
|
},
|
||||||
|
"viewsh-module-infra-server": {
|
||||||
|
"module": "viewsh-module-infra/viewsh-module-infra-server",
|
||||||
|
"jarName": "viewsh-module-infra-server",
|
||||||
|
"port": 48082,
|
||||||
|
"memory": {
|
||||||
|
"limit": "768m",
|
||||||
|
"reservation": "512m",
|
||||||
|
"jvm": {
|
||||||
|
"xms": "512m",
|
||||||
|
"xmx": "768m"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cpu": "1.0",
|
||||||
|
"priority": "core",
|
||||||
|
"healthCheck": "/actuator/health",
|
||||||
|
"dependencies": ["mysql", "redis", "nacos"]
|
||||||
|
},
|
||||||
|
"viewsh-module-iot-server": {
|
||||||
|
"module": "viewsh-module-iot/viewsh-module-iot-server",
|
||||||
|
"jarName": "viewsh-module-iot-server",
|
||||||
|
"port": 48083,
|
||||||
|
"memory": {
|
||||||
|
"limit": "1024m",
|
||||||
|
"reservation": "768m",
|
||||||
|
"jvm": {
|
||||||
|
"xms": "768m",
|
||||||
|
"xmx": "1024m"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cpu": "1.5",
|
||||||
|
"priority": "core",
|
||||||
|
"healthCheck": "/actuator/health",
|
||||||
|
"dependencies": ["mysql", "redis", "nacos", "rocketmq"],
|
||||||
|
"description": "IoT 业务服务,处理设备数据,需要较大内存"
|
||||||
|
},
|
||||||
|
"viewsh-module-iot-gateway": {
|
||||||
|
"module": "viewsh-module-iot/viewsh-module-iot-gateway",
|
||||||
|
"jarName": "viewsh-module-iot-gateway",
|
||||||
|
"port": 48084,
|
||||||
|
"memory": {
|
||||||
|
"limit": "1024m",
|
||||||
|
"reservation": "768m",
|
||||||
|
"jvm": {
|
||||||
|
"xms": "768m",
|
||||||
|
"xmx": "1024m"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cpu": "1.5",
|
||||||
|
"priority": "core",
|
||||||
|
"healthCheck": "/actuator/health",
|
||||||
|
"dependencies": ["redis", "rocketmq"],
|
||||||
|
"description": "IoT 设备网关,处理设备连接,需要较大内存"
|
||||||
|
},
|
||||||
|
"viewsh-module-ops-server": {
|
||||||
|
"module": "viewsh-module-ops/viewsh-module-ops-server",
|
||||||
|
"jarName": "viewsh-module-ops-server",
|
||||||
|
"port": 48085,
|
||||||
|
"memory": {
|
||||||
|
"limit": "384m",
|
||||||
|
"reservation": "256m",
|
||||||
|
"jvm": {
|
||||||
|
"xms": "256m",
|
||||||
|
"xmx": "384m"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cpu": "0.5",
|
||||||
|
"priority": "core",
|
||||||
|
"healthCheck": "/actuator/health",
|
||||||
|
"dependencies": ["mysql", "redis", "nacos"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"middleware": {
|
||||||
|
"mysql": {
|
||||||
|
"image": "mysql:8.0",
|
||||||
|
"port": 3306,
|
||||||
|
"memory": {
|
||||||
|
"limit": "2048m",
|
||||||
|
"reservation": "1024m"
|
||||||
|
},
|
||||||
|
"cpu": "1.0",
|
||||||
|
"volumes": ["mysql-data:/var/lib/mysql"]
|
||||||
|
},
|
||||||
|
"redis": {
|
||||||
|
"image": "redis:7-alpine",
|
||||||
|
"port": 6379,
|
||||||
|
"memory": {
|
||||||
|
"limit": "512m",
|
||||||
|
"reservation": "256m"
|
||||||
|
},
|
||||||
|
"cpu": "0.5",
|
||||||
|
"volumes": ["redis-data:/data"]
|
||||||
|
},
|
||||||
|
"nacos": {
|
||||||
|
"image": "nacos/nacos-server:v2.2.3",
|
||||||
|
"port": 8848,
|
||||||
|
"memory": {
|
||||||
|
"limit": "1024m",
|
||||||
|
"reservation": "768m"
|
||||||
|
},
|
||||||
|
"cpu": "1.0",
|
||||||
|
"volumes": ["nacos-data:/home/nacos/data"]
|
||||||
|
},
|
||||||
|
"rocketmq-namesrv": {
|
||||||
|
"image": "apache/rocketmq:5.1.4",
|
||||||
|
"port": 9876,
|
||||||
|
"memory": {
|
||||||
|
"limit": "512m",
|
||||||
|
"reservation": "256m"
|
||||||
|
},
|
||||||
|
"cpu": "0.5"
|
||||||
|
},
|
||||||
|
"rocketmq-broker": {
|
||||||
|
"image": "apache/rocketmq:5.1.4",
|
||||||
|
"port": 10911,
|
||||||
|
"memory": {
|
||||||
|
"limit": "1536m",
|
||||||
|
"reservation": "1024m"
|
||||||
|
},
|
||||||
|
"cpu": "1.0",
|
||||||
|
"volumes": ["rocketmq-data:/home/rocketmq/store"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resourceSummary": {
|
||||||
|
"totalApplicationMemory": "4.5GB",
|
||||||
|
"totalMiddlewareMemory": "5.5GB",
|
||||||
|
"totalEstimatedMemory": "10GB",
|
||||||
|
"note": "内存配置可在 .env 文件中调整,核心服务(iot-server, iot-gateway)已分配更大内存"
|
||||||
|
}
|
||||||
|
}
|
||||||
81
scripts/cleanup.sh
Normal file
81
scripts/cleanup.sh
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# AIOT Platform - 清理脚本
|
||||||
|
# 清理旧镜像和容器,释放存储空间
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# 颜色输出
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
log_info() {
|
||||||
|
echo -e "${GREEN}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warn() {
|
||||||
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info "========================================="
|
||||||
|
log_info "AIOT Platform 清理开始"
|
||||||
|
log_info "========================================="
|
||||||
|
|
||||||
|
# 显示当前磁盘使用情况
|
||||||
|
log_info "当前磁盘使用情况:"
|
||||||
|
df -h | grep -E "Filesystem|/$"
|
||||||
|
echo ""
|
||||||
|
log_info "当前 Docker 磁盘使用:"
|
||||||
|
docker system df
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 清理停止的容器
|
||||||
|
log_info "清理停止的容器..."
|
||||||
|
docker container prune -f
|
||||||
|
|
||||||
|
# 清理悬空镜像
|
||||||
|
log_info "清理悬空镜像..."
|
||||||
|
docker image prune -f
|
||||||
|
|
||||||
|
# 清理旧版本镜像(保留最近 3 个版本)
|
||||||
|
log_info "清理旧版本镜像(保留最近 3 个版本)..."
|
||||||
|
|
||||||
|
SERVICES="viewsh-gateway viewsh-module-system-server viewsh-module-infra-server viewsh-module-iot-server viewsh-module-iot-gateway viewsh-module-ops-server"
|
||||||
|
|
||||||
|
for service in $SERVICES; do
|
||||||
|
log_info "处理服务: ${service}"
|
||||||
|
|
||||||
|
# 获取所有镜像,按时间排序,删除除了最新 3 个之外的所有镜像
|
||||||
|
docker images "localhost:5000/${service}" --format "{{.ID}} {{.Tag}}" | \
|
||||||
|
grep -v "latest" | \
|
||||||
|
tail -n +4 | \
|
||||||
|
awk '{print $1}' | \
|
||||||
|
xargs -r docker rmi -f 2>/dev/null || true
|
||||||
|
done
|
||||||
|
|
||||||
|
# 清理未使用的卷(谨慎使用)
|
||||||
|
log_warn "是否清理未使用的卷? (y/N)"
|
||||||
|
read -r response
|
||||||
|
if [ "$response" = "y" ] || [ "$response" = "Y" ]; then
|
||||||
|
log_info "清理未使用的卷..."
|
||||||
|
docker volume prune -f
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 清理构建缓存
|
||||||
|
log_info "清理 Docker 构建缓存..."
|
||||||
|
docker builder prune -f --filter "until=24h"
|
||||||
|
|
||||||
|
# 显示清理后的磁盘使用情况
|
||||||
|
echo ""
|
||||||
|
log_info "========================================="
|
||||||
|
log_info "清理完成"
|
||||||
|
log_info "========================================="
|
||||||
|
echo ""
|
||||||
|
log_info "清理后磁盘使用情况:"
|
||||||
|
df -h | grep -E "Filesystem|/$"
|
||||||
|
echo ""
|
||||||
|
log_info "清理后 Docker 磁盘使用:"
|
||||||
|
docker system df
|
||||||
230
scripts/deploy.sh
Normal file
230
scripts/deploy.sh
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# AIOT Platform - 部署脚本
|
||||||
|
# 滚动更新部署,支持健康检查和自动回滚
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# 颜色输出
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# 配置
|
||||||
|
COMPOSE_FILE="docker-compose.core.yml"
|
||||||
|
REGISTRY="localhost:5000"
|
||||||
|
HEALTH_CHECK_TIMEOUT=120
|
||||||
|
HEALTH_CHECK_INTERVAL=5
|
||||||
|
|
||||||
|
# 日志函数
|
||||||
|
log_info() {
|
||||||
|
echo -e "${GREEN}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warn() {
|
||||||
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查服务健康状态
|
||||||
|
check_health() {
|
||||||
|
local service=$1
|
||||||
|
local container_name="aiot-${service}"
|
||||||
|
local elapsed=0
|
||||||
|
|
||||||
|
log_info "等待 ${service} 健康检查..."
|
||||||
|
|
||||||
|
while [ $elapsed -lt $HEALTH_CHECK_TIMEOUT ]; do
|
||||||
|
if docker inspect --format='{{.State.Health.Status}}' "$container_name" 2>/dev/null | grep -q "healthy"; then
|
||||||
|
log_info "${service} 健康检查通过 ✓"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep $HEALTH_CHECK_INTERVAL
|
||||||
|
elapsed=$((elapsed + HEALTH_CHECK_INTERVAL))
|
||||||
|
echo -n "."
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
log_error "${service} 健康检查超时 ✗"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# 备份当前运行的镜像标签
|
||||||
|
backup_current_tags() {
|
||||||
|
log_info "备份当前镜像标签..."
|
||||||
|
|
||||||
|
docker-compose -f "$COMPOSE_FILE" config --services | while read service; do
|
||||||
|
local current_image=$(docker inspect "aiot-${service}" --format='{{.Config.Image}}' 2>/dev/null || echo "")
|
||||||
|
if [ -n "$current_image" ]; then
|
||||||
|
echo "${service}=${current_image}" >> .deploy_backup
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log_info "备份完成: .deploy_backup"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 部署单个服务
|
||||||
|
deploy_service() {
|
||||||
|
local service=$1
|
||||||
|
|
||||||
|
log_info "========================================="
|
||||||
|
log_info "部署服务: ${service}"
|
||||||
|
log_info "========================================="
|
||||||
|
|
||||||
|
# 拉取最新镜像
|
||||||
|
log_info "拉取最新镜像..."
|
||||||
|
if ! docker-compose -f "$COMPOSE_FILE" pull "$service"; then
|
||||||
|
log_error "拉取镜像失败"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 启动新容器
|
||||||
|
log_info "启动新容器..."
|
||||||
|
if ! docker-compose -f "$COMPOSE_FILE" up -d "$service"; then
|
||||||
|
log_error "启动容器失败"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
if ! check_health "$service"; then
|
||||||
|
log_error "${service} 部署失败,准备回滚..."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "${service} 部署成功 ✓"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# 回滚服务
|
||||||
|
rollback_service() {
|
||||||
|
local service=$1
|
||||||
|
|
||||||
|
log_warn "回滚服务: ${service}"
|
||||||
|
|
||||||
|
# 从备份文件读取之前的镜像
|
||||||
|
if [ -f .deploy_backup ]; then
|
||||||
|
local backup_image=$(grep "^${service}=" .deploy_backup | cut -d'=' -f2)
|
||||||
|
|
||||||
|
if [ -n "$backup_image" ]; then
|
||||||
|
log_info "回滚到镜像: ${backup_image}"
|
||||||
|
|
||||||
|
docker-compose -f "$COMPOSE_FILE" stop "$service"
|
||||||
|
docker tag "$backup_image" "${REGISTRY}/${service}:latest"
|
||||||
|
docker-compose -f "$COMPOSE_FILE" up -d "$service"
|
||||||
|
|
||||||
|
if check_health "$service"; then
|
||||||
|
log_info "${service} 回滚成功 ✓"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_error "${service} 回滚失败 ✗"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# 主部署流程
|
||||||
|
main() {
|
||||||
|
log_info "========================================="
|
||||||
|
log_info "AIOT Platform 部署开始"
|
||||||
|
log_info "========================================="
|
||||||
|
|
||||||
|
# 检查 .env 文件
|
||||||
|
if [ ! -f .env ]; then
|
||||||
|
log_error ".env 文件不存在,请先复制 .env.example 并配置"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 加载环境变量
|
||||||
|
source .env
|
||||||
|
|
||||||
|
# 备份当前状态
|
||||||
|
rm -f .deploy_backup
|
||||||
|
backup_current_tags
|
||||||
|
|
||||||
|
# 获取要部署的服务列表
|
||||||
|
local services_to_deploy=""
|
||||||
|
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
# 部署所有核心服务
|
||||||
|
services_to_deploy=$(docker-compose -f "$COMPOSE_FILE" config --services | grep -v -E "mysql|redis|nacos|rocketmq")
|
||||||
|
else
|
||||||
|
# 部署指定服务
|
||||||
|
services_to_deploy="$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "待部署服务: ${services_to_deploy}"
|
||||||
|
|
||||||
|
# 部署服务
|
||||||
|
local failed_services=""
|
||||||
|
|
||||||
|
for service in $services_to_deploy; do
|
||||||
|
if ! deploy_service "$service"; then
|
||||||
|
failed_services="${failed_services} ${service}"
|
||||||
|
|
||||||
|
# 尝试回滚
|
||||||
|
rollback_service "$service"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# 清理旧镜像
|
||||||
|
log_info "清理悬空镜像..."
|
||||||
|
docker image prune -f
|
||||||
|
|
||||||
|
# 部署结果
|
||||||
|
echo ""
|
||||||
|
log_info "========================================="
|
||||||
|
if [ -z "$failed_services" ]; then
|
||||||
|
log_info "所有服务部署成功 ✓"
|
||||||
|
log_info "========================================="
|
||||||
|
rm -f .deploy_backup
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
log_error "以下服务部署失败: ${failed_services}"
|
||||||
|
log_error "========================================="
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 显示帮助
|
||||||
|
show_help() {
|
||||||
|
cat << EOF
|
||||||
|
用法: $0 [服务名...]
|
||||||
|
|
||||||
|
部署 AIOT Platform 服务到生产环境
|
||||||
|
|
||||||
|
参数:
|
||||||
|
无参数 部署所有应用服务
|
||||||
|
服务名... 部署指定的服务
|
||||||
|
|
||||||
|
示例:
|
||||||
|
$0 # 部署所有服务
|
||||||
|
$0 viewsh-gateway # 仅部署 gateway
|
||||||
|
$0 viewsh-module-iot-server viewsh-module-iot-gateway # 部署多个服务
|
||||||
|
|
||||||
|
服务列表:
|
||||||
|
- viewsh-gateway
|
||||||
|
- viewsh-module-system-server
|
||||||
|
- viewsh-module-infra-server
|
||||||
|
- viewsh-module-iot-server
|
||||||
|
- viewsh-module-iot-gateway
|
||||||
|
- viewsh-module-ops-server
|
||||||
|
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# 参数处理
|
||||||
|
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
|
||||||
|
show_help
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 执行主流程
|
||||||
|
main "$@"
|
||||||
48
scripts/rollback.sh
Normal file
48
scripts/rollback.sh
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# AIOT Platform - 回滚脚本
|
||||||
|
# 快速回滚到上一个版本
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# 颜色输出
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
log_info() {
|
||||||
|
echo -e "${GREEN}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查备份文件
|
||||||
|
if [ ! -f .deploy_backup ]; then
|
||||||
|
log_error "未找到备份文件 .deploy_backup"
|
||||||
|
log_error "无法执行回滚操作"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "========================================="
|
||||||
|
log_info "AIOT Platform 回滚开始"
|
||||||
|
log_info "========================================="
|
||||||
|
|
||||||
|
# 读取备份并回滚
|
||||||
|
while IFS='=' read -r service image; do
|
||||||
|
log_info "回滚服务: ${service} -> ${image}"
|
||||||
|
|
||||||
|
docker-compose -f docker-compose.core.yml stop "$service"
|
||||||
|
docker tag "$image" "localhost:5000/${service}:latest"
|
||||||
|
docker-compose -f docker-compose.core.yml up -d "$service"
|
||||||
|
|
||||||
|
log_info "${service} 回滚完成 ✓"
|
||||||
|
done < .deploy_backup
|
||||||
|
|
||||||
|
log_info "========================================="
|
||||||
|
log_info "回滚完成"
|
||||||
|
log_info "========================================="
|
||||||
Reference in New Issue
Block a user