들어가며 — 셋을 같은 줄에 세우는 흔한 오해
쿠버네티스 운영을 어느 정도 해본 분이라면 "우리는 Helm 쓰는데 Operator도 써야 하나요?", "GitOps 도입하면 Helm은 버리는 건가요?" 같은 질문을 한 번쯤 받거나 던져봤을 것입니다. 이 질문들이 자주 헷갈리는 이유는 Operator, Helm, GitOps가 마치 같은 자리를 두고 경쟁하는 세 후보처럼 보이기 때문입니다.
그러나 이 셋은 애초에 서로 다른 문제를 풉니다. 한 줄로 요약하면 이렇습니다.
- **Helm**은 쿠버네티스 매니페스트를 패키징하고 템플릿화하는 도구입니다. "무엇을 배포할지"를 묶음으로 정의합니다.
- **GitOps**는 Git 저장소의 선언을 클러스터 상태로 지속 동기화하는 운영 방식입니다. "어떻게 배포하고 유지할지"를 정의합니다.
- **Operator**는 특정 애플리케이션의 운영 지식을 코드로 담은 컨트롤러입니다. "배포 이후의 운영을 누가 대신할지"를 정의합니다.
즉 이들은 경쟁 관계가 아니라 대체로 보완 관계입니다. 실제 프로덕션에서는 셋을 동시에 쓰는 경우가 가장 흔합니다. 이 글의 목표는 각 도구의 본질을 분명히 갈라놓고, 어떤 상황에서 무엇을 선택해야 하는지에 대한 판단 기준을 세우는 것입니다.
세 가지의 본질 — 패키징, 동기화, 컨트롤러
Helm — 패키징과 템플릿
Helm의 핵심은 "차트(chart)"라는 패키지입니다. 차트는 여러 쿠버네티스 매니페스트(Deployment, Service, ConfigMap 등)를 한 단위로 묶고, 값(values)을 주입해 환경별로 다른 결과를 찍어냅니다.
templates/deployment.yaml (Helm 템플릿 일부)
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
template:
spec:
containers:
- name: app
image: my-app:1.2.3
같은 차트에 값만 바꿔 dev/stage/prod를 만드는 것이 Helm의 전형적 사용법입니다. 중요한 점은 Helm 자체는 "한 번 렌더링해서 클러스터에 보내는" 도구라는 것입니다. helm install이나 helm upgrade를 실행한 그 순간에만 동작하고, 그 뒤에 누가 클러스터를 손으로 바꿔도 Helm은 알지 못합니다. Helm은 정적 배포 도구입니다.
GitOps — 선언적 지속 동기화
GitOps는 도구라기보다 운영 모델입니다. 핵심 원칙은 네 가지입니다.
1. 시스템의 바람직한 상태(desired state)를 전부 선언으로 표현한다.
2. 그 선언을 Git에 버전 관리한다. Git이 단일 진실 공급원(single source of truth)이 된다.
3. 승인된 변경은 자동으로 클러스터에 적용된다.
4. 에이전트가 실제 상태와 선언을 지속적으로 비교하고, 드리프트가 생기면 알리거나 되돌린다.
Argo CD나 Flux 같은 도구가 이 모델을 구현합니다. 여기서 결정적 차이는 4번, **지속적 reconcile**입니다. 누가 클러스터에서 replicas를 수동으로 바꿔도 Argo CD는 곧 그것을 드리프트로 감지하고 Git 상태로 되돌립니다. Helm이 "발사 후 망각"이라면 GitOps는 "끊임없이 조준 보정"입니다.
Operator — 운영 지식을 담은 컨트롤러
Operator는 한 단계 더 깊이 들어갑니다. Helm과 GitOps가 쿠버네티스의 빌트인 리소스를 다루는 데 집중한다면, Operator는 새로운 리소스 종류(CRD, Custom Resource Definition)를 추가하고 그것을 돌보는 전용 컨트롤러를 함께 배포합니다.
예를 들어 PostgreSQL Operator를 설치하면 클러스터에 PostgresCluster 같은 새로운 리소스 타입이 생깁니다. 사용자는 이렇게 선언합니다.
apiVersion: postgres.example.com/v1
kind: PostgresCluster
metadata:
name: orders-db
spec:
instances: 3
version: "16"
backup:
schedule: "0 2 * * *"
이 선언 하나에 대해 Operator는 StatefulSet 생성, 복제 구성, 페일오버, 백업 스케줄링, 마이너 버전 업그레이드까지 알아서 처리합니다. 사람이 직접 했어야 할 운영 절차(Day-2 operations)를 코드로 자동화한 것입니다.
핵심 차이를 그림으로 보면 이렇습니다.
Helm: values → [한 번 렌더링] → 매니페스트 → 클러스터
(실행 시점에만 동작)
GitOps: Git 선언 → [에이전트가 계속 비교] → 클러스터
(드리프트 자동 교정, 지속 reconcile)
Operator: CR 선언 → [전용 컨트롤러가 계속 reconcile] → 빌트인 리소스 + 운영 행위
(백업/페일오버/업그레이드까지 자동화)
정적 배포 vs 지속적 reconcile
세 도구를 가르는 가장 중요한 축은 "한 번만 동작하는가, 계속 동작하는가"입니다.
| 구분 | 동작 시점 | 드리프트 교정 | 비유 |
| --- | --- | --- | --- |
| Helm 단독 | 명령 실행 순간 | 없음 | 사진 한 장 찍기 |
| GitOps | 항상(주기적 + 이벤트) | 자동 | 자동 초점 카메라 |
| Operator | 항상(이벤트 기반) | 자동 + 운영 행위 | 전담 운영자 채용 |
Helm을 단독으로 쓰면 클러스터 상태는 점점 선언에서 벗어납니다. 누군가 핫픽스로 kubectl edit을 하고, 또 누군가 replicas를 임시로 올리고, 시간이 지나면 "지금 클러스터에 뭐가 떠 있는지"를 아무도 정확히 모릅니다. GitOps는 바로 이 드리프트 문제를 구조적으로 막습니다. Operator는 여기서 한 발 더 나가, 단순히 매니페스트를 맞추는 것을 넘어 애플리케이션 고유의 운영 행위(백업, 페일오버 등)까지 reconcile 루프 안에서 처리합니다.
Day-1 vs Day-2 — 경계가 어디인가
운영 업계에서는 작업을 Day-0/Day-1/Day-2로 나눠 부릅니다.
- **Day-0**: 설계와 계획. 아키텍처 결정, 용량 산정.
- **Day-1**: 설치와 초기 배포. "처음 띄우기".
- **Day-2**: 운영. 업그레이드, 백업/복구, 스케일링, 장애 대응, 패치. 시스템 수명의 대부분을 차지하는 구간.
이 구분으로 세 도구의 강점을 보면 명확해집니다.
- **Helm**은 Day-1에 강합니다. 복잡한 애플리케이션을 한 번에 깔끔히 설치하는 데 최적입니다. 그러나 Day-2 운영 행위는 Helm 자체에 들어 있지 않습니다.
- **GitOps**는 Day-1과 Day-2를 모두 거듭니다. 초기 배포부터 이후의 모든 변경을 동일한 Git 워크플로로 관리합니다. 다만 GitOps도 "선언을 맞추는" 일까지이고, 애플리케이션 내부의 복잡한 운영(예: DB 페일오버 절차)을 스스로 알지는 못합니다.
- **Operator**는 Day-2에 강합니다. 애플리케이션 특유의 운영 지식이 컨트롤러 코드에 들어 있어, 사람이 매번 개입할 필요를 없앱니다.
결론적으로 Operator는 Day-2 운영이 복잡하고 반복적인 스테이트풀 애플리케이션(데이터베이스, 메시지 큐, 검색 엔진)에서 진가를 발휘합니다. 반면 운영이 단순한 스테이트리스 웹 애플리케이션이라면 Operator는 과한 선택일 수 있습니다.
조합 패턴 — 실전에서는 셋을 함께 쓴다
가장 흔하고 권장되는 구성은 셋의 역할을 겹치지 않게 배치하는 것입니다.
패턴 1 — GitOps로 Helm 차트를 배포
Argo CD나 Flux는 Helm 차트를 직접 소스로 다룰 수 있습니다. 즉 차트는 패키징 단위로 쓰고, 그것을 클러스터에 적용하고 유지하는 일은 GitOps 에이전트가 맡습니다.
Argo CD Application — Helm 차트를 GitOps로 관리
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/example/my-app-chart
targetRevision: main
path: charts/my-app
helm:
valueFiles:
- values-prod.yaml
destination:
server: https://kubernetes.default.svc
namespace: my-app
syncPolicy:
automated:
prune: true
selfHeal: true
이렇게 하면 Helm의 템플릿 능력과 GitOps의 지속 동기화/드리프트 교정을 동시에 얻습니다. helm install을 사람이 직접 실행하는 일은 사라지고, 모든 변경은 Git PR을 통해 흐릅니다.
패턴 2 — Helm으로 Operator를 설치하고, GitOps로 CR을 관리
Operator 자체는 보통 Helm 차트나 OLM(Operator Lifecycle Manager)으로 설치합니다. 즉 Operator 컨트롤러의 배포는 Helm이, 그 Operator가 다루는 커스텀 리소스(CR)는 GitOps가 관리합니다.
[플랫폼 팀]
Helm 차트로 PostgreSQL Operator 설치 (컨트롤러 + CRD)
│
▼
[애플리케이션 팀]
Git에 PostgresCluster CR 선언 → Argo CD가 동기화
│
▼
Operator가 CR을 reconcile → StatefulSet/백업/페일오버 자동화
이 구성에서 각자의 책임이 깔끔히 나뉩니다. Helm은 "Operator 소프트웨어를 깐다", GitOps는 "선언을 클러스터와 일치시킨다", Operator는 "선언된 DB를 실제로 운영한다". 세 도구가 서로의 영역을 침범하지 않습니다.
패턴 3 — App-of-Apps와 부트스트랩
대규모 플랫폼에서는 Argo CD의 App-of-Apps 패턴으로 "Argo CD가 다른 모든 Application을 관리"하게 하고, 그 Application들이 다시 Helm 차트(공통 인프라)와 Operator 설치, 그리고 워크로드를 배포합니다. 클러스터 부트스트랩 전체가 하나의 Git 트리에서 흘러나오는 구조입니다.
의사결정 테이블 — 무엇을 언제
다음 표는 상황별로 어떤 도구가 적합한지 정리한 것입니다.
| 상황 | 권장 도구 | 이유 |
| --- | --- | --- |
| 스테이트리스 웹앱 배포 | Helm + GitOps | 운영이 단순, Operator 불필요 |
| 환경별 값만 다른 다수 배포 | Helm + GitOps | 템플릿 + 지속 동기화 |
| 프로덕션 데이터베이스 운영 | Operator | 백업/페일오버/업그레이드 자동화 필요 |
| 메시지 큐, 검색 엔진 운영 | Operator | 복잡한 Day-2 운영 |
| 여러 클러스터 일관 관리 | GitOps | Git 단일 진실 공급원 |
| 드리프트가 잦은 환경 | GitOps | selfHeal로 자동 교정 |
| 사내 표준 워크플로 자동화 | Operator(또는 단순 컨트롤러) | 운영 지식의 코드화 |
| 일회성 실험/PoC | Helm 단독 | 가장 빠름, 거버넌스 불필요 |
표를 외우기보다 한 가지 질문으로 압축할 수 있습니다. **"이 시스템의 운영(Day-2)이 반복적이고 애플리케이션 특유의 지식을 요구하는가?"** 그렇다면 Operator를, 아니라면 Helm + GitOps 조합을 먼저 검토하세요.
사례별 추천
- **신생 SaaS의 백엔드 API**: Helm 차트 한 개 + Argo CD. 배포가 단순하고 변경이 잦으니 GitOps의 PR 기반 흐름이 가장 큰 이점을 줍니다. Operator는 아직 필요 없습니다.
- **금융권 망분리 환경의 PostgreSQL**: 검증된 Operator를 Helm으로 설치하고, CR은 GitOps로 관리. 백업과 페일오버를 사람 손에 맡길 수 없는 환경이므로 Operator의 자동화가 핵심입니다.
- **수십 개 엣지 클러스터의 공통 에이전트 배포**: GitOps(Flux의 멀티테넌시 또는 Argo CD의 ApplicationSet) 중심. 클러스터마다 동일 선언을 일관 적용하는 것이 최우선입니다.
- **머신러닝 플랫폼의 모델 서빙**: 서빙 프레임워크가 제공하는 Operator(CRD 기반)를 쓰고, 그 위 워크로드는 GitOps로 관리.
Operator가 과한 경우 — 비용을 직시하기
Operator는 강력하지만 공짜가 아닙니다. 다음 경우라면 Operator는 과잉 엔지니어링일 가능성이 큽니다.
- 운영 행위가 단순해서 Deployment 한 줄 수정으로 충분한 경우. CRD와 컨트롤러를 만들/운영할 이유가 없습니다.
- 팀에 컨트롤러를 유지보수할 Go 역량이나 인력이 없는 경우. 직접 만든 Operator는 또 하나의 유지보수 대상이 됩니다.
- 변경 빈도가 낮아 자동화 투자 회수가 안 되는 경우.
Operator의 진짜 비용은 코드 작성보다 **장기 유지보수**에 있습니다. 쿠버네티스 버전이 올라갈 때마다 controller-runtime 의존성을 따라가야 하고, RBAC 권한을 정교하게 관리해야 하며, reconcile 로직의 버그는 곧 운영 사고로 이어집니다. "외부의 잘 관리되는 Operator를 쓰는 것"과 "직접 Operator를 만드는 것"은 전혀 다른 비용 구조임을 명확히 구분해야 합니다.
마이그레이션 — Helm 차트에서 Operator로
성숙한 애플리케이션은 종종 Helm 차트로 시작했다가 운영 복잡도가 커지면서 Operator로 진화합니다. 전형적 경로는 이렇습니다.
1. **현재 차트의 운영 부담을 목록화한다.** 사람이 수동으로 하던 절차(백업, 스케일링, 장애 복구)를 모두 적습니다. 이것이 Operator가 자동화할 후보입니다.
2. **CRD의 스키마를 설계한다.** 사용자가 선언할 최소한의 의도(예: 인스턴스 수, 버전, 백업 스케줄)만 남기고 나머지는 컨트롤러가 결정하게 합니다.
3. **점진적으로 전환한다.** 처음에는 Operator가 기존 차트가 만들던 것과 동일한 리소스를 생성하게 하고, 이후 백업/페일오버 같은 운영 행위를 단계적으로 추가합니다.
4. **공존 기간을 둔다.** 기존 Helm 배포와 Operator 기반 배포를 한동안 병행 운영하며 동등성을 검증한 뒤 전환합니다.
반대로 "굳이 Operator로 갈 필요가 없다"는 결론이 나오면 그대로 Helm + GitOps에 머무는 것도 충분히 옳은 선택입니다. 마이그레이션 자체가 목적이 되어선 안 됩니다.
운영 비용 비교
| 항목 | Helm + GitOps | Operator(외부 도입) | Operator(자체 개발) |
| --- | --- | --- | --- |
| 초기 도입 난이도 | 낮음 | 중간 | 높음 |
| Day-2 자동화 수준 | 제한적 | 높음 | 높음 |
| 유지보수 부담 | 낮음 | 중간(업스트림 추적) | 높음(직접 코드 관리) |
| 필요 역량 | YAML, GitOps | 도입/설정 | Go, controller-runtime |
| 장애 시 디버깅 | 매니페스트 추적 | CR + 컨트롤러 로그 | 컨트롤러 코드까지 |
이 표의 핵심 메시지는 "자동화 수준이 높아질수록 유지보수 부담도 함께 오른다"는 것입니다. 자동화는 공짜가 아니라 비용을 미래로 옮기는 일에 가깝습니다.
관측과 트러블슈팅 — 도구별로 어디를 보는가
장애가 났을 때 어디를 들여다봐야 하는지는 도구마다 다릅니다. 이 지도를 미리 갖고 있으면 대응 속도가 크게 빨라집니다.
| 증상 | Helm 관점 | GitOps 관점 | Operator 관점 |
| --- | --- | --- | --- |
| 배포가 반영 안 됨 | helm history 확인 | Application 동기화 상태 확인 | CR status와 컨트롤러 로그 |
| 리소스가 자꾸 되돌아감 | (해당 없음) | selfHeal 드리프트 교정 의심 | reconcile 루프 의심 |
| 일부 환경만 다름 | values 파일 비교 | 환경별 소스 경로 확인 | CR spec 차이 확인 |
| 리소스가 안 지워짐 | helm uninstall 잔여 | prune 비활성 의심 | finalizer 미완료 의심 |
핵심은 "이 리소스를 지금 누가 소유하고 있는가"를 먼저 파악하는 것입니다. Helm이 만든 것인지, GitOps가 동기화한 것인지, Operator가 reconcile한 것인지에 따라 손대는 위치가 완전히 달라집니다. 같은 리소스를 두 주체가 관리하면 바로 그곳이 충돌의 진원지입니다.
소유권을 라벨로 추적하기
세 도구는 모두 자신이 만든 리소스에 식별 메타데이터를 남깁니다. Helm은 어노테이션과 라벨로 릴리스를 표시하고, Argo CD는 tracking 라벨로 어떤 Application 소유인지 남기며, Operator는 보통 ownerReference로 부모 CR을 가리킵니다. 장애 분석의 첫걸음은 문제 리소스의 메타데이터를 읽어 "누구의 것인지" 확인하는 것입니다.
이 리소스의 출처를 추적하는 순서
1. kubectl get <resource> -o yaml 로 메타데이터 확인
2. ownerReference 가 있으면 → Operator(또는 빌트인 컨트롤러) 소유
3. Argo CD tracking 라벨이 있으면 → GitOps 소유
4. Helm release 어노테이션이 있으면 → Helm 소유
함정 모음
- **GitOps와 Helm의 release 충돌**: helm install로 직접 설치한 릴리스를 나중에 Argo CD가 관리하려 하면 소유권 충돌이 납니다. 처음부터 한쪽으로 통일하세요.
- **Operator를 GitOps로 설치할 때 CRD 순서**: CRD가 만들어지기 전에 CR을 적용하려 하면 동기화가 실패합니다. Argo CD의 sync wave나 Flux의 의존성으로 순서를 보장해야 합니다.
- **selfHeal가 Operator의 정상 동작을 되돌리는 경우**: Operator가 status나 일부 필드를 갱신하는데 GitOps가 그것을 드리프트로 오인해 되돌리면 무한 충돌이 납니다. ignoreDifferences로 컨트롤러 관리 필드를 제외하세요.
- **values의 비밀 정보**: Helm values에 평문 비밀을 넣고 Git에 커밋하는 사고가 흔합니다. Sealed Secrets, External Secrets Operator, SOPS 등을 함께 쓰세요.
의사결정 흐름도
표를 흐름도로 바꾸면 실제 선택 순간에 더 유용합니다.
시작: 무엇을 배포/운영하려는가?
│
├─ 일회성 실험인가? ──예──> Helm 단독으로 충분
│
└─ 아니오
│
├─ 여러 환경/클러스터에 일관 적용이 필요한가? ──예──> GitOps 도입
│ (Helm/Kustomize를 소스로)
│
└─ 이 시스템의 Day-2 운영이 복잡한가?
(백업/페일오버/버전 업그레이드가 반복적인가?)
│
├─ 예 ──> 잘 관리되는 외부 Operator가 있는가?
│ ├─ 있다 ──> 그 Operator를 Helm으로 설치 + CR을 GitOps로
│ └─ 없다 ──> 자체 Operator 개발 검토 (비용 직시)
│
└─ 아니오 ──> Helm + GitOps로 충분
이 흐름도의 분기점은 단 두 개입니다. 첫째 "일관성이 필요한가"(→ GitOps), 둘째 "Day-2가 복잡한가"(→ Operator). 대부분의 의사결정은 이 두 질문으로 끝납니다.
흔한 안티패턴
세 도구를 다루며 자주 빠지는 함정을 모았습니다.
- **모든 것을 Operator로 만들려는 충동**: 자동화할 수 있다는 사실이 자동화해야 한다는 뜻은 아닙니다. 단순한 워크로드에 CRD를 도입하면 인지 부하와 유지보수 부담만 늘어납니다.
- **GitOps 없이 Operator만 도입**: Operator는 CR을 reconcile하지만, 그 CR 자체가 사람의 손에서 임의로 관리되면 다시 드리프트와 추적 불가 문제가 생깁니다. CR도 Git에 두세요.
- **Helm을 명령형으로 사용**: helm install/upgrade를 사람이 직접, 그것도 환경별로 다르게 실행하면 재현성이 무너집니다. 차트는 패키징에만 쓰고 적용은 GitOps에 맡기세요.
- **비밀 정보를 values 평문으로**: 가장 흔한 보안 사고입니다. 비밀은 항상 별도 도구로 암호화하세요.
- **도구를 목적으로 착각**: "우리도 Operator 만들자", "GitOps 멋지니 도입하자"는 동기는 위험합니다. 출발점은 언제나 해결할 운영 문제여야 합니다.
실전 시나리오 — 한 서비스의 진화를 따라가며
추상적 비교만으로는 감이 잘 안 옵니다. 가상의 주문 서비스가 시간이 지나며 세 도구를 어떻게 차례로 받아들이는지 따라가 보겠습니다.
1단계 — Helm 단독 (출범 직후)
서비스 초기에는 Deployment 하나, Service 하나, ConfigMap 하나가 전부입니다. 개발자가 노트북에서 helm upgrade를 직접 실행해 배포합니다. 이 단계에서는 이것으로 충분합니다. 거버넌스도, 드리프트 걱정도 아직 큰 문제가 아닙니다. 다만 슬슬 다음 증상이 보이기 시작합니다.
- "어제 누가 prod에 뭘 배포했지?"에 답할 기록이 없습니다.
- 스테이징과 프로덕션의 values가 미묘하게 달라졌는데 왜인지 모릅니다.
- 배포 권한이 특정 개발자의 노트북에 묶여 있습니다.
2단계 — GitOps 도입 (팀이 커지며)
여기서 Argo CD를 도입합니다. 차트는 그대로 두고, 배포 트리거만 Git PR로 옮깁니다. 이제 모든 변경이 PR 리뷰를 거치고, 누가 언제 무엇을 바꿨는지 Git 히스토리에 남으며, 드리프트는 selfHeal로 자동 교정됩니다. 개발자 노트북의 helm 명령은 사라집니다.
변화: "사람이 클러스터에 직접 apply"
→ "사람이 Git에 PR, 에이전트가 클러스터에 apply"
이 전환의 핵심 이득은 배포 그 자체가 아니라 **감사 가능성과 일관성**입니다.
3단계 — Operator 도입 (데이터 계층이 무거워지며)
주문량이 늘며 PostgreSQL을 직접 운영하기 시작합니다. 백업, 페일오버, 마이너 버전 업그레이드가 사람 손을 떠나야 하는 수준이 됩니다. 이때 검증된 PostgreSQL Operator를 도입합니다. Operator 컨트롤러는 Helm으로 설치하고, PostgresCluster CR은 GitOps로 관리합니다. 앞서 본 조합 패턴 2가 바로 이 단계입니다.
이 진화의 교훈은 분명합니다. **처음부터 셋을 다 도입할 필요가 없습니다.** 운영 복잡도가 실제로 그 도구를 정당화하는 시점에 하나씩 더합니다. 과한 선행 투자는 그 자체가 부채입니다.
자주 묻는 질문
**Q. Helm은 이제 구식인가요? Kustomize나 GitOps가 대체하나요?**
아닙니다. Helm은 여전히 가장 널리 쓰이는 패키징 도구이고, GitOps 에이전트들은 Helm 차트를 일급으로 지원합니다. Kustomize는 템플릿 없이 오버레이로 변형하는 다른 접근이며, Helm과 경쟁한다기보다 취향과 상황에 따라 선택됩니다. 셋 다 GitOps 아래에서 공존할 수 있습니다.
**Q. Operator를 쓰면 GitOps가 필요 없나요?**
오히려 함께 쓰는 것이 정석입니다. Operator는 "CR을 어떻게 운영할지"를 알고, GitOps는 "그 CR 선언을 어떻게 클러스터와 일치시킬지"를 압니다. 역할이 다릅니다.
**Q. 작은 팀인데 셋 다 배워야 하나요?**
아닙니다. 작은 팀이라면 Helm + GitOps만으로 대부분의 스테이트리스 워크로드를 잘 운영할 수 있습니다. Operator는 정말로 복잡한 스테이트풀 운영이 생겼을 때, 그것도 가급적 잘 관리되는 외부 Operator를 도입하는 형태로 시작하세요.
**Q. 직접 만든 Operator와 Helm 차트, 어느 쪽이 유지보수가 쉽나요?**
일반적으로 Helm 차트가 훨씬 쉽습니다. Operator는 Go 코드와 controller-runtime 의존성, RBAC, reconcile 로직이라는 지속적 유지보수 표면을 추가합니다. 자동화의 가치가 그 부담을 넘어설 때만 정당화됩니다.
**Q. GitOps 에이전트로 Argo CD와 Flux 중 무엇을 골라야 하나요?**
둘 다 OpenGitOps 원칙을 충실히 구현하며, 핵심 기능은 거의 동등합니다. Argo CD는 풍부한 웹 UI와 직관적인 동기화 시각화가 강점이고, Flux는 GitOps Toolkit이라는 모듈식 구성과 경량성이 강점입니다. UI를 중시하는 팀은 Argo CD, 코드/CRD 중심의 조립을 선호하는 팀은 Flux를 흔히 택합니다. 이 글의 결론(셋의 역할 분담)은 어느 쪽을 택하든 동일하게 적용됩니다.
**Q. Operator를 도입하면 클러스터 권한이 너무 커지지 않나요?**
타당한 우려입니다. Operator는 자신이 관리하는 리소스에 대한 권한이 필요하고, 데이터베이스 Operator라면 Secret 접근이 불가피한 경우가 많습니다. 그래서 신뢰할 수 있는 출처의 Operator를 쓰고, 설치 시 부여되는 RBAC를 반드시 검토하며, 가능하면 네임스페이스 범위로 한정하는 것이 좋습니다. 권한 검토는 Operator 선택의 핵심 기준 중 하나입니다.
셋을 함께 담는 저장소 구조 예시
이론을 실제 디렉토리로 옮기면 더 분명해집니다. 다음은 Helm + GitOps + Operator를 한 저장소에서 운영하는 전형적 구조입니다.
platform-gitops/
├── bootstrap/
│ └── argocd-apps.yaml # App-of-Apps: 아래 모든 것을 관리
├── infrastructure/
│ ├── postgres-operator/ # Operator 설치 (Helm 차트 참조)
│ │ └── application.yaml
│ └── cert-manager/ # 공통 인프라 (Helm)
│ └── application.yaml
├── databases/
│ └── orders-db/
│ └── postgrescluster.yaml # Operator가 reconcile할 CR (GitOps 관리)
└── apps/
└── orders-api/
├── application.yaml # Helm 차트를 가리키는 Argo CD Application
└── values-prod.yaml # 환경별 값
이 구조에서 각 층의 역할이 디렉토리로 드러납니다.
- infrastructure/는 Operator와 공통 컴포넌트를 Helm으로 설치합니다.
- databases/는 Operator가 다루는 CR을 GitOps로 선언합니다.
- apps/는 일반 워크로드를 Helm 차트 + GitOps로 배포합니다.
- bootstrap/의 App-of-Apps가 이 전체를 하나로 묶어, 빈 클러스터에 한 번만 적용하면 나머지가 자동으로 흘러나옵니다.
핵심은 sync wave로 순서를 보장하는 것입니다. Operator(와 그 CRD)가 먼저 설치된 뒤에 databases/의 CR이 적용되어야 합니다. Argo CD라면 어노테이션으로 wave 번호를 매겨 이 의존성을 표현합니다. 이 한 장의 디렉토리 구조가 이 글 전체의 결론을 시각적으로 요약합니다. 세 도구가 각자의 자리에서, 충돌 없이, 하나의 Git 트리 아래 공존하는 모습입니다.
마치며
세 도구를 다시 한 문장으로 정리하면 이렇습니다. Helm은 **묶고**, GitOps는 **맞추고**, Operator는 **돌봅니다**. 이들은 경쟁자가 아니라 운영 스택의 서로 다른 층입니다. 대부분의 프로덕션은 "Helm으로 패키징, GitOps로 동기화, 필요한 곳에만 Operator로 운영 자동화"라는 조합으로 수렴합니다.
선택의 출발점은 언제나 운영의 복잡도입니다. 단순한 워크로드에 Operator를 끌어들이지 말고, 복잡한 스테이트풀 운영을 사람 손에 맡기지 마세요. 도구의 본질을 알면 "무엇을 언제 쓸지"는 자연스럽게 따라옵니다.
참고 자료
- Kubernetes Operator 패턴 공식 문서: https://kubernetes.io/docs/concepts/extend-kubernetes/operator/
- Kubebuilder Book: https://book.kubebuilder.io/
- Operator SDK: https://sdk.operatorframework.io/
- Operator Lifecycle Manager (OLM): https://olm.operatorframework.io/
- Helm 공식 문서: https://helm.sh/docs/
- Argo CD 공식 문서: https://argo-cd.readthedocs.io/
- Flux 공식 문서: https://fluxcd.io/flux/
- OpenGitOps 원칙: https://opengitops.dev/
- controller-runtime: https://pkg.go.dev/sigs.k8s.io/controller-runtime
- Argo CD App-of-Apps 패턴: https://argo-cd.readthedocs.io/en/stable/operator-manual/cluster-bootstrapping/
- Argo CD Sync Waves: https://argo-cd.readthedocs.io/en/stable/user-guide/sync-waves/
- Kustomize 공식 문서: https://kustomize.io/
- kubebuilder GitHub: https://github.com/kubernetes-sigs/kubebuilder
현재 단락 (1/234)
쿠버네티스 운영을 어느 정도 해본 분이라면 "우리는 Helm 쓰는데 Operator도 써야 하나요?", "GitOps 도입하면 Helm은 버리는 건가요?" 같은 질문을 한 번쯤 받...