요약
Kubernetes를 의심하던 엔지니어가 개념을 뜯어보니 핵심 설계는 훌륭했지만 설정 방식은 엉망이었습니다.
오랫동안 시스템 관리와 인프라 개발을 해 왔지만 Kubernetes는 써 본 적 없는 작성자가 2024년 7월에 쓴 글입니다. '괜한 복잡성'이라는 막연한 인상을 사실로 바꾸려고, 클러스터를 운영해 보기 전에 개념부터 공부했습니다.
왜 중요한가
- 회의론자도 핵심 설계는 인정했습니다. 그래서 Kubernetes의 복잡함과 설계의 품질을 따로 봐야 한다는 근거가 됩니다.
- 비판의 과녁이 Kubernetes 자체보다 그 위에 얹힌 Helm 같은 템플릿 도구라는 점을 짚었습니다.
- 기존 튜토리얼이 빠뜨린 '이 구성 요소가 왜 필요한가'와 그 대가를 처음 배우는 사람의 눈으로 정리했습니다.
핵심 내용
- Helm 차트가 텍스트 템플릿으로 YAML을 찍어 내면서 들여쓰기를 손으로 맞추는 방식은 잘못이라고 비판했습니다.
- 설정값의 의미가 다른 문자열 필드에 따라 바뀌는 KEDA 설정처럼, 구조 대신 문자열에 기대는 관행도 문제로 꼽았습니다.
- 원하는 상태에 이를 때까지 관찰과 조치를 반복하는 컨트롤러는 자원을 관리하는 올바른 방식이라고 평가했습니다.
- Service는 노드마다 도는 kube-proxy가 방화벽 규칙(netfilter)을 고쳐 트래픽을 파드로 보내는 구조라 "환상적"이라고 했습니다.
- 큐 길이 같은 지표로 복제본 수를 조절하고 싶은데 HPA(수평 자동 확장기)는 CPU 위주라 아쉽다고 했습니다.
HN 반응
- 여러 이용자가 Helm의 문자열 치환에 질렸다며 Kustomize, jsonnet, Pkl, Cue 같은 대안을 권했고, Helm도 단순하게만 쓰면 괜찮다는 반론도 나왔습니다.
- 지표 기반 확장은 Kubernetes 1.23부터 사용자 정의 지표로 HPA에서 할 수 있다는 보충이 있었지만, 설정이 번거롭다는 데에는 다들 동의했습니다.
저는 helm의 텍스트 보간(text interpolation)이 얼마나 싫은지 길게 글로 쓴 적이 있습니다.
그 문제를 간단한 "설정 언어"로 풀어 보려는 Apple의 Pkl과 CNCF의 KCL로 몇 가지 실험을 해 봤습니다.
둘 다 널리 쓰이는 것 같지는 않습니다. 다만 Apple이 내부에서 Pkl을 쓴다는 이야기는 예전에 들은 적이 있습니다.
LLM이 나온 덕분에, 겹겹이 쌓인 미친 구조 속에서 YAML 렌더링 간접 계층 3단을 따라가며 진짜 버그를 찾는 일이 한결 쉬워졌다는 점은 고맙게 생각합니다. 놀랄 만큼 잘하거든요. 원칙주의자라면 이런 시스템을 보고 경악하겠지만, 현실적으로는 예전에 유지보수 면에서 꽤 큰 문제였던 미친 템플릿 구조들이 훨씬 덜 골칫거리가 됐습니다.
저는 helm의 텍스트 보간도 그렇게까지 싫지는 않습니다. 끔찍한 건 맞지만, 외부 시스템이 helm에 넘길 텍스트를 렌더링하고 그걸 나중에 Kustomize가 또 처리하는 상황이 되기 전까지는 딱히 문제가 되지 않습니다. 텍스트 하나가 클러스터에 닿기 전에 말 그대로 서로 다른 YAML 렌더링 파이프라인을 3~4개나 거칠 수 있습니다. 정신 나간 일이죠.
Hashicorp도 말씀하신 설정 언어와 비슷한 발상으로 HCL을 만들었습니다. 결과가 어땠는지는 다들 아시지만, 접근 방식 자체에 내재한 문제라기보다는 설계가 미심쩍었던 쪽이 아닐까 싶습니다.
AI를 전면적으로 도입했다 해도 좋은 상황은 아닙니다. helm 차트를 건드릴 때마다 에이전트가 들여쓰기 버그를 고치느라 토큰 10000개를 태우길 바라십니까?
사람이 힘들어하는 일은 AI도 힘들어합니다.
그리고 맞아요, HCL은 끔찍한 설계였습니다. Cue, jsonnet, dhall, pkl 등 다른 건 뭐든 더 나았을 겁니다. (HCL이 만들어질 때 이 중 뭐가 이미 있었는지는 잘 모르겠네요.)
그래서 저는 업스트림 차트를 쓸 때가 아니면 가능한 한 jsonnet(kubecfg를 통해)만 씁니다.
문자열 보간은 생태계의 역병입니다. YAML이 원래는 디버그용 보조 도구에 불과했다는 점을 생각하면 더더욱 그렇고요...
K8s가 문자열 보간으로 돌아간다는 걸 알게 된 대목이 특히 재미있었어요. 참고로 그 기분은 평생 안 사라집니다. nindent를 만질 때마다 정신이 한 조각씩 깎여 나가거든요.
약간 곁가지 이야기이긴 한데, 소프트웨어 인프라를 관리하는 일 전반이 이상한 문화입니다. 더 나은 방법이 많이 있을 텐데도, 소프트웨어 업계의 인프라 쪽은 어느 정도 평행 세계에 사는 듯해서 아쉽습니다. 업계의 다른 영역에서 얻은 교훈 상당수가 거기서는 (적어도 아직은) 널리 퍼져 있지 않습니다. 은행조차 인프라 관리의 주류 관행에 비하면 똑똑하고 혁신적으로 보일 정도입니다.
버전이 붙은 소프트웨어 패키지와 설정의 계층 구조를 셸 스크립트와 yaml로 기술하는 것은 오류가 나기 쉽습니다. git의 텍스트 파일을 데이터베이스처럼 쓰는 것도 마찬가지고요. CI 시스템을 제대로 된 서비스의 허술한 대용품으로 쓰는 것도 그렇습니다(간단한 일 하나를 하려면 먼저 세상을 처음부터 만들어야 하죠). 이런 식의 사례가 끝도 없습니다. 주류 업계는 타입, 프로그래밍 언어, 관계형 데이터베이스, 그래프 등 온갖 것을 알고 있지만, 이런 것들이 인프라 세계에는 아직 충분히 스며들지 못했습니다. 타입의 가치는 소프트웨어와 데이터베이스를 개발하면서 땀과 피로 값을 치르고 얻은 깨달음입니다. 기본적인 SQL 데이터베이스를 쓰는 기본적인 CRUD 애플리케이션만 해도 이런 프랑켄슈타인 괴물들을 안전성, 속도, 확장성 모든 면에서 압도합니다.
결국 차이는 인프라와 앱 소프트웨어의 API 표면에서 나온다고 봅니다. 소프트웨어를 개발할 때는 타입 같은 걸 써서 모든 것을 내가 생각하는 방식대로 담아내고 추상화할 수 있습니다. 하지만 인프라는 남들이 만든 소프트웨어를 잔뜩 관리하는 일이고, 그 소프트웨어마다 일하는 방식에 대한 나름의 구상이 있습니다. 그러니 결국 최소 공통분모인 평범한 ASCII로 되돌아가는 경우가 많은 거죠.
(버전이 붙은 소프트웨어 패키지와 설정의 계층 구조를 셸 스크립트와 yaml로 기술하는 것은 오류가 나기 쉽습니다.)
똑같은 일을 하는 설정/마크업 언어가 너무 많습니다.
이 문제는 1960년대에 이미 해결됐어요. S-표현식(S-Expression)입니다. 모든 것에 그걸 썼어야 했습니다. 설정, 코드, 웹사이트, 뭐든지요. 무한히 확장할 수 있고 파싱도 쉽습니다.
저도 lisp 덕후지만, 왜 여기서 sexp가 답이라고 하시는 겁니까? 많은 설정 포맷의 요점은 설정이 본격적인 프로그래밍 언어까지 되는 것을 원치 않는다는 데 있다고 생각합니다.
sexp 자체는 프로그래밍 언어가 아닙니다.
(많은 설정 포맷의 요점은 설정이 본격적인 프로그래밍 언어까지 되는 것을 원치 않는다는 데 있다고 생각합니다.)
누구나 처음 목표는 그렇죠. 하지만 결국엔 반드시 새기 마련입니다.
모든 걸 이상한 짬뽕으로 몰아가는 가장 큰 갈림길은, 아무도 서드파티 물건의 설정을 위해 임의의 프로그래밍 언어로 쓴 설정을 이해하고 싶어 하지 않는다는 점이라고 봅니다. 설정은 읽으면 무슨 일이 일어나는지 바로 알 수 있길 바라니까요. 하지만 자기 물건에 대해서는 더 많은 힘과 표현력을 원할 이유를 찾아냅니다...
(누구나 처음 목표는 그렇죠. 하지만 결국엔 반드시 새기 마련입니다.)
정말 맞는 말입니다. 그리고 새기 전까지는 온갖 이상한 템플릿이나 다른 형태의 추상화로 감싸야 하죠.
제가 예전에 Chef의 설정 언어를 정말 좋아했던 이유 중 하나가 이겁니다. Ruby DSL이어서, DSL이 기본적으로 못 하는 일은 그 자리에서 그냥 Ruby를 쓰면 됐거든요.
물론 남용하기 쉽지만, 조심해서 쓰면 강력한 만큼 우아했습니다.
(설정이 본격적인 프로그래밍 언어까지 되는 것을 원치 않는다.)
원치 않는다고들 하지만, 설정 포맷은 어느 시점이 되면 결국 전부 튜링 완전해집니다.
Cue처럼 튜링 완전하지 않은 설정 언어도 몇 개 있습니다. 항상 종료되고 평가가 결정적(deterministic)이라는 점이 유용합니다.
Cue는 스키마 검증 기능도 풍부해서 동적 언어보다 낫습니다.
저는 말씀하신 보간이 일어나는 helm보다 kustomize를 확실히 더 선호합니다.
저도 Helm보다 Kustomize가 좋습니다. 다만 vars를 없앤 건 제대로 된 대체재도 없이 내린 멍청한 결정이었습니다.
https://github.com/kubernetes-sigs/kustomize/issues/4049
저는 스타트업 하나를 통째로 Kustomize로 세팅했습니다. 정말 좋았는데, 상속 기능을 쓰는 사람이 저 말고는 거의 없었어요. 다들 전부 복붙했죠. 그러다 회사가 커져서 전담 데브옵스 인력을 뽑았더니, 그 사람들이 곧바로 Helm으로 갈아타기 시작하더군요. 저는 두 손 들고 소프트웨어 개발에 집중하는 쪽으로 돌아갔습니다.
NGO 관련 일을 한 적이 있는데, 거기서는 "적정 기술(appropriate technology)"이라는 말을 합니다. 무엇보다 현지 주민에게 맞는 기술을 뜻합니다. 저는 Helm 같은 도구나 Go 같은 언어가 많은 팀에게는 적정 기술이라고 보기 시작했습니다. 그렇다고 제 집에서 쓸 양수기를 페달식으로 바꿀 생각은 없지만요.
YAML 대신 JSON을 쓸 수도 있습니다.
하지만 훨씬 못생겨서 아무도 안 쓰죠. 그러니까 우리는 프로그램이 못생기는 게 싫어서 제정신으로 살 자격이 없다는 얘기인가요?
요점은 문자열이 얼마나 예쁘냐가 아닙니다. 요점은 문자열이 아예 없어야 한다는 겁니다. 이건 컴퓨터 프로그램이니까 평범한 객체를 써야죠. 파이썬 코드
my_pod = nignx_pod()는 공백이 너무 많거나 적다는 이유로 구문 오류를 던지는 일이 절대 없습니다.k8s 타입을 protobuf로 가져와서 protobuf 인코딩으로 k8s API와 통신하는 프로그램을 짜는 걸 아무도 막지 않습니다. 타입은 이미 전부 proto입니다(CRD는 제외하고요). 다만 사용자 친화적인 방법이 아니라서 대부분 안 쓰는 겁니다.
아니요, 굳이 명령형으로 protobuf를 휘두르는 방법까지 갈 필요 없습니다.
https://tanka.dev/
https://www.pulumi.com/
이 밖에도 지금은 많이 있습니다. 하지만 helm이 먼저 인기를 끌었기 때문에, 집단행동 문제 탓에 업계가 helm에서 갈아타려면 영원히 걸릴 겁니다.
이거 사실상 crossplane 아닌가요? https://www.crossplane.io/
당연히 던지죠. 줄 앞에 공백 몇 개만 붙여도 오류가 납니다.
잠깐, 그게 어떻게 도움이 되죠? 텍스트 템플릿이 YAML보다 JSON을 만드는 데 더 적합한가요? 잘 구조화된 데이터를 문법 없는 텍스트인 양 다루려는 건, 포맷이 뭐든 간에 제 눈에는 제정신이 아닌 짓으로 보입니다...
(기본적으로는 Horizontal Pod Autoscaler인데, 센서가 "CPU"만이 아닌 것입니다.)
Kubernetes 1.23부터 "커스텀 메트릭"으로 HPA를 제어하는 기능이 지원됩니다: https://kubernetes.io/blog/2026/07/14/custom-metrics-exporter-kubernetes/
설정이 좀 복잡하냐고요? 물론입니다! 하지만 비교적 탄탄하고 잘 지원되는 내장 컨트롤러를 커스텀 메트릭과 함께 쓸 수 있습니다.
(설정이 좀 복잡하냐고요? 물론입니다!)
Kubernetes™
HPA를 설정하려고 할 때 저만 그런 줄 알았어요.
노드 쪽에 해당하는 도구는 KEDA라고 하는데, 비용 효율적인 클라우드 환경을 설계할 때도 아주 유용합니다.
helm 말고 말 그대로 다른 건 뭐든 쓰면 k8s 생활이 100배는 나아질 겁니다.
Helm은 훌륭한 도구지만 반드시 단순하게 유지해야 합니다. 저희 회사도 Helm을 쓰는데, 차트의 95%는 하드코딩된 YAML 템플릿이고 dev/test/prod마다 다른 값에만 Helm 변수를 가끔 뿌려 두는 정도입니다. 아주 잘 돌아가고, 그 용도로는 Kustomize보다 훨씬 편합니다. 하지만 공개 저장소에서 볼 수 있는 초복잡한 Helm 차트를 유지보수하고 싶은 마음은 전혀 없습니다.