Files
val-blog/gch3n-infra/deploy.sh
T

168 lines
4.7 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 "$@"