이코에코(Eco²)/Clean Architecture Migration

이코에코(Eco²) Clean Architecture #3: Auth 버전 분리, 점진적 배포

mango_fr 2025. 12. 31. 03:27

배포 전략: Canary

리팩토링된 코드를 바로 배포해 클라이언트로 노출하는 것은 위험. Canary 배포로 일부 트래픽만 새 버전으로 라우팅하여 검증, PR에 부착된 Canary 라벨을 감지해 Canary 태그를 부착한 채로 패키징.

┌─────────────────────────────────────────────────────────┐
│  Istio VirtualService                                   │
├─────────────────────────────────────────────────────────┤
│  X-Canary: true  →  auth-api-canary (version: v2)      │
│  (default)       →  auth-api (version: v1)              │
└─────────────────────────────────────────────────────────┘

Kubernetes Manifest 구조

workloads/domains/auth/
├── base/
│   ├── deployment.yaml          # Stable (v1)
│   ├── deployment-canary.yaml   # Canary (v2)
│   ├── service.yaml
│   ├── destination-rule.yaml    # Istio 서브셋
│   └── kustomization.yaml
├── dev/
│   ├── kustomization.yaml
│   └── patch-deployment.yaml
└── prod/
    └── kustomization.yaml

Stable Deployment

# workloads/domains/auth/base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: auth-api
  labels:
    app: auth-api
    version: v1
spec:
  selector:
    matchLabels:
      app: auth-api
      version: v1
  template:
    spec:
      containers:
      - name: auth-api
        image: docker.io/mng990/eco2:auth-api-dev-latest

Canary Deployment

# workloads/domains/auth/base/deployment-canary.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: auth-api-canary
  labels:
    app: auth-api
    version: v2
    release: canary
spec:
  selector:
    matchLabels:
      app: auth-api
      version: v2
  template:
    spec:
      containers:
      - name: auth-api
        image: docker.io/mng990/eco2:auth-api-dev-canary
        env:
        - name: RELEASE_TYPE
          value: canary

CI/CD 파이프라인

경로 감지 (apps/ + domains/)

domains/auth/(legacy)apps/auth/(v2) 모두 지원:

# .github/workflows/ci-services.yml
on:
  push:
    paths:
      - "apps/auth/**"      # Clean Architecture
      - "domains/auth/**"   # Legacy

서비스 감지 로직

# Changed files 분석
for path in files:
    parts = path.split("/")

    # apps/<service>/ 경로 감지 (Clean Architecture)
    if len(parts) >= 2 and parts[0] == "apps":
        svc = parts[1]
        if svc in API_SERVICES:
            services.append(svc)

    # domains/<service>/ 경로 감지 (Legacy)
    elif len(parts) >= 2 and parts[0] == "domains":
        svc = parts[1]
        if svc in API_SERVICES:
            services.append(svc)

Dockerfile 자동 감지

- name: Determine Dockerfile path
  id: dockerfile
  run: |
    SERVICE="${{ matrix.service }}"
    # Clean Architecture 버전 우선
    if [ -f "apps/${SERVICE}/Dockerfile" ]; then
      echo "path=apps/${SERVICE}/Dockerfile" >> "$GITHUB_OUTPUT"
    else
      echo "path=domains/${SERVICE}/Dockerfile" >> "$GITHUB_OUTPUT"
    fi

- name: Build and push
  uses: docker/build-push-action@v5
  with:
    file: ${{ steps.dockerfile.outputs.path }}

PYTHONPATH 설정

- name: Determine source path
  id: source
  run: |
    SERVICE="${{ matrix.service }}"
    if [ -d "apps/${SERVICE}" ]; then
      echo "path=apps/${SERVICE}" >> "$GITHUB_OUTPUT"
      echo "pythonpath=${{ github.workspace }}" >> "$GITHUB_OUTPUT"
    else
      echo "path=domains/${SERVICE}" >> "$GITHUB_OUTPUT"
      echo "pythonpath=${{ github.workspace }}/domains/${SERVICE}" >> "$GITHUB_OUTPUT"
    fi

- name: Pytest
  working-directory: ${{ steps.source.outputs.path }}
  env:
    PYTHONPATH: ${{ steps.source.outputs.pythonpath }}
  run: pytest

Canary 빌드 워크플로우

PR에 canary 라벨 추가 시 Canary 이미지 자동 빌드:

# .github/workflows/ci-canary.yml
on:
  pull_request:
    types: [labeled, synchronize]

jobs:
  check-canary-label:
    steps:
      - name: Check for canary label
        run: |
          if echo "$PR_LABELS" | jq -e 'contains(["canary"])'; then
            echo "is_canary=true" >> "$GITHUB_OUTPUT"
          fi

  api-canary-build:
    if: needs.check-canary-label.outputs.is_canary == 'true'
    steps:
      - name: Prepare canary tags
        run: |
          CANARY_TAG="${SERVICE}-api-dev-canary"
          echo "tags=${IMAGE_REPO}:${CANARY_TAG}" >> "$GITHUB_OUTPUT"

      - name: Build and push canary image
        uses: docker/build-push-action@v5

ArgoCD GitOps 연동

ApplicationSet

# clusters/dev/apps/40-apis-appset.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: dev-apis
spec:
  generators:
  - list:
      elements:
      - name: auth
        phase: '1'
  template:
    metadata:
      name: dev-api-{{name}}
      annotations:
        # Image Updater - digest 변경 감지
        argocd-image-updater.argoproj.io/image-list: stable=docker.io/mng990/eco2:{{name}}-api-dev-latest
        argocd-image-updater.argoproj.io/stable.update-strategy: digest
    spec:
      source:
        path: workloads/domains/{{name}}/dev
      syncPolicy:
        automated:
          prune: true
          selfHeal: true

배포 플로우

1. apps/auth/** 변경
       ↓
2. GitHub Actions (ci-services.yml)
       ↓
3. Docker Build (apps/auth/Dockerfile)
       ↓
4. Push: mng990/eco2:auth-api-dev-latest
       ↓
5. ArgoCD Image Updater (digest 감지)
       ↓
6. workloads/domains/auth/dev/ Sync
       ↓
7. Kubernetes Deployment 업데이트

Canary 테스트 방법

헤더 기반 라우팅

# Canary 버전 요청
curl -H "X-Canary: true" https://api.growbin.app/api/v1/auth/health

# Stable 버전 요청 (기본)
curl https://api.growbin.app/api/v1/auth/health

Pod 확인

# Stable Pod
kubectl get pods -n auth -l version=v1

# Canary Pod
kubectl get pods -n auth -l version=v2

# 로그 비교
kubectl logs -n auth -l version=v2 --tail=100 -f

롤백 전략

Canary 문제 발생 시

# Canary Deployment 스케일 다운
kubectl scale deployment auth-api-canary -n auth --replicas=0

# 또는 삭제
kubectl delete deployment auth-api-canary -n auth

ArgoCD Rollback

# Application 히스토리 확인
kubectl get applications dev-api-auth -n argocd -o yaml

# 이전 버전으로 롤백
argocd app rollback dev-api-auth

파이프라인 요약

워크플로우 트리거 결과물
ci-services.yml push to develop auth-api-dev-latest
ci-canary.yml PR + canary label auth-api-dev-canary
ci-auth-relay.yml workers 변경 auth-relay-dev-latest

 

 

매니페스트 이미지 태그 버전
deployment.yaml auth-api-dev-latest v1 (stable, legacy)
deployment-canary.yaml auth-api-dev-canary v2 (canary)

 


References