2P by GN⁺ | ★ favorite | 댓글 1개
  • Kubernetes처럼 설정 대상이 늘어나는 환경에서는 YAML 파일을 직접 늘리는 방식이 곧 한계에 닿으며, YAML 템플릿보다 설정 데이터를 생성하는 접근이 더 적합해짐
  • Helm chart는 values.yaml과 Go 템플릿으로 값을 주입하지만, 선택 필드, 배열, 맵이 들어가는 순간 조건문과 들여쓰기 부담이 커짐
  • YAML은 공백 규칙이 엄격한데 Helm 템플릿 파서는 YAML 구조를 이해하지 못해, toYamlindent 조합이 쉽게 취약한 설정 생성으로 이어짐
  • YAML은 JSON의 상위 집합이라 상호 변환이 단순하며, Jsonnet은 외부 변수, 조건부 필드, 맵 조합, 객체 병합으로 설정 객체 생성을 코드처럼 다룸
  • kr8은 Jsonnet 기반 흐름으로 여러 Kubernetes 클러스터 설정을 만들고 조작하며, 복잡한 YAML 문자열 조립 대신 객체를 직접 생성·변형하는 쪽을 택함

설정 복잡성은 YAML 파일 수가 늘어날 때 시작됨

  • 애플리케이션과 인프라가 일정 규모를 넘어서면 설정 복잡성이 빠르게 커짐
  • 배포 대상이 1~2개라면 YAML 설정 파일을 직접 작성해도 충분하지만, 그 이상으로 늘어나면 설정을 체계적으로 관리해야 함
  • 여러 설정 파일이 필요한 이유는 대개 같은 대상이라도 일부 값이 서로 다르기 때문임
    • dev, stg, prod 같은 환경별 배포
    • Europe, North America 같은 지역별 배포
  • 모든 설정이 다른 것은 아니지만, 차이가 충분히 크면 공통 부분과 다른 부분을 분리해 관리해야 함
  • 설정 관리 분야는 이런 문제를 오래 다뤄왔고, 여러 도구가 각자의 방식으로 YAML을 활용해 왔음
  • Puppet에 포함된 hiera는 계층적으로 변수를 조회할 수 있어 강력하고 유연하며, YAML 자체를 템플릿할 필요를 크게 줄임

Helm chart에서 드러나는 YAML 템플릿의 문제

  • 클라우드 컴퓨팅과 Kubernetes로 설정 대상이 운영체제 위 계층까지 확장되면서 CloudFormationHelm 같은 도구가 등장함
  • Helm chart는 values.yaml에 정의한 외부 파라미터를 받아 렌더링할 수 있음
  • 단순 문자열 값은 비교적 간단함
image: "{{ .Values.image }}"
  • values.yamlimage 값을 지정하면 해당 값이 템플릿에 들어감
  • 선택 필드처럼 더 복잡한 설정을 다루기 시작하면 문제가 커짐
{{- with .resourceGroup  }}
    resourceGroup: {{ .  }}
{{- end }}
  • 선택 값은 비워둘 수 없기 때문에 조건문과 루프가 필요하고, 템플릿이 쉽게 지저분해짐
  • 배열이나 맵을 넣을 때는 toYamlindent를 조합해야 함
{{- with .Values.podAnnotations  }}
      annotations:
{{ toYaml . | indent 8  }}
{{- end  }}
  • toYaml로 YAML을 다시 YAML로 바꾸는 함수 호출도 어색하지만, 더 큰 문제는 공백 처리

YAML 공백 규칙과 템플릿 엔진의 충돌

  • YAML은 들여쓰기와 공백 규칙이 엄격함
  • 다음 예시는 유효하거나 완전한 YAML이 아님
something: nothing
  hello: goodbye
  • 사람이 직접 작성한다면 백스페이스를 몇 번 눌러 고칠 수 있지만, 템플릿 시스템으로 YAML을 생성할 때는 단순하지 않음
  • 설정 파일이 5~10개를 넘는 수준이라면 직접 작성보다 설정 생성이 필요해짐
  • .Values.podAnnotations 값을 이미 들여쓰기된 annotations 아래에 넣으려면, 값 자체도 정확한 수준으로 들여쓰기해야 함
  • Go 템플릿 파서는 YAML을 이해하지 않기 때문에, 템플릿 문법을 보기 좋게 들여쓰기하려 해도 문제가 생김
{{- with .Values.podAnnotations }}
      annotations:
      {{ toYaml . | indent 6 }}
{{- end  }}
  • 템플릿 시스템이 YAML 구조를 모르는 상태에서 공백과 조건문을 함께 다루면, 복잡한 설정 생성이 점점 어려워짐
  • JSON을 직접 작성하는 방식도 주석 부재와 쉼표 누락 문제 때문에 적합하지 않으며, 이런 불편 때문에 YAML이 쓰이게 됨

Jsonnet은 JSON 설정을 생성하는 데이터 템플릿 언어임

  • YAML은 JSON의 상위 집합이므로 JSON과 YAML 사이 변환은 단순함
  • 여러 애플리케이션과 프로그래밍 언어는 JSON과 YAML을 기본적으로 파싱하거나 변환할 수 있음
  • Python에서도 YAML을 읽어 JSON으로 출력할 수 있음
python -c 'import json, sys, yaml ; y=yaml.safe_load(sys.stdin.read()) ; print(json.dumps(y))'
  • Jsonnet은 스스로를 데이터 템플릿 언어라고 부르며, 핵심 목적은 JSON 설정을 생성하는 것임
  • Jsonnet의 설계 배경은 design rationale에서 확인할 수 있음

외부 변수와 선택 필드 처리

  • Jsonnet은 외부 변수를 사용해 설정 값을 주입할 수 있음
{

  image: std.extVar('image'),

}
  • CLI에서 외부 변수를 넘기면 JSON 결과가 생성됨
jsonnet image.jsonnet -V image="my-image"
{
   "image": "my-image"
}
  • 선택 필드는 템플릿 조건문을 문자열 안에 끼워 넣지 않고, 코드의 조건식으로 표현할 수 있음
// define a variable - yes, jsonnet also has comments
local rg = null;
{

  image: std.extVar('image'),
  // if the variable is null, this will be blank
  [if rg != null then 'resourceGroup']: rg,

}
  • rgnull이면 resourceGroup 필드는 결과에 포함되지 않음
  • 값을 지정하면 해당 필드가 출력됨

맵과 객체 조작이 YAML 들여쓰기보다 단순함

  • Kubernetes pod annotation처럼 맵을 설정에 넣는 경우, Jsonnet에서는 값을 변수로 정의한 뒤 객체에 배치할 수 있음
local annotations = {
  'nginx.ingress.kubernetes.io/app-root': '/',
  'nginx.ingress.kubernetes.io/enable-cors': true,
};

{
  metadata: { // annotations are nested under the metadata of a pod
    annotations: annotations,
  },

}
  • 이 방식은 YAML 템플릿에서 들여쓰기를 맞추는 것보다 훨씬 단순함
  • 생성 결과는 metadata.annotations 아래에 annotation 맵이 들어간 JSON 객체임
{
   "metadata": {
      "annotations": {
         "nginx.ingress.kubernetes.io/app-root": "/",
         "nginx.ingress.kubernetes.io/enable-cors": true
      }
   }
}
  • 기존 객체에 annotation을 추가하는 작업도 Jsonnet에서는 + 연산자로 처리할 수 있음
local annotations = {
  'nginx.ingress.kubernetes.io/app-root': '/',
  'nginx.ingress.kubernetes.io/enable-cors': true,
};

{
  metadata: {
    annotations: annotations,
  },
} + { // this adds another JSON object
  metadata+: { // I'm using the + operator, so we'll append to the existing metadata
    annotations+: { // same as above
      something: 'nothing',
    },
  },
}
  • 결과 객체에는 기존 annotation에 something: "nothing"이 추가됨
{
   "metadata": {
      "annotations": {
         "nginx.ingress.kubernetes.io/app-root": "/",
         "nginx.ingress.kubernetes.io/enable-cors": true,
         "something": "nothing"
      }
   }
}
  • 단순 예시에서는 코드가 더 길어 보일 수 있지만, 설정이 복잡해질수록 객체를 이런 방식으로 조작하는 기능이 유용해짐

kr8은 Jsonnet 방식으로 Kubernetes 설정을 다룸

  • kr8은 여러 Kubernetes 클러스터의 설정을 쉽고 단순하게 만들고 조작하기 위해 이런 방법들을 사용함
  • 핵심 흐름은 YAML 템플릿을 공백과 조건문으로 조립하는 대신, JSON 설정 객체를 생성하고 필요한 방식으로 변형하는 것임

댓글과 토론

Hacker News 의견들
  • YAML로 쓴 설정은 이제 완전히 질렸음. GitHub Actions에서 가장 싫은 부분이고, 안정성보다도 더 나쁨
    멋진 도구가 설정에 YAML 파일을 요구하는 걸 보면 바로 불안해짐. Terraform의 HCL, AWS Step Functions의 ASL 같은 독자 설정 언어도 마찬가지임
    선언형 API를 원하는 건 괜찮지만, 그 선언을 프로그램으로 생성할 수 있게 해줬으면 함. 코드로 선언하고 생성하는 설정이 훨씬 나은 경험이었고, AWS CDK가 이걸 정말 잘했음
    타입 안전한 언어와 좋은 IDE 지원으로 클라우드 인프라 정의를 작성할 수 있고, 2년 전 이후 업데이트도 안 된 플러그인에 기대지 않아도 됨

    • 이쯤 되니 YAML보다 순수 JSON이 더 낫다고 느낌. 결정타는 deno fmt에는 JSON 포매터가 있지만 YAML 포매터는 없다는 점이었음
      JSON 포매터는 밀리초 단위로 도는 단일 바이너리인데, YAML 자동 포매팅은 사실상 Prettier를 써야 하고 Prettier는 NPM 절반에 의존하며 시작과 실행에 2초쯤 걸림
      그래서 회사 저장소에서 JSON으로 바꿀 수 있는 YAML 파일은 전부 JSON으로 옮겼고, 적어도 나는 훨씬 만족함. 아무도 불평하지 않았음
      여러 편집기는 JSON의 $schema 태그도 지원함. 제품에 이 기능을 추가했는데, 문서를 읽지 않고도 탭만 눌러 설정 파일을 만들 수 있어 정말 좋음
      YAML도 YAML language server로 가능하지만 탭 키가 들여쓰기에도 필요해서 사용성이 별로임. JSON도 완벽하진 않지만 적어도 텍스트 "no"가 참은 아님
    • YAML을 쓰는 장점으로 JSON에는 주석이 없다는 말을 자주 듣는데, 왜 완전히 다른 언어로 갈아타야 하는지 모르겠음
      설정을 읽기 전에 주석을 제거하는 필터를 하나 넣으면 되지 않나? YAML로 바꾸는 것보다 어려울 리 없음
      YAML이 읽기 쉽다는 말도 잘 이해되지 않음. 설정을 다루는 고통은 중괄호와 대괄호를 몇 초 덜 파싱해서가 아니라, 수백 줄 설정 속에서 빠진 공백이나 탭 때문에 뭐가 잘못됐는지 알기 어려운 데서 옴
    • GitHub Actions는 무엇으로 설정하든 별로였을 것임. 자료구조로 프로그램을 설명하려고 하기 때문임
      Ansible도 같은 실수를 하고, 수많은 도구가 마찬가지임
    • AWS CloudFormation 시절부터 비슷하게 생각해 왔음. 4년 전에 AWS가 공개한 JSON 파일에서 모든 리소스와 Python 타입 힌트를 생성하는 실험적 CloudFormation 생성기를 만들었고 꽤 잘 동작했음: https://github.com/weberc2/nimbus/blob/master/examples/src/n...
      CDK가 그런 방식으로 동작하는지는 잘 모르겠음. 조금 만져봤을 때는 내가 만든 “CloudFormation 생성” 경험과는 꽤 달랐고, CDK의 장점을 제대로 깨닫지 못했음
      YAML/템플릿 문제를 상속/마법 문제로 바꾸는 느낌이었음. AWS CDK, Terraform CDK, Pulumi를 써본 사람들의 경험을 더 듣고 싶음
    • GitHub Actions에서는 YAML 앵커를 지원하지 않아서 더 고통스러움. 이게 최소한의 합성 가능성은 제공했을 텐데 아쉬움
      https://github.com/actions/runner/issues/1182
  • YAML 템플릿이 꽤 미친 짓이라는 데 동의하지만, 왜 가짜 언어를 그만 쓰고 진짜 프로그래밍 언어를 쓰지 않는지는 늘 이해가 안 됨
    복잡한 로직이 필요하면 프로그래밍 언어로 YAML/JSON/무엇이든 생성하면 됨. Ruby, Python, 그 외 어떤 언어든 Jsonnet이나 Go 템플릿 같은 이상한 유사 언어 없이도 필요한 걸 제공함
    그냥 코드를 쓰면 템플릿 엔진의 불투명한 이상한 문제에 훨씬 덜 물림. 어떤 진짜 언어를 쓰든 훨씬 나아짐

    • 예전에 엄청난 수의 서버에서 부트스트랩과 패치용으로 주기적으로 실행되는 Ansible 플레이북을 관리하는 일을 맡은 적이 있음
      비슷한 작업에 Chef를 써본 적이 있는데, Ruby라서 원하는 로직을 쉽게 정의하고 반복문과 제대로 된 변수를 쓸 수 있어 좋았음
      Ansible이 비프로그래머를 위해 설계된 건 이해하지만, 기본적인 프로그래밍에 익숙한 사람에게 조건부 작업과 반복이 많은 Ansible 플레이북을 Jinja 템플릿의 장황한 문법 안에 가두는 것보다 더한 지옥은 없음
    • 요즘 스택에서는 언어 임베딩이라는 아키텍처가 거의 잊힌 듯함. 예전에는 충분히 복잡한 애플리케이션이라면 핵심은 C/C++/Java 등으로 만들고, 스크립팅이 필요하면 위에 LISP나 Lua 같은 걸 내장했음
      그런데 지금은 JSON/TOML/YAML 파서를 가져와 readConfig 함수를 만드는 게 보통이고, 내장 인터프리터가 더 적합한 자리도 그렇게 처리함
      개발자 입장에서는 완전한 언어 임베딩과 애플리케이션 바인딩을 제공하는 것보다 설정 포맷에 복잡성을 더하는 게 쉬움. 그래서 방법 자체를 잊어버렸거나, 가능하다는 생각조차 못 하는 것 같음
    • Pulumi는 선호하는 언어로 작성하고 HCL을 버릴 수 있어 매력적이지만, 개인적으로는 명확히 더 나쁘다고 봄. 인프라 코드는 선언형이어야 예측 가능성, 재현성, 유지보수성이 커짐
      Chef/Puppet 시대에 IaC에 로직을 넣기 시작했다가 업그레이드도 유지보수도 불가능한 거대한 난장판이 된 곳들이 많았음. Chef/Pulumi 방식도 가능은 하지만, 스타일과 유지보수에 아주 엄격한 사람이 필요함
      큰 팀과 장기 유지보수에는 Terraform/Puppet 모델이 더 낫다고 봄. HCL이 짜증나고 Python/TypeScript 등을 쓰는 게 해방감 있더라도, 순수 선언형 코드는 많은 스파게티를 막아줌
    • 문제는 언어 덕후들이 다른 언어 덕후들을 위해 언어를 만든다는 데 있음
      지금 유행하는 언어 설계 요소를 넣고 싶어 하고, 자체 호스팅을 원하고, 빠른 멀티스레드 웹서버도 작성할 수 있게 만들다 보니 개념적으로 복잡해짐
      시스템 엔지니어/DevOps를 위한 Logo 같은 단순한 장난감 언어가 필요함. 원래 K&R C 책 정도 크기의 책 한 권으로 설명 가능해야 함
      동적 타입, 주말에 배울 수 있는 제어 구조, 스레딩이나 동시성 없음, 객체지향이나 상속 없음, 함수형/모듈형 설계, 그리고 다른 언어와 프레임워크에서 쉽게 호출하고 호출될 수 있는 FFI 모델이 필요함
      문제는 언어 덕후들이 스스로를 제어하지 못해 기능을 계속 추가하고, 그게 핵심 라이브러리와 스타일 가이드에 들어가 초보자도 다 배워야 하게 만든다는 점임
      나 자신도 배열/해시맵에 each/map류 함수를 추가하고 일급 함수와 클로저를 넣고 싶어질 텐데, 그게 실수일 수 있음. 이미 설정용 불변 함수형 언어가 있지만, 템플릿 YAML을 쓰는 95% 이상은 그런 방식으로 프로그래밍을 배우고 싶어 하지 않아서 널리 퍼지기 어려움
    • 설정 파일을 생성한다는 점을 강조하고 싶음. 설정 자체는 JSON 파일 등에 들어갈 수 있는 형태로 제한하는 게 매우 유용함
      설정이 단순해지고, 소비하기 쉬워지고, 문서화하기도 좋아짐. 하지만 설정 파일을 작성할 때는 프로그래밍 언어를 써야 하고, 가능하면 오류 검사와 자동완성, 인라인 문서를 제공하는 정적 타입 언어가 좋음
      AWS CDK가 좋은 예임. 순수 CloudFormation 작성은 고통스럽지만, CDK는 CloudFormation에 프로그래밍 기능을 덧붙이는 게 아니라 CloudFormation을 생성해 줌. AWS가 소비하는 입력은 여전히 비교적 단순하고 안정적인 CloudFormation임
  • 제목을 보자마자 Kubernetes 얘기일 줄 알았음
    Kubernetes API는 꽤 직관적이고 잘 정의된 JSON 스키마가 있음. k8s를 배우는 시간의 대부분은 API 사용법을 이해하는 데 써야 하는데, 실제로는 Helm 차트 사용법을 알아내는 데 쓰고 있음
    Jsonnet, Ksonnet, Nu, CUE가 그렇게 큰 인기를 얻었다고 보진 않음. 대부분은 Kustomize를 쓰는 것 같고, 이유는 비교적 직관적이며 kubectl에 내장돼 있기 때문임
    원하는 도구는 k8s 스키마에 대한 타입 검사, 검증과 버전 폐기 경고를 정의 작성자에게 제공하고, 사용자가 쉽게 검사할 수 있는 단일 산출물을 주며, 클러스터가 어떤 객체/버전도 지원하지 않으면 원자적으로 실패하고, 기본 도구 체인에 내장돼 있어야 함
    Bun이나 Deno TypeScript 스크립트가 인자를 받는 함수를 내보내고 정의 목록을 반환하게 하면 deno compile 등과 함께 잘 맞을 것 같지만, 기본 도구 체인 내장 조건을 어김

    • 이건 소프트웨어 전반의 패턴임. 시스템이 기반하는 원시 요소와 기본기를 배우는 대신, 너무 어렵다는 이유로 그 위의 추상화를 잔뜩 배움
      하위 수준 세부사항으로부터는 보호받지만, 문제가 생기면 진단과 디버깅을 어렵게 만드는 거대한 추상화 스택을 상대해야 함
      실제로 무슨 일이 벌어지는지 알아내기가 훨씬 어려워지고, 추상화 계층에 의존하게 되어 공급자가 내놓는 업데이트와 의존성 그래프 속 다른 문제까지 떠안아야 함
    • 우리 시스템에서는 jsonnet을 쓰고 있고 k8s와는 전혀 관련이 없음. 큰 인기를 얻었다기보다 복잡한 설정을 위한 틈새 도구이고, 많이 홍보된 도구도 아님
      필요한 일을 거의 문제 없이 해주고, 크로스플랫폼이며 여러 언어에 걸쳐 쓸 수 있음. C++, .NET, JVM 실행 파일에 내장해 봤음
      결과 JSON 설정은 대안인 toml/yaml/hocon/ini 등에서는 찾기 어려운 방대한 도구와 함께 쓸 수 있음. HOCON을 JVM이 아닌 언어에서 쓰려 했지만 항상 어떤 경계 사례가 걸렸음
    • 두 번째 요구사항은 아마 못 맞추고, 세 번째는 확실히 못 맞추지만: https://cdk8s.io/docs/latest/
    • 단순하게 유지하자는 생각은 좋고, 설치 방식으로도 가능한 한 kustomize나 순수 yaml을 쓰려 함
      하지만 실제로 큰 시스템을 관리하다 보면 결국 템플릿의 이점을 피할 수 없음
    • kustomize와 특히 helm은 너무 헷갈리는데, Kubernetes YAML 파일은 쓰고 이해하기가 매우 쉬움
  • 개발자들이 설정을 제대로 다루는 방법을 얼마나 적게 고민하는지 보면 웃김
    그냥 파일에 저장되거나 코드로 생성되는 키와 값의 묶음처럼 보이지만, 사실 이게 전부임. 프로그래밍 자체
    모든 것은 설정이고, 모든 함수 인자도 일종의 설정임. 외부 파일의 모든 설정은 결국 어떤 식으로든 함수 인자가 됨
    문제는 코드의 일반 텍스트 표현임. 선언형 설정 파일은 모든 걸 한곳에서 볼 수 있어서 좋아 보이지만, 설정을 프로그램으로 만들면 어디를 바꿔야 하는지 찾기 어려움
    코드가 실시간으로 실행돼 최종 설정의 표현을 보여주고, 각 최종 설정값이 어떻게 생성됐는지 추적할 수 있다면 문제가 안 됨. 그런데 이 기능은 꽤 간단한데도 그런 식으로 설계된 시스템이 없음. 설정은 늘 사후 고려사항임
    이 개념을 프로그래밍 전체로 확장하면, 하나의 설정값에 의존하는 모든 코드와 그 변환을 볼 수 있어야 함
    또한 대부분의 설정은 관계형/그래프형이라 중앙 데이터베이스에 두는 편이 더 나을 수 있음. 서로 다른 설정값은 서로 관련됨. 그러니 데이터베이스/그래프 편집기에서 설정을 봐야 함
    일반 텍스트에서 벗어나면 훨씬 단순해지기 시작하지만, 앞서 말한 언어 기능도 여전히 필요함

    • 비슷한 일을 한 고객사에서 정말 열심히 시도 중임. 제품별로 1500줄이 넘는 “설정” 파일이 있고, 이걸 기술 도면과 생산 파일을 만드는 데 사용함
      설정들은 관련 변수를 묶기 위해 이름 규칙을 쓰려 하고 있음
      실제 중첩 자료구조, 아마 JSON으로 옮기고 싶지만, 엔지니어들이 코드는 절대 쓰지 않으려 해서 코드로서의 설정은 불가능함. 앞서 말한 단점도 있음
      다음 생각은 설정을 더 잘 보여주고 수정할 방법이 있어야 한다는 것임. 최종 제품의 표현을 탐색하고, 부품을 선택해 그 방식으로 매개변수를 수정하는 시각적 UI를 생각했음
      이런 방향이 맞는지 궁금함. 아니라면 조금 더 설명해줬으면 함. 이 애플리케이션의 핵심은 설정임
  • 더 나쁜 건, CI/CD 같은 곳에서는 YAML이 거의 프로그래밍 언어가 된다는 점임. 그것도 매우 장황하고, 직관적이지 않고, 명세가 나쁘고, 공급자별로 다른 언어임

    • 2010년대 초반 Java의 실수를 반복하는 것과 거의 같음. 당시에는 애플리케이션 전체가 의존성 주입을 설정하는 거대한 XML 덩어리로 붙어 있는 경우가 많았음
      DTD와 XML 검증이 있었음에도 늦게 터지고, 해석하기 어려운 오류 메시지를 내는 익숙한 특성이 있었음
      그때 많은 좌절은 XML을 향했지만, 2020년대 중반의 YAML 지옥을 보면 문제는 마크업 언어 자체가 아니었음
    • 정확함. 우리는 ytt[0]를 쓰는데, “Python 방언인 Starlark 프로그래밍 언어의 약간 수정된 버전”임
      YAML 템플릿 어딘가에 로직을 묻는 건 정말 싫음
      [0] https://tanzu.vmware.com/developer/guides/ytt-gs/
    • Kubernetes를 다루는 일부 현장에서는 진지하게 YAML 엔지니어라는 말을 씀
    • YAML은 직렬화 형식계의 Bradford Pear 같음. 처음엔 좋아 보이지만, 프로젝트가 오래되고 YAML이 커지면 자기 가지 무게에 눌려 무너짐
    • 더 나쁜 건 세대마다 이 실수를 반복한다는 점임. S-표현식이 답인지는 모르겠지만, Terraform HCL은 애초에 만들어지지 말았어야 함
  • Helm이 이긴 게 정말 슬픔. 회사에서 오픈소스 k8s 관련 일을 하는데, 사용자 100%가 Helm 차트를 만들어 달라고 해서 결국 만들어야 했음
    작업하기 비참함. 파일 이름은 foo.yaml 같은데 실제로는 YAML이 아니라 편집기가 도와주지 못함. 모든 데이터를 indent 4로 흘려보내 YAML 정렬을 맞춰야 함
    가장 우울한 건 Kubernetes 기능을 전부 자기 방식으로 다시 노출해야 한다는 점임. 누군가 deployment.spec.template.spec.fooBars를 추가하고 싶어 하면 values.yamldeploymentFooBars를 추가하고 연결해야 함. 모든 기능마다 반복됨
    진짜 “나쁜 것이 좋은 것”이 잘못 굴러간 사례임. 나도 템플릿을 구현하려고 sed -e s/$FOO/foo/g 같은 끔찍한 짓을 한 적이 있고, 아마 Helm도 그렇게 시작했을 것임. 결과는 엉망임
    개인적으로는 kubectl에 들어가기 전부터 Kustomize를 써왔고 늘 꽤 만족했음. 이상한 점은 많지만, 적어도 생성하는 객체의 의미를 이해해서 시간을 아껴줌
    Jsonnet이 훨씬 좋음. 우리 k8s 앱의 일부로 복잡한 트래픽 라우팅을 위한 Envoy 배포를 함께 제공하는데, Envoy 설정은 장황하지만 Jsonnet으로 다루기가 쉬움: https://github.com/pachyderm/pachyderm/blob/master/etc/gener...
    진지하게 jsonnet을 Go 템플릿 언어로 트랜스파일해서 전부 Jsonnet으로 구현하는 걸 고려 중임. 적어도 조금은 유지보수 가능하고, helm install이 그냥 동작할 테니 아무도 모를 것임
    하지만 Helm이 Kubernetes의 종말이 될 것 같음. 어떤 경쟁 컴퓨터 할당/컨테이너 실행 도구가 설정을 위한 제대로 된 언어를 들고 나오면 하룻밤 사이에 넘어갈 것임

    • sed -e s/$FOO/foo/g를 쓰고 싶어질 때는 조금 더 표준적이고 나은 해결책으로 envsubst를 확인해보면 좋음
      jsonnet으로 Helm 차트를 템플릿화하거나 수정하는 주제라면 Tanka도 도움이 될 수 있음: https://tanka.dev/helm
    • Helm이 Kubernetes의 종말이 될 거라는 전망을 믿고 싶음
      다만 내가 일한 곳들은 여전히 변화가 무서워서 tf/hcl과 helm을 그대로 쓰고 있음. 적어도 개인 프로젝트에서는 조금 숨통이 트임
  • 여기에는 문제가 있다고 봄. 다만 설정 언어로 YAML을 고르는 유형의 사람이 이걸 문제로 볼지는 잘 모르겠음
    사람 중심 자료 표현과 컴퓨터 중심 자료 표현 사이에는 직접적인 충돌이 있음. 컴퓨터는 Lisp 비슷한 것을 좋아하고, 사람은 Python 비슷한 것을 좋아함
    Kubernetes 설정을 컴퓨터로 조작하고 싶은 사람이라면 Kubernetes가 YAML을 쓰는 데 은근히 짜증날 것임. 하지만 Kubernetes 커뮤니티는 주로 YAML 쪽 사람들처럼 보이니, 프로그래밍 로직이 들어가면 설정 파일 작업이 끔찍해진다는 걸 왜 신경 쓰겠나 싶음
    YAML의 단점이 바로 이 상황이고, k8s 관련 사람들은 대체로 그 정도는 예상할 만큼 충분히 영리하다고 봄
    “YAML은 JSON의 상위집합”이라는 말은 명세 작성자가 문서에 뭘 쓰든 실제로는 맞다고 생각하지 않음. YAML 설정을 전부 JSON으로 바꾸면 DevOps 팀이 화낼 것임
    두 데이터 형식은 같은 의미 표현을 가질 수 있지만, 같은 CPU 아키텍처로 컴파일되는 모든 언어도 그렇음. JSON과 YAML은 실무에서는 별개이고, 둘을 섞는 건 좋은 생각이 아님

    • 아이러니하게도 기억이 맞다면 k8s 매니페스트는 처음부터 기계 생성을 전제로 했고, 사람이 직접 쓰는 용도가 아니었음
      물론 사람들은 결국 손으로 썼고, 견디기 어려워지자 템플릿을 붙이기 시작했음. 항상 일이 그렇게 흘러가는 듯함
      손으로 쓴 텍스트는 기계 생성 설정 직렬화 텍스트로 대체되기보다, 원래는 여전히 손으로 쓴 텍스트에 템플릿이 붙은 형태로 대체됨
    • “YAML은 JSON의 상위집합”이라는 말은 모든 JSON 문서가 유효한 YAML 문서라는 뜻일 뿐임. YAML이 JSON과 같다는 뜻은 아님
  • 개인적인 원칙은 기계가 읽는 코드를 생성하는 데 문자열 보간을 쓰면 안 된다는 것임. 템플릿 언어는 멋진 문자열 보간일 뿐임
    SQL 삽입과 사이트 간 스크립팅의 결과를 모두 봤음. 임의의 텍스트를 인터프리터에 계속 넣는 한 이런 일은 계속 벌어짐
    그래서 HTML을 만들 때도 템플릿 파일을 쓰면 안 된다고 봄
    HTML용 템플릿 언어 대안으로는 Ruby의 Haml, JavaScript의 Pug가 있음. 이 언어들은 태그, 속성, 텍스트 노드의 전체 트리를 지정하는 정의된 방법을 제공함
    Python식 의미 있는 들여쓰기가 싫다면 JavaScript에는 JSX가 있음. JSX의 HTML처럼 보이는 부분은 웹 문서 트리를 만드는 createElement 표현식들로 컴파일되고, 그 트리는 필요하면 HTML로 출력할 수 있음
    Haml, Pug, JSX는 HTML을 출력할 수 있어도 템플릿 언어가 아님. 마찬가지로 JSON.stringify(myObj)는 JSON용 템플릿 언어가 아님
    기계가 읽는 코드는 가능하다면 대상 언어의 알려진 구조를 이해하고 활용하는 도구로 생성해야 함

    • Haml, Pug, JSX가 템플릿 언어가 아니라는 건, 템플릿 언어를 “멋진 문자열 보간”이라고 보는 개인적인 정의를 쓰지 않는 한 말이 안 됨
      Haml은 웹 문서 안의 인라인 코드를 피하고 HTML을 더 깔끔하게 만들기 위한 템플릿 시스템이고, Pug는 Node.js용 기능 풍부한 템플릿 엔진임
      JSX가 엄밀히 템플릿 언어가 아니라는 데는 동의할 수 있음
      결국 이들은 모두 HTML로 컴파일됨. 다만 문자열 보간이 아니라, 구문 트리로 파싱되고 유효한 구조에 대한 내부 이해를 바탕으로 HTML로 렌더링되는 언어임
      YAML 템플릿은 멋진 문자열 보간이고, 템플릿 언어가 아니거나 적어도 형편없이 구현된 템플릿 언어임
    • 모든 템플릿 언어가 문자열 템플릿 언어는 아님. 예를 들어 PHP를 텍스트용 템플릿 언어로 본다면, 같은 논리로 XQuery는 XML용 템플릿 언어임
    • 이게 문제의 본질임. YAML과 템플릿은 그냥 주의를 흐리는 요소임. 결국 문자열이 너무 일반적인 타입이고 우리가 게으르게 쓴다는 데로 귀결됨
      개인 규칙은 값이 문자열에 들어갈 때마다 반드시 올바르게 인코딩해야 한다는 것임
      예전에 이 주제로 글을 썼음: https://kevincox.ca/2022/02/08/escape-everything/
      요약하면 모든 문자열에는 HTML, SQL, 사람이 읽는 터미널 출력 같은 지켜야 할 형식이 있음. 어떤 값을 문자열에 넣을 때마다 그 형식에 맞게 제대로 인코딩해야 하지만, 우리는 거의 그렇게 하지 않음
  • 우리는 cuelang으로 옮기는 중임 [1]. 개인적으로 Jsonette보다 설계가 낫다고 봄
    Kubernetes에는 이미 상태 조정이 있으니 이 구성에서 빠진 건 삭제뿐인데, 이제 prune 기능으로 해결 가능함 [2]
    [1] https://cuelang.org/docs/integrations/k8s/
    [2] https://kubernetes.io/blog/2023/05/09/introducing-kubectl-ap...

    • cuelang을 추천할 수 있음. 회사에서 쓰기 시작했는데 정말 좋음
      오류 메시지 몇몇은 해석하기 조금 어렵지만, 사전에 잡아주는 오류가 워낙 많아서 받아들일 만함. 이제 직접 yaml을 써야 하는 몇 번의 순간은 비교하면 너무 지루하게 느껴짐
  • 이럴 때 보통 “우리의 구세주 CUELang을 들어보셨나요?”라고 끼어듦: https://cuelang.org/
    아직 튜링 완전하지 않지만 중복 제거에는 충분히 표현력이 있고, 같은 언어로 스키마와 데이터를 같은 파일이나 별도 파일에 정의할 수 있으며 합집합 타입도 있음
    YAML이나 JSON을 생성할 수 있고, 자기 자신 또는 YAML/JSON 파일을 검증할 수 있음
    가장 큰 단점은 현재 구현이 Go뿐이라 하위 프로세스나 FFI가 필요할 수 있다는 점임

    • 우리는 아주 간결한 cuelang 파일을 받아들이는 파이프라인이 있음
      그런 다음 각 애플리케이션용 JSON 파일을 만들고, 어떤 도구가 XML 정의를 만들며, 그 정의가 아키텍트들이 소유한 XLS에 적용되고, 거기서 Helm 차트에 적용할 YAML을 뱉음
      차트는 k8s 클라이언트를 배포하고, 그 클라이언트가 API를 통해 JSON으로 메인 클러스터와 상호작용함
      시간이 좀 걸렸지만, 각 작업에 가장 좋은 도구를 쓰고 있음
    • dhall과 비교하면 어떤가?