Files
nomadamas__k-skill/docs/deploy-k-skill-proxy.md
2026-06-25 22:04:42 +09:00

11 KiB

k-skill-proxy 배포 가이드 (gpu01 + cron + Docker)

k-skill-proxy 프로덕션은 이제 Google Cloud Run이 아니라 gpu01의 Docker 컨테이너로 운영한다. GitHub Actions의 main push/merge 자동 배포는 프로덕션 배포 경로가 아니다.

이 문서는 gpu01의 cron 기반 자동 배포, 시크릿/env 배치, 헬스체크, 로그, 롤백, 그리고 기존 GCP 리소스 정리 절차를 정리한다. 일반 contributor는 읽지 않아도 되며, 프록시 운영 maintainer가 gpu01 운영·복구·인계를 할 때 기준 문서로 사용한다.

운영 사실

항목 값
프로덕션 호스트 gpu01
공개 도메인 https://k-skill-proxy.nomadamas.org
런타임 Docker container
컨테이너 이름 k-skill-proxy
후보 컨테이너 이름 k-skill-proxy-candidate
이미지 태그 k-skill-proxy:<git-sha>
컨테이너 이미지 정의 packages/k-skill-proxy/Dockerfile
Docker build context repo root (/opt/k-skill/current 기준)
배포 스크립트 scripts/deploy-k-skill-proxy-gpu01.sh
권장 repo checkout /opt/k-skill/current
비밀 env 파일 /etc/k-skill-proxy/secrets.env (0600 또는 더 엄격)
배포 대상 config /etc/k-skill-proxy/deploy.env (0640 또는 더 엄격)
로그 디렉터리 /var/log/k-skill-proxy
상태 디렉터리 /var/lib/k-skill-proxy
프로덕션 포트 host 127.0.0.1:4020 → container 8080
후보 smoke 포트 host 127.0.0.1:4021 → container 8080
배포 트리거 gpu01 cron이 host-configured deploy SHA/ref만 배포

배포 의미론

  • main merge 자체는 프로덕션을 바꾸지 않는다.
  • gpu01 cron은 /etc/k-skill-proxy/deploy.env에 명시된 KSKILL_PROXY_DEPLOY_SHA 또는 KSKILL_PROXY_DEPLOY_REF만 배포한다.
  • 배포 대상이 설정되지 않았으면 스크립트는 fail-closed로 종료한다.
  • 스크립트는 절대 origin/main을 기본값으로 삼지 않는다.
  • 권장 초기 컷오버 방식은 KSKILL_PROXY_DEPLOY_SHA=<full commit sha>를 고정하는 것이다.
  • git 기반 승격이 더 편하면 KSKILL_PROXY_DEPLOY_REF=production 같은 전용 production branch/ref를 쓰되, 그 ref를 갱신하는 행위가 곧 프로덕션 승격이다.

배포 흐름

cron이 실행하는 한 번의 배포는 다음 순서로만 성공한다.

  1. flock으로 중복 실행을 막는다.
  2. /etc/k-skill-proxy/deploy.env와 /etc/k-skill-proxy/secrets.env를 검증한다.
  3. repo root에서 git fetch --prune origin을 실행한다.
  4. 명시된 deploy SHA/ref를 commit SHA로 해석한다.
  5. 현재 배포된 SHA와 같으면 아무 것도 바꾸지 않고 종료한다.
  6. 기존 serving state를 /var/lib/k-skill-proxy/rollback-state.env에 저장한다.
  7. checkout을 resolved SHA로 detach/force 이동한 뒤 repo root build context로 이미지를 빌드한다.
  8. 후보 컨테이너를 127.0.0.1:4021에서 띄운다.
  9. 후보 컨테이너의 local /health가 ok: true이고 필수 upstream 설정 boolean이 true인지 확인한다.
  10. 기존 프로덕션 컨테이너를 교체해 127.0.0.1:4020에 새 컨테이너를 띄운다.
  11. https://k-skill-proxy.nomadamas.org/health public smoke를 수행한다.
  12. 대표 read-only public route smoke를 수행한다.
  13. 모든 public smoke가 통과한 뒤에만 /var/lib/k-skill-proxy/deployed-sha를 갱신한다.

public smoke 실패는 경고가 아니라 배포 실패다. 상태 파일을 갱신하지 않고, 가능한 경우 이전 컨테이너/image/port/routing state로 rollback을 시도한다.

1회성 gpu01 셋업

sudo install -d -m 0755 /opt/k-skill
sudo install -d -m 0750 /etc/k-skill-proxy
sudo install -d -m 0750 /var/lib/k-skill-proxy
sudo install -d -m 0750 /var/log/k-skill-proxy

# repo checkout. 운영자가 실제 deploy user 권한에 맞춰 owner를 조정한다.
sudo git clone https://github.com/NomaDamas/k-skill.git /opt/k-skill/current

필수 도구:

  • git
  • docker
  • curl
  • python3
  • flock (util-linux)

reverse proxy는 https://k-skill-proxy.nomadamas.org를 gpu01의 127.0.0.1:4020으로 전달해야 한다. DNS/TLS/reverse proxy 설정은 repo 밖의 호스트 설정이므로 컷오버 때 public /health로 반드시 검증한다.

배포 대상 설정

/etc/k-skill-proxy/deploy.env 예시:

# 가장 안전한 초기 컷오버: 정확한 commit SHA 고정
KSKILL_PROXY_DEPLOY_SHA=0123456789abcdef0123456789abcdef01234567

# 또는 전용 production ref를 의도적으로 운용할 때만 사용
# KSKILL_PROXY_DEPLOY_REF=production

KSKILL_PROXY_REPO_DIR=/opt/k-skill/current
KSKILL_PROXY_CONTAINER_NAME=k-skill-proxy
KSKILL_PROXY_IMAGE_NAME=k-skill-proxy
KSKILL_PROXY_HOST_PORT=4020
KSKILL_PROXY_CANDIDATE_PORT=4021
KSKILL_PROXY_CONTAINER_PORT=8080
KSKILL_PROXY_PUBLIC_BASE_URL=https://k-skill-proxy.nomadamas.org
KSKILL_PROXY_STATE_DIR=/var/lib/k-skill-proxy
KSKILL_PROXY_LOG_DIR=/var/log/k-skill-proxy

둘 다 설정되어 있으면 KSKILL_PROXY_DEPLOY_SHA가 우선한다. 둘 다 없으면 배포하지 않는다.

프로덕션 승격 절차

pinned SHA 방식

  1. dev → main merge 후 검증할 commit SHA를 정한다.
  2. gpu01에서 /etc/k-skill-proxy/deploy.env의 KSKILL_PROXY_DEPLOY_SHA를 해당 full SHA로 바꾼다.
  3. cron이 다음 주기에 배포하거나, 운영자가 스크립트를 수동 실행한다.

production branch 방식

  1. production 같은 전용 branch/ref를 운영한다.
  2. maintainer가 의도적으로 해당 ref를 fast-forward 또는 갱신한다.
  3. gpu01 cron은 KSKILL_PROXY_DEPLOY_REF=production만 해석해서 배포한다.

이 방식을 써도 main merge 단독으로는 배포되지 않는다.

시크릿과 runtime env

/etc/k-skill-proxy/secrets.env는 repo에 넣지 않는다. root 또는 전용 deploy user만 읽을 수 있게 0600 또는 더 엄격하게 둔다.

필수/운영 env 예시:

AIR_KOREA_OPEN_API_KEY=...
KMA_OPEN_API_KEY=...
SEOUL_OPEN_API_KEY=...
HRFCO_OPEN_API_KEY=...
OPINET_API_KEY=...
DATA_GO_KR_API_KEY=...
DATA4LIBRARY_AUTH_KEY=...
FOODSAFETYKOREA_API_KEY=...
KAKAO_REST_API_KEY=...
KEDU_INFO_KEY=...
KRX_API_KEY=...
KOSIS_API_KEY=...
NAVER_SEARCH_CLIENT_ID=...
NAVER_SEARCH_CLIENT_SECRET=...
LAW_OC=...

# 선택
LAW_REFERER=
LAW_USER_AGENT=

# runtime knobs
KSKILL_PROXY_HOST=0.0.0.0
KSKILL_PROXY_NAME=k-skill-proxy
KSKILL_PROXY_CACHE_TTL_MS=300000
KSKILL_PROXY_RATE_LIMIT_WINDOW_MS=60000
KSKILL_PROXY_RATE_LIMIT_MAX=60
PORT=8080

Docker access 보안 경계

Docker daemon, Docker socket, docker group, sudo docker, 컨테이너 inspect/start 권한은 모두 프로덕션 시크릿 접근 권한과 동일하게 취급한다. 컨테이너 env, bind mount, 로그, 대체 컨테이너 실행을 통해 upstream API key를 읽을 수 있기 때문이다.

  • 편의상 일반 사용자를 docker group에 추가하지 않는다.
  • cron user는 프로덕션 운영자로 간주하고 의도적으로 선택한다.
  • 배포 스크립트는 set -x를 쓰지 않고 env 값을 로그에 출력하지 않는다.
  • Docker socket을 외부에 노출하지 않는다.

cron 설정

crontab 예시:

*/10 * * * * flock -n /var/lock/k-skill-proxy-deploy.lock /opt/k-skill/current/scripts/deploy-k-skill-proxy-gpu01.sh >> /var/log/k-skill-proxy/deploy.log 2>&1

cron은 production deploy trigger지만, 배포 대상은 항상 /etc/k-skill-proxy/deploy.env의 explicit SHA/ref다. cron 주기가 돌아도 대상 SHA가 바뀌지 않았으면 스크립트는 skip한다.

수동 배포/점검

/opt/k-skill/current/scripts/deploy-k-skill-proxy-gpu01.sh
curl -fsS https://k-skill-proxy.nomadamas.org/health

로그:

tail -f /var/log/k-skill-proxy/deploy.log
docker logs --tail=200 k-skill-proxy

smoke test 기준

local health

curl -fsS http://127.0.0.1:4021/health

candidate 단계에서는 후보 포트 4021, production 단계에서는 4020을 확인한다. /health JSON은 ok: true여야 하며, upstreams의 *Configured boolean이 true인지 확인한다.

public health

curl -fsS https://k-skill-proxy.nomadamas.org/health

public health 실패는 배포 실패다. deployed-state를 갱신하지 않는다.

대표 route smoke

기본 대표 route는 health만으로 잡히지 않는 public routing/API path 회귀를 확인하기 위한 read-only 요청이다. 운영 환경에서 upstream quota/availability를 고려해 route를 조정할 수 있다.

curl -fsS --get 'https://k-skill-proxy.nomadamas.org/v1/fine-dust/report' \
  --data-urlencode 'stationName=종로구'

upstream 자체 장애가 확인된 경우에만 운영자가 로그에 사유를 남기고 route smoke를 별도 처리한다. public /health는 항상 필수다.

rollback

배포 스크립트는 production switch 전에 /var/lib/k-skill-proxy/rollback-state.env에 다음 값을 저장한다.

  • timestamp
  • previous deployed SHA
  • previous image tag 또는 image ID
  • previous container name
  • previous container ID
  • previous host port → container port mapping
  • previous reverse-proxy upstream target
  • previous routing state
  • previous health status

수동 rollback 기본 절차:

sudo -E /opt/k-skill/current/scripts/deploy-k-skill-proxy-gpu01.sh --rollback
curl -fsS https://k-skill-proxy.nomadamas.org/health

문제가 이미지가 아니라 reverse proxy/DNS/TLS인 경우에는 저장된 routing state에 따라 이전 upstream target 또는 proxy 설정을 복원하고 proxy를 reload한다. rollback은 이전 이미지를 띄우는 것만으로 충분하다고 가정하지 않는다.

문제 해결

증상 확인할 것
no deploy target configured /etc/k-skill-proxy/deploy.env에 KSKILL_PROXY_DEPLOY_SHA 또는 KSKILL_PROXY_DEPLOY_REF가 있는지 확인
health의 *Configured가 false /etc/k-skill-proxy/secrets.env에 해당 upstream key가 있는지 확인
Docker build 실패 repo root에서 -f packages/k-skill-proxy/Dockerfile .로 빌드되는지 확인
public health 실패 DNS, TLS, reverse proxy upstream, firewall, container port mapping 확인
cron은 도는데 배포가 안 됨 deployed SHA와 resolved SHA가 같은지, lock이 오래 잡혀 있는지, 로그 권한 확인
rollback 후에도 public 장애 reverse proxy/routing state가 이전 target으로 복원됐는지 확인

기존 GCP/Cloud Run 정리

GitHub Actions의 Cloud Run 자동 배포는 즉시 비활성화한다. 다만 Cloud Run, Artifact Registry, WIF, Secret Manager 리소스는 gpu01 안정화 기간 동안 legacy rollback/비교용으로 잠시 남길 수 있다.

정리 순서:

  1. gpu01 public /health와 대표 route smoke가 안정적으로 통과하는지 확인한다.
  2. GitHub에 push-to-main 배포 workflow가 남아 있지 않은지 확인한다.
  3. rollback window가 끝나면 Cloud Run traffic, Artifact Registry image, WIF provider/service account, Secret Manager secret을 수동 정리한다.
  4. 정리 기록은 운영 로그나 PR 본문에 남긴다.