-
이코에코(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.yamlStable 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-latestCanary 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/healthPod 확인
# 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 authArgoCD Rollback
# Application 히스토리 확인 kubectl get applications dev-api-auth -n argocd -o yaml # 이전 버전으로 롤백 argocd app rollback dev-api-auth
파이프라인 요약
워크플로우 트리거 결과물 ci-services.ymlpush to develop auth-api-dev-latestci-canary.ymlPR + canary label auth-api-dev-canaryci-auth-relay.ymlworkers 변경 auth-relay-dev-latest매니페스트 이미지 태그 버전 deployment.yamlauth-api-dev-latestv1 (stable, legacy) deployment-canary.yamlauth-api-dev-canaryv2 (canary)
References
'이코에코(Eco²) > Clean Architecture Migration' 카테고리의 다른 글
이코에코(Eco²) Clean Architecture #5: Message Consumer (1) 2025.12.31 이코에코(Eco²) Clean Architecture #4 Auth Persistence Offloading (0) 2025.12.31 이코에코(Eco²) Clean Architecture #2: Auth Clean Architecture 구현 초안 (0) 2025.12.31 이코에코(Eco²) Clean Architecture #1: Auth 문제사안 도출, 리팩토링 전략 (0) 2025.12.31 이코에코(Eco²) Clean Architecture #0: Auth 리팩토링.MD (0) 2025.12.31