Helm lookup 함수, ArgoCD랑 같이 쓰면 함정 있다
Helm lookup 함수, ArgoCD랑 같이 쓰면 함정 있다
오늘 동료가 PR 리뷰 부탁한다고 해서 봤는데, Helm chart에서 lookup 함수를 쓰는 부분이 있었다. 클러스터에 이미 떠 있는 ConfigMap을 읽어서 그 값을 기반으로 다른 리소스를 만드는 패턴. 코드는 깔끔했고 로컬 helm install로도 잘 돌았는데, 내가 한 마디 했다. "이거 ArgoCD에서 안 될걸요."
근데 동료는 "어 저번에 다른 차트에서도 비슷하게 썼는데 됐는데?"라고 했고, 결국 같이 한 번 더 들여다보기로 했다. 그 김에 정리.
lookup이 뭐였더라
lookup은 Helm 3에서 추가된 템플릿 함수다. 차트 렌더링 시점에 K8s API 서버를 쳐서 기존 리소스를 읽어올 수 있다. 예를 들면 이런 식.
{{- $existing := lookup "v1" "Secret" .Release.Namespace "db-credentials" -}}
{{- if $existing }}
password: {{ $existing.data.password }}
{{- else }}
password: {{ randAlphaNum 32 | b64enc }}
{{- end }}
이미 시크릿이 있으면 재사용하고, 없으면 새로 생성. helm upgrade할 때마다 비밀번호가 바뀌는 사고를 막는 흔한 패턴이다.
함정 1: helm template에선 항상 빈 값
근데 공식 문서에도 나와 있는데, lookup은 helm template이나 helm install --dry-run에선 클러스터 연결 자체를 안 한다. 그래서 항상 빈 dict를 리턴한다.
뭐가 문제냐면, CI에서 helm template으로 차트를 검증하는 파이프라인이 있다면 — 우리 팀도 그렇다 — 그 단계에선 lookup 결과가 항상 비어 있다는 거다. 위 예제 같은 fallback 패턴이면 그나마 괜찮은데, lookup 결과가 nil이면 panic 나는 코드는 CI에서 다 깨진다.
3.13부터 --dry-run=server 옵션이 생겼다. 이걸 쓰면 클러스터에 실제로 쿼리를 날린다. 다만 CI 러너가 클러스터 접근 권한이 있어야 해서, 실무에서 채택하려면 또 한 번 고민해야 한다.
함정 2: ArgoCD가 진짜 문제
이게 오늘의 본론. ArgoCD의 manifest 생성은 내부적으로 helm template을 쓴다. 즉 ArgoCD가 차트를 렌더링할 때 lookup은 항상 빈 값을 리턴한다. 위에서 말한 그 함정이 그대로 적용된다.
argo-cd 이슈 #5202에서 몇 년째 트래킹되고 있는 문제고, 결론은 "기본적으로 지원 안 함, 별도 플래그 필요"다. ArgoCD 2.7부터는 차트별로 helm.passCredentials나 --api-versions, 그리고 별도의 helm hook 패턴 등을 쓸 수 있는데, lookup만큼은 ArgoCD CMP(Config Management Plugin)을 만들어서 우회하는 게 현실적이다.
동료의 차트가 "저번엔 됐다"고 한 이유는 단순했다. 그 차트의 lookup 호출이 nil이어도 동작하는 fallback 경로가 있었던 거다. 즉 lookup이 작동한 게 아니라, lookup 실패 시 분기가 잘 짜여 있었던 거였다.
그래서 어떻게 쓰냐
우리 팀 가이드는 단순하다.
- lookup 결과는 항상 nil일 수 있다고 가정한다. fallback 없는 lookup은 PR 리뷰에서 막는다.
- GitOps 환경에서 비밀번호 보존이 목적이라면 ExternalSecrets나 Sealed Secrets로 가라. lookup 의존하지 마라.
- lookup이 꼭 필요하면, 그 차트는 ArgoCD가 아닌 helm CLI로 직접 배포한다. 보통 인프라 차트 한두 개에만 해당된다.
생각해보면 lookup 자체가 "선언적 GitOps"의 철학과 살짝 안 맞는다. 클러스터 상태에 따라 렌더링 결과가 바뀐다는 건, 같은 Git revision이 다른 매니페스트를 만들어낼 수 있다는 뜻이니까. 디버깅도 어렵고, 재현도 어렵다.
마무리
저번에 이거 때문에 새벽에 한 번 깬 적이 있어서 동료한테 잔소리한 것 같다. 모르고 쓰면 첫 배포는 잘 되는데, 두 번째 sync에서 갑자기 password 필드가 비어버리는 식으로 터진다. 그 시점엔 이미 앱이 DB 못 붙어서 502 깔리는 중이고.
혹시 lookup 함수 잘 쓰고 계신 분들 패턴 있으면 댓글로 공유 부탁드립니다.