DevOpsDevOps 학습 로드맵 · 5/13

yaml 복붙 관리의 드리프트 문제를 Kustomize overlay로 구조화하기

로컬·개발·에어갭 3환경 매니페스트를 복사해서 관리하다 겪은 드리프트 문제부터, Kustomize base/overlay 구조로 옮기는 절차, ArgoCD Application 연동, 드리프트 재발 방지 검증까지.

2026-07-249 min read
#Kustomize#Kubernetes#GitOps#ArgoCD#환경 분리

목표

이 로드맵은 환경을 로컬·개발·에어갭 3개로 나눠 운영하는데, 지금까지는 환경별로 매니페스트 디렉토리를 통째로 복사해서 관리하고 있었다 - k8s/local/, k8s/dev/, k8s/airgap/ 세 폴더에 거의 동일한 yaml이 중복돼 있는 구조다. 이 시리즈는 그 복붙 관리가 실제로 어떤 문제(드리프트)를 만드는지 겪어보고, Kustomize의 base/overlay 구조로 옮겨 그 문제를 구조적으로 없애는 절차를 다룬다.

개념: 설정 드리프트(Config Drift)란 무엇인가

드리프트는 "같아야 할 설정이 시간이 지나며 조금씩 달라지는 것"을 말한다. 환경별로 yaml을 복사해서 관리하면, 공통으로 고쳐야 할 부분(예: 리소스 요청량, 헬스체크 경로)을 수정할 때마다 3개 파일을 전부 손으로 고쳐야 한다. 사람이 하는 일이라 언젠가 한 곳을 빠뜨리게 되고, 그 순간부터 "왜 개발 환경에서는 되는데 에어갭에서는 안 되지?"라는 질문이 생긴다 - 코드가 문제가 아니라 환경 설정이 몰래 갈라져 있어서 생기는 장애다.

Kustomize는 이 문제를 "공통 부분(base)과 환경별 차이(overlay)를 분리"하는 방식으로 푼다. base에는 모든 환경이 공유하는 원본 매니페스트만 두고, 각 환경은 base를 그대로 참조하면서 자신에게 필요한 차이만 patch로 얹는다. 복붙이 아니라 참조이므로, base를 고치면 모든 환경에 자동으로 반영된다 - 드리프트가 애초에 발생할 수 없는 구조가 된다.

방식공통 설정 변경 시환경별 차이
yaml 복붙 (기존)N개 파일을 전부 손으로 수정 - 누락 위험파일 전체가 환경마다 별도로 존재
Kustomize overlaybase 1곳만 수정 - 모든 환경에 자동 반영patch로 차이만 명시적으로 선언

실제 구축 절차

1. 기존 복붙 구조 확인 - 어디가 실제로 갈라져 있는지 먼저 찾는다

Kustomize로 옮기기 전에, 지금 세 환경의 yaml이 실제로 어디서 다른지부터 diff로 확인한다. 이 작업 자체가 "우리가 인지하지 못한 드리프트가 이미 있었는지"를 드러낸다.

diff -r k8s/local/ k8s/dev/
diff -r k8s/dev/ k8s/airgap/

여기서 나오는 차이는 크게 두 종류로 갈린다 - 의도한 차이(에어갭 환경만 이미지 레지스트리가 harbor.local인 것 등)와 의도하지 않은 차이(누군가 dev에서만 리소스 limit을 고치고 다른 환경엔 반영 안 한 것). 후자를 발견하는 순간이 바로 "복붙 관리의 대가"를 실감하는 지점이다.

2. base 추출 - 공통 매니페스트만 남긴다

세 환경에서 공통인 부분만 뽑아 base/로 옮긴다. 여기엔 환경별로 달라지는 값(이미지 레지스트리, replica 수, context-path 등)을 넣지 않는다 - 넣는 순간 base가 다시 특정 환경에 종속된다.

k8s/
├── base/
│   ├── kustomization.yaml
│   ├── deployment.yaml
│   ├── service.yaml
│   └── configmap.yaml
└── overlays/
    ├── local/
    ├── dev/
    └── airgap/
# k8s/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
  - service.yaml
  - configmap.yaml

3. overlay 작성 - 환경별 차이만 patch로 선언

각 overlay는 base를 참조(resources: [../../base])하면서, 자신에게 필요한 차이만 patch로 얹는다.

# k8s/overlays/airgap/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - ../../base
images:
  - name: api-server
    newName: harbor.local/proxy-cache/api-server   # 에어갭만 내부 미러 레지스트리 사용
patches:
  - target:
      kind: Deployment
      name: api-server
    patch: |-
      - op: replace
        path: /spec/replicas
        value: 5
  - path: configmap-patch.yaml
# k8s/overlays/airgap/configmap-patch.yaml - context-path 등 에어갭 전용 값만 override
apiVersion: v1
kind: ConfigMap
metadata:
  name: api-server-config
data:
  EXTERNAL_API_HOST: "localhost:8081"   # 에어갭 환경은 내부 API를 path prefix로 우회
# 렌더링 결과 미리보기 - 실제 적용 전에 최종 yaml이 의도대로 나오는지 확인
kustomize build k8s/overlays/local/
kustomize build k8s/overlays/dev/
kustomize build k8s/overlays/airgap/

kustomize build는 base와 patch를 합쳐 최종 yaml을 출력만 할 뿐 클러스터에 아무것도 적용하지 않는다 - 세 환경의 렌더링 결과를 나란히 비교해서 의도한 차이만 남아있는지, 실수로 base 내용이 깨지지 않았는지 눈으로 검증할 수 있다.

4. ArgoCD Application에 Kustomize 연동

시리즈 2에서 만든 ArgoCD GitOps 배포에 Kustomize를 편입한다. Applicationsource.path가 overlay 디렉토리를 가리키게만 바꾸면, ArgoCD가 자동으로 kustomize build를 실행해서 그 결과를 배포한다 - 별도 빌드 스크립트가 필요 없다.

# argocd-application-airgap.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: api-server-airgap
spec:
  source:
    repoURL: https://git.internal/infra-manifests.git
    path: k8s/overlays/airgap
    targetRevision: main
  destination:
    server: https://kubernetes.default.svc
    namespace: default
  syncPolicy:
    automated:
      prune: true
      selfHeal: true   # 클러스터에서 누가 kubectl로 직접 고쳐도 Git 상태로 되돌림

selfHeal: true가 이 구조의 마지막 조각이다 - Kustomize로 드리프트를 구조적으로 없앴어도, 누군가 클러스터에 kubectl edit으로 직접 손을 대면 Git과 실제 상태가 다시 갈라질 수 있다. ArgoCD의 self-heal은 그 수동 변경을 감지해 Git에 선언된 상태로 자동으로 되돌린다 - "설정 소스는 Git 하나뿐"이라는 원칙을 배포 이후에도 강제하는 셈이다.

5. 드리프트 재발 방지 검증

# 의도적으로 클러스터에 직접 변경을 가한 뒤
kubectl scale deployment api-server -n airgap --replicas=99

# ArgoCD가 감지해 Git 선언 값으로 되돌리는지 확인
kubectl argo rollouts get rollout api-server -n airgap --watch   # 또는
argocd app get api-server-airgap --refresh

검증 방법

  • 3환경의 kustomize build 렌더링 결과를 diff로 비교해, 의도한 차이만 남아있는지 확인
  • base를 수정했을 때 3환경 모두에 자동으로 반영되는지 확인 (환경별로 따로 고치지 않아도 됨)
  • 클러스터에 수동으로 가한 변경이 ArgoCD self-heal로 Git 선언 상태로 자동 복구되는지 확인
  • 1번 절차에서 발견한 "의도하지 않은 차이"가 overlay 구조로 옮긴 뒤 문서화된 patch로 명시됐는지 확인

체크리스트

  • 기존 3환경 yaml을 diff로 비교해 의도한 차이/의도하지 않은 차이 구분
  • 공통 매니페스트를 base로 추출
  • 환경별 차이를 overlay patch로 분리 (이미지 레지스트리, replica 수, 설정값 등)
  • kustomize build로 3환경 렌더링 결과 사전 검증
  • ArgoCD Application의 source.path를 overlay로 전환
  • selfHeal 활성화 후 수동 변경이 자동 복구되는지 검증