#!/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="172.17.16.7: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 "$@"