Table of contents
- 환경이 늘어나면 생기는 문제
- Kustomize — overlay로 환경 분리
- Helm — values로 설정 분리
- Kustomize vs Helm — 언제 무엇을
- 환경별 배포 전략
환경이 늘어나면 생기는 문제
3편에서 하나의 Application을 등록하는 건 해봤다. 그런데 실무에서는 dev, staging, production 같은 환경이 여럿 있고, 환경마다 설정이 조금씩 다르다. 레플리카 수, 이미지 태그, 리소스 제한, ConfigMap 값 등이 환경별로 달라야 한다.
가장 단순한 방법은 환경별로 매니페스트를 통째로 복사하는 것이다. 하지만 이러면 공통 부분을 수정할 때마다 모든 환경의 파일을 일일이 고쳐야 하고, 하나를 빠뜨리면 환경 간 불일치가 생긴다. 유지보수의 악몽이다.
Kustomize와 Helm은 이 문제를 각각 다른 방식으로 풀어준다. 그리고 ArgoCD는 두 도구를 모두 네이티브로 지원한다.
Kustomize — overlay로 환경 분리
Kustomize는 kubectl에 내장되어 있을 정도로 Kubernetes 생태계에서 표준적인 도구다. 핵심 아이디어는 base 매니페스트를 두고, 환경별 차이만 overlay로 덧씌우는 것이다.
디렉토리 구조
전형적인 Kustomize 프로젝트 구조는 이렇다.
k8s/
├── base/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ └── service.yaml
├── overlays/
│ ├── dev/
│ │ ├── kustomization.yaml
│ │ └── patch-replicas.yaml
│ └── prod/
│ ├── kustomization.yaml
│ ├── patch-replicas.yaml
│ └── patch-resources.yaml
base/에 공통 매니페스트를 놓고, overlays/dev/와 overlays/prod/에 환경별 변경사항만 패치 형태로 정의한다.
base 작성
base는 3편에서 만든 매니페스트와 거의 같다. 여기에 kustomization.yaml만 추가하면 된다.
# k8s/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
resources 필드에 이 디렉토리에서 관리할 매니페스트를 나열한다. Kustomize는 이 파일을 기준으로 어떤 리소스를 처리할지 결정한다.
dev overlay
dev 환경에서는 레플리카를 1개로 줄이고 싶다. 패치 파일을 만든다.
# k8s/overlays/dev/patch-replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-demo
spec:
replicas: 1
이 패치를 base에 적용하도록 kustomization.yaml을 작성한다.
# k8s/overlays/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- path: patch-replicas.yaml
namePrefix: dev-
commonLabels:
env: dev
resources에서 ../../base를 참조해 base 매니페스트를 가져오고, patches에서 변경사항을 덮어씌운다. namePrefix를 쓰면 모든 리소스 이름 앞에 dev-가 붙어서 환경 간 구분이 쉬워진다.
최종 결과물은 kubectl kustomize로 미리 확인할 수 있다.
kubectl kustomize k8s/overlays/dev
이 명령은 실제로 적용하지 않고, base에 overlay를 합친 최종 매니페스트만 출력해준다. 배포 전에 예상 결과를 검증하는 용도로 유용하다.
prod overlay
프로덕션 환경은 좀 더 복잡하다. 레플리카를 3개로 늘리고, 리소스 제한도 추가한다.
# k8s/overlays/prod/patch-replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-demo
spec:
replicas: 3
# k8s/overlays/prod/patch-resources.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-demo
spec:
template:
spec:
containers:
- name: nginx
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
# k8s/overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- path: patch-replicas.yaml
- path: patch-resources.yaml
namePrefix: prod-
commonLabels:
env: prod
base는 하나인데 overlay만 다르다. 공통 부분을 수정하면 모든 환경에 반영되고, 환경별 차이점만 overlay에서 관리하면 된다.
ArgoCD에 Kustomize 앱 등록
ArgoCD는 Kustomize를 자동으로 감지한다. kustomization.yaml이 있는 경로를 path로 지정하면 된다.
dev 환경 Application은 이렇게 만든다.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: nginx-demo-dev
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/<your-username>/argocd-example.git
targetRevision: HEAD
path: k8s/overlays/dev
destination:
server: https://kubernetes.default.svc
namespace: dev
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
prod 환경도 path와 namespace만 바꾸면 된다.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: nginx-demo-prod
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/<your-username>/argocd-example.git
targetRevision: HEAD
path: k8s/overlays/prod
destination:
server: https://kubernetes.default.svc
namespace: prod
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
같은 Git 리포를 소스로 쓰지만, path가 다르기 때문에 각 환경에 맞는 매니페스트가 적용된다. ArgoCD가 해당 경로의 kustomization.yaml을 감지하면 자동으로 kustomize build를 실행한 결과물을 클러스터에 적용한다.
Helm — values로 설정 분리
Kustomize가 패치 기반이라면, Helm은 템플릿 기반이다. 매니페스트를 Go 템플릿으로 작성하고, values 파일로 변수를 주입하는 방식이다.
Helm 차트 구조
기본적인 Helm 차트 구조는 이렇다.
helm-chart/
├── Chart.yaml
├── values.yaml
├── values-dev.yaml
├── values-prod.yaml
└── templates/
├── deployment.yaml
└── service.yaml
Chart.yaml은 차트의 메타데이터를 담고, values.yaml은 기본값, 환경별 values 파일은 해당 환경에서 덮어쓸 값을 정의한다.
Chart.yaml
차트의 이름과 버전을 정의한다.
# helm-chart/Chart.yaml
apiVersion: v2
name: nginx-demo
description: A simple Nginx demo chart
type: application
version: 0.1.0
appVersion: "1.27"
템플릿 작성
Helm 템플릿은 Go 템플릿 문법을 쓴다. {{ .Values.xxx }}로 values 파일의 값을 참조한다.
# helm-chart/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-nginx
labels:
app: {{ .Release.Name }}-nginx
spec:
replicas: {{ .Values.replicas }}
selector:
matchLabels:
app: {{ .Release.Name }}-nginx
template:
metadata:
labels:
app: {{ .Release.Name }}-nginx
spec:
containers:
- name: nginx
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
- containerPort: 80
resources:
requests:
cpu: {{ .Values.resources.requests.cpu }}
memory: {{ .Values.resources.requests.memory }}
limits:
cpu: {{ .Values.resources.limits.cpu }}
memory: {{ .Values.resources.limits.memory }}
# helm-chart/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-nginx
spec:
selector:
app: {{ .Release.Name }}-nginx
ports:
- port: 80
targetPort: 80
values 파일 분리
기본값은 values.yaml에 둔다.
# helm-chart/values.yaml
replicas: 1
image:
repository: nginx
tag: "1.27"
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 100m
memory: 128Mi
dev 환경은 기본값을 그대로 쓰되, 필요한 것만 오버라이드한다.
# helm-chart/values-dev.yaml
replicas: 1
image:
tag: "1.27"
prod 환경은 레플리카를 늘리고 리소스를 넉넉히 잡는다.
# helm-chart/values-prod.yaml
replicas: 3
image:
tag: "1.27"
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
Helm은 values 파일을 계층적으로 머지하기 때문에, 환경별 파일에는 바꿀 값만 적으면 나머지는 values.yaml의 기본값이 쓰인다.
ArgoCD에 Helm 앱 등록
ArgoCD에서 Helm 차트를 등록할 때는 source에 Helm 관련 설정을 추가한다.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: nginx-demo-helm-dev
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/<your-username>/argocd-example.git
targetRevision: HEAD
path: helm-chart
helm:
valueFiles:
- values-dev.yaml
destination:
server: https://kubernetes.default.svc
namespace: dev
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
source.helm.valueFiles에 환경별 values 파일을 지정하는 게 핵심이다. prod Application은 values-prod.yaml로 바꾸면 된다.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: nginx-demo-helm-prod
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/<your-username>/argocd-example.git
targetRevision: HEAD
path: helm-chart
helm:
valueFiles:
- values-prod.yaml
destination:
server: https://kubernetes.default.svc
namespace: prod
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
CLI로도 가능하다. --helm-set으로 개별 값을 오버라이드하거나, --values로 파일을 지정할 수 있다.
argocd app create nginx-demo-helm-dev \
--repo https://github.com/<your-username>/argocd-example.git \
--path helm-chart \
--values values-dev.yaml \
--dest-server https://kubernetes.default.svc \
--dest-namespace dev
Kustomize vs Helm — 언제 무엇을
둘 다 써본 경험에서 정리하자면 이런 기준이 있다.
Kustomize가 적합한 경우:
- 매니페스트가 이미 plain YAML로 작성되어 있을 때
- 환경 간 차이가 레플리카 수, 라벨, 리소스 제한 정도로 작을 때
- 외부 의존성 없이 kubectl만으로 처리하고 싶을 때
Helm이 적합한 경우:
- 복잡한 조건 분기가 필요할 때 (if/else, range)
- 차트를 패키징해서 재사용하거나 배포할 때
- values 파일 하나로 수십 개 설정을 관리해야 할 때
실무에서는 두 가지를 섞어 쓰는 경우도 많다. 외부 Helm 차트를 ArgoCD로 배포하면서, 내부 서비스는 Kustomize로 관리하는 식이다. ArgoCD가 둘 다 지원하니 프로젝트에 맞는 도구를 고르면 된다.
환경별 배포 전략
마지막으로, 환경별 Application을 효율적으로 관리하는 패턴 하나를 소개한다. ApplicationSet이라는 ArgoCD의 기능을 쓰면, 환경마다 Application YAML을 따로 만들지 않고 하나의 템플릿으로 여러 환경을 생성할 수 있다.
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: nginx-demo-set
namespace: argocd
spec:
generators:
- list:
elements:
- env: dev
namespace: dev
replicas: "1"
- env: prod
namespace: prod
replicas: "3"
template:
metadata:
name: "nginx-demo-{{env}}"
spec:
project: default
source:
repoURL: https://github.com/<your-username>/argocd-example.git
targetRevision: HEAD
path: "k8s/overlays/{{env}}"
destination:
server: https://kubernetes.default.svc
namespace: "{{namespace}}"
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
generators에 환경 목록을 정의하고, template에서 {{env}}로 참조한다. 이 ApplicationSet 하나를 적용하면 nginx-demo-dev와 nginx-demo-prod 두 개의 Application이 자동으로 만들어진다. 환경이 추가될 때 elements에 항목 하나만 더하면 되니 확장성이 좋다.
여기까지 ArgoCD 시리즈 4편을 마무리한다. 1편에서 GitOps의 개념을 잡고, 2편에서 설치하고, 3편에서 Application을 등록하고, 4편에서 환경별 배포 전략까지 다뤘다. 이 정도면 ArgoCD를 실무에 도입할 때 필요한 기본기는 갖춘 셈이다.
실제로 운영하다 보면 RBAC 설정, 시크릿 관리(Sealed Secrets나 External Secrets), 알림 연동(Slack/Teams), App of Apps 패턴 같은 주제가 자연스럽게 따라온다. 그 부분은 나중에 별도로 다뤄보겠다.
Loading comments...