今回はKYAMLというKubernetes向けYAMLのサブセットについて少し調べてみました。
きっかけ
先日Kubernetes v1.34に関するページが公開されました。ここ最近のリリース情報を追いかけられてなかったので久しぶりに見ていたところ、 KYAML
という見慣れない単語を見かけました。気になって調べてみたのが今回の記事の経緯です。
KYAMLとは
KYAMLとは、Kubernetes向けのYAMLとして、KubernetesのリソースをYAML形式のファイルで管理する際の課題を軽減することが目的で作られた形式です。
※KYAMLがいつ頃に登場したかはわかりませんが、Kustomizeの2019年のPRでKYAMLに関するものがあるのは確認しました。
KYAMLでは以下のような仕様が設定されています。これらは従来の定義ファイル形式の課題を軽減するためのものです。
- マッピング(キーと値のペア)には常に
{}
を使用する。 - リスト(配列)には常に
[]
を使用する。 - 文字列の値は常にダブルクォート
""
で囲む。 - キーは、曖昧になる可能性がない限りクォートしない。
- コメント (
#
) を使用できる。 - 末尾のカンマを許容する。
KubernetesのリソースをIaCで管理する場合、おそらく現在はYAMLまたはJSON形式のファイルが最もよく使われているかと思います。しかしYAML/JSONを使うことで、いくつかの課題があります。
YAMLの課題1: 意図しない型変換
ここでは The Norway Problem
という問題を少し紹介します。これは、世界各国の国名を国名コードで順に記載すると、YAMLはノルウェーを表す NO
という文字列を False
と変換する問題です。
# YAMLファイルでこういった定義をすると countries: - GB - IE - FR - DE - NO # NOがfalseと解釈されます。 >>> from pyyaml import load >>> load(the_configuration) {'countries': ['GB', 'IE', 'FR', 'DE', False]}
このような意図しない型変換を避けるため、KYAMLでは文字列の値を常に ””
で囲むというルールを設けています。
YAMLの課題2: インデントが複雑になる
これはリソースの定義ファイルが巨大になるほど顕在化する問題です。例えばArgoCDをインストールする時に使う定義ファイルの一つを見てみると、本記事執筆時点で352行の分量があります。
ArgoCDに限らず、現状多くのOSSはYAML形式でマニフェストファイルを提供しています。YAMLはインデントの位置でデータ構造が決まるため、インデントがずれていると正常に解釈されなくなります。これは行数が増えるほど気づくのが困難になります。
KYAMLではリストには []
、マップには {}
を常に使います。この括弧内ではインデントがずれていてもデータ構造を正しく解釈するため、インデントが複雑になるという課題を部分的に軽減してくれます。
JSONの課題1: コメントアウトできない
YAMLとJSONを比較すると、コメントアウトの可否は大きな違いの一つです。コメントアウトが使えることで、コードからでは読み取るのが難しい設定の意図なども記載でき、可読性やメンテナンス性の向上に貢献します。
JSONはコメントアウトをサポートしておらず、利用するにはYAMLなど別の形式に移行するかJSONC (JSON with Comments) などの拡張形式を使う必要があります。多くのOSSの定義ファイルでYAMLが使われているのは、JSONがコメントアウトをサポートしていないことも一つの要因ではないかと考えています。
KYAMLではコメントアウトを有効にすることで、JSONの持つ課題を解消します。
JSONの課題2: 末尾カンマが許容されない
JSONでは末尾のカンマも許容されていません。この仕様があると、例えばあるオプションの定義を追加しようとしたとき、以下のような作業が必要となります。これは手動作業時のタイプミスの原因にもなりますし、自動化プログラムでファイルを扱うときのロジックも複雑になります。
またJSONファイルをGitでバージョン管理する場合も、本来差分として確認したい「設定項目の追加」だけでなく、「既存項目の末尾へのカンマ追加」という不要な差分が表示されるため、レビュアーにとってはノイズになります。
KYAMLでは末尾のカンマを許容することで、こういった課題を解消します。
KYAML形式のファイルでPodを起動する
最後に、実際にKYAML形式の定義ファイルを使ってNginx Podを起動します。クラスターは何でもよいですが、今回はAmazon EKS (Auto Mode) ver 1.33を使用しました。
[cloudshell-user@ip-10-135-0-67 ~]$ kubectl get all -A NAMESPACE NAME READY STATUS RESTARTS AGE kube-system pod/metrics-server-6545cd44cd-ns9tj 0/1 Pending 0 16s kube-system pod/metrics-server-6545cd44cd-zzzps 0/1 Pending 0 16s NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE default service/kubernetes ClusterIP 10.100.0.1 <none> 443/TCP 8m42s kube-system service/eks-extension-metrics-api ClusterIP 10.100.212.22 <none> 443/TCP 8m40s kube-system service/metrics-server ClusterIP 10.100.254.51 <none> 443/TCP 17s NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE kube-system deployment.apps/metrics-server 0/2 2 0 17s NAMESPACE NAME DESIRED CURRENT READY AGE kube-system replicaset.apps/metrics-server-6545cd44cd 2 2 0 17s [cloudshell-user@ip-10-135-0-67 ~]$
クラスターを作成後に以下のような定義ファイルを用意し、kubectl経由でデプロイします。
[cloudshell-user@ip-10-135-0-67 ~]$ cat nginx.kyaml { apiVersion: "v1", kind: "Pod", metadata: { name: "nginx-kyaml-example", labels: { app: "nginx", }, }, spec: { containers: [ { name: "nginx", image: "nginx:stable", ports: [ { containerPort: 80, }, ], }, ], }, } [cloudshell-user@ip-10-135-0-67 ~]$ kubectl apply -f nginx.kyaml pod/nginx-kyaml-example created
問題なくPodが起動することを確認できます。
[cloudshell-user@ip-10-135-0-67 ~]$ kubectl get pods -A NAMESPACE NAME READY STATUS RESTARTS AGE default nginx-kyaml-example 1/1 Running 0 54s kube-system metrics-server-6545cd44cd-ns9tj 1/1 Running 0 3m3s kube-system metrics-server-6545cd44cd-zzzps 1/1 Running 0 3m3s [cloudshell-user@ip-10-135-0-67 ~]$
なおkubectlはver 1.33時点ではkyaml形式の出力をサポートしておらず、ver 1.34でこの機能が追加される予定とのことです。
[cloudshell-user@ip-10-135-0-67 ~]$ kubectl get pods nginx-kyaml-example -okyaml error: unable to match a printer suitable for the output format "kyaml", allowed formats are: custom-columns,custom-columns-file,go-template,go-template-file,json,jsonpath,jsonpath-as-json,jsonpath-file,name,template,templatefile,wide,yaml