#!/usr/bin/env bash set -euo pipefail # galaxis-agent 배포 스크립트 # Usage: ./deploy.sh [--dry-run] SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" DRY_RUN=false if [[ "${1:-}" == "--dry-run" ]]; then DRY_RUN=true echo "[DRY-RUN] Docker 명령은 실행하지 않고 출력만 합니다." echo "" fi HEALTH_URL="${HEALTH_URL:-http://localhost:8100}" HEALTH_RETRY_INTERVAL=5 HEALTH_MAX_WAIT=60 # 색상 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' info() { echo -e "${GREEN}[INFO]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } error() { echo -e "${RED}[ERROR]${NC} $*"; } # ── Pre-flight 체크 ──────────────────────────────────── info "Pre-flight 체크 시작..." # 1. Docker if ! command -v docker &>/dev/null; then error "Docker가 설치되어 있지 않습니다." echo " 설치: https://docs.docker.com/engine/install/" exit 1 fi info "Docker: $(docker --version)" # 2. Docker Compose if ! docker compose version &>/dev/null; then error "Docker Compose가 설치되어 있지 않습니다." echo " 설치: https://docs.docker.com/compose/install/" exit 1 fi info "Docker Compose: $(docker compose version --short)" # 3. .env 파일 (docker compose config보다 먼저 확인 — env_file 의존) if [[ ! -f .env ]]; then error ".env 파일이 없습니다." echo " cp .env.example .env 후 값을 설정하세요." exit 1 fi info ".env 파일 OK" # 4. 필수 환경변수 검증 (grep 기반 — 셸 환경 오염 방지) REQUIRED_VARS=( ANTHROPIC_API_KEY GITEA_TOKEN GITEA_WEBHOOK_SECRET DISCORD_TOKEN DISCORD_CHANNEL_ID FERNET_KEY AGENT_API_KEY ) MISSING=() for var in "${REQUIRED_VARS[@]}"; do if ! grep -qE "^${var}=.+" .env; then MISSING+=("$var") fi done if [[ ${#MISSING[@]} -gt 0 ]]; then error "필수 환경변수가 설정되지 않았습니다:" for var in "${MISSING[@]}"; do echo " - $var" done echo " .env 파일을 확인하세요." exit 1 fi info "필수 환경변수 OK (${#REQUIRED_VARS[@]}개 확인)" # 5. docker-compose.yml 유효성 if ! docker compose config --quiet 2>/dev/null; then error "docker-compose.yml 유효성 검증 실패." echo " docker compose config 으로 상세 에러를 확인하세요." exit 1 fi info "docker-compose.yml 유효성 OK" # 6. galaxis-net 네트워크 if ! docker network inspect galaxis-net &>/dev/null; then warn "galaxis-net 네트워크가 없습니다. 생성합니다." if [[ "$DRY_RUN" == true ]]; then echo " [DRY-RUN] docker network create galaxis-net" else docker network create galaxis-net fi fi info "galaxis-net 네트워크 OK" echo "" info "Pre-flight 체크 완료!" echo "" # ── 배포 ──────────────────────────────────────────────── if [[ "$DRY_RUN" == true ]]; then echo "[DRY-RUN] 실행될 명령:" echo " docker compose build" echo " docker compose up -d" echo " Health check: ${HEALTH_URL}/health (${HEALTH_RETRY_INTERVAL}초 간격, 최대 ${HEALTH_MAX_WAIT}초)" echo "" info "Dry-run 완료. 실제 배포는 --dry-run 없이 실행하세요." exit 0 fi info "galaxis-agent 이미지 빌드 중..." docker compose build info "서비스 시작 중..." docker compose up -d # ── Health check ──────────────────────────────────────── info "Health check 시작 (${HEALTH_RETRY_INTERVAL}초 간격, 최대 ${HEALTH_MAX_WAIT}초)..." ELAPSED=0 HEALTHY=false while [[ $ELAPSED -lt $HEALTH_MAX_WAIT ]]; do if curl -sf "${HEALTH_URL}/health" | grep -q '"status".*"ok"' 2>/dev/null; then HEALTHY=true break fi sleep "$HEALTH_RETRY_INTERVAL" ELAPSED=$((ELAPSED + HEALTH_RETRY_INTERVAL)) echo " 대기 중... (${ELAPSED}초)" done if [[ "$HEALTHY" != true ]]; then error "Health check 실패: ${HEALTH_URL}/health 가 ${HEALTH_MAX_WAIT}초 내에 응답하지 않았습니다." echo "" echo " 로그 확인: docker compose logs" echo " 수동 롤백:" echo " docker compose down" echo " git checkout <이전_커밋>" echo " ./deploy.sh" exit 1 fi # 개별 health check ENDPOINTS=("/health" "/health/gitea" "/health/discord" "/health/queue" "/health/costs") ALL_OK=true echo "" for ep in "${ENDPOINTS[@]}"; do if curl -sf "${HEALTH_URL}${ep}" >/dev/null 2>&1; then info " ${ep} ... OK" else warn " ${ep} ... FAIL" ALL_OK=false fi done echo "" if [[ "$ALL_OK" == true ]]; then info "배포 성공! 모든 health check 통과." else warn "배포 완료. 일부 health check가 실패했습니다." echo " 로그 확인: docker compose logs" fi