11#! /usr/bin/env bash
22set -euo pipefail
33
4- cd /srv/docsa/infra
4+ # 사용법:
5+ # /srv/docsa/infra/deploy.sh dev
6+ # /srv/docsa/infra/deploy.sh staging
7+ # 환경변수(선택):
8+ # DEPLOY_TAG : 덮어쓸 이미지 태그(dev, dev-<sha>, staging, staging-<sha> 등)
9+ # IMAGE_BASE : GHCR 이미지 경로 (기본: ghcr.io/prgrms-web-devcourse-final-project/docsa-backend)
10+ # SERVICE : 배포할 서비스명 (기본 app)
11+ # HEALTH_TIMEOUT : 헬스 대기시간 초 (기본 120)
512
6- # 특정 태그로 배포시 DEPLOY_TAG=...
7- IMAGE_BASE=" ghcr.io/prgrms-web-devcourse-final-project/docsa-backend"
8- TAG=" ${DEPLOY_TAG:- } " # 비어있으면 compose에 적힌 태그 사용
13+ TARGET=" ${1:- } " # ← dev 또는 staging 인자 필수
14+ if [[ " $TARGET " != " dev" && " $TARGET " != " staging" ]]; then
15+ echo " Usage: $0 <dev|staging>"
16+ exit 2
17+ fi
18+
19+ IMAGE_BASE=" ${IMAGE_BASE:- ghcr.io/ prgrms-web-devcourse-final-project/ docsa-backend} "
20+ SERVICE=" ${SERVICE:- app} "
21+ HEALTH_TIMEOUT=" ${HEALTH_TIMEOUT:- 120} "
22+
23+ cd " $( dirname " $0 " ) "
24+ ROOT=" $( pwd) "
25+
26+ # 타깃에 따라 compose/env 자동 선택
27+ if [[ " $TARGET " == " dev" ]]; then
28+ COMPOSE_FILE=" $ROOT /docker-compose.yml"
29+ ENV_FILE=" $ROOT /.env"
30+ else
31+ COMPOSE_FILE=" $ROOT /docker-compose.stg.yml"
32+ ENV_FILE=" $ROOT /.stg.env"
33+ fi
34+
35+ TAG=" ${DEPLOY_TAG:- $TARGET } " # ← 기본은 채널 태그(dev|staging), 입력 있으면 우선
36+
37+ # 임시 override 파일로 이미지 태그만 덮어쓰기
938OVR=" "
1039if [[ -n " $TAG " ]]; then
11- OVR=" docker-compose.override.deploy.yml"
40+ OVR=" $ROOT / docker-compose.override.deploy.yml"
1241 cat > " $OVR " << EOF
1342services:
14- app :
43+ ${SERVICE} :
1544 image: ${IMAGE_BASE} :${TAG}
1645EOF
1746fi
1847
19- FILES=(-f docker-compose.yml)
20- [[ -n " $OVR " ]] && FILES+=(-f " $OVR " )
48+ # docker compose 인자 구성 (-p로 프로젝트 격리, --env-file로 env 명시)
49+ ARGS=(-f " $COMPOSE_FILE " )
50+ [[ -n " $OVR " ]] && ARGS+=(-f " $OVR " )
51+ ARGS+=(--env-file " $ENV_FILE " )
52+
53+ echo " [deploy] target=$TARGET tag=$TAG "
54+ echo " [deploy] compose=$COMPOSE_FILE env=$ENV_FILE service=$SERVICE "
55+
56+ # 1) 유효성 검사(문법/치환 확인)
57+ docker compose " ${ARGS[@]} " config > /dev/null
2158
22- # 최신 이미지 받고 교체
23- docker compose " ${FILES [@]} " pull app
24- docker compose " ${FILES [@]} " up -d app
59+ # 2) 이미지 풀 + 대상 서비스만 업데이트
60+ docker compose " ${ARGS [@]} " pull " $SERVICE " || true
61+ docker compose " ${ARGS [@]} " up -d " $SERVICE "
2562
26- # 헬스체크 대기 (최대 120s)
27- echo -n " Waiting for app (docsa-app) to be healthy"
63+ # 3) 컨테이너 ID를 compose로 조회(이름 하드코딩 회피)
64+ CID=" $( docker compose " ${ARGS[@]} " ps -q " $SERVICE " | tail -n1 || true) "
65+ if [[ -z " $CID " ]]; then
66+ echo " No container found for service '$SERVICE ' (project $PROJECT )" ; exit 1
67+ fi
68+
69+ # 4) 헬스체크 대기
70+ echo -n " Waiting for $SERVICE to be healthy"
2871ok=0
29- for i in {1..60}; do
30- status=" $( docker inspect -f ' {{.State.Health.Status}}' docsa-app 2> /dev/null || echo none) "
31- if [[ " $status " == " healthy" ]]; then ok=1; echo -e " \nApp healthy" ; break ; fi
72+ ITER=$(( HEALTH_TIMEOUT / 2 ))
73+ for _ in $( seq 1 " $ITER " ) ; do
74+ status=" $( docker inspect -f ' {{.State.Health.Status}}' " $CID " 2> /dev/null || echo none) "
75+ if [[ " $status " == " healthy" ]]; then ok=1; echo -e " \n$SERVICE healthy" ; break ; fi
3276 sleep 2; echo -n " ."
3377done
3478
35- # 임시 override 제거 & 청소
36- [[ -n " $OVR " ]] && rm -f " $OVR "
79+ # 5) 임시 override 청소 + 이미지 정리
80+ [[ -n " $OVR " ]] && rm -f " $OVR " || true
3781docker image prune -f > /dev/null 2>&1 || true
3882
39- # 실패 처리
83+ # 6) 실패 시 로그 출력 후 종료
4084if [[ $ok -eq 0 ]]; then
41- echo -e " \nApp failed to become healthy (status=$status )"
42- docker logs --tail=200 docsa-app || true
85+ echo -e " \n $SERVICE failed to become healthy (status=${ status:- unknown} )"
86+ docker logs --tail=200 " $CID " || true
4387 exit 1
44- fi
88+ fi
0 commit comments