Kubernetes YAMLをKYAMLとして(出力を)見やすく整形する方法、そしてなぜそうしたいのか

YAMLは長年Kubernetesマニフェストを書く標準的なやり方となっています。 読者のみなさんが目にしたそれぞれの例、チュートリアル、設定ファイルはYAMLで書かれています。 問題はYAMLが悪いフォーマットであるということではありません。 それはYAMLが多くの選択肢を与えてくれるということです。 そして、Kubernetesマニフェストを書くことに関して、それらの選択肢のすべてが等しくいい訳ではないということです。 いくつかの機能は、ファイルがより読みにくくなったり、使い方を間違えやすかったり、あるいは驚くべき振る舞いを引き起こしたりします。

興味深いところは、Kubernetesが実際にはそれらの特徴をほとんど必要としないということです。 KubernetesはYAMLのごく一部分に依存しているだけです。 これは簡単な質問へと繋がりました。 もしKubernetesがYAMLのごく一部分を必要とするだけなのなら、その部分を標準化して、それ以外は避けるのはどうでしょうか? 新しい設定言語を導入する代わりに、SIG CLIは、YAMLを書くために、より厳格で、より一貫した方法、すなわちKYAMLを導入しました。

KYAMLとは?

KYAMLは、KEP 5295で提案されているように、何も変更することなく既存のエコシステムによって解析できるようデザインされた、標準YAMLの厳格なサブセット(もしくは「方言」)です。 新しいフォーマットや新しいパーサーを導入しません。 YAMLを書く時に選択する範囲を狭めるだけです。 そのため、誰もが結果的に同じ選択をすることになります。

KYAMLを新しい言語というよりむしろ、合意されたスタイルの様なものとして捉えてください。 KYAMLで有効なものはYAMLでも有効です。

KYAMLはどう解決するのか

標準YAMLにはいくつかの有名な落とし穴があり、JSONにも独自の落とし穴がないわけではありません。

空白文字の扱いの厳密さ。 インデントがYAMLにおいて構造を定義します。 間違えてインデントされたファイルは、構文的には有効なままでありながら、意図したものとは異なるオブジェクトを表してしまう可能性があります。 これはHelmのようなテンプレートツールにおいて特に厄介です。そうしたツールでは、YAMLの文脈の外からインデントを操作することになるためです。

暗黙の型変換。 YAMLでは文字列のクォートは任意です。 これは便利でなくなるまでは便利に聞こえます。 文字列のように見えるいくつかの値が、警告なしで他の型に変換されます。 定番の例は"Norway Bug"です。

country: NO

標準YAMLでは、NOは文字列型の"NO"ではなく、真偽値のfalseとして解釈されます。 そしてそれが少なからぬ人々の不意を突いてきました。

JSONもその答えではありません。 JSONはコメントサポートが欠けており、末尾のカンマについて厳密で、すべてのキーをクォートする必要があります。 これらのことは、良い設定を書く体験に役立ちません。

KYAMLは、構造と型を明示的にすることでこれらすべてに対処します:

  • 構造に関して空白に依存しない
  • 暗黙の型変換が起こらないよう必ず値文字列をクォートする
  • マップと構造体には必ず{}を使用する
  • リストには必ず[]を使用する
  • JSONと違ってコメントと末尾のカンマを許可する
  • KYAMLとJSON両方が{で始まるので、一目でKYAMLとJSONを区別できるよう---ヘッダを含める

YAMLはこれを、多くの人が使う従来のブロックスタイルとは対照的に、フロースタイルと呼びます。 KYAMLはJSONとYAMLの中程に位置します。 デフォルトのYAMLよりも明示的で、JSONよりも親しみやすいのです。

こちらは比較のために両方のフォーマットで書かれた同じPodのマニフェストです。

標準YAML

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  labels:
    app: demo
spec:
  containers:
    - name: nginx
      image: nginx:1.20

KYAML

---
{
  apiVersion: "v1",
  kind: "Pod",
  metadata: {
    name: "my-pod",
    labels: {
      app: "demo",
    },
  },
  spec: {
    containers: [{
      name: "nginx",
      image: "nginx:1.20",
    }],
  },
}

ダブルクォーテーションで囲まれた文字列、マップの周りの波括弧、リストの周りの角括弧と末尾のカンマには注意してください。 追加のシンタックスは、インデントに頼る代わりにドキュメント構造を明確にします。

YAMLをKYAMLとして(出力を)見やすく整形するには

KYAML出力を得るには異なるやり方があります。

オプション1: kubectl -o kyaml

Kubernetes 1.34からkubectlはネイティブ出力フォーマットとしてKYAMLをサポートしています。

# Kubernetes 1.35+ (ベータ; デフォルトで機能が有効になっているが、まだ-o kyaml CLIパラメーターは必要とする)
kubectl get deployment my-app -o kyaml

# Kubernetes 1.34 (アルファ、オプトイン)
export KUBECTL_KYAML=true
kubectl get deployment my-app -o kyaml

出力をファイルに保存するには:

kubectl get deployment my-app -o kyaml > my-app.yaml

現在、KYAMLをデフォルト出力フォーマットにする計画はありません。 もしデフォルトでKYAMLを使用したいなら、kubercで好みのデフォルトを設定することが可能です。 更なる詳細については、kuberc documentationを確認してください。

# Kubernetes 1.36+
kubectl kuberc set --section defaults --command get --option output=kyaml

# Kubernetes 1.33–1.35 (alpha接頭辞はまだ必要です)
kubectl alpha kuberc set --section defaults --command get --option output=kyaml

オプション2: Kubernetesのyamlfmt

sigs.k8s.io/yamlは、ファイルをKYAMLに変換することが可能なyamlfmtツールを提供します。

Go経由のインストール:

go install sigs.k8s.io/yaml/yamlfmt@latest

yamlfmtをファイルに対して実行すると、KYAMLバージョンを標準出力に出力します。 ディレクトリも受け入れることができます。 その場合、ディレクトリ内の各ファイルを変換し、出力します。 そのため、変換を確実に反映させたい場合、ファイルへの出力をリダイレクトする必要があります。

yamlfmt -o=kyaml my-deployment.yaml

完全な変換の代わりに、diffを表示することもできます:

yamlfmt -o=kyaml -d my-deployment.yaml

オプション3: Googleのyamlfmt

既存のファイルを変換するために、Googleのyamlfmtは専用のkyaml formatterをv0.21.0で追加しました。 Go経由でインストールするか、releases pageからバイナリを入手してください:

go install github.com/google/yamlfmt/cmd/yamlfmt@latest

pre-commit hookとしても、CIパイプライン向けのDocker imageとしても利用可能です。

プロジェクトルートに.yamlfmt設定を追加してください:

formatter:
  type: kyaml

ファイルを変更しないで出力を事前確認してください:

yamlfmt -dry my-deployment.yaml

その後適用してください:

yamlfmt my-deployment.yaml

ディレクトリ全体を変換するには:

yamlfmt ./k8s/

kyamlフォーマッターは追加設定はなく、オプションをデフォルトフォーマッターと共有しません。 そのため、それらを混在させるとエラーになります。

利用可能なモードやフラグの詳細については、command usage docsを確認してください。

KYAMLを採用する価値はありますか?

それぞれの有効なKYAMLファイルは有効なYAMLファイルです。 そのため、KYAMLで書いたものは何であれ、既存のツール、kubectl、CIパイプラインを変更する必要はありません。 1.34以降に限らず、あらゆるバージョンのkubectlへの入力としてKYAMLを渡すことさえ可能です。 なぜなら最終的にそれはただのYAMLなのです。

KYAMLは厳密には必須ではありません。 ブロックスタイルYAMLを書き続けることができ、うまくいきます。 しかし、特にチームや大規模なリポジトリにおいて、設定のエラーを起こしにくく、より一貫性のあるものにするための意図的な選択です。

それは移行というよりも、むしろより良い習慣です。