168 lines
4.7 KiB
Bash
Executable File
168 lines
4.7 KiB
Bash
Executable File
#!/bin/bash
|
||
set -e
|
||
|
||
# 蓝绿部署脚本(Caddy 版本)
|
||
# 用法: ./deploy.sh [blue|green]
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
cd "$SCRIPT_DIR"
|
||
|
||
# 颜色定义
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# 日志函数
|
||
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||
|
||
# 自动发现 Docker 网络名(兼容 docker compose 前缀)
|
||
detect_network() {
|
||
local net
|
||
net=$(docker network ls --format "{{.Name}}" | grep -E '^(gch3n-infra_)?gch3n-network$' | head -1 || true)
|
||
if [ -z "$net" ]; then
|
||
# 回退到默认 bridge,避免脚本直接失败
|
||
net="bridge"
|
||
log_warn "未发现 gch3n-network,回退使用 bridge 网络"
|
||
fi
|
||
echo "$net"
|
||
}
|
||
|
||
# 获取当前活跃的环境
|
||
get_active_color() {
|
||
local active=$(docker ps --filter "name=app-" --format "{{.Names}}" | grep -E "app-(blue|green)" | head -1)
|
||
if [ -z "$active" ]; then
|
||
echo "none"
|
||
else
|
||
echo "$active" | sed 's/app-//'
|
||
fi
|
||
}
|
||
|
||
# 健康检查(主机侧)
|
||
check_health() {
|
||
local color=$1
|
||
local port=$2
|
||
local max_attempts=30
|
||
local attempt=0
|
||
|
||
log_info "检查 $color 健康状态 (127.0.0.1:$port)..."
|
||
|
||
while [ $attempt -lt $max_attempts ]; do
|
||
if curl -fsS "http://127.0.0.1:$port/health" >/dev/null 2>&1; then
|
||
log_info "$color 健康检查通过 ✓"
|
||
return 0
|
||
fi
|
||
|
||
attempt=$((attempt + 1))
|
||
log_warn "健康检查尝试 $attempt/$max_attempts 失败,等待 2 秒..."
|
||
sleep 2
|
||
done
|
||
|
||
log_error "$color 健康检查失败 ✗"
|
||
return 1
|
||
}
|
||
|
||
# 部署到指定环境
|
||
deploy_to() {
|
||
local target_color=$1
|
||
local target_port=$2
|
||
local other_color=$3
|
||
local network_name=$4
|
||
|
||
log_info "开始部署到 $target_color 环境..."
|
||
|
||
# 1. 构建新镜像
|
||
log_info "构建 Docker 镜像..."
|
||
docker build -t "gch3n/app:$target_color" -t "gch3n/app:latest" ./app
|
||
|
||
# 2. 停止目标环境(如果存在)
|
||
if docker ps -q --filter "name=app-$target_color" | grep -q .; then
|
||
log_warn "停止现有的 $target_color 容器..."
|
||
docker stop "app-$target_color" || true
|
||
docker rm "app-$target_color" || true
|
||
fi
|
||
|
||
# 3. 启动新容器
|
||
log_info "启动 $target_color 容器(network=$network_name)..."
|
||
docker run -d \
|
||
--name "app-$target_color" \
|
||
--network "$network_name" \
|
||
-p "127.0.0.1:$target_port:$target_port" \
|
||
-e "PORT=$target_port" \
|
||
-e "DEPLOY_COLOR=$target_color" \
|
||
--health-cmd="wget --quiet --tries=1 --spider http://localhost:$target_port/health || exit 1" \
|
||
--health-interval=10s \
|
||
--health-timeout=5s \
|
||
--health-retries=3 \
|
||
--health-start-period=10s \
|
||
--restart=unless-stopped \
|
||
"gch3n/app:$target_color"
|
||
|
||
# 4. 健康检查
|
||
if ! check_health "$target_color" "$target_port"; then
|
||
log_error "$target_color 部署失败,执行回滚..."
|
||
docker stop "app-$target_color" || true
|
||
docker rm "app-$target_color" || true
|
||
|
||
if [ "$other_color" != "none" ]; then
|
||
log_warn "保持 $other_color 环境运行"
|
||
fi
|
||
exit 1
|
||
fi
|
||
|
||
# 5. 停止旧环境(Caddy 会自动只转发健康后端)
|
||
if [ "$other_color" != "none" ] && [ "$other_color" != "$target_color" ]; then
|
||
log_info "停止旧环境 $other_color..."
|
||
docker stop "app-$other_color" || true
|
||
docker rm "app-$other_color" || true
|
||
fi
|
||
|
||
log_info "部署完成!当前活跃环境: $target_color"
|
||
log_info "Caddy 会自动反代到健康实例,访问: https://app.gch3n.online"
|
||
}
|
||
|
||
# 主逻辑
|
||
main() {
|
||
log_info "========================================"
|
||
log_info " gch3n.online 蓝绿部署脚本 (Caddy)"
|
||
log_info "========================================"
|
||
|
||
local network_name
|
||
network_name=$(detect_network)
|
||
|
||
# 获取当前活跃环境
|
||
local active=$(get_active_color)
|
||
log_info "当前活跃环境: $active"
|
||
|
||
# 确定目标环境
|
||
local target=$1
|
||
if [ -z "$target" ]; then
|
||
# 自动选择非活跃环境
|
||
if [ "$active" = "blue" ]; then
|
||
target="green"
|
||
else
|
||
target="blue"
|
||
fi
|
||
log_info "自动选择目标环境: $target"
|
||
fi
|
||
|
||
# 验证参数
|
||
if [ "$target" != "blue" ] && [ "$target" != "green" ]; then
|
||
log_error "无效的环境: $target,只能是 blue 或 green"
|
||
exit 1
|
||
fi
|
||
|
||
# 确定端口
|
||
local target_port=3001
|
||
[ "$target" = "green" ] && target_port=3002
|
||
|
||
# 执行部署
|
||
deploy_to "$target" "$target_port" "$active" "$network_name"
|
||
}
|
||
|
||
# 执行
|
||
main "$@"
|
||
|