[CloudNeta] EKS 워크샵 스터디 (6) - GitOps와 SaaS 플랫폼 엔지니어링 Part 1 - 개념과 인프라 설계
이번 게시글에서는 EKS 워크샵 스터디 제 6주차 내용을 작성합니다.
이 글은 2부로 나누어집니다.
들어가며
이번 게시글에서는 아래 내용을 설명합니다:
- GitOps 핵심 원칙과 SaaS 환경을 시나리오로 한 CI/CD 활용 공유
- Flux v2를 이용해 Git 저장소와 클러스터 상태를 동기화하는 구조
- Flux Tofu Controller + Terraform으로 테넌트별 AWS 인프라 자동 프로비저닝
- Helm 차트를 활용한 멀티테넌트 SaaS 배포 패턴
이번 실습 환경은 아래와 같은 구성을 가집니다.

GitOps와 SaaS 플랫폼 엔지니어링에 대해
GitOps와 플랫폼 엔지니어링을 설명하기에 앞서 발전 흐름을 한번 살펴보고 넘어갑시다.
DevOps 의 대두
먼저 과거에 회사에서 서버를 운영하던 모습을 살펴봅시다. 서버를 구성하기 위해 기본적으로 배선작업을 하고, 랙(Server Rack)이나 캐비넷에 설치하던 시절이 있었습니다[1]. 이러한 시대에는 개발팀(Dev)과 운영팀(Ops)이 분리되어 하드웨어, 소프트웨어 파트를 각각 맡았습니다. 회사의 성장에 따라 이런 조직도 자연스레 커지고 복잡해지며, 개발/운영에 드는 비용이 증대되었죠. 2010년 전후로 클라우드 컴퓨팅의 시대가 도래하며 하드웨어 제품을 대여할 수 있게 되고, Chef, Puppet, Terraform 등과 같은 소프트웨어가 등장하여 이러한 수고를 "모든" 회사가 하지 않아도 되는 시대가 왔습니다. 이러한 시대의 흐름을 타고 개발과 운영이 한데 어우러진, 이른바 "DevOps 운동"이 시작되었습니다.
플랫폼 엔지니어링의 등장
개발과 운영이 점점 더 고도화되고, 다양한 도구들이 등장하니 회사마다의 요구사항을 저마다 풀기 위한 시도가 등장했습니다. 이는 플랫폼 엔지니어링(Platform Engineering)이라는 개념인데요. 이는 클라우드 환경에서 운영부담을 최소화하고 개발에 온전히 집중할 수 있도록 추상화를 제공하는 방법입니다. 회사마다의 접근방식을 설계하고 더 나아가 거버넌스를 세워 마치 회사 내의 제품처럼 관리합니다. 이러면 개발자가 고도화된 인프라, 보안, 그리고 CI/CD까지 관여하는 것이 아니라 온전히 개발에 집중할 수 있는 환경을 갖출 수 있도록 서비스를 구성하는 또 하나의 일이 되는 것이지요. 결국 플랫폼 팀에게는 내부 개발자를 위한 또 하나의 제품을 만드는 일이 됩니다.
많은 논의들이 있지만 이 글에선 당근의 접근 방식, 그리고 무신사의 접근 방식을 통해 배운 점을 간략히 요약해보려 합니다[2].
다음은 당근의 접근 방식입니다. '설정보다 관습(Convention over Configuration)', '의견이 반영된(Opinionated) 단순한 시스템', '문서 없이도 사용 가능한 편의성'이라는 세 가지 핵심 가치를 내세웠습니다. 배포 프로세스의 파편화와 복잡한 YAML 관리, SRE 팀에 반복되는 배포 설정 요청으로 인한 병목 현상이 이들의 문제였으며, 셀프 서비스, 설정 추상화, 소유권 관리, 비용 가시성을 주요 방향성으로 삼아, 기존 ArgoCD UI를 직접 사용하는 방식 대신 쿠버네티스를 몰라도 배포할 수 있도록 설정을 추상화한 새로운 배포 시스템을 구축하고, 개발자가 테스트부터 배포까지 전 과정을 직접 수행하는 셀프 서비스 환경과 프로젝트 소유권 관리 체계를 함께 구현했습니다.
다음은 무신사의 접근 방식입니다. '단순 반복적인 토일(Toil) 업무 제거', '인지 부하 감소를 위한 추상화', '데이터 기반의 비용 가시성 확보'라는 세 가지 핵심 가치를 기준점으로 잡습니다. 인프라 형상의 공존으로 인한 복잡성 증가와, 신규 서비스 구축 시 SRE 팀에 집중되는 병목 현상, 그리고 빈번한 마케팅 이벤트에 따른 수동 증설 부담이 이들의 문제였으며 **셀프 서비스 지향 아키텍처, 소프트웨어 카탈로그(Backstage 기반), 가드레일, 핀옵스(FinOps)**의 4대 방향성을 주요 거버넌스로 두고, '런치패드' 서비스를 통해 최소 정보만으로 인프라를 자동 프로비저닝, 이벤트 달력과 연동해 서버를 자동 증설하는 등의 자체 구현을 시도했습니다. 현재는 Amazon Bedrock을 연동해 장애 영향도 파악 등에 생성형 AI를 실험 중에 있습니다.
이렇듯 회사의 요구사항을 충족하는 것이기 때문에 정답이 없으며, 회사와 각 팀 간의 주요 내용을 잘 조율하기 위한 하나의 방향성과 그 해결방식이라 할 수 있지요.
GitOps란?
GitOps는 인프라 및 애플리케이션 설정을 선언적으로 정의하고, 이를 Git을 통해 관리하는 방식입니다. opengitops.dev에서는 이를 네 가지 원칙으로 정리하고 있습니다.
첫째, **선언적 정의(Declarative)**입니다. 시스템의 상태를 '어떻게(How)' 도달할지가 아니라, '어떤 상태(What)'여야 하는지를 YAML 파일 등으로 선언적으로 기술합니다. 둘째, **버전 관리와 불변성(Versioned and Immutable)**입니다. 모든 선언적 설정은 Git에 저장되어 변경 이력이 남으며, 문제 발생 시 특정 시점으로의 롤백이 용이해집니다. 셋째, **자동 반영(Pulled Automatically)**입니다. Git에 저장된 Desired State는 사람이 직접 명령어를 입력하지 않아도 시스템이 변경 사항을 감지해 스스로 클러스터에 반영합니다. 넷째, **지속적인 조정(Continuously Reconciled)**입니다. 소프트웨어 에이전트가 실제 시스템의 Actual State와 Git에 정의된 Desired State를 지속적으로 비교하며, 둘 사이에 Drift가 발생하면 자동으로 원래 상태로 되돌립니다.
이 네 가지 원칙의 핵심을 한 마디로 표현하면 Auto Reconciliation이라 할 수 있습니다. 누군가 kubectl 명령어로 클러스터 설정을 수동으로 변경하더라도, GitOps 에이전트가 이를 감지하고 Git에 정의된 상태로 자동 복구함으로써 시스템의 일관성을 유지합니다.
SaaS DevOps에 대해
이렇듯 복잡해지는 비즈니스 요구사항에 대응하기 위해서는 SaaS DevOps 환경에서도 GitOps 활용이 더욱 중요해집니다. 빠른 릴리즈와 효율적인 운영이 가능해지고, 새로운 테넌트가 추가될 때마다 온보딩 과정도 한층 수월해집니다.
한편 SaaS 앱은 사일로(Silo), 하이브리드(Hybrid), 풀(Pool) 등 다양한 배포 모델을 사용하는데, 이 때 테넌트 온보딩 과정에서 리소스를 프로비저닝하는데 필요한 요구 사항이 다릅니다.
예시 - GitOps를 활용한 EKS 기반 SaaS 앱의 레퍼런스 아키텍처
아래 아키텍처를 보고 필요한 부분을 설명드리겠습니다.

- ① ~ ② 시작점: 설정 정의와 인프라 선언: 개발자가 Configuration File을 작성하면(①), DevOps 엔지니어가 이를 받아 CloudFormation 및 하단 별도 VPC에 위치한 EC2 Code-Server IDE(Terraform)로 인프라를 선언합니다(②). 인프라 자체를 코드로 관리하는 IaC의 출발점입니다.
- ③ 메인 VPC: 3개 AZ에 걸친 네트워크 계층 구성: 메인 VPC는 Availability Zone A/B/C에 걸쳐 Public → Private → Intra 서브넷의 3계층으로 구성됩니다(③). Intra 서브넷에는 EKS Managed ENI가 배치되어 컨트롤 플레인과의 통신을 담당하고, Private 서브넷에는 실제 워크로드 노드(c7g, m7g, r7g)가 올라갑니다.
- ④ ~ ⑤ 하단 컨트롤 플레인(핵심 컨트롤러들): Amazon EKS 클러스터 위에는 역할별로 구분된 컨트롤러들이 올라갑니다(⑤). Karpenter Controller는 노드 오토스케일링을 담당하며 Private 서브넷의 NodePool과 연결되어 워크로드 수요에 따라 노드를 동적으로 프로비저닝합니다. Argo Workflows와 Argo Events는 테넌트 온보딩 자동화 파이프라인의 실행 엔진 역할을 하며, TF Controller는 테라폼을 쿠버네티스 리소스처럼 선언적으로 관리하여 GitOps 흐름 안에서 인프라 프로비저닝까지 처리할 수 있게 해줍니다. LB Controller는 우측의 AWS Network Load Balancer와 연동되어 트래픽 진입점을 구성합니다.
- ⑥ Flux Controller(GitOps의 중심): AZ A의 Private 서브넷에는 Flux Controller가 위치합니다(⑥). 우측 상단 Gitea 저장소를 지속적으로 감시하며, Desired State와 Actual State 간의 Drift를 감지해 자동으로 조정합니다. 멀티테넌트 환경에서 각 테넌트의 설정을 Git 단위로 격리·관리하는 핵심 컴포넌트입니다.
- ⑦ Developer → Gitea(GitOps 트리거): 개발자는 Gitea 저장소에 직접 푸시함으로써(⑦) 배포를 트리거합니다. kubectl이나 ArgoCD UI를 직접 건드리지 않아도 Git 조작만으로 전체 파이프라인이 동작하는 구조입니다.
실습 1. 앱 및 인프라 설계
들어가며
현대 조직에서 팀은 일반적으로 애플리케이션 개발팀과 인프라/운영팀, 두 가지로 나뉩니다. 각 팀의 소유권과 책임은 명확히 구분되어 있으며, 대부분의 애플리케이션은 IAM 역할, SQS 큐, DynamoDB 테이블 같은 특정 인프라 구성 요소를 필요로 합니다. 이 때문에 개발자가 인프라팀에 리소스 생성을 요청하는 상황이 반복적으로 발생합니다. 앞서 당근, 무신사의 예시를 본 것처럼요. 조직 규모가 커지면 이 요청이 걷잡을 수 없이 커지고, 개선할 엄두도 안 나고 문제가 기하급수적으로 커집니다.

이를 해결하기 위해 개발자 주도형 인프라(Developer-driven Infrastructure)라는 패러다임이 나옵니다. 사전에 보안/거버넌스 구성 속에 만들어진 인프라를 개발자가 직접 프로비저닝하고 관리하며, IaC와 자동화 도구를 통해 셀프서비스 구성까지 마치면 주요 테넌트마다 신속하게 사용할 수 있죠.

세션 소개
위와 같은 인프라 구성을 위한 쿠버네티스 인프라 자동화 툴체인을 살펴봅시다.
이번 세션에서는 멀티테넌트 환경에서 팀이 반드시 다뤄야 하는 고유한 복잡성을 해소하는, 완전히 동작하는 EKS 기반 SaaS DevOps 자동화 솔루션을 살펴봅니다. 목표는 Flux v2, Argo Workflows, Helm 및 기타 쿠버네티스 도구를 활용하여 테넌트별 Amazon Elastic Kubernetes Service(EKS) 환경을 프로비저닝하고 구성하는 다양한 티어와 배포 모델을 지원하는 엔드투엔드 솔루션을 구축하는 것이 목표입니다.
개발자 주도 환경
앞서 말한 상황에 대해 인프라 청사진을 구성하고, IaC와 자동화 도구를 통한 셀프 서비스 기능으로 구동되도록 하는 것이 목표입니다.

1-1. 시작하기
이번 워크샵으로 SaaS 멀티테넌트 환경에서의 DevOps 프로세스를 단순화할 수 있는 플랫폼 경험을 제공하는 것을 목표로 합니다. 실습 1의 환경구성은 아래와 같습니다:

먼저, 아래 요소들이 프로비저닝되어 있으니 확인할 예정입니다.
| 구성 요소 | 설명 |
|---|---|
| Amazon EKS 클러스터 | Flux, Argo Workflows 등 애드온 포함 |
| Amazon ECR | 애플리케이션 컨테이너 이미지 및 Helm 차트 저장소 |
| Gitea 저장소 | GitOps 릴리스, 앱 템플릿, 마이크로서비스 코드 |
| AWS 리소스 | 정상 작동을 위한 네트워킹, IAM 등 |
리소스 살펴보기
$ kubectl get ns
NAME STATUS AGE
argo-events Active 1d12h
argo-workflows Active 1d12h
aws-system Active 1d12h
default Active 1d12h
flux-system Active 1d12h
karpenter Active 1d12h
kube-node-lease Active 1d12h
kube-public Active 1d12h
kube-system Active 1d12h
kubecost Active 1d12h
onboarding-service Active 1d12h
pool-1 Active 1d12h
flux 리소스 살펴보기
Flux는 이 솔루션의 핵심 컴포넌트입니다. Flux는 Git과 ECR에 정의된 환경 설정을 감시하며 클러스터에 변경 사항을 배포하는데, 이를 통해 클러스터의 배포 상태가 Git에 선언된 Desired State와 일치하도록 유지하고, 올바른 버전의 Helm 차트가 클러스터에 배포되도록 보장합니다. 본 실습에서는 Flux를 통해 아래 리소스가 배포되어 있음을 확인하실 수 있습니다.
gitrepository: Flux가 변경 사항을 감시하고 클러스터 상태와 조정(Reconcile)하는 대상 저장소입니다.helmrepository: Helm 차트가 저장된 Helm 저장소의 위치를 정의합니다.helmchart: Flux가 감시하며 각 helmrepository에서 가져오는 차트입니다.helmrelease: helmchart의 실제 배포 단위입니다. 하나의 차트를 여러 번 배포할 수 있으며, 이 경우에는 여러 테넌트 각각에 대한 배포가 이에 해당합니다.kustomization: Flux가 gitrepository의 변경 사항을 가리키는 포인터로 활용하는 쿠버네티스 설정 관리자입니다.
$ flux --help
Command line utility for assembling Kubernetes CD pipelines the GitOps way.
Usage:
flux [command]
Examples:
# Check prerequisites
flux check --pre
# Install the latest version of Flux
flux install
# Create a source for a public Git repository
flux create source git webapp-latest \
--url=https://github.com/stefanprodan/podinfo \
--branch=master \
--interval=3m
# List GitRepository sources and their status
flux get sources git
# Trigger a GitRepository source reconciliation
flux reconcile source git flux-system
# Export GitRepository sources in YAML format
flux export source git --all > sources.yaml
# Create a Kustomization for deploying a series of microservices
flux create kustomization webapp-dev \
--source=webapp-latest \
--path="./deploy/webapp/" \
--prune=true \
--interval=5m \
--health-check="Deployment/backend.webapp" \
--health-check="Deployment/frontend.webapp" \
--health-check-timeout=2m
# Trigger a git sync of the Kustomization's source and apply changes
flux reconcile kustomization webapp-dev --with-source
# Suspend a Kustomization reconciliation
flux suspend kustomization webapp-dev
# Export Kustomizations in YAML format
flux export kustomization --all > kustomizations.yaml
# Resume a Kustomization reconciliation
flux resume kustomization webapp-dev
# Delete a Kustomization
flux delete kustomization webapp-dev
# Delete a GitRepository source
flux delete source git webapp-latest
# Uninstall Flux and delete CRDs
flux uninstall
Available Commands:
bootstrap Deploy Flux on a cluster the GitOps way.
build Build a flux resource
check Check requirements and installation
completion Generates completion scripts for various shells
create Create or update sources and resources
debug Debug a flux resource
delete Delete sources and resources
diff Diff a flux resource
envsubst envsubst substitutes the values of environment variables
events Display Kubernetes events for Flux resources
export Export resources in YAML format
get Get the resources and their status
help Help about any command
install Install or upgrade Flux
list List artifacts
logs Display formatted logs for Flux components
migrate Migrate the Flux custom resources to their latest API version
pull Pull artifacts
push Push artifacts
reconcile Reconcile sources and resources
resume Resume suspended resources
stats Stats of Flux reconciles
suspend Suspend resources
tag Tag artifacts
trace Trace in-cluster objects throughout the GitOps delivery pipeline
tree Print the resources reconciled by Flux
uninstall Uninstall Flux and its custom resource definitions
version Print the client and server-side components version information.
Flags:
--as string Username to impersonate for the operation. User could be a regular user or a service account in a namespace.
--as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups.
--as-uid string UID to impersonate for the operation.
--cache-dir string Default cache directory (default "/home/ec2-user/.kube/cache")
--certificate-authority string Path to a cert file for the certificate authority to authenticate the Kubernetes API server
--client-certificate string Path to a client certificate file for TLS authentication to the Kubernetes API server
--client-key string Path to a client key file for TLS authentication to the Kubernetes API server
--cluster string The name of the kubeconfig cluster to use
--context string The name of the kubeconfig context to use
--disable-compression If true, opt-out of response compression for all requests to the server
-h, --help help for flux
--insecure-skip-tls-verify If true, the Kubernetes API server's certificate will not be checked for validity. This will make your HTTPS connections insecure
--kube-api-burst int The maximum burst queries-per-second of requests sent to the Kubernetes API. (default 300)
--kube-api-qps float32 The maximum queries-per-second of requests sent to the Kubernetes API. (default 50)
--kubeconfig string Path to the kubeconfig file to use for CLI requests.
-n, --namespace string If present, the namespace scope for this CLI request (default "flux-system")
--server string The address and port of the Kubernetes API server
--timeout duration timeout for this operation (default 5m0s)
--tls-server-name string Server name to use for server certificate validation. If it is not provided, the hostname used to contact the server is used
--token string Bearer token for authentication to the API server
--user string The name of the kubeconfig user to use
--verbose print generated objects
-v, --version version for flux
Use "flux [command] --help" for more information about a command.
$ flux get all
NAME REVISION SUSPENDED READY MESSAGE
ocirepository/capacitor v0.4.8@sha256:1efcb443 False True stored artifact for digest 'v0.4.8@sha256:1efcb443'
NAME REVISION SUSPENDED READY MESSAGE
gitrepository/flux-system refs/heads/main@sha1:06abc42a False True stored artifact for revision 'refs/heads/main@sha1:06abc42a'
gitrepository/terraform-v0-0-1 v0.0.1@sha1:2d19a84a False True stored artifact for revision 'v0.0.1@sha1:2d19a84a'
NAME REVISION SUSPENDED READY MESSAGE
helmrepository/argo sha256:77d58f2f False True stored artifact: revision 'sha256:77d58f2f'
helmrepository/eks-charts sha256:d5d7cd31 False True stored artifact: revision 'sha256:d5d7cd31'
helmrepository/helm-application-chart False True Helm repository is Ready
helmrepository/helm-tenant-chart False True Helm repository is Ready
helmrepository/karpenter False True Helm repository is Ready
helmrepository/kubecost False True Helm repository is Ready
helmrepository/metrics-server sha256:ba69c5bb False True stored artifact: revision 'sha256:ba69c5bb'
helmrepository/tf-controller sha256:1fcad0f6 False True stored artifact: revision 'sha256:1fcad0f6'
NAME REVISION SUSPENDED READY MESSAGE
helmchart/flux-system-argo-events 2.4.3 False True pulled 'argo-events' chart with version '2.4.3'
helmchart/flux-system-argo-workflows 0.40.11 False True pulled 'argo-workflows' chart with version '0.40.11'
helmchart/flux-system-aws-load-balancer-controller 1.6.2 False True pulled 'aws-load-balancer-controller' chart with version '1.6.2'
helmchart/flux-system-karpenter 1.4.0 False True pulled 'karpenter' chart with version '1.4.0'
helmchart/flux-system-kubecost 2.1.0 False True pulled 'cost-analyzer' chart with version '2.1.0'
helmchart/flux-system-metrics-server 3.11.0 False True pulled 'metrics-server' chart with version '3.11.0'
helmchart/flux-system-onboarding-service 0.0.1 False True pulled 'application-chart' chart with version '0.0.1'
helmchart/flux-system-pool-1 0.0.1 False True pulled 'helm-tenant-chart' chart with version '0.0.1'
helmchart/flux-system-tf-controller 0.16.0-rc.4 False True pulled 'tf-controller' chart with version '0.16.0-rc.4'
NAME LAST SCAN SUSPENDED READY MESSAGE
imagerepository/consumer-image-repository 2026-04-26T18:21:22Z False True successful scan: found 2 tags with checksum 1935083242
imagerepository/payments-image-repository 2026-04-26T18:21:22Z False True successful scan: found 2 tags with checksum 1935607531
imagerepository/producer-image-repository 2026-04-26T18:21:22Z False True successful scan: found 2 tags with checksum 1935869675
NAME IMAGE TAG READY MESSAGE
imagepolicy/consumer-image-policy 002437807755.dkr.ecr.us-west-2.amazonaws.com/consumer prd-20260424T054925Z True Latest image tag for 002437807755.dkr.ecr.us-west-2.amazonaws.com/consumer resolved to prd-20260424T054925Z
imagepolicy/payments-image-policy 002437807755.dkr.ecr.us-west-2.amazonaws.com/payments prd-20260424T054908Z True Latest image tag for 002437807755.dkr.ecr.us-west-2.amazonaws.com/payments resolved to prd-20260424T054908Z
imagepolicy/producer-image-policy 002437807755.dkr.ecr.us-west-2.amazonaws.com/producer prd-20260424T054944Z True Latest image tag for 002437807755.dkr.ecr.us-west-2.amazonaws.com/producer resolved to prd-20260424T054944Z
NAME LAST RUN SUSPENDED READY MESSAGE
imageupdateautomation/consumer-update-automation-pooled-envs 2026-04-26T18:19:12Z False True repository up-to-date
imageupdateautomation/consumer-update-automation-tenants 2026-04-26T18:19:12Z False True repository up-to-date
imageupdateautomation/payments-update-automation-pooled-envs 2026-04-26T18:17:24Z False True repository up-to-date
imageupdateautomation/payments-update-automation-tenants 2026-04-26T18:19:12Z False True repository up-to-date
imageupdateautomation/producer-update-automation-pooled-envs 2026-04-26T18:17:24Z False True repository up-to-date
imageupdateautomation/producer-update-automation-tenants 2026-04-26T18:19:12Z False True repository up-to-date
NAME REVISION SUSPENDED READY MESSAGE
helmrelease/argo-events 2.4.3 False True Helm install succeeded for release argo-events/argo-events.v1 with chart argo-events@2.4.3
helmrelease/argo-workflows 0.40.11 False True Helm install succeeded for release argo-workflows/argo-workflows.v1 with chart argo-workflows@0.40.11
helmrelease/aws-load-balancer-controller 1.6.2 False True Helm install succeeded for release aws-system/aws-load-balancer-controller.v1 with chart aws-load-balancer-controller@1.6.2
helmrelease/karpenter 1.4.0 False True Helm install succeeded for release karpenter/karpenter.v1 with chart karpenter@1.4.0
helmrelease/kubecost 2.1.0 False True Helm install succeeded for release kubecost/kubecost.v1 with chart cost-analyzer@2.1.0
helmrelease/metrics-server 3.11.0 False True Helm install succeeded for release kube-system/metrics-server.v1 with chart metrics-server@3.11.0
helmrelease/onboarding-service 0.0.1 False True Helm install succeeded for release onboarding-service/onboarding-service.v1 with chart application-chart@0.0.1
helmrelease/pool-1 0.0.1 False True Helm upgrade succeeded for release pool-1/pool-1.v2 with chart helm-tenant-chart@0.0.1
helmrelease/tf-controller 0.16.0-rc.4 False True Helm install succeeded for release flux-system/tf-controller.v1 with chart tf-controller@0.16.0-rc.4
NAME REVISION SUSPENDED READY MESSAGE
kustomization/capacitor v0.4.8@sha256:1efcb443 False True Applied revision: v0.4.8@sha256:1efcb443
kustomization/controlplane refs/heads/main@sha1:06abc42a False True Applied revision: refs/heads/main@sha1:06abc42a
kustomization/dataplane-pooled-envs refs/heads/main@sha1:06abc42a False True Applied revision: refs/heads/main@sha1:06abc42a
kustomization/dataplane-tenants refs/heads/main@sha1:06abc42a False True Applied revision: refs/heads/main@sha1:06abc42a
kustomization/dependencies refs/heads/main@sha1:06abc42a False True Applied revision: refs/heads/main@sha1:06abc42a
kustomization/flux-system refs/heads/main@sha1:06abc42a False True Applied revision: refs/heads/main@sha1:06abc42a
kustomization/infrastructure refs/heads/main@sha1:06abc42a False True Applied revision: refs/heads/main@sha1:06abc42a
kustomization/sources refs/heads/main@sha1:06abc42a False True Applied revision: refs/heads/main@sha1:06abc42a
Gitea 저장소 접근 설정
아키텍처에서 살펴보았듯, gitea 서버에 있는 다양한 저장소에 접근할 수 있어야 합니다. 먼저 아래 명령을 통해 gitea 서버의 접근정보를 확인합니다.
[ec2-user@ip-10-0-1-245 environment]$ export GITEA_PRIVATE_IP=$(kubectl get configmap saas-infra-outputs -n flux-system -o jsonpath='{.data.gitea_url}')
export GITEA_PUBLIC_IP=$(kubectl get configmap saas-infra-outputs -n flux-system -o jsonpath='{.data.gitea_public_url}')
export GITEA_PORT="3000"
[ec2-user@ip-10-0-1-245 environment]$ export GITEA_ADMIN_PASSWORD=$(aws ssm get-parameter --name "/eks-saas-gitops/gitea-admin-password" --with-decryption --query 'Parameter.Value' --output text)
[ec2-user@ip-10-0-1-245 environment]$ echo "=== Gitea Web Interface Access ==="
echo "Public URL (for browser access): $GITEA_PUBLIC_IP"
echo "Username: admin"
echo "Password: $GITEA_ADMIN_PASSWORD"
echo "=================================="
echo ""
echo "Use the PUBLIC URL above to access Gitea from your web browser."
=== Gitea Web Interface Access ===
Public URL (for browser access): http://<REDACTED>:3000
Username: admin
Password: <REDACTED>
==================================
Use the PUBLIC URL above to access Gitea from your web browser.
이후 ConfigMap에서 필요한 값을 가지고 와 클론할 준비를 합니다.
# Extract Gitea configuration from ConfigMap
export GITEA_TOKEN=$(kubectl get configmap saas-infra-outputs -n flux-system -o jsonpath='{.data.gitea_token}')
# Set up the repository paths used throughout the workshop
export REPO_PATH="/home/ec2-user/environment/microservice-repos"
export GITOPS_REPO_PATH="/home/ec2-user/environment/gitops-gitea-repo"
mkdir -p $REPO_PATH
[ec2-user@ip-10-0-1-245 environment]$ cd $REPO_PATH
# Clone the microservice repositories
git clone http://admin:${GITEA_TOKEN}@${GITEA_PRIVATE_IP}:${GITEA_PORT}/admin/producer.git
git clone http://admin:${GITEA_TOKEN}@${GITEA_PRIVATE_IP}:${GITEA_PORT}/admin/consumer.git
git clone http://admin:${GITEA_TOKEN}@${GITEA_PRIVATE_IP}:${GITEA_PORT}/admin/payments.git
# Verify the repositories were cloned successfully
echo "Microservice repositories:"
ls -la $REPO_PATH
echo ""
echo "GitOps repository:"
ls -la $GITOPS_REPO_PATH
Cloning into 'producer'...
remote: Enumerating objects: 8, done.
remote: Counting objects: 100% (8/8), done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 8 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (8/8), done.
Cloning into 'consumer'...
remote: Enumerating objects: 8, done.
remote: Counting objects: 100% (8/8), done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 8 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (8/8), done.
Cloning into 'payments'...
remote: Enumerating objects: 8, done.
remote: Counting objects: 100% (8/8), done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 8 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (8/8), done.
Microservice repositories:
total 0
drwxrwxr-x. 5 ec2-user ec2-user 54 Apr 26 18:30 .
drwxr-xr-x. 4 ec2-user ec2-user 86 Apr 26 18:30 ..
drwxrwxr-x. 4 ec2-user ec2-user 93 Apr 26 18:30 consumer
drwxrwxr-x. 4 ec2-user ec2-user 93 Apr 26 18:30 payments
drwxrwxr-x. 4 ec2-user ec2-user 93 Apr 26 18:30 producer
GitOps repository:
total 4
drwxr-xr-x. 9 ec2-user ec2-user 156 Apr 24 05:50 .
drwxr-xr-x. 4 ec2-user ec2-user 86 Apr 26 18:30 ..
drwxr-xr-x. 7 ec2-user ec2-user 147 Apr 26 15:18 .git
-rw-r--r--. 1 ec2-user ec2-user 1132 Apr 24 05:50 .gitignore
drwxr-xr-x. 3 ec2-user ec2-user 24 Apr 24 05:50 application-plane
drwxr-xr-x. 3 ec2-user ec2-user 24 Apr 24 05:50 clusters
drwxr-xr-x. 3 ec2-user ec2-user 24 Apr 24 05:50 control-plane
drwxr-xr-x. 4 ec2-user ec2-user 56 Apr 24 05:50 helm-charts
drwxr-xr-x. 4 ec2-user ec2-user 36 Apr 24 05:50 infrastructure
drwxr-xr-x. 3 ec2-user ec2-user 21 Apr 24 05:50 terraform
마지막으로 flux가 gitea 저장소 서버를 잘 모니터링하고 있는지 살펴봅니다. flux-system 과 terraform-v0-0-1 두 GitRepository가 READY=True 상태인지 확인하였습니다.
[ec2-user@ip-10-0-1-245 microservice-repos]$ kubectl -n flux-system get gitrepository
NAME URL AGE READY STATUS
flux-system http://10.35.48.28:3000/admin/eks-saas-gitops.git 1d12h True stored artifact for revision 'refs/heads/main@sha1:06abc42a8f9343dc24ebdc92e6e14bdee9eb6cac'
terraform-v0-0-1 http://10.35.48.28:3000/admin/eks-saas-gitops.git 1d12h True stored artifact for revision 'v0.0.1@sha1:2d19a84a88a6ded7c9aa8ac76508452e3f1d48b2'
1-2. Terraform & OpenTofu Controller
이번 애플리케이션 인프라는 Terraform 모듈을 사용하여 정의/관리됩니다. 저장소 내 모듈 구조는 아래와 같습니다:
tree /home/ec2-user/environment/gitops-gitea-repo/terraform/modules/ -L 2
gitops-gitea-repo/terraform/modules/
├── flux_cd/ # EKS에 Flux를 설치하는 데 필요한 리소스
├── gitea/ # Gitea 저장소에 필요한 리소스
├── gitops-saas-infra/ # 워크숍 전체 인프라 구성 리소스
└── tenant-apps/ # 테넌트 애플리케이션 전용 인프라 구성 요소
├── data.tf
├── main.tf
├── outputs.tf
├── variables.tf
└── versions.tf
아키텍처를 살펴보시면 아래와 같습니다:

이 실습에서 핵심은 tenant-apps 모듈로, 이 모듈 하나로 새 테넌트 온보딩 시 필요한 SQS 큐, DynamoDB 테이블, IRSA(IAM Role for Service Account) 등 모든 인프라를 프로비저닝할 수 있다는 것이 특징입니다.
테라폼 모듈을 확인해보기
경로에 접근해서 init/plan 을 해봅시다.
cd /home/ec2-user/environment/gitops-gitea-repo/
cat << EOF > terraform_test.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.100.0"
}
}
}
provider "aws" {}
module "test_tenant_apps" {
source = "./terraform/modules/tenant-apps"
tenant_id = "test"
enable_producer = true
enable_consumer = true
}
EOF
enable_producer 옵션의 true/false 차이는?irsa_role 의 유무입니다. 단순히 변수 하나를 컨트롤하는 것 만으로 내부 주요 설정을 크게 제어할 수 있다는 것이 장점입니다.
Tofu Controller 연동
Tofu Controller는 이 아키텍처에서 핵심적인 역할을 담당합니다. Terraform CRD라는 커스텀 리소스 정의를 활용하여 Terraform 모듈을 원활하게 연동할 수 있게 해줍니다.
Terraform Controller 동작 흐름
flowchart TD
Git[("Git 저장소
(Terraform CRD & 모듈)")]
Flux["Flux
(Source/Kustomize Controller)"]
CRD["Terraform CRD
(클러스터에 생성)"]
TFC["TF Controller
(flux-system 네임스페이스 감시)"]
Runner["tf-runner Pod"]
AWS["AWS 리소스 프로비저닝
(SQS, DynamoDB 등)"]
Secret[("Kubernetes Secret
tfstate / tfplan")]
Git -->|① 변경 감시| Flux
Flux -->|② Reconciliation| CRD
CRD -->|③ 생성/변경 감지| TFC
TFC -->|④ 파드 실행| Runner
Runner -->|⑤ Terraform 모듈 실행| AWS
Runner -->|⑥ state / plan 저장| Secret- Flux의 Git 저장소 감시: Flux는 Terraform CRD와 모듈이 포함된 Git 저장소의 변경 사항을 지속적으로 감시합니다.
- Terraform CRD 생성: Git 저장소에 정의된 Terraform CRD가 클러스터에 생성되면, Flux가 이를 감지하고 Reconciliation 프로세스를 시작합니다.
- TF Controller의 Terraform CRD 감시: TF Controller는
flux-system네임스페이스 내의 Terraform CRD를 모니터링하다가, 새로운 CRD가 생성되거나 기존 CRD가 업데이트되면 필요한 작업을 시작합니다. - tf-runner 파드 실행: TF Controller가 tf-runner 파드를 띄웁니다. 이 파드는 Git 저장소에서 지정된 Terraform 모듈을 가져와 실행하며, CRD에 정의된 대로 인프라를 관리합니다.
- 관리 리소스 프로비저닝: tf-runner 파드가 Terraform 모듈의 정의에 따라 Amazon SQS 큐, Amazon DynamoDB 테이블 등 필요한 리소스를 프로비저닝합니다.
- 상태를 시크릿에 저장: Terraform 실행의 상태(state)와 플랜(plan)은 쿠버네티스 시크릿(
tfstate,tfplan)으로 저장되어 이후 Terraform 작업에서도 접근할 수 있습니다.
Tofu Controller 파드의 상태를 확인합니다.
kubectl get po -n flux-system
NAME READY STATUS RESTARTS AGE
capacitor-76b57ffc6f-2w92w 1/1 Running 1 (131m ago) 159m
helm-controller-6fbc6bbbc9-7pfpz 1/1 Running 0 160m
kustomize-controller-9db49689-jdlt6 1/1 Running 0 160m
notification-controller-86d4b4cd45-m72lt 1/1 Running 0 160m
source-controller-cfb87bd97-57mhs 1/1 Running 0 160m
tf-controller-cf8b957d7-dvqk7 1/1 Running 0 159m
tf-controller 파드는 Terraform CRD 매니페스트를 감시합니다. Terraform CRD가 클러스터에 추가되면 별도의 파드에서 tf-runner를 실행하고, 지정된 모듈을 가져와 실행합니다. 동작을 직접 확인하기 위해 Terraform CRD를 생성해봅니다.
cat << EOF > /home/ec2-user/environment/gitops-gitea-repo/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
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
EOF
생성된 파일에서 sourceRef는 Flux가 사용하는 또 다른 커스텀 리소스 정의인 terraform-v0-0-1 GitRepository를 가리킵니다. 이 리소스를 살펴봅니다.
kubectl get GitRepository terraform-v0-0-1 -nflux-system -oyaml | grep -i spec -C10
spec:
interval: 300s
ref:
tag: v0.0.1
secretRef:
name: flux-system
timeout: 60s
url: http://10.35.48.243:3000/admin/eks-saas-gitops.git
GitRepository는 v0.0.1 태그를 가리키는 Gitea 저장소를 참조하며, 해당 태그의 리소스를 감시합니다. 이 태그는 워크샵 환경 구성 시 생성된 것입니다.
git tag
Terraform CRD에 정의된 path는 앞서 테스트한 tenant-apps 모듈을 가리킵니다.
spec:
path: ./terraform/modules/tenant-apps
GitOps 방식으로 배포하려면, Flux가 이를 Reconcile할 수 있도록 kustomization.yaml에 새 파일에 대한 참조를 추가합니다.
cat << EOF > /home/ec2-user/environment/gitops-gitea-repo/application-plane/production/tenants/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- basic
- advanced
- premium
- example-tenant-terraform-crd.yaml
EOF
Flux가 감시하는 main 브랜치에 변경 사항을 커밋하고 푸시합니다.
cd /home/ec2-user/environment/gitops-gitea-repo/
git pull origin main
git status
git add .
git commit -am "Added example terraform CRD for testing"
git push origin main
프로세스를 빠르게 진행하려면 다음 명령어로 Flux Reconciliation을 강제 실행합니다.
flux reconcile source git flux-system
잠시 후 po/example-tenant-tf-runner 파드가 실행되는 것을 확인할 수 있습니다. 컨트롤러가 실행한 이 파드는 tenant-apps Terraform 모듈을 수행합니다. 로그는 다음과 같이 확인합니다.
kubectl get po -nflux-system
kubectl logs po/example-tenant-tf-runner -nflux-system -f
Terraform이 Amazon EKS에 생성한 리소스를 검증합니다. 먼저 example-tenant용 DynamoDB 테이블이 프로비저닝됐는지 확인합니다.
aws dynamodb list-tables
consumer-example-tenant- 접두사를 가진 테이블을 확인할 수 있습니다. 이어서 SQS 큐도 확인합니다.
aws sqs list-queues
마찬가지로 consumer-example-tenant- 접두사를 가진 SQS 큐를 확인할 수 있습니다.
Terraform CRD 삭제
Tofu Controller를 통해 Terraform이 생성한 리소스를 삭제하려면, 저장소에서 CRD를 제거하고 kustomization 파일에서 해당 참조를 삭제하면 됩니다.
Terraform CRD 파일을 삭제합니다.
rm /home/ec2-user/environment/gitops-gitea-repo/application-plane/production/tenants/example-tenant-terraform-crd.yaml
kustomization.yaml에서 Terraform CRD 참조를 제거합니다.
cat << EOF > /home/ec2-user/environment/gitops-gitea-repo/application-plane/production/tenants/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- basic
- advanced
- premium
EOF
변경 사항을 커밋하고 푸시합니다.
cd /home/ec2-user/environment/gitops-gitea-repo/
git pull origin main
git add .
git commit -m "Removed Terraform CRD and reference from kustomization.yaml"
git push origin main
Flux Reconciliation을 강제 실행합니다.
flux reconcile source git flux-system
tf-runner의 terraform destroy 로그는 다음과 같이 확인합니다.
kubectl logs po/example-tenant-tf-runner -nflux-system -f
리소스 삭제 여부를 최종 검증합니다.
aws dynamodb list-tables
aws sqs list-queues
이번 실습에서는 Tofu Controller를 연동하여 Terraform CRD를 통해 Terraform 모듈을 관리하고, 인프라 프로비저닝을 자동화한 뒤 생성된 리소스를 검증했습니다. 다음에는 Helm 차트와, 쿠버네티스 애플리케이션 배포·관리에서 Helm이 담당하는 역할을 살펴보겠습니다.
Helm 차트
Helm 차트는 쿠버네티스 애플리케이션을 정의하고 설치하며 관리하는 강력한 방법입니다. 쿠버네티스 리소스를 패키지로 묶어 일관된 방식으로 관리할 수 있게 해줍니다. Helm 차트의 구조와 이번 워크샵에서의 활용 방식을 살펴봅니다.
Helm 차트 구조
Helm 차트는 연관된 쿠버네티스 리소스들을 기술하는 파일들의 모음으로, 다음과 같은 디렉토리 구조로 구성됩니다.
├── Chart.yaml # 차트에 대한 정보
├── values.yaml # 차트의 기본 설정값
├── charts/ # 의존 차트를 담는 디렉토리
├── templates/ # 쿠버네티스 매니페스트 템플릿을 담는 디렉토리
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── hpa.yaml
│ ├── serviceaccount.yaml
│ ├── terraform.yaml
│ └── ...
Chart.yaml: 차트의 이름, 버전, 설명 등 메타데이터를 담습니다.values.yaml: 차트 템플릿에 사용할 기본값을 정의합니다. 사용자는 이 값을 재정의하여 배포를 커스터마이징할 수 있습니다.charts/: 이 차트가 의존하는 하위 차트들을 담습니다.templates/: values와 결합하여 최종 매니페스트를 생성하는 쿠버네티스 매니페스트 템플릿들을 담습니다.
워크샵에서 사용하는 Helm 차트
이번 워크샵에서는 두 가지 주요 Helm 차트를 사용합니다.
- 테넌트 애플리케이션용 Helm 차트: 테넌트별 애플리케이션을 배포하는 차트로, 프로듀서(producer)와 컨슈머(consumer) 등 여러 서비스를 하나의 설치 단위로 묶습니다.
- 애플리케이션 차트: 애플리케이션과 1:1로 매핑되는 차트로, 테넌트에 종속되지 않습니다.
테넌트 애플리케이션용 Helm 차트의 구조는 다음과 같습니다.
├── Chart.yaml
├── values.yaml
├── templates/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── hpa.yaml
│ ├── serviceaccount.yaml
│ ├── terraform.yaml
│ └── ...
최소 values 파일 예시
Helm 차트를 테스트할 때는 필수 파라미터만 담은 간소화된 values.yaml을 사용할 수 있습니다.
# values.yaml
tenantId: "example-tenant"
apps:
producer:
enabled: true
consumer:
enabled: true
values.yaml은 템플릿에 사용할 설정값을 제공합니다. 최소한의 값만 정의한다는 것은 일부 파라미터만 재정의하고 나머지는 차트의 기본값에 의존한다는 의미입니다. 모든 설정 옵션을 일일이 재정의하지 않아도 되므로 빠른 테스트와 커스터마이징에 유리합니다.
Helm 차트 테스트
helm template 명령어를 사용하면 실제 배포 없이 쿠버네티스 매니페스트를 렌더링해볼 수 있습니다. 먼저 test-values.yaml 파일을 생성합니다.
cat << EOF > /home/ec2-user/environment/gitops-gitea-repo/helm-charts/helm-tenant-chart/test-values.yaml
tenantId: "example-tenant"
apps:
producer:
enabled: true
consumer:
enabled: true
EOF
프로듀서와 컨슈머 서비스를 모두 활성화한 상태로 차트를 테스트합니다.
cd /home/ec2-user/environment/gitops-gitea-repo
helm template example-tenant ./helm-charts/helm-tenant-chart --values ./helm-charts/helm-tenant-chart/test-values.yaml
터미널에 렌더링된 쿠버네티스 매니페스트가 출력되며, 프로듀서와 컨슈머 서비스 각각의 리소스를 확인할 수 있습니다.
이번엔 프로듀서 서비스를 비활성화하고 다시 테스트해봅니다. values.yaml에서 producer.enabled를 false로 변경합니다.
cat << EOF > /home/ec2-user/environment/gitops-gitea-repo/helm-charts/helm-tenant-chart/test-values.yaml
tenantId: "example-tenant"
apps:
producer:
enabled: false
consumer:
enabled: true
EOF
helm template example-tenant ./helm-charts/helm-tenant-chart --values ./helm-charts/helm-tenant-chart/test-values.yaml
프로듀서가 비활성화되어 디플로이먼트는 생성되지 않지만, 프로듀서에 대한 라우트는 여전히 생성됩니다. 특정 테넌트의 프로듀서가 비활성화된 경우 요청을 공용(pooled) 프로듀서로 포워딩하기 위함입니다. 이는 폴백(fallback) 메커니즘을 통해 서비스 연속성을 보장하는 구조로, 이후 티어 전략 섹션에서 리소스 관리와 테넌트별 서비스 수준 처리 방식을 더 자세히 다룹니다.
테스트가 끝났으면 임시 파일을 정리합니다.
rm -rf /home/ec2-user/environment/gitops-gitea-repo/helm-charts/helm-tenant-chart/test-values.yaml
이번 섹션에서는 Helm 차트의 구조를 살펴보고, helm template 명령어로 생성된 매니페스트를 직접 검증해봤습니다. 다음 섹션에서는 HelmRelease CRD를 활용하여 Helm 차트를 Flux와 연동하고, GitOps 원칙에 따라 Helm 배포를 관리하는 방법을 살펴보겠습니다.
Helm 차트와 Flux 연동
이 섹션에서는 Helm 차트를 Flux와 연동하여 쿠버네티스 애플리케이션을 관리하는 방법을 살펴봅니다. HelmRelease를 사용해 테넌트 애플리케이션용 Helm 차트를 배포합니다. 시작에 앞서, Helm 차트는 이미 패키징되어 ECR에 푸시된 상태입니다. 다음과 같이 확인합니다.
# ConfigMap에서 값 가져오기
AWS_ACCOUNT_ID=$(kubectl get configmap saas-infra-outputs -n flux-system -o jsonpath='{.data.account_id}')
ECR_HELM_CHART_URL=$(kubectl get configmap saas-infra-outputs -n flux-system -o jsonpath='{.data.ecr_helm_chart_url}')
ECR_REGISTRY=$(echo $ECR_HELM_CHART_URL | cut -d'/' -f1)
ECR_REPOSITORY=$(echo $ECR_HELM_CHART_URL | cut -d'/' -f2-)
AWS_REGION=$(echo $ECR_HELM_CHART_URL | cut -d'.' -f4)
# Docker를 ECR에 인증
aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ECR_REGISTRY
# ECR 리포지토리의 아티팩트(이미지) 목록 조회
aws ecr list-images --repository-name $ECR_REPOSITORY --region $AWS_REGION
0.0.1 태그로 패키징된 Helm 차트 하나가 이미 푸시되어 있는 것을 확인할 수 있습니다.
{
"imageIds": [
{
"imageDigest": "sha256:15806915991455232e0c7084008389392aa106d62bddd6c553ff062a35a46083",
"imageTag": "0.0.1"
}
]
}
HelmRelease는 Flux가 제공하는 CRD로, Helm 릴리즈를 선언적으로 관리할 수 있게 해줍니다. YAML 파일로 Helm 릴리즈를 정의하면 Flux가 클러스터에서의 배포와 관리를 자동으로 처리합니다.
다음은 example-tenant에 대한 HelmRelease 예시입니다.
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"
sourceRef:
kind: HelmRepository
name: helm-tenant-chart
values:
tenantId: example-tenant
apps:
producer:
enabled: true # 사일로(Silo) 배포 -- 프리미엄 티어는 테넌트별 전용 배포
consumer:
enabled: true # 사일로(Silo) 배포 -- 프리미엄 티어는 테넌트별 전용 배포
위 예시에서 sourceRef는 HelmRepository를 가리키고 있습니다. 이 경우 Helm 차트는 Amazon ECR에 저장됩니다. HelmRepository의 상세 정보는 다음과 같이 확인합니다.
$ kubectl get HelmRepository -nflux-system | grep -i helm-tenant-chart
helm-tenant-chart oci://123456789012.dkr.ecr.us-west-2.amazonaws.com/gitops-saas/
Flux가 Helm 릴리즈를 관리하고 배포하는 데 사용하는 ECR 리포지토리를 확인할 수 있습니다.
HelmRelease 생성
Step 1: HelmRelease YAML 파일 생성
cat << EOF > /home/ec2-user/environment/gitops-gitea-repo/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"
sourceRef:
kind: HelmRepository
name: helm-tenant-chart
values:
tenantId: example-tenant
apps:
producer:
enabled: true
consumer:
enabled: true
EOF
Step 2: Kustomization에 HelmRelease 추가
GitOps 방식으로 배포하려면 kustomization.yaml에 새 파일에 대한 참조를 추가합니다.
cat << EOF >> /home/ec2-user/environment/gitops-gitea-repo/application-plane/production/tenants/kustomization.yaml
- example-tenant-helmrelease.yaml
EOF
Step 3: 변경 사항 커밋 및 푸시
cd /home/ec2-user/environment/gitops-gitea-repo/
git pull origin main
git add .
git commit -m "Added HelmRelease for example-tenant"
git push origin main
Step 4: Flux Reconciliation 강제 실행
flux reconcile source git flux-system
리소스 확인
HelmRelease를 Git에 푸시하고 Flux가 Reconcile을 완료하면, 쿠버네티스와 AWS에 생성된 리소스를 확인합니다.
쿠버네티스 리소스 확인
example-tenant 네임스페이스가 생성됐는지 확인합니다.
kubectl get namespaces
해당 네임스페이스의 리소스 목록을 조회합니다.
kubectl get all -n example-tenant
AWS 리소스 확인
example-tenant용 DynamoDB 테이블과 SQS 큐가 생성됐는지 확인합니다.
aws dynamodb list-tables
aws sqs list-queues
리소스 정리
HelmRelease와 연관된 리소스를 삭제하려면 HelmRelease YAML 파일을 제거하고 kustomization.yaml을 업데이트합니다.
rm /home/ec2-user/environment/gitops-gitea-repo/application-plane/production/tenants/example-tenant-helmrelease.yaml
sed -i '/example-tenant-helmrelease.yaml/d' /home/ec2-user/environment/gitops-gitea-repo/application-plane/production/tenants/kustomization.yaml
변경 사항을 커밋하고 푸시합니다.
cd /home/ec2-user/environment/gitops-gitea-repo/
git pull origin main
git add .
git commit -m "Removed HelmRelease for example-tenant"
git push origin main
Flux Reconciliation을 강제 실행합니다.
flux reconcile source git flux-system
Flux가 Reconcile을 완료하고 Helm 릴리즈와 Terraform 리소스를 정리하는 데 다소 시간이 걸릴 수 있습니다. tf-runner 로그는 다음과 같이 확인합니다.
kubectl logs po/example-tenant-tf-runner -nflux-system -f
리소스 삭제 여부를 최종 검증합니다.
kubectl get namespaces
kubectl get all -n example-tenant
aws dynamodb list-tables
aws sqs list-queues
이번 섹션에서는 HelmRelease를 생성하여 Helm 차트와 Flux를 연동하고, 쿠버네티스 및 AWS에 생성된 리소스를 확인한 뒤 정상적으로 정리되는 과정까지 살펴봤습니다. 다음 섹션에서는 티어 전략을 더 깊이 살펴보고, 테넌트별 리소스를 효율적으로 관리하는 방법을 다루겠습니다.
물론 현재에도 여전히 유효하며, 필요에 따라 자체적으로 꾸리는 경우도 존재하죠! ↩︎
이 외에도 AWS가 플랫폼 엔지니어링을 소개하고 수많은 고객사와 협업한 방식 도 소개시켜 드리고 싶지만, 최소 4개의 회사에 대해 첨예한 논의방안이 있으므로 여러분들도 살펴보시기를 강권합니다. ↩︎