構成記述系DSL 11本の調査
構成記述系DSL 11本(Pkl / Bicep / KCL / CUE / Dhall / Jsonnet / Nickel / cdktf / cdk8s / Crossplane / Helm)を、同じ枠組み — 記述言語の位置づけ / 抽象化機構 / 3自由度の評価 / 2×2配置 / 設計判断 — で観察した調査データ。調査日は全DSL共通で2026-04-26、公式ドキュメントの当時の記述に基づく。
3自由度は「抽象化の追加」「複数量の制御」「実装の切替」で、各自由度を「言語が決める」か「規約に委ねる」かで判定する。
Pkl (Apple)
公式: https://pkl-lang.org/main/current/language-reference/index.html
記述言語の位置づけ:外部DSL。Appleが公開した構成記述専用言語で、独自パーサと評価器を持つ。評価は純粋(副作用なし。例外は read() による外部リソース読み込み)、late-bound(object propertiesは遅延評価)。文法はJava/Scala系の見た目で、module, class, function, let 等の予約語を持つ。
抽象化機構:
| 単位 | 構文 | 役割 |
|---|---|---|
| module | ファイル単位(.pkl) | 設定単位 / 再利用単位 |
| class | class Foo extends Bar { ... } | 単一継承の型定義 |
| function | function greet(b: Bird): String = "Hello, \(b.name)!" | 値の計算抽象 |
| object | new { ... } / amends { ... } | レコード値、プロトタイプ的な拡張可 |
module組み立ては import "uri"(値として読み込み)、amends "uri"(ベースにして上書き拡張)、extends "uri"(クラス的に継承)の三つ。
3自由度:
- 抽象化の追加 = 規約に委ねる(純粋関数限定)。ユーザ定義関数あり、高階関数あり(
Listing<Bird>(isDistinctBy((it) -> it.name))のようにラムダを引数に渡せる)、ユーザクラスも単一継承で組み合わせ可 - 複数量の制御 = 言語が決める + 標準ライブラリ。ループ構文(
for,while)は言語になく、標準ライブラリのmap/filter/forEach系メソッドで集合操作する。if式はあり、必ずelse必須。量を動かすこと自体はList(1, 2, 3).map((i) -> new Server { port = 8000 + i })のように可能で、標準ライブラリAPIという中間層に押し込まれている - 実装の切替 = 言語が決める。
import/amends/extendsのURIは全てリテラル文字列のみで、変数や式で実行時に切り替えられない
2×2配置:外部DSL × 言語が決める、ただし抽象化の追加だけは「規約に委ねる」寄り。
設計判断: 「外部DSLを選びつつ、構成データ計算の表現力(関数 / 高階関数 / 標準ライブラリ)は汎用言語に近づける」ハイブリッド。最重要の「実装の切替」だけを厳格にロックし、出力されるオブジェクトツリーの構成が静的解析だけで決まる。「複数量の制御」を標準ライブラリに集約する設計は、評価器の表現力を絞って停止性を確保する効果を持つ。得たものは構成データの最終形が静的に決まる安心感と純粋性による再現性、失ったものは「実行時条件で別moduleを選ぶ」抽象化(if env == "prod" then import "prod.pkl" else import "dev.pkl" は不可)。
Bicep (Microsoft)
公式: https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/file
記述言語の位置づけ:外部DSL。Azure専用で、コンパイルするとARM template (JSON)を生成する。2段階評価(Bicepファイル → ARM template → Azure Deploymentがapply)、宣言的(公式: "the order of elements doesn't affect how deployment is processed")、副作用なし。
抽象化機構:
| 単位 | 構文 | 役割 |
|---|---|---|
| Bicep file | .bicep ファイル | デプロイ単位 / module単位 |
| resource | resource <name> '<type>@<version>' = { ... } | 個別リソース層 |
| module | module <name> '<path>' = { ... } | 複数リソース束ね層 |
| type | type <name> = <type-expression> | ユーザ定義型(union, object) |
| func | func <name> (...) <type> => <expr> | ユーザ定義関数 |
| var / param | var <name> = <expr> / param <name> <type> = <default> | ローカル変数 / デプロイ時パラメータ |
3自由度:
- 抽象化の追加 = 言語が決める。
funcで関数定義は可能(func buildUrl(https bool, hostname string, path string) string => '...')、ユーザ定義型typeもあり。しかし高階関数なし(関数を引数に取る/返す/変数に入れるは仕様にない)、クラス継承もない - 複数量の制御 = 言語が決める(専用構文
for)。公式Loops節: "Add iterative loops to your Bicep file to define multiple copies of: A resource, A module, A variable, A property, An output." 構文はmodule stgModule './example.bicep' = [for i in range(0, moduleCount): { ... }]。ifによる条件付きデプロイも専用構文。汎用言語のforループではなくresource/module宣言の右辺に置ける専用式 - 実装の切替 = 言語が決める。module pathは文字列リテラルで、string interpolationや変数参照を持たない位置に設計されている。registry path (
br/public:...:tag)やtemplate spec path (ts:...)も同じくリテラル。ただしifでmoduleを出すかどうかは条件分岐できる
2×2配置:外部DSL × 言語が決める。3自由度すべてが言語側で制限される典型例。
設計判断: ARM template (JSON)の手書きを置き換えるauthoring layer。コンパイル先がJSONなのでARM templateの表現力が天井になる(「実行時に別templateを読み込む」はARM templateの世界に存在しないのでBicepでも実装できない)。制約表現は @minLength, @maxLength, @allowed 等のdecorator中心。得たものはARM templateへの1:1対応の予測性、エディタ/lint/IntelliSenseの効きやすさ、Azure専用に特化した型システム。失ったものは任意のヘルパー抽象化、cross-cloudの汎用性、動的なmodule選択。
KCL (CNCF)
公式: https://www.kcl-lang.io/docs/reference/lang/tour
記述言語の位置づけ:外部DSL。Kubernetes / Cloud Native寄りの構成記述言語でPythonに似た見た目、CNCF Sandboxプロジェクト。評価モデルは純粋関数("KCL functions cannot modify external variables, but can only reference external variables.")、immutability優先、schemaは順序非依存(計算は依存ベース)。
抽象化機構:
| 単位 | 構文 | 役割 |
|---|---|---|
| module | ファイル / ディレクトリ | 名前空間、importの単位 |
| schema | schema Foo: ... | 型 + 制約 + デフォルト値の定義 |
| lambda | lambda x: int, y: int -> int { x + y } | 関数値(一級) |
| rule | ランタイム制約検証 | バリデーション |
| mixin / protocol | schema合成 / 制約 | 多重継承的な再利用 |
schemaは単一継承 + mixin合成(schema Employee(Person): / mixin [FullNameMixin])。
3自由度:
- 抽象化の追加 = 規約に委ねる。lambda式でユーザ定義関数、高階関数あり(公式: "Functions can capture external variables and be passed as arguments"、
funcOther = lambda f, para: int { f(para) })。schemaの単一継承 + mixin合成で構造的抽象化も可。副作用は禁止で、これは「規約に委ねる」中でも安全側に倒した設計。Pklよりも明示的に関数値を一級として扱う - 複数量の制御 = 言語が決める(list/dict comprehension)。
[_x for _x in range(20) if _x % 2 == 0]、{str(i): 2 * i for i in range(3)}、quantifier式(map,filter,all,any)。ステートメントレベルのforループはなく、量を動かすのは式レベルで完結する - 実装の切替 = 言語が決める。
import .model1/import service/import ..model as modelのpathはリテラルのみ
2×2配置:外部DSL × ハイブリッド(3自由度のうち2つが「言語が決める」、1つが「規約に委ねる」)。Pklとの違いは、Pklがループ構文なしで標準ライブラリ map/filter のみなのに対し、KCLはlist/dict comprehensionを持つ点。
設計判断: 「Pythonの見た目を借りつつ、副作用と動的読み込みを禁止する」。得たものはPython経験者の学習コストの低さ、構成データ計算の表現力、schemaの型安全と制約検証。失ったものは副作用ベースの抽象化と動的module選択。
CUE
公式: https://cuelang.org/docs/reference/spec/
記述言語の位置づけ:外部DSL。data validation / configuration / code generationの特化言語で、Goから派生したが言語仕様は独立。評価モデルはunification中心(a & b はgreatest lower boundを計算する二項演算子で、結合則・交換則・冪等性を満たす)、順序非依存("combining CUE values in any order always gives the same result")、constraint = type = valueをgraph unification modelで単一概念として扱う。
抽象化機構:
| 単位 | 構文 | 役割 |
|---|---|---|
| package | ディレクトリ単位 | 名前空間 |
| definition | #Foo: { ... } | 型(closed struct) |
| regular field | foo: ... | 値 |
| comprehension | [for x in a if cond { x+1 }] | 集合計算 |
「型と値を区別しない」設計で、#Foo: { name: string } は型として、foo: { name: "alice" } は値として、両者を & でunifyできる。
3自由度:
- 抽象化の追加 = 言語が決める。ユーザ定義関数なし(仕様には
f(a1, ..., an)の呼び出し構文があるがbuiltins限定)、高階関数なし、lambdaなし。抽象化はdefinition (#Foo)で型 + 制約を切り出す形のみで、値レベルの再利用ヘルパーは書けない - 複数量の制御 = 言語が決める(comprehension限定)。仕様: "Comprehensions define a clause sequence that consists of a sequence of for, if, and let clauses, nesting from left to right." 使用箇所はlistとstructの値構築に限定され、トップレベルの
for文や手続き的ループはない - 実装の切替 = 言語が決める。ImportPathは文字列リテラルで、変数や式で動的に決められない
2×2配置:外部DSL × 言語が決める(3自由度すべて)。Terraform / Carina / Bicepと同じ象限だが、抽象化機構がunificationという独自の数学的基礎に乗っている点で特殊。
設計判断: 「unificationに賭けた純粋関数型DSL」。「抽象化 = 関数」ではなく「抽象化 = constraintのunification」で、関数で値を計算する代わりにdefinitionで制約を書きunifyで組み合わせる。順序非依存 / 結合則 / 交換則 / 冪等により部分評価・統合・検証が可能になる。Turing不完全(仕様レベルで停止性を保つために関数定義や再帰を許さない)。得たものは構成データの数学的取り扱い、停止性保証、validation/policyとしての強さ。失ったものは命令的な計算ロジックの抽象化と命令型プログラマの直感との整合。
Dhall
公式: https://dhall-lang.org/ / https://github.com/dhall-lang/dhall-lang
記述言語の位置づけ:外部DSL。"JSON + functions + types + imports" を標榜する純粋関数型 + 全域(total)な構成記述言語。Turing不完全("Dhall is not Turing-complete. Evaluation always terminates, no exceptions")、全域関数型(System F-ω + α拡張、Haskellに近い表現力で再帰は禁止、代わりに Natural/fold 等の組込み再帰子のみ)、純粋(評価結果はnormal formにreduceされる)、強い静的型付け。
抽象化機構:
| 単位 | 構文 | 役割 |
|---|---|---|
| lambda | λ(x: T) → expr | 関数値(一級) |
| let | let x = ... in ... | 局所束縛 |
| record | { name = "alice", age = 30 } | 値の集約 |
| union | < Some : Text | None > | 直和型 |
| import | ./foo.dhall / https://... | 別module読み込み |
3自由度:
- 抽象化の追加 = 規約に委ねる(全域性で制限)。ラムダで関数定義可、高階関数あり(関数を引数にも戻り値にもできる)、record / union型でデータ抽象化可。ただし再帰禁止で、ループは組込み
List/fold,Natural/fold等の再帰子経由 - 複数量の制御 = 言語が決める + 組込み再帰子。専用ループ構文はなく、
List/build,List/map,Natural/fold等の組込み関数を組み合わせる。CUEとKCLの中間で、comprehensionのような専用構文ではなく組込み関数(高階関数)で表現する - 実装の切替 = 言語が決める。import (
./foo.dhall,https://example.com/foo.dhall,env:HOME)はcompile-timeに静的解決され、hashによるintegrity checkが可能。動的なimport(実行時にURLを組み立てる)は不可
2×2配置:外部DSL × ハイブリッド(Pkl / KCLと類似象限)。
設計判断: 「Turing不完全 + 純粋関数型 + 静的import + 暗号学的hash」。importのhash integrity (let pkg = https://example.com/pkg.dhall sha256:abc...)で改竄検出が可能。normal formにより、どんなに抽象化を重ねても最終的には関数もimportも解決した形に到達する。得たものは停止性、再現性、構成データの数学的厳格さ。失ったものは任意再帰の表現力、ストリーミング処理、部分的なlazy評価。CUEと並ぶ純粋実装だが、CUEがunification中心なのに対しDhallはlambda calculus + importというHaskell的な系譜。
Jsonnet
記述言語の位置づけ:外部DSL (JSON拡張)。Google由来のdata templating languageで、"Any JSON document is a valid Jsonnet program" — JSONの上位互換。評価モデルはlazy("variable initializers are not evaluated until the variable is used")、純粋関数型、ただし error 文で停止可能なため部分的にnon-total。
抽象化機構:
| 単位 | 構文 | 役割 |
|---|---|---|
| function | local my_function(x, y=10) = x + y; | 関数定義 |
| lambda | (function(x) x * x)(5) | 関数値 |
| object | { name: "alice", greet():: "hi" } | レコード |
mixin / + | Base + { f: 5 }, Base { f: 5 } | 合成 |
| import | local lib = import 'lib.libsonnet'; | 別ファイル読み込み |
オブジェクト合成はmixin型で、super で基底参照、self 後期束縛、:: でhidden field、+: でdeep merge。
3自由度:
- 抽象化の追加 = 規約に委ねる。ユーザ定義関数 / ラムダあり、高階関数あり("Functions are first class citizens")、mixin合成によるオブジェクト拡張・継承的な再利用
- 複数量の制御 = 言語が決める(comprehension中心)。list comprehension (
[x + 3 for x in arr])、object comprehension ({['f' + x]: true for x in arr if x % 2 == 0})、ネスト可。ステートメントレベルのループはない - 実装の切替 = 言語が決める。importのpathは文字列リテラルで、変数や式での動的指定は不可
2×2配置:外部DSL × ハイブリッド(Pkl / KCL / Dhallと同じパターン)。
設計判断: 「JSON互換 + lazy + 純粋関数型 + mixin合成」。既存JSONツールチェーンとの相互運用が容易で学習コストが低い。mixin合成はクラス継承ではなく + 演算子による合成(CUEのunificationに似るが + は非可換で右辺が勝つ)。得たものは既存JSON設定の拡張パスとしての自然さ、lazy評価による柔軟な参照解決、シンプルなmixin。失ったものは静的型(型システムなし、CUE / Pkl / KCLよりruntime errorが多い)と停止性保証。
Nickel (Tweag)
記述言語の位置づけ:外部DSL。Tweagが開発した構成記述向けの汎用関数型言語で、Nix / Dhall / CUEの系譜。lazy(Nix的)、純粋関数型、opt-in静的型 + ランタイムcontracts(型は付けても付けなくてもよい。contractsは値検証)、CUE的なmerge演算子 &。
抽象化機構:
| 単位 | 構文 | 役割 |
|---|---|---|
| function | fun a b => a + b (curried) | 関数値(一級) |
| let | let add1 = add 1 in add1 2 | 局所束縛、部分適用 |
| record | {a = 1, b.c = 2} | レコード値 |
| enum / match | match { 'Some x => x, 'None => 0 } | 直和型 |
| type / contract | 5 : Number (型) / 5 | Number (contract) | 静的検査 / 動的検証 |
| import | import "file.ncl" | 別ファイル読み込み |
merge & | {foo = 1} & {bar = 2} | レコード合成 |
mergeにはpriority annotation (default, force, priority N)があり、衝突解消ルールを明示できる。
3自由度:
- 抽象化の追加 = 規約に委ねる。
funで関数定義、curried syntax、高階関数あり(部分適用let add1 = add 1が自然に動く)、record / enumで構造的データ、contractsで「値が満たすべき制約」をユーザ定義可 - 複数量の制御 = 規約に委ねる。専用ループ構文はなく、array操作(
[1] @ [2, 3]、組込みのarray.map等)と関数合成で表現する - 実装の切替 = 言語が決める。
import "file.ncl"のpathは文字列リテラル。NICKEL_IMPORT_PATH環境変数でbase pathは変えられるが、import文自体は静的
2×2配置:外部DSL × ハイブリッド。ただし複数量制御も「規約に委ねる」寄り(専用comprehension構文を持たず関数で扱う)で、5DSLの中ではより規約寄りの傾きを持つ。
設計判断: 「lazy + opt-in型 + contracts + merge」。opt-in型は必要な箇所に付けるgradual typing的設計で、contractsは静的型では表現しづらい制約(range、正規表現マッチ等)をruntime validationで表現する。mergeはCUEのunificationに近いが、衝突時の解決ルールを明示する。得たものは段階的に型を導入できる柔軟性、contractsによる動的検証の組み合わせ、mergeによる分散構成の合成。失ったものは静的型強制と停止性保証(Turing完全と思われる)。
cdktf (CDK for Terraform)
公式: https://developer.hashicorp.com/terraform/cdktf
記述言語の位置づけ:汎用言語。TypeScript / Python / Java / C# / Go (CDKと同じくjsiiベース)。コードを実行するとTerraform JSONをsynthする。CDKとの違いは、synth先(CFN template vs Terraform JSON)、対象provider(AWS専用vs Terraform Registry全部)、Constructライブラリの厚み(AWS L2/L3のような厚い抽象は提供されない)。
抽象化機構:
| 単位 | 構文 | 役割 |
|---|---|---|
| App | new App() | 複数Stackの最上位 |
| TerraformStack | class MyStack extends TerraformStack | デプロイ単位(state単位) |
| Construct | class MyComponent extends Construct | 再利用単位 |
| Resource | new aws.s3.S3Bucket(this, 'b', {...}) | 個別リソース |
| TerraformModule | new TerraformHclModule(this, ...) | 既存Terraform moduleの埋め込み |
constructsライブラリはCDKと共有。
3自由度:
- 抽象化の追加 = 規約に委ねる。ホスト言語の関数 / クラス / 継承で自由。CDKと全く同じ
- 複数量の制御 = 規約に委ねる + 言語が決める のハイブリッド。ホスト言語の
for,map,filterで自由に書けるが、Terraformプリミティブのfor_each/countも使える。リソースにforEach/countプロパティを設定するとTerraformがruntimeで展開する形にも書ける。つまり「ホスト言語側で展開」と「Terraform側で展開」の2経路が選べる - 実装の切替 = 規約に委ねる。ホスト言語のクラス選択 /
if分岐で自由。TerraformHclModuleのsource指定は文字列だが、ホスト言語側で動的に組み立てた文字列を渡せる。synth時 = ホスト言語実行時 = 動的に決まってよい、Terraform apply時 = もう静的という2段階で、実質ホスト言語による動的選択が可能
2×2配置:汎用言語(ベタ書き) × 規約に委ねる。CDKと同じ象限。ただし「複数量の制御」で2経路を選べる点が特徴。
設計判断: 「CDKと同じ汎用言語ベース、synth先だけTerraform」。得たものはTerraformエコシステム(provider / state backend / 既存module)を享受しつつ汎用言語で書けること、CDK経験者の移行のしやすさ。失ったものはCDKのAWS L2/L3のような厚い抽象、およびTerraform JSONという中間表現の存在によるdebugの二重化(synth結果とplan結果を両方見る必要)。現実のツールは複数の制御層を併存させるケースがあることを示す例。
cdk8s
記述言語の位置づけ:汎用言語。TypeScript / Python / Java / Go (jsiiベース)。コードを実行するとKubernetes YAML manifestsをsynthする。stateなし(synth結果のYAMLを kubectl apply するだけで、stateはKubernetes API serverが持つ)。
抽象化機構:
| 単位 | 構文 | 役割 |
|---|---|---|
| App | new App() | 複数Chartの最上位 |
| Chart | class MyChart extends Chart | YAML manifestの出力単位 |
| Construct | class MyComponent extends Construct | 再利用単位 |
| ApiObject | new k.KubeDeployment(this, ...) | 個別リソース |
cdk8s-plusライブラリがDeployment / Service / ConfigMap / Pod / RBAC / Ingress等の高レベル抽象(CDKのL2相当)を提供する。
3自由度:抽象化の追加・複数量の制御・実装の切替すべて規約に委ねる。Kubernetes側にTerraform for_each のような展開機構はない(Helmの range は別レイヤ)ので、cdktfのような「2経路」もなく、ホスト言語1経路で全部扱う。
2×2配置:汎用言語(ベタ書き) × 規約に委ねる。この象限の純粋形。
設計判断: 「CDKと同じ設計をKubernetesに転用」。state管理を持たずKubernetes API serverに委ねる。既存K8s manifestとの互換性があり、Helm chartや生YAMLから段階的に移行できる。得たものは汎用言語の抽象化機構、type safety、IDE統合。失ったものはHelmのようなtemplate配布エコシステム(cdk8s chartはnpm/PyPI経由)とKubernetesコミュニティ主流との距離。
Crossplane Compositions
公式: https://docs.crossplane.io/v2.2/composition/compositions/
記述言語の位置づけ: 外部DSL (YAML) + functionsレイヤ(Go / KCL / Python等) の二層構造。CompositionそのものはKubernetes CRD (apiVersion: apiextensions.crossplane.io/v1, kind: Composition)としてYAMLで定義されるが、Pipeline modeのfunctionsが呼べて、そのfunctionsは任意の言語で実装可能(function-patch-and-transform / function-kcl / function-python等)。
抽象化機構:
| 単位 | 構文 | 役割 |
|---|---|---|
| CompositeResourceDefinition (XRD) | YAML CRD | ユーザ向けAPIの型定義 |
| Composite Resource (XR) | YAML | XRDを実体化したインスタンス |
| Composition | YAML | XRから実リソース群を生成するrecipe |
| Function | コード(Go/KCL/Python/...) | パイプラインのステップ |
| Composed Resource | YAML (関数出力) | 実際に作られるリソース |
Pipeline mode:
spec:
mode: Pipeline
pipeline:
- step: patch-and-transform
functionRef:
name: function-patch-and-transform
input: ...
- step: kcl-templating
functionRef:
name: function-kcl
input: ...
各functionは前段の出力(composed resourcesの集合)を受け取り、変換して次段に渡す。
3自由度:
- 抽象化の追加 = ハイブリッド(二層)。YAML層は抽象化機構なし(関数定義もクラスもなく宣言のみ)。function層は任意言語で書けるので、function-kclならKCLの高階関数とschema、function-go-templatingならGo template、function-pythonならPythonが使える
- 複数量の制御 = function経由。YAML層に直接の
for/range構文はない。pipeline自体は静的な配列("Crossplane calls them all. It calls them in the order they appear in the pipeline.")で、量の制御はfunction内部の関心事 - 実装の切替 = ハイブリッド。functionの選択は
functionRef.nameのリテラル文字列で動的に変えられない。しかしCompositionの選択はComposition Selector (label match)で動的に選ばれる — XR側でlabelを動的に付けることで「どのCompositionが選ばれるか」を実行時に変えられる。Terraformのsourceリテラル制約より柔軟
2×2配置:外部DSL (YAML) × 多層で、単純2×2に収まらない。最も近いのは「外部DSL × 言語が決める(YAML層) + 任意言語 × 規約に委ねる(function層)」のハイブリッド。
設計判断: 「Kubernetes API上に構築された二層IaC」。YAML層は宣言的に閉じている(kubectlで操作、kustomizeやHelmと組み合わせ可能)一方、function層に拡張性を全部押し付ける(functionはOCIイメージとして配布、複数言語で書ける)。Composition SelectorはXRのlabelに応じて適用Compositionを動的に選択できるため、環境別(dev/staging/prod)の使い分けが可能になる — これはTerraform / Bicep / Carinaにはない動的選択軸。得たものはKubernetes API上の宣言性、functionによる任意言語拡張、Composition Selectorによる環境切替。失ったものは全体像の見通し(YAML + functionコード + XRDの三者を読む必要)、デバッグの複雑化、type safetyの弱さ。「拡張ポイントを言語境界で開ける」という第三の選択肢を示す例で、PulumiのMLCも類似の発想。
Helm
公式: https://helm.sh/docs/chart_template_guide/
記述言語の位置づけ:外部DSL (Go templateにSprig関数ライブラリを足したもの)。YAML manifestに {{ ... }} プレースホルダを埋め込むtemplateベースで、コンパイル先はKubernetes manifest。chart利用者は values.yaml でtemplateを埋める。
抽象化機構:
| 単位 | 構文 | 役割 |
|---|---|---|
| Chart | ディレクトリ単位 | 配布単位 |
| template | templates/*.yaml | manifestテンプレート |
| helper | _helpers.tpl の define / template | 再利用可能なtemplate片 |
| subchart | charts/ ディレクトリ + Chart.yaml のdependencies | 子chart |
| values.yaml | YAML | パラメタ注入 |
3自由度:
- 抽象化の追加 = 言語が決める。
defineでnamed templateを定義しtemplate/includeで呼び出せるが、関数値は一級ではない(Go templateの関数は事前登録されたものを呼ぶだけで、変数に入れる/引数で渡すはできない)。高階関数なし - 複数量の制御 = 言語が決める(Go templateの
range)。{{ range .Values.pizzaToppings }} ... {{ end }}でリスト/マップをイテレート、if/else、with (scope)と組み合わせ可能 - 実装の切替 = 言語が決める(dependenciesリテラル)。subchartは
Chart.yamlのdependenciesでname / version / repositoryをリテラル文字列で静的に名前指定する。ただしvalues.yamlでsubchartの有効/無効を切り替えられる(condition,tags)。「どのchartを使うか」は静的、「使うかどうか」は動的 — Bicepのmoduleのif条件と同質
2×2配置:外部DSL × 言語が決める。Terraform / Carina / Bicep / CUEと同じ象限。
設計判断: 「Go template + YAML mix-in」。values.yamlの分離はchart開発者と利用者の責任分界(chartは機能、valuesは設定)を作る。subchartの静的依存は、Helm chartがartifactとして配布されるため依存がparse時に決定的でないと不便だという要請から来る。得たものはKubernetes設定のwidely-adopted標準、エコシステム(Helm Hub, Artifact Hub)の充実、values.yamlによる設定/機能の分離。失ったものは静的型(template内の式は型なし)、関数の自由度、デバッグの難しさ。typedでない点で同象限の他ツールと異なる。template engine系DSL (Go template, Jinja2等)は「文字列置換 + 限定的な制御フロー」を共通骨格とし、抽象化機構が薄い。