一方向だけでは真実の源にならない
한국어 원문으로 표시합니다.
한 줄 요약
git 이 유일한 진실의 원천이라는 주장은 양방향일 때만 성립합니다. automated 만 켜면 한 방향이라 git 과 클러스터가 조용히 갈라집니다.
왜 양방향이어야 하나
GitOps 의 핵심 주장은 하나입니다. git 이 유일한 진실의 원천이고, 클러스터는 그 사본이다.
그 주장이 성립하려면 양방향이어야 합니다.
| 설정 | 하는 일 |
|---|---|
automated |
git 을 고치면 클러스터가 따라온다 |
selfHeal |
클러스터를 고치면 되돌아온다 |
prune |
git 에서 지운 것을 클러스터에서도 지운다 |
automated 만 켜면 한 방향입니다. kubectl 로 직접 고친 것이 그대로 남고, git 과 클러스터가 조용히 갈라집니다. 대개 급한 장애 때문입니다 — 새벽에 kubectl scale 로 늘리고 아침에 git 반영을 잊습니다. 그러면 다음 배포 때 그 차이가 한꺼번에 되돌려집니다.
sync 와 health 는 다른 것을 말합니다
sync— git 과 클러스터가 같은가health— 배포된 것이 잘 도는가
OutOfSync 인데 Healthy 일 수 있습니다. 누가 손으로 고쳤지만 그 상태로 잘 도는 경우입니다. 반대로 Synced 인데 Degraded 일 수도 있습니다.
selfHeal 에는 대가가 있습니다
긴급 조치를 할 수 없습니다. 그리고 손으로 고친 것이 흔적도 없이 사라지므로, 고친 사람은 자기 변경이 왜 사라졌는지 모릅니다. 그래서 "긴급 조치도 git 을 거친다" 를 팀이 미리 합의해 두어야 합니다.
또 HPA 와 싸웁니다. git 에 replicas 를 적어 두면 HPA 가 늘릴 때마다 Argo 가 되돌립니다.
OutOfSync 가 안 사라질 때
가장 자주 겪는 운영 문제입니다. git 과 클러스터가 같아 보이는데 계속 OutOfSync
입니다. 원인은 대개 누군가가 필드를 채워 넣기 때문 입니다.
| 채워 넣는 주체 | 예 | 대응 |
|---|---|---|
| 어드미션 웹훅 | 사이드카 주입, 기본 라벨 | ignoreDifferences 로 그 경로를 뺀다 |
| 컨트롤러 | HPA 의 replicas, clusterIP |
git 에서 그 필드를 지운다 |
| API 서버 기본값 | imagePullPolicy, terminationGracePeriodSeconds |
git 에 명시해 맞춘다 |
# Application 스펙
spec:
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas # HPA 가 관리한다
- group: ""
kind: Service
jsonPointers:
- /spec/clusterIP # API 서버가 정한다
무엇이 다른지는 화면 대신 명령으로 봅니다.
argocd app diff <앱> --local ./manifests # 로컬과 클러스터
argocd app get <앱> -o json | jq '.status.resources[] | select(.status!="Synced")'
동기화 순서와 웨이브
리소스를 한꺼번에 적용하면 순서 문제가 납니다. CRD 가 없는데 CR 을 적용하거나, DB 가 뜨기 전에 애플리케이션이 뜹니다. Argo 는 sync wave 로 순서를 정합니다.
metadata:
annotations:
argocd.argoproj.io/sync-wave: "-1" # 작을수록 먼저
Argo 는 기본 순서(네임스페이스 → CRD → 나머지)를 먼저 적용하고, 그 안에서 웨이브를
봅니다. 웨이브 사이에는 앞 웨이브의 리소스가 Healthy 가 될 때까지 기다립니다.
그래서 Health 판정이 없는 커스텀 리소스를 앞 웨이브에 두면 영원히 기다릴 수
있습니다 — 그때는 Hook 이나 헬스 체크 루아 스크립트를 쓰거나 웨이브를 합칩니다.
앱 오브 앱스와 프로젝트
애플리케이션이 수십 개가 되면 Application 자체를 git 으로 관리합니다(app of apps). 그러면 새 서비스를 추가하는 일이 매니페스트 한 장을 커밋하는 일이 됩니다.
여기에 AppProject 로 울타리를 칩니다. 어느 저장소에서, 어느 클러스터의, 어느
네임스페이스에, 어떤 종류의 리소스를 만들 수 있는지 제한합니다. 이것이 없으면
누군가의 Application 하나가 kube-system 을 건드릴 수 있습니다.
spec:
sourceRepos: ["https://git.internal/labhub/*"]
destinations:
- namespace: "labhub-*"
server: https://kubernetes.default.svc
clusterResourceWhitelist: [] # 클러스터 범위 리소스는 아예 금지
실무에서 진짜 중요한 것
selfHeal 을 켜기 전에 긴급 조치 절차를 먼저 합의합니다. 손으로 고친 것이 흔적도 없이 사라지므로, 고친 사람은 자기 변경이 왜 없어졌는지 모릅니다. "긴급 조치도 git 을 거친다" 가 합의되지 않은 팀에서 켜면 새벽 장애가 두 번 납니다.
HPA 를 쓰는 워크로드는 git 에서 replicas 를 빼야 합니다. 적어 두면 HPA 가 늘릴 때마다 Argo 가 되돌려, 부하가 오르는 동안 두 컨트롤러가 서로 싸웁니다.
Synced 초록불을 배포 완료로 읽지 않습니다. sync 는 git 과 같다는 뜻이고 health 는 잘 돈다는 뜻이라, Synced 이면서 Degraded 인 상태가 실제로 존재합니다. 배포 판정은 두 값을 함께 봐야 합니다.
다음 실습에서 진짜 Argo CD 위에 git 서버까지 세우고, 이것들을 직접 일으켜 봅니다.