Skip to content

Deployment

Jinmu Go edited this page Aug 19, 2026 · 10 revisions

Deployment

백엔드는 OCI 위의 k3s 클러스터에 GitOps 방식으로 배포한다.

k3s는 경량 Kubernetes 배포판이다. 따라서 Deployment, Service, Ingress, Kustomize 같은 표준 Kubernetes API를 쓰지만, 실제 운영 클러스터와 네트워크 운영 주체는 k3s이다.

역할 분리

위치 담당
dnd-15th-3-backend Dockerfile, GHCR 이미지, deploy/k8s 매니페스트, 환경별 이미지 태그
ArgoCD 이 레포의 배포 매니페스트 감시 및 k3s 반영
인프라 관리자 전용 레포 k3s namespace, ArgoCD Project/RBAC, Cloudflare Tunnel/Access 설정

팀원은 이 백엔드 레포만 수정한다. 인프라 관리자 레포나 운영 클러스터에 직접 kubectl apply하지 않는다.

환경

Git branch k3s namespace Kustomize overlay API hostname 용도
develop dnd-15th-3-dev deploy/k8s/overlays/develop momo-dev.jinmu.me 팀 통합 개발 환경
main dnd-15th-3-prod deploy/k8s/overlays/main momo.jinmu.me 안정 배포 환경

기능 브랜치는 develop으로 PR을 보내고, 검증된 developmain으로 PR을 보낸다.

GitOps 배포 구조

flowchart LR
    infra["atlas-infra<br/>main"]
    backend["dnd-15th-3-backend<br/>develop / main"]
    appConfig["deploy/argocd<br/>Application definitions"]
    devOverlay["deploy/k8s/overlays/develop"]
    prodOverlay["deploy/k8s/overlays/main"]
    ghcr["GHCR<br/>linux/arm64 image"]

    root["Argo CD Application: apps"]
    pointer["Argo CD Application: dnd-15th-3"]
    devApp["Application: dnd-15th-3-dev"]
    prodApp["Application: dnd-15th-3-prod"]
    updater["Image Updater<br/>develop only"]

    devNamespace["Namespace: dnd-15th-3-dev<br/>momo-dev.jinmu.me"]
    prodNamespace["Namespace: dnd-15th-3-prod<br/>momo.jinmu.me"]

    infra -->|root App of Apps| root
    root -->|creates| pointer
    pointer -->|reads deploy/argocd| appConfig
    appConfig -->|defines| devApp
    appConfig -->|defines| prodApp
    devApp -->|reads| devOverlay
    prodApp -->|reads| prodOverlay
    devOverlay -->|sync and selfHeal| devNamespace
    prodOverlay -->|sync and selfHeal| prodNamespace
    backend -->|push and workflow| ghcr
    ghcr -->|new develop tag| updater
    updater -->|live image parameter| devApp
Loading

런타임 및 데이터 흐름

flowchart TB
    client["Client"]
    cloudflare["Cloudflare Edge<br/>momo-dev.jinmu.me / momo.jinmu.me"]
    tunnel["Cloudflare Tunnel<br/>cloudflared"]
    traefik["Traefik<br/>NodePort 30080"]
    ingress["Ingress: momo-api<br/>path /"]
    apiService["Service: momo-api<br/>port 80 → 3000"]
    api["Deployment: momo-api<br/>1 replica per environment<br/>dnd-15th-3-dev / dnd-15th-3-prod"]
    dbService["Service: momo-postgres<br/>port 5432"]
    postgres["StatefulSet: momo-postgres<br/>1 replica"]
    pvc["local-path PVC<br/>dev 5Gi / prod 10Gi"]
    backup["CronJob: momo-postgres-backup<br/>04:00 KST"]
    objectStorage["OCI Object Storage<br/>PostgreSQL dump"]

    client --> cloudflare
    cloudflare -->|public hostname route| tunnel
    tunnel -->|HTTP to node port| traefik
    traefik --> ingress
    ingress --> apiService
    apiService --> api
    api --> dbService
    dbService --> postgres
    postgres --> pvc
    backup --> dbService
    backup --> objectStorage
Loading

현재 API와 PostgreSQL은 각 환경에서 1 replica로 실행된다. API replica를 늘려도 PostgreSQL은 단일 인스턴스이므로 전체 구조가 고가용성이 되는 것은 아니다.

배포 흐름

feature branch PR → develop merge
  → GitHub Actions: linux/arm64 이미지 build
  → GHCR: develop-<commit-sha> push
  → deploy/k8s/overlays/develop 이미지 태그 commit
  → ArgoCD가 변경 감지
  → k3s dnd-15th-3-dev namespace rollout

develop → main PR도 같은 흐름으로 main 환경에 배포

이미지 태그는 develop-<sha> 또는 main-<sha>의 불변 태그다. latest를 사용하지 않으므로, 어느 코드가 실행 중인지 Git과 ArgoCD에서 추적하고 이전 SHA로 롤백할 수 있다.

팀원이 수정하는 파일

Dockerfile                         # 애플리케이션 컨테이너
.github/workflows/image.yml        # ARM build + 배포 태그 갱신
deploy/k8s/base/                   # 공통 Deployment, Service, Ingress
deploy/k8s/overlays/develop/       # develop 환경 차이
deploy/k8s/overlays/main/          # main 환경 차이
deploy/sealed-secrets.pub.pem      # Secret 암호화용 공개 인증서

배포된 앱은 /api/docs에서 Swagger UI를 제공한다. API 명세는 배포 후 해당 경로에서 확인한다.

현재 readiness/liveness/startup probe는 다음 health endpoint를 사용한다.

엔드포인트 용도 확인 항목
GET /health (dev) App level 종합 상태 Startup/readiness probe, DB + PostGIS + OCI Storage
GET /health (prod) App level 종합 상태 Startup/readiness probe, DB + OCI Storage
GET /health/live Liveness probe 프로세스 생존

Kubernetes의 startupreadiness probe는 /health, liveness probe는 /health/live를 사용한다. 현재 dev /healthdatabase, postgis, storage를 함께 확인한다. 정상일 때 info.postgisdetails.postgis에는 status: "up"과 설치 버전이 포함되며, PostGIS extension이 없거나 버전 쿼리가 실패하면 503을 반환한다. prod는 PostGIS 승격 전이므로 기존 database·storage 계약을 유지한다. /health/live는 두 환경 모두 외부 의존성과 무관하게 프로세스 생존 여부만 확인한다.

새 기능이 시작 의존성(DB 등)을 추가하면 readiness/health 기준도 함께 갱신한다.

시크릿

평문 .env, API 키, DB 비밀번호는 commit/PR/채팅에 올리지 않는다. 필요한 값은 공개 인증서로 SealedSecret을 만들고 암호화된 파일만 환경 overlay에 추가한다.

kubeseal --format=yaml --cert deploy/sealed-secrets.pub.pem \
  < secret.plain.yaml > deploy/k8s/overlays/develop/secret.sealed.yaml
rm secret.plain.yaml

공개 인증서는 암호화만 가능하고 복호화는 불가하다. 실제 Secret 값은 k3s의 sealed-secrets controller만 복호화한다.

개발자의 dev DB 접근

cluster-admin kubeconfig는 인프라 관리자만 보유한다. 개발자가 dev 클러스터 DB에 붙어야 할 때는 네임스페이스 한정 최소 권한 kubeconfig를 발급한다.

환경 정책
dev 제한 kubeconfig 발급 — DB 자격증명 Secret 읽기 + port-forward만 허용
prod 직접 접근 불가 — 필요 시 인프라 관리자가 쿼리 대행

dev RBAC(db-developer)은 deploy/k8s/overlays/develop/db-access.yaml에 정의되어 있고, 허용 범위는 정확히 다음뿐이다.

  • momo-postgres-auth / momo-statistics-postgres-auth Secret 읽기
  • pods/portforward 생성 (port-forward 대상 해석에 필요한 조회 포함)

발급 플로우(인프라 관리자):

# 1. db-access.yaml이 develop에 머지되어 ArgoCD가 sync한 뒤
bash scripts/make-db-kubeconfig.sh   # kubeconfig.db-developer.yaml 생성

# 2. 생성된 파일을 안전한 채널로 전달 (commit/채팅 붙여넣기 금지)

개발자 사용법:

export KUBECONFIG=$HOME/.kube/db-developer.yaml
mise run cluster-check   # 접속 확인
mise run dev-api         # 또는 mise run db-forward-dev

토큰 유출이 의심되면 db-developer-token Secret을 삭제·재생성해 전체 발급분을 일괄 폐기할 수 있다. 구성원별 개별 발급이 필요하면 ServiceAccount를 분리한다.

확인과 장애 전달

  • PostgreSQL/PostGIS 이미지 pull, migration, backup, 노드 DNS와 DB 복구는 PostGIS K3s Operations 런북을 따른다.
  • ArgoCD dnd-viewer 계정은 DND 앱과 로그만 read-only로 볼 수 있다.
  • dev 배포에서는 Sync operation의 PreSync 단계에서 momo-api-migration Hook이 성공했는지 확인한다. 성공한 Hook Job은 자동 삭제될 수 있으므로 Pod/Job 목록에 없으면 ArgoCD operation history에서 실행 결과와 로그를 확인한다.
  • dev 배포 후 /health200 OK이고 database, postgis, storage가 모두 status: "up"인지 확인한다. prod는 PostGIS 승격 전까지 database, storage를 확인한다.
  • ImagePullBackOff, CrashLoopBackOff, PVC Pending, Secret 누락은 ArgoCD 상태와 이벤트를 첨부해 인프라 관리자에게 전달한다.
  • 앱을 멈추려면 Deployment replica 또는 CronJob suspend를 Git PR로 변경한다. UI에서 Delete/Sync하지 않는다.
  • 영구 삭제는 PVC까지 지울 수 있으므로 팀원이 단독으로 진행하지 않는다.

첫 배포 전 관리자 체크

  1. GitHub Actions의 GHCR package write 권한과 public package visibility 확인
  2. ArgoCD Application이 develop/main overlay를 각각 감시하도록 등록
  3. Cloudflare Tunnel에 momo-dev.jinmu.me, momo.jinmu.me hostname 추가
  4. ArgoCD dnd-viewer 계정과 Cloudflare Access 이메일 allowlist 설정

Clone this wiki locally