이 글은 가시다님의 AEWS 스터디 자료를 기반으로 작성했습니다
kubectl apply 없이 Git Push만으로 배포가 완결된다values 값만 달리하여 테넌트별·티어별로 다른 배포 구조를 만든다. 신규 티어 추가 = 새 템플릿 파일 하나 작성으로 완결된다latest 대신 타임스탬프또는 commit SHA를 사용한다. Flux Image Automation이 새 태그를 감지하면 Git에 자동 커밋하여 배포를 트리거한다
SaaS 환경에서는 단일 애플리케이션이 아니라 다수의 테넌트를 동시에 운영한다. 테넌트별로 격리 수준, 인프라 구성, 배포 버전이 다를 수 있으며 신규 테넌트 온보딩도 지속적으로 발생한다.
수동 배포 방식은 이 구조에서 다음과 같은 문제를 만든다.
운영 효율성 저하. 테넌트 수가 늘어날수록 동일한 배포 작업을 반복해야 하는 횟수가 선형적으로 증가한다. 인프라팀이 병목이 된다.
환경 간 불일치. 테넌트별로 수동 작업이 개입될수록 설정 차이(Configuration Drift)가 누적된다. 어느 시점에는 각 테넌트 환경이 서로 다른 상태가 된다.
온보딩 자동화 불가. 신규 테넌트를 추가할 때마다 SQS 큐, DynamoDB 테이블, IAM 역할, Kubernetes 네임스페이스, Deployment 등을 수동으로 생성해야 한다. 실수가 발생하기 쉽고 일관성을 보장할 수 없다.
롤백 기준 부재. 특정 테넌트 환경만 이전 버전으로 되돌려야 할 때, 어떤 상태가 "이전"인지 추적할 수단이 없다.
GitOps는 Git을 모든 환경 상태의 기준으로 삼는다. 현재 클러스터에 무엇이 배포되어 있는지, 어떻게 구성되어 있는지를 Git 히스토리에서 항상 확인할 수 있다. 특정 시점으로의 롤백은 Git revert로 처리된다.
OpenGitOps 기준의 4원칙이다.
Declarative (선언적 정의). 시스템 상태를 "어떻게 도달할지(How)"가 아니라 "어떤 상태여야 하는지(What)"로 정의한다. YAML 파일로 최종 결과 상태를 기술하고, 실행 방법은 도구가 결정한다.
Versioned and Immutable (버전 관리 및 불변성). 모든 선언적 설정은 Git에 저장된다. 변경 이력이 남고, 특정 시점으로의 복구가 가능하다.
Pulled Automatically (자동 반영). Git에 저장된 Desired State는 에이전트(Flux)가 자동으로 클러스터에 반영한다. 사람이 직접 명령어를 실행하지 않는다.
Continuously Reconciled (지속적 조정). 에이전트가 실제 클러스터 상태(Actual State)와 Git의 선언 상태(Desired State)를 지속적으로 비교한다. 두 상태 사이에 차이(Drift)가 발생하면 자동으로 수정한다. 누군가 kubectl로 직접 설정을 변경해도 Git의 상태로 자동 복구된다.

개발자 코드 Push (Gitea / GitHub)
│
▼
Gitea Actions — 컨테이너 이미지 빌드 & ECR 푸시
│
│
▼
Flux Image Reflector — ECR에서 새 이미지 태그 감지
│
▼
Flux Image Automation — Git에 새 태그로 자동 커밋
│
▼
Flux Source Controller — GitRepository 변경 감지
│
├─ Flux Kustomize Controller
│ └─ Kustomization → HelmRelease 적용
│
├─ Flux Helm Controller
│ └─ HelmRelease → ECR Helm Chart로 EKS에 배포
│
└─ Tofu Controller (tf-controller)
└─ Terraform CRD → SQS / DynamoDB / IRSA 프로비저닝
SQS 이벤트 기반 자동 온보딩 흐름:
SQS 메시지 전송 (tenant_id, tenant_tier, release_version)
│
▼
Argo Events Sensor — SQS 감지 → WorkflowTemplate 트리거
│
▼
Argo Workflows
├─ Gitea 저장소 클론
├─ 티어 템플릿 기반 HelmRelease 파일 생성
└─ Git 커밋 & 푸시
│
▼
Flux 감지 → EKS 배포 + Tofu Controller → AWS 인프라 프로비저닝
| 구성 요소 | 역할 |
|---|---|
| Gitea | Git 저장소. GitOps 릴리스, 앱 템플릿, 마이크로서비스 코드 관리. Gitea Actions로 이미지 빌드 트리거 |
| Amazon ECR | 컨테이너 이미지 및 Helm Chart 저장소. 이미지 태그로 배포 버전 추적 |
| Flux v2 | GitOps 엔진. Git과 클러스터 상태를 지속적으로 동기화. Source / Kustomize / Helm / Image 컨트롤러로 구성 |
| Tofu Controller (tf-controller) | Terraform 실행을 Kubernetes CRD로 추상화. Git에 Terraform CRD 추가 시 AWS 인프라 자동 생성 |
| Helm | Kubernetes 리소스 패키징 도구. 동일 Chart에 values만 달리하여 테넌트별 배포 구성 |
| HelmRelease | Flux가 제공하는 CRD. Helm 릴리스를 선언적으로 관리. ECR의 Helm Chart를 참조하여 배포 |
| Argo Workflows | 테넌트 온보딩/오프보딩 워크플로우 실행 엔진 |
| Argo Events | SQS 메시지를 감지하여 Argo Workflows 트리거 |
| Amazon SQS | 온보딩/오프보딩 이벤트 메시지 큐 |
| Amazon DynamoDB | 테넌트별 Consumer 데이터 저장소. Tofu Controller가 테넌트 단위로 프로비저닝 |
| IRSA | Pod별 AWS 권한 부여 체계. Node IAM 역할 대신 ServiceAccount 단위로 최소 권한 적용 |
Flux는 여러 컨트롤러로 구성되며 각각 역할이 분리되어 있다.
주요 리소스 유형과 역할:
| 리소스 유형 | 역할 |
|---|---|
GitRepository | Flux가 변경 사항을 감시하는 Git 저장소 |
HelmRepository | Helm Chart 저장소 위치 (ECR 포함) |
HelmChart | 각 소스에서 가져온 Helm Chart |
HelmRelease | 실제 배포 단위. 하나의 Chart를 여러 테넌트에 배포 가능 |
Kustomization | GitRepository를 가리키는 포인터. Kubernetes 구성 관리 |
ImageRepository / ImagePolicy | 새 컨테이너 이미지 태그 자동 감지 및 정책 적용 |
ImageUpdateAutomation | 새 이미지 감지 시 Git에 자동 커밋 (Image Automation) |
Flux는 두 개의 GitRepository를 운영한다. 하나는 클러스터 전체 설정용, 하나는 Terraform 모듈 버전 고정용이다.
kubectl get gitrepository -n flux-system


terraform-v0-0-1은 v0.0.1 태그를 참조한다. Terraform 모듈이 특정 버전에 고정되므로, 모듈 변경이 의도치 않게 기존 테넌트에 영향을 주지 않는다.
# terraform-v0-0-1 GitRepository 상세 확인
kubectl get GitRepository terraform-v0-0-1 -n flux-system -o yaml | grep -A5 spec
spec:
interval: 300s
ref:
tag: v0.0.1 # 특정 태그 고정
url: http://10.35.48.130:3000/admin/eks-saas-gitops.git
Kustomization은 GitRepository를 가리키는 포인터 역할을 한다. 디렉터리 구조에 따라 어떤 리소스를 어떤 순서로 적용할지 정의한다.
flux get kustomizations
Flux의 Image Automation은 ECR에 새 이미지 태그가 올라오면 Git에 자동으로 커밋하여 배포를 트리거하는 기능이다.
# 이미지 정책 확인 — 감지된 최신 태그 확인
flux get imagepolicies
# Image Update Automation 상태 확인
flux get imageupdateautomations
이미지 태그에 prd- 접두사를 사용하는 이유는 ImagePolicy의 필터 조건으로 프로덕션 이미지만 선별하기 위해서다. QA 빌드나 브랜치 빌드가 프로덕션에 자동 배포되지 않도록 구분한다.
Flux 조정을 즉시 강제 실행하려면:
flux reconcile source git flux-system


테넌트 애플리케이션에 필요한 AWS 인프라는 tenant-apps Terraform 모듈로 추상화되어 있다.
tree gitops-repo/terraform/modules/ -L 2
terraform/modules/
├── flux_cd/ # EKS에 Flux 설치 리소스
├── gitea/ # Gitea 저장소 리소스
├── gitops-saas-infra/ # 전체 인프라 구성
└── tenant-apps/ # 테넌트 전용 인프라
├── main.tf
├── variables.tf
├── outputs.tf
└── versions.tf
tenant-apps 모듈 하나로 신규 테넌트 온보딩에 필요한 모든 AWS 리소스를 프로비저닝할 수 있다.
enable_producer, enable_consumer 변수로 생성 범위를 제어한다.
# enable_producer=true, enable_consumer=true 시 생성 리소스
terraform plan
# Plan: 11 to add — SQS, DynamoDB, IAM Policy x2, IRSA Role x2, SSM Parameter x2 등
# enable_producer=false, enable_consumer=true 시
terraform plan
# Plan: 10 to add — producer 전용 IRSA Role이 제외됨
Tofu Controller가 동작하는 흐름:
Git에 Terraform CRD 파일 추가 (git push)
│
▼
Flux가 변경 감지 → 조정 시작
│
▼
tf-controller가 Terraform CRD 감지
│
▼
tf-runner Pod 실행 → Git에서 Terraform 모듈 Pull
│
▼
Terraform apply → SQS, DynamoDB, IRSA 생성
│
▼
실행 상태와 plan을 Kubernetes Secret으로 저장
example-tenant를 위한 Terraform CRD 파일:
# application-plane/production/tenants/example-tenant-terraform-crd.yaml
apiVersion: infra.contrib.fluxcd.io/v1alpha2
kind: Terraform
metadata:
name: example-tenant
namespace: flux-system
spec:
path: ./terraform/modules/tenant-apps
interval: 1m
approvePlan: auto
destroyResourcesOnDeletion: true # CRD 삭제 시 AWS 리소스도 자동 정리
sourceRef:
kind: GitRepository
name: terraform-v0-0-1 # 특정 태그 버전의 모듈을 참조
vars:
- name: tenant_id
value: example-tenant
- name: enable_producer
value: true
- name: enable_consumer
value: true
writeOutputsToSecret:
name: example-tenant-infra-output # IRSA ARN 등 출력값을 Secret으로 저장
kustomization.yaml에 등록하여 Flux가 인식하도록 한다:
# application-plane/production/tenants/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- basic
- advanced
- premium
- example-tenant-terraform-crd.yaml # 추가
변경을 Git에 Push하면 Flux가 감지하여 tf-runner Pod를 실행한다:
git add .
git commit -m "Added Terraform CRD for example-tenant"
git push origin main
# Flux 즉시 반영
flux reconcile source git flux-system
# tf-runner Pod 생성 확인
kubectl get po -n flux-system
# example-tenant-tf-runner 1/1 Running 0 25s
# 실행 로그 확인
kubectl logs po/example-tenant-tf-runner -n flux-system -f
tree helm-charts/
helm-charts/
├── helm-tenant-chart/ # 테넌트 애플리케이션용 (Producer + Consumer 묶음)
│ ├── Chart.yaml
│ ├── values.yaml # 기본 설정값 (values.yaml.template 기반)
│ └── templates/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── hpa.yaml
│ ├── serviceaccount.yaml
│ └── terraform.yaml # Terraform CRD도 Helm으로 패키징
└── application-chart/ # 개별 애플리케이션 단위 (Onboarding Service 등)
두 Chart의 역할 차이:
| Chart | 용도 | 특징 |
|---|---|---|
helm-tenant-chart | 테넌트별 앱 배포 (Producer + Consumer 묶음) | tenant_id로 리소스 격리, Terraform CRD 포함 |
application-chart | 개별 애플리케이션 배포 | Onboarding Service 등 단일 앱에 활용 |
HelmRelease는 Flux가 제공하는 CRD로 Helm 릴리스를 선언적으로 관리한다.
HelmRelease (Git에 선언)
│
▼
Flux Helm Controller
│
▼
HelmRepository (ECR의 Helm Chart 참조)
│
▼
Kubernetes 클러스터에 실제 배포
example-tenant 전용 HelmRelease:
# application-plane/production/tenants/example-tenant-helmrelease.yaml
apiVersion: v1
kind: Namespace
metadata:
name: example-tenant
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: example-tenant-premium
namespace: flux-system
spec:
releaseName: example-tenant-premium
targetNamespace: example-tenant
storageNamespace: example-tenant
interval: 1m0s
chart:
spec:
chart: helm-tenant-chart
version: "0.x" # 0.x 범위 내 최신 버전 자동 추적
sourceRef:
kind: HelmRepository
name: helm-tenant-chart # ECR HelmRepository 참조
values:
tenantId: example-tenant
apps:
producer:
enabled: true # 전용 Producer 배포 (Silo)
consumer:
enabled: true # 전용 Consumer 배포 (Silo)
kustomization.yaml에 등록 후 Push:
resources:
- basic
- advanced
- premium
- example-tenant-helmrelease.yaml
git add .
git commit -m "Added HelmRelease for example-tenant"
git push origin main
flux reconcile source git flux-system
# Chart 패키징
cd helm-charts/helm-tenant-chart
helm package . --version 0.0.1
# ECR 로그인
AWS_REGION=ap-northeast-2
ECR_REGISTRY=123456789012.dkr.ecr.${AWS_REGION}.amazonaws.com
aws ecr get-login-password --region $AWS_REGION \
| docker login --username AWS --password-stdin $ECR_REGISTRY
# ECR에 Push
helm push ./helm-tenant-chart-0.0.1.tgz oci://${ECR_REGISTRY}/gitops-saas
# 업로드 확인
aws ecr list-images --repository-name gitops-saas/helm-tenant-chart
Flux의 HelmRepository가 ECR을 바라보도록 구성되어 있으면, Push된 Chart를 자동으로 감지한다:
kubectl get HelmRepository -n flux-system | grep helm-tenant-chart
SaaS 애플리케이션은 고객군에 따라 격리 수준과 비용이 다른 티어 구조를 채택한다. 동일한 helm-tenant-chart를 사용하면서 values 설정만 달리하여 각 티어를 구현한다.
| 티어 | Producer | Consumer | 인프라 | 격리 수준 | 비용 |
|---|---|---|---|---|---|
| Basic | 공유 (pool-1) | 공유 (pool-1) | 공유 | 낮음 | 낮음 |
| Advanced | 공유 (pool-1) | 전용 네임스페이스 | Consumer만 전용 | 중간 | 중간 |
| Premium | 전용 네임스페이스 | 전용 네임스페이스 | 전용 | 높음 | 높음 |
producer.enabled: false이더라도 Ingress 라우팅 규칙은 남아 있다. 전용 Producer가 없는 테넌트의 요청을 공유 pool-1 Producer로 자동 라우팅하기 위한 의도적인 설계다.
tree application-plane/production/tier-templates/
tier-templates/
├── basic_env_template.yaml # Pool 환경 (공유 리소스)
├── basic_tenant_template.yaml # Basic 테넌트 HelmRelease 템플릿
├── advanced_tenant_template.yaml
└── premium_tenant_template.yaml
Premium 티어 템플릿 (premium_tenant_template.yaml):
apiVersion: v1
kind: Namespace
metadata:
name: {TENANT_ID}
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: {TENANT_ID}-premium
namespace: flux-system
spec:
releaseName: {TENANT_ID}-premium
targetNamespace: {TENANT_ID}
interval: 1m0s
chart:
spec:
chart: helm-tenant-chart
version: "{RELEASE_VERSION}.x"
sourceRef:
kind: HelmRepository
name: helm-tenant-chart
values:
tenantId: {TENANT_ID}
apps:
producer:
enabled: true # Silo — 전용 배포
consumer:
enabled: true # Silo — 전용 배포
Advanced 티어는 Producer를 공유(pool-1)하고 Consumer만 전용으로 배포하는 혼합 모델이다. 데이터 처리 격리가 필요하지만 요청 생성은 공유 인프라로 충분한 경우에 적합하다.
cat << EOF > application-plane/production/tier-templates/advanced_tenant_template.yaml
apiVersion: v1
kind: Namespace
metadata:
name: {TENANT_ID}
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: {TENANT_ID}-advanced
namespace: flux-system
spec:
releaseName: {TENANT_ID}-advanced
targetNamespace: {TENANT_ID}
interval: 1m0s
chart:
spec:
chart: helm-tenant-chart
version: "{RELEASE_VERSION}.x"
sourceRef:
kind: HelmRepository
name: helm-tenant-chart
values:
tenantId: {TENANT_ID}
apps:
producer:
envId: pool-1 # 공유 pool-1 Producer로 라우팅
enabled: false # Pool 배포 — 전용 배포 없음
ingress:
enabled: true
consumer:
enabled: true # Silo — 전용 배포
ingress:
enabled: true
image:
tag: "0.1" # {"$imagepolicy": "flux-system:consumer-image-policy:tag"}
EOF
Advanced 테넌트 수동 프로비저닝 (Argo Workflows 자동화 이전 단계):
export TENANT_ID=tenant-t1d6c
export RELEASE_VERSION=0.0
mkdir -p application-plane/production/tenants/advanced
# 템플릿 복사 후 변수 치환
cp tier-templates/advanced_tenant_template.yaml \
tenants/advanced/${TENANT_ID}.yaml
sed -i "s|{TENANT_ID}|$TENANT_ID|g" tenants/advanced/${TENANT_ID}.yaml
sed -i "s|{RELEASE_VERSION}|$RELEASE_VERSION|g" tenants/advanced/${TENANT_ID}.yaml
# kustomization.yaml 생성
cat << EOF > tenants/advanced/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ${TENANT_ID}.yaml
EOF
git add . && git commit -m "Adding ${TENANT_ID} with Advanced Tier" && git push origin main
flux reconcile source git flux-system
배포 결과 검증. environment 필드로 라우팅 대상을 확인한다:
APP_LB=http://$(kubectl get ingress -n tenant-t1d6c \
-o json | jq -r .items[0].status.loadBalancer.ingress[0].hostname)
curl -s -H "tenantID: tenant-t1d6c" $APP_LB/producer | jq
curl -s -H "tenantID: tenant-t1d6c" $APP_LB/consumer | jq
Producer는 공유 pool-1, Consumer는 전용 tenant-t1d6c에서 실행되고 있음을 확인할 수 있다.

8섹션에서 수행한 템플릿 복사 → 변수 치환 → Git 커밋 → Push 과정을 Argo Workflows가 자동화한다. SQS 메시지 하나로 전체 온보딩 파이프라인이 시작된다.
# 사전 구성된 워크플로우 템플릿 확인
kubectl get workflowtemplates -n argo-workflows
워크플로우 정의 파일 구조:
tree control-plane/production/workflows/
workflows/
├── event-bus.yaml
├── tenant-deployment-sensor.yaml
├── tenant-deployment-workflow-template.yaml
├── tenant-offboarding-sensor.yaml
├── tenant-offboarding-workflow-template.yaml
├── tenant-onboarding-sensor.yaml
└── tenant-onboarding-workflow-template.yaml
Argo Events Sensor는 SQS 큐를 감시하다가 메시지가 도착하면 WorkflowTemplate 기반으로 워크플로우를 실행한다.

SQS 메시지 수신 (Argo Events Sensor)
│ { tenant_id, tenant_tier, release_version }
▼
온보딩 워크플로우 트리거
│
├─ Gitea 저장소 클론
├─ tier-templates/{tier}_tenant_template.yaml 복사
├─ {TENANT_ID}, {RELEASE_VERSION} 변수 치환
├─ tenants/{tier}/{tenant_id}.yaml 생성
├─ kustomization.yaml 업데이트
└─ Git 커밋 & 푸시
│
▼
Flux 감지 → EKS 배포
Tofu Controller → AWS 인프라 생성
Premium 테넌트 온보딩:
export ONBOARDING_QUEUE=$(kubectl get configmap saas-infra-outputs -n flux-system \
-o jsonpath='{.data.argoworkflows_onboarding_queue_url}')
aws sqs send-message \
--queue-url $ONBOARDING_QUEUE \
--message-body '{
"tenant_id": "tenant-1",
"tenant_tier": "premium",
"release_version": "0.0"
}'
Basic 테넌트 온보딩:
aws sqs send-message \
--queue-url $ONBOARDING_QUEUE \
--message-body '{
"tenant_id": "tenant-2",
"tenant_tier": "basic",
"release_version": "0.0"
}'
Advanced 테넌트 온보딩:
aws sqs send-message \
--queue-url $ONBOARDING_QUEUE \
--message-body '{
"tenant_id": "tenant-3",
"tenant_tier": "advanced",
"release_version": "0.0"
}'
워크플로우 생성 및 진행 상태 확인:
kubectl -n argo-workflows get workflow
# Argo Workflows Web UI 접속
ARGO_URL=$(kubectl -n argo-workflows get service/argo-workflows-server \
-o json | jq -r '.status.loadBalancer.ingress[0].hostname')
echo http://$ARGO_URL:2746/workflows
온보딩 완료 후 3개 티어 전체 결과를 한 번에 검증한다:
# HelmRelease 상태
flux get helmreleases
NAME REVISION READY MESSAGE
pool-1 0.0.1 True Helm install succeeded
tenant-1-premium 0.0.1 True Helm install succeeded
tenant-2-basic 0.0.1 True Helm install succeeded
tenant-3-advanced 0.0.1 True Helm install succeeded
# 티어별 Kubernetes 리소스 비교
kubectl -n tenant-1 get deployment # Premium: producer + consumer 전용
kubectl -n tenant-2 get deployment # Basic: 전용 Deployment 없음 (pool-1 공유)
kubectl -n tenant-3 get deployment # Advanced: consumer만 전용
kubectl -n pool-1 get deployment # pool-1: Basic + Advanced 공유 환경
엔드포인트로 티어별 라우팅 검증:
APP_LB=http://$(kubectl get ingress -n tenant-1 \
-o json | jq -r .items[0].status.loadBalancer.ingress[0].hostname)
# Premium — 모두 전용
curl -s -H "tenantID: tenant-1" $APP_LB/producer | jq .environment # "tenant-1"
curl -s -H "tenantID: tenant-1" $APP_LB/consumer | jq .environment # "tenant-1"
# Basic — 모두 공유
curl -s -H "tenantID: tenant-2" $APP_LB/producer | jq .environment # "pool-1"
curl -s -H "tenantID: tenant-2" $APP_LB/consumer | jq .environment # "pool-1"
# Advanced — Producer 공유, Consumer 전용
curl -s -H "tenantID: tenant-3" $APP_LB/producer | jq .environment # "pool-1"
curl -s -H "tenantID: tenant-3" $APP_LB/consumer | jq .environment # "tenant-3"
온보딩과 동일한 이벤트 드리븐 방식으로 처리된다.
export OFFBOARDING_QUEUE=$(kubectl get configmap saas-infra-outputs -n flux-system \
-o jsonpath='{.data.argoworkflows_offboarding_queue_url}')
aws sqs send-message \
--queue-url $OFFBOARDING_QUEUE \
--message-body '{
"tenant_id": "tenant-t1d6c",
"tenant_tier": "advanced"
}'
GitOps의 핵심 메커니즘은 Auto Reconciliation이다. Flux는 클러스터 상태와 Git 선언 상태 사이의 차이를 지속적으로 감지하고 자동으로 수정한다. 누군가 kubectl로 직접 설정을 변경해도 Git 상태로 자동 복구된다.
인프라와 애플리케이션 배포가 동일한 흐름으로 처리된다. Tofu Controller로 AWS 인프라를, HelmRelease로 Kubernetes 리소스를, 모두 Git 파일 추가/삭제로 제어한다. 생성과 삭제 모두 Git 조작만으로 완결된다.
terraform-v0-0-1 태그로 Terraform 모듈 버전을 고정한다. 모듈 변경이 기존 테넌트 인프라에 의도치 않게 적용되는 것을 방지한다. 신규 모듈 버전 도입 시 새 GitRepository 태그를 만들고, 테넌트별로 점진적으로 마이그레이션할 수 있다.
동일한 Helm Chart, 다른 values로 티어를 구현한다. enabled: true/false 값 하나로 전용 배포(Silo)와 공유 배포(Pool) 방식이 결정된다. 신규 티어 추가는 새 템플릿 파일 하나를 작성하는 것으로 완결된다.
이미지 태그는 타임스탬프 또는 commit SHA를 사용한다. latest는 덮어쓰이므로 Flux Image Automation이 변경 감지를 할 수 없다. prd-20260413T183235Z 같은 고유 태그가 있어야 Image Policy가 최신 태그를 감지하고 Git에 자동 커밋하여 배포를 트리거할 수 있다.
온보딩/오프보딩의 자동화 범위는 Git 조작까지다. Argo Workflows는 SQS 메시지를 받아 Git에 파일을 추가하거나 삭제하는 역할만 한다. 실제 클러스터 반영과 AWS 인프라 프로비저닝은 Flux와 Tofu Controller가 담당한다. 각 컴포넌트의 책임이 명확히 분리되어 있다.