govon-runtime / scripts /deploy.sh
umyunsang's picture
sync: scripts/ (verify_e2e_tool_calling.py)
769e684 verified
#!/usr/bin/env bash
set -euo pipefail
# ──────────────────────────────────────────────
# GovOn Blue/Green Deployment Script
#
# Usage:
# ./scripts/deploy.sh deploy <image-tag> Deploy new version
# ./scripts/deploy.sh rollback Rollback to previous version
# ./scripts/deploy.sh status Show current deployment status
# ./scripts/deploy.sh health Check health of active deployment
# ──────────────────────────────────────────────
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
COMPOSE_FILE="${PROJECT_DIR}/docker-compose.prod.yml"
STATE_FILE="${PROJECT_DIR}/.deploy-state"
HEALTH_TIMEOUT=120
HEALTH_INTERVAL=5
# Colors
RED='\033[0;31m'
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_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# ──────────────────────────────────────────────
# State management
# ──────────────────────────────────────────────
get_active_slot() {
if [ -f "$STATE_FILE" ]; then
cat "$STATE_FILE"
else
echo "none"
fi
}
get_inactive_slot() {
local active
active=$(get_active_slot)
if [ "$active" = "blue" ]; then
echo "green"
else
echo "blue"
fi
}
get_slot_port() {
local slot=$1
if [ "$slot" = "blue" ]; then echo 8001; else echo 8002; fi
}
# ──────────────────────────────────────────────
# Health check with retry
# ──────────────────────────────────────────────
wait_for_health() {
local port=$1
local elapsed=0
log_info "ν—¬μŠ€μ²΄ν¬ λŒ€κΈ° 쀑 (포트: ${port}, νƒ€μž„μ•„μ›ƒ: ${HEALTH_TIMEOUT}초)..."
while [ $elapsed -lt $HEALTH_TIMEOUT ]; do
if curl -sf "http://localhost:${port}/health" > /dev/null 2>&1; then
echo ""
log_info "ν—¬μŠ€μ²΄ν¬ 톡과 (${elapsed}초 μ†Œμš”)"
return 0
fi
sleep $HEALTH_INTERVAL
elapsed=$((elapsed + HEALTH_INTERVAL))
printf "."
done
echo ""
log_error "ν—¬μŠ€μ²΄ν¬ μ‹€νŒ¨ (${HEALTH_TIMEOUT}초 νƒ€μž„μ•„μ›ƒ)"
return 1
}
# ──────────────────────────────────────────────
# Prerequisites check
# ──────────────────────────────────────────────
check_prerequisites() {
if ! command -v docker &>/dev/null; then
log_error "Dockerκ°€ μ„€μΉ˜λ˜μ–΄ μžˆμ§€ μ•ŠμŠ΅λ‹ˆλ‹€."
exit 1
fi
if ! docker compose version &>/dev/null; then
log_error "Docker Composeκ°€ μ„€μΉ˜λ˜μ–΄ μžˆμ§€ μ•ŠμŠ΅λ‹ˆλ‹€."
exit 1
fi
if [ ! -f "$COMPOSE_FILE" ]; then
log_error "Compose νŒŒμΌμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€: ${COMPOSE_FILE}"
exit 1
fi
}
# ──────────────────────────────────────────────
# Deploy new version
# ──────────────────────────────────────────────
cmd_deploy() {
local image_tag="${1:-latest}"
local active
local target
local target_port
active=$(get_active_slot)
target=$(get_inactive_slot)
target_port=$(get_slot_port "$target")
check_prerequisites
log_info "=== GovOn 배포 μ‹œμž‘: v${image_tag} ==="
log_info "ν˜„μž¬ ν™œμ„± 슬둯: ${active}"
log_info "배포 λŒ€μƒ 슬둯: ${target}"
echo ""
# Set the tag for the target slot
if [ "$target" = "blue" ]; then
export BLUE_TAG="$image_tag"
else
export GREEN_TAG="$image_tag"
fi
# Pull new image
log_info "이미지 풀링: ghcr.io/govon-org/govon:${image_tag}..."
docker pull "ghcr.io/govon-org/govon:${image_tag}"
# Create volume directories
mkdir -p "${PROJECT_DIR}/models" "${PROJECT_DIR}/data" "${PROJECT_DIR}/agents" "${PROJECT_DIR}/configs"
# Start target slot
log_info "${target} 슬둯 μ‹œμž‘ 쀑..."
docker compose -f "$COMPOSE_FILE" --profile "$target" up -d
# Wait for health
if wait_for_health "$target_port"; then
log_info "${target} 배포가 정상 μž‘λ™ν•©λ‹ˆλ‹€!"
# Update state
echo "$target" > "$STATE_FILE"
log_info "ν™œμ„± 슬둯 λ³€κ²½: ${active} -> ${target}"
# Stop previous slot
if [ "$active" != "none" ]; then
log_info "이전 ${active} 슬둯 쀑지 쀑..."
docker compose -f "$COMPOSE_FILE" --profile "$active" down
fi
echo ""
log_info "=== 배포 μ™„λ£Œ ==="
cmd_status
else
log_error "배포 μ‹€νŒ¨! λ‘€λ°± μˆ˜ν–‰ 쀑..."
docker compose -f "$COMPOSE_FILE" --profile "$target" down
log_error "μ‹€νŒ¨ν•œ 배포λ₯Ό μ •λ¦¬ν–ˆμŠ΅λ‹ˆλ‹€. 이전 버전이 계속 ν™œμ„± μƒνƒœμž…λ‹ˆλ‹€."
exit 1
fi
}
# ──────────────────────────────────────────────
# Rollback to previous version
# ──────────────────────────────────────────────
cmd_rollback() {
local active
local previous
local prev_port
active=$(get_active_slot)
previous=$(get_inactive_slot)
prev_port=$(get_slot_port "$previous")
check_prerequisites
if [ "$active" = "none" ]; then
log_error "λ‘€λ°±ν•  ν™œμ„± 배포가 μ—†μŠ΅λ‹ˆλ‹€."
exit 1
fi
log_warn "=== λ‘€λ°± μ‹œμž‘: ${active} -> ${previous} ==="
# Start previous slot
docker compose -f "$COMPOSE_FILE" --profile "$previous" up -d
if wait_for_health "$prev_port"; then
# Stop current active
docker compose -f "$COMPOSE_FILE" --profile "$active" down
echo "$previous" > "$STATE_FILE"
echo ""
log_info "=== λ‘€λ°± μ™„λ£Œ. ν™œμ„± 슬둯: ${previous} ==="
cmd_status
else
log_error "λ‘€λ°± μ‹€νŒ¨! μˆ˜λ™ μ‘°μΉ˜κ°€ ν•„μš”ν•©λ‹ˆλ‹€."
log_error "ν˜„μž¬ ν™œμ„± 슬둯(${active})은 κ·ΈλŒ€λ‘œ μœ μ§€λ©λ‹ˆλ‹€."
docker compose -f "$COMPOSE_FILE" --profile "$previous" down
exit 1
fi
}
# ──────────────────────────────────────────────
# Show deployment status
# ──────────────────────────────────────────────
cmd_status() {
local active
active=$(get_active_slot)
local blue_status
local green_status
blue_status=$(docker ps --filter name=govon-blue --format '{{.Status}}' 2>/dev/null || echo "stopped")
green_status=$(docker ps --filter name=govon-green --format '{{.Status}}' 2>/dev/null || echo "stopped")
[ -z "$blue_status" ] && blue_status="stopped"
[ -z "$green_status" ] && green_status="stopped"
echo ""
echo "========================================"
echo " GovOn 배포 μƒνƒœ"
echo "========================================"
echo " ν™œμ„± 슬둯 : ${active}"
echo " Blue (8001): ${blue_status}"
echo " Green (8002): ${green_status}"
echo "========================================"
}
# ──────────────────────────────────────────────
# Health check
# ──────────────────────────────────────────────
cmd_health() {
local active
local port
active=$(get_active_slot)
if [ "$active" = "none" ]; then
log_error "ν™œμ„± 배포가 μ—†μŠ΅λ‹ˆλ‹€."
exit 1
fi
port=$(get_slot_port "$active")
if curl -sf "http://localhost:${port}/health" > /dev/null 2>&1; then
log_info "ν™œμ„± 배포(${active})κ°€ μ •μƒμž…λ‹ˆλ‹€."
else
log_error "ν™œμ„± 배포(${active})κ°€ λΉ„μ •μƒμž…λ‹ˆλ‹€!"
exit 1
fi
}
# ──────────────────────────────────────────────
# Main
# ──────────────────────────────────────────────
case "${1:-help}" in
deploy) cmd_deploy "${2:-latest}" ;;
rollback) cmd_rollback ;;
status) cmd_status ;;
health) cmd_health ;;
*)
echo "GovOn Blue/Green 배포 슀크립트"
echo ""
echo "μ‚¬μš©λ²•: $0 {deploy <tag>|rollback|status|health}"
echo ""
echo "λͺ…λ Ήμ–΄:"
echo " deploy <tag> μƒˆ 버전 배포 (κΈ°λ³Έκ°’: latest)"
echo " rollback 이전 λ²„μ „μœΌλ‘œ λ‘€λ°±"
echo " status ν˜„μž¬ 배포 μƒνƒœ 확인"
echo " health ν™œμ„± 배포 ν—¬μŠ€μ²΄ν¬"
exit 1
;;
esac