#!/usr/bin/env bash ############################################################################### # [폐쇄망 안에서 실행] compose 로 전체 스택을 띄운다. # # ./bin/up-compose.sh # docker compose # ./bin/up-compose.sh podman # podman-compose (= bin/up-podman-compose.sh) # ./bin/up-compose.sh --dry-run # 아무것도 만들지 않고 실행될 명령만 출력 # # depends_on 의 condition 해석은 구현마다 다르므로, 여기서는 compose 에 맡기지 않고 # 단계별로 올린 뒤 스크립트가 직접 healthy/exit0 를 기다린다. 두 엔진에서 같은 순서로 # 뜨고, 어디서 멈췄는지도 로그에 남는다. ############################################################################### . "$(dirname -- "${BASH_SOURCE[0]}")/lib.sh" parse_common_args "$@" resolve_engine "${ENGINE_ARG:-docker}" COMPOSE_FILE="${DEPLOY_DIR}/compose.yml" # ── 사전 준비 ─────────────────────────────────────────────────────────────── # COMPOSE 배열을 만들기 전에 override 파일을 확정해야 한다 (-f 순서가 그대로 굳는다). load_env check_replica_counts check_litellm_config PROJECT="${COMPOSE_PROJECT_NAME:-pi-continuum}" require_conf # bin/apply-patches.sh 가 만든 현장 패치 override. # compose 는 -f 를 여러 번 주면 서비스별 volumes·ports 를 이어 붙인다. PATCH_FILE=() if [ -f "${DEPLOY_DIR}/compose.patches.yml" ]; then PATCH_FILE=(-f "${DEPLOY_DIR}/compose.patches.yml") log "현장 패치 적용: compose.patches.yml" fi # .env 의 EXPOSE_*_PORT 로 호스트 포트 override 를 만든다 (없으면 파일도 지운다). PORTS_FILE=() write_ports_override [ -n "${PORTS_OVERRIDE}" ] && PORTS_FILE=(-f "${PORTS_OVERRIDE}") if [ "${ENGINE_KIND}" = podman ]; then if command -v podman-compose >/dev/null 2>&1; then COMPOSE=(podman-compose -f "${COMPOSE_FILE}" ${PATCH_FILE[@]+"${PATCH_FILE[@]}"} ${PORTS_FILE[@]+"${PORTS_FILE[@]}"}) elif podman compose version >/dev/null 2>&1; then COMPOSE=(podman compose -f "${COMPOSE_FILE}" ${PATCH_FILE[@]+"${PATCH_FILE[@]}"} ${PORTS_FILE[@]+"${PORTS_FILE[@]}"}) else die "podman-compose 가 없다. 'pip install podman-compose' 또는 배포판 패키지로 설치하거나, compose 없이 띄우는 ./bin/up-podman-run.sh 를 쓴다." fi # podman 의 DNS 주소는 네트워크마다 달라 nginx 에 127.0.0.11 을 못 박을 수 없다. export NGINX_UPSTREAM="${NGINX_UPSTREAM:-podman}" else if docker compose version >/dev/null 2>&1; then COMPOSE=(docker compose -f "${COMPOSE_FILE}" ${PATCH_FILE[@]+"${PATCH_FILE[@]}"} ${PORTS_FILE[@]+"${PORTS_FILE[@]}"}) elif command -v docker-compose >/dev/null 2>&1; then COMPOSE=(docker-compose -f "${COMPOSE_FILE}" ${PATCH_FILE[@]+"${PATCH_FILE[@]}"} ${PORTS_FILE[@]+"${PORTS_FILE[@]}"}) else die "docker compose 가 없다. compose 없이 띄우는 ./bin/up-docker-run.sh 를 쓴다." fi export NGINX_UPSTREAM="${NGINX_UPSTREAM:-docker}" fi compose() { if [ "${DRY_RUN}" = true ]; then # up 만 흉내 낸다. config/ps 같은 읽기 명령은 그대로 실행해도 안전하다. case "$1" in up) dry "${COMPOSE[*]} $*"; return 0 ;; esac fi ( cd "${DEPLOY_DIR}" && "${COMPOSE[@]}" "$@" ) } wait_svc() { # dry-run 이면 기다릴 컨테이너가 없다 [ "${DRY_RUN}" = true ] && { dry "wait $1"; return 0; } local mode="$2" if [ "${mode}" = exit0 ]; then wait_exit0 "$(compose_cid "$1")" "$3" else wait_healthy "$(compose_cid "$1")" "$3"; fi } compose_cid() { # 서비스 이름 → 컨테이너 ID (엔진별 이름 규칙에 기대지 않는다) local svc="$1" cid cid="$(compose ps -q "${svc}" 2>/dev/null | head -1 || true)" if [ -z "${cid}" ]; then cid="$(engine ps -aq --filter "label=com.docker.compose.service=${svc}" \ --filter "label=com.docker.compose.project=${PROJECT}" 2>/dev/null | head -1 || true)" fi if [ -z "${cid}" ]; then cid="$(engine ps -aq --filter "label=io.podman.compose.service=${svc}" 2>/dev/null | head -1 || true)" fi [ -n "${cid}" ] || die "서비스 ${svc} 의 컨테이너를 찾지 못했다." printf '%s\n' "${cid}" } # ── 사전 점검 ─────────────────────────────────────────────────────────────── [ "${DRY_RUN}" = true ] || prepare_log_dirs log "이미지 적재 확인" while IFS= read -r tag; do [ -n "${tag}" ] || continue engine image inspect "${tag}" >/dev/null 2>&1 \ || die "이미지 ${tag} 가 없다. 먼저 ./bin/load-images.sh ${ENGINE_KIND} 를 실행한다." done < <(required_images) log "compose 파일 검증" compose config --quiet || die "compose 파일 검증 실패." # ── 기동 ──────────────────────────────────────────────────────────────────── log "1/4 인프라 (postgres · redis · minio)" compose up -d postgres redis minio wait_svc postgres healthy 180 wait_svc redis healthy 120 wait_svc minio healthy 180 log "2/4 MinIO 버킷 생성" compose up -d minio-init wait_svc minio-init exit0 120 log "3/4 bootstrap (migrate + seeding)" compose up -d bootstrap wait_svc bootstrap exit0 1800 log "4/4 애플리케이션 · 처리 서비스 · 엣지" if [ "${LITELLM_ENABLED}" = true ]; then compose up -d litellm wait_svc litellm healthy 300 fi compose up -d web worker-default worker-high worker-log scheduler alpha-processing nginx wait_svc web healthy 300 # 모델 적재에 시간이 걸린다. start_period 60s + 재시도. wait_svc alpha-processing healthy 600 if [ "${DRY_RUN}" = true ]; then log "dry-run 끝 — 실제로 만들어진 것은 없다" exit 0 fi log "기동 완료" compose ps cat <