Skip to content
ioob.dev
Go back

ArgoCD 4편 — Kustomize와 Helm

· 7분 읽기
ArgoCD 시리즈 (4/8)
  1. ArgoCD 1편 — ArgoCD란
  2. ArgoCD 2편 — 설치와 초기 설정
  3. ArgoCD 3편 — Application 등록
  4. ArgoCD 4편 — Kustomize와 Helm
  5. ArgoCD 5편 — 싱크 전략: Auto Sync, Self Heal, 그리고 순서 제어
  6. ArgoCD 6편 — 멀티 클러스터와 ApplicationSet
  7. ArgoCD 7편 — RBAC과 SSO: 팀 단위 접근 제어
  8. ArgoCD 8편 — 실전 패턴: App of Apps, CI 연동, 트러블슈팅
Table of contents

Table of contents

환경이 늘어나면 생기는 문제

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 앱을 감지하고 렌더링한 뒤 클러스터에 적용하기까지의 흐름은 이렇다.

sequenceDiagram
    participant Git as Git Repository
    participant Repo as argocd-repo-server
    participant Ctrl as application-controller
    participant K8s as Kubernetes API

    Ctrl->>Repo: overlays/dev 렌더링 요청
    Repo->>Git: clone & checkout
    Repo->>Repo: kustomize build (base + patches)
    Repo-->>Ctrl: 최종 매니페스트 반환
    Ctrl->>K8s: diff (desired vs live)
    Ctrl->>K8s: apply (patch/create/delete)
    K8s-->>Ctrl: 상태 업데이트

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 환경도 pathnamespace만 바꾸면 된다.

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 파일이 여러 개 있을 때 어떻게 병합되어 최종 값이 만들어지는지 그림으로 보자.

flowchart LR
    V1["values.yaml\n(전역 기본값)\nreplicas: 1\nimage.tag: 1.27\nresources: {..}"] --> MERGE{"values 병합\n(뒤에 오는 것이 우선)"}
    V2["values-prod.yaml\n(prod 오버라이드)\nreplicas: 3\nresources: {..}"] --> MERGE
    MERGE --> FINAL["최종 Values\nreplicas: 3 (오버라이드)\nimage.tag: 1.27 (기본)\nresources: {..} (오버라이드)"]
    FINAL --> TMPL["templates/\nhelm template 실행"]
    TMPL --> YAML["렌더링된 최종 매니페스트"]

기본값은 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가 적합한 경우:

Helm이 적합한 경우:

실무에서는 두 가지를 섞어 쓰는 경우도 많다. 외부 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-devnginx-demo-prod 두 개의 Application이 자동으로 만들어진다. 환경이 추가될 때 elements에 항목 하나만 더하면 되니 확장성이 좋다.


다음 편에서는 ArgoCD의 싱크 전략을 다룬다. Auto Sync, Self Heal, Sync Wave 같은 설정으로 배포의 타이밍과 순서를 제어하는 방법을 살펴본다.

5편: 싱크 전략 — Auto Sync, Self Heal, 그리고 순서 제어


Related Posts

Share this post on:

Comments

Loading comments...


Previous Post
ArgoCD 3편 — Application 등록
Next Post
ArgoCD 5편 — 싱크 전략: Auto Sync, Self Heal, 그리고 순서 제어