ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 이코에코(Eco²) Clean Architecture #3: Auth 버전 분리, 점진적 배포
    이코에코(Eco²)/Clean Architecture Migration 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

    댓글

ABOUT ME

🎓 부산대학교 정보컴퓨터공학과 학사: 2017.03 - 2023.08
☁️ Rakuten Symphony Jr. Cloud Engineer: 2024.12.09 - 2025.08.31
🏆 2025 AI 새싹톤 우수상 수상: 2025.10.30 - 2025.12.02
🌏 이코에코(Eco²) 백엔드/인프라 고도화 중: 2025.12 - Present

Designed by Mango