#!/usr/bin/env bash # /opt/dbx-orderlist/docker-compose.yml 를 안전하게 교체하고 재기동·검증한다. # # 변경 요지: # - ports: "8000:8000" 제거 (호스트 외부 노출 차단) # - expose: "8000" 추가 (도커 네트워크 내부에서만 접근) # - 외부 네트워크 npm_default 에 영구 attach (NPM 리버스 프록시용) # # 사용법: # sudo bash fix-orderlist-compose.sh # # 멱등(idempotent): 여러 번 실행해도 결과는 동일. set -euo pipefail COMPOSE_DIR="/opt/dbx-orderlist" COMPOSE_FILE="${COMPOSE_DIR}/docker-compose.yml" BACKUP_FILE="${COMPOSE_FILE}.bak.$(date +%Y%m%d-%H%M%S)" red() { printf '\033[31m%s\033[0m\n' "$*"; } green() { printf '\033[32m%s\033[0m\n' "$*"; } yellow() { printf '\033[33m%s\033[0m\n' "$*"; } blue() { printf '\033[34m%s\033[0m\n' "$*"; } # ── 0. 사전 검사 ──────────────────────────────────────────────── if [[ ! -f "${COMPOSE_FILE}" ]]; then red "ERROR: ${COMPOSE_FILE} 를 찾을 수 없습니다." exit 1 fi if ! docker network inspect npm_default >/dev/null 2>&1; then red "ERROR: npm_default 네트워크가 존재하지 않습니다. NPM 컨테이너가 실행 중인지 확인하세요." exit 1 fi # ── 1. 백업 ───────────────────────────────────────────────────── blue "[1/5] 기존 compose 파일 백업 → ${BACKUP_FILE}" cp -a "${COMPOSE_FILE}" "${BACKUP_FILE}" # ── 2. 새 compose 파일 작성 ──────────────────────────────────── blue "[2/5] 새 docker-compose.yml 작성" cat > "${COMPOSE_FILE}" <<'YAML' services: app: build: . image: orderlist-app:latest container_name: orderlist-app restart: unless-stopped env_file: - .env environment: # .env에 적힌 값이 컨테이너에서 그대로 보입니다. # 컨테이너 내부에서 DB_HOST는 반드시 postgres-db 여야 합니다. DB_HOST: ${DB_HOST:-postgres-db} # 호스트 포트 바인딩 없음. NPM 리버스 프록시(npm_default 네트워크)로만 접근. expose: - "8000" networks: - postgres_net - npm_net - default healthcheck: test: ["CMD", "curl", "-fsS", "http://localhost:8000/health/db"] interval: 30s timeout: 5s retries: 5 start_period: 20s networks: # 이미 운영 중인 postgres-db 컨테이너가 속해 있는 외부 네트워크 postgres_net: external: true name: ${POSTGRES_NETWORK:-postgres-net} # NPM(Nginx Proxy Manager) 컨테이너가 속해 있는 외부 네트워크 npm_net: external: true name: npm_default default: driver: bridge YAML # ── 3. 재기동 ─────────────────────────────────────────────────── blue "[3/5] docker compose up -d" cd "${COMPOSE_DIR}" docker compose up -d # ── 4. 검증 ───────────────────────────────────────────────────── blue "[4/5] 검증" echo yellow "── 호스트 포트 바인딩 (8000 외부 노출 여부) ──" PORT_LINE="$(docker ps --format '{{.Names}}\t{{.Ports}}' | grep '^orderlist-app' || true)" echo "${PORT_LINE}" if echo "${PORT_LINE}" | grep -q '0\.0\.0\.0:8000'; then red "FAIL: 0.0.0.0:8000 매핑이 아직 남아 있습니다." exit 1 else green "OK: 호스트 0.0.0.0:8000 매핑 제거됨." fi echo yellow "── orderlist-app 의 네트워크 ──" NETS="$(docker inspect orderlist-app --format '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}')" echo "${NETS}" for required in npm_default; do if ! echo " ${NETS} " | grep -q " ${required} "; then red "FAIL: ${required} 네트워크에 연결되지 않았습니다." exit 1 fi done green "OK: npm_default 네트워크 연결 확인." # 재시도 helper — uvicorn 기동 + DB 연결까지 최대 ${1}초 대기 wait_until_200() { local desc="$1"; shift local cmd="$*" local code="" local i for i in $(seq 1 60); do code="$(eval "${cmd}" 2>/dev/null || true)" if [[ "${code}" == "200" ]]; then echo "HTTP ${code} (try ${i})" green "OK: ${desc}" return 0 fi sleep 1 done red "FAIL: ${desc} — 최종 HTTP ${code:-ERR} (60초 대기 후에도 200 미수신)" echo yellow "── orderlist-app 최근 로그 (디버그용) ──" docker logs --tail 40 orderlist-app || true return 1 } echo yellow "── NPM 컨테이너에서 orderlist-app 호출 (최대 60초 대기) ──" wait_until_200 "NPM 컨테이너에서 200 응답" \ "docker exec npm-app sh -lc \"curl -fsS -o /dev/null -w '%{http_code}' http://orderlist-app:8000/health/db\"" echo yellow "── 외부 도메인 헬스 체크 (최대 60초 대기) ──" wait_until_200 "https://dbx.no1king.freeddns.org/orderlist/health/db → 200" \ "curl -fsS -o /dev/null -w '%{http_code}' https://dbx.no1king.freeddns.org/orderlist/health/db" # ── 5. 완료 ───────────────────────────────────────────────────── echo green "[5/5] 완료. 백업: ${BACKUP_FILE}" echo yellow "남은 수동 점검 (다른 머신/서버 공인 IP에서):" echo " curl --max-time 5 http://<서버공인IP>:8000/ # → Connection refused 여야 정상"