Spaces:
Paused
Paused
| 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 | |