1.はじめに
この記事はNTTテクノクロス Advent Calendar 2025(シリーズ1)の19日目の記事です。
こんにちは、NTTテクノクロスの小尾です。昨年、CNCF Kubernetes認定資格5種類を取得し、Kubestronautになりました。
私がKubestronautということもあり今回はKubernetes v1.34.0で追加されたKYAMLについて紹介します。
2.KYAMLとは
Kubernetes v1.34.0のアルファ版新機能としてKYAMLのサポートが追加されました。
KYAMLとは、Kubernetes向けに最適化されたYAMLのサブセットです。
つまり、これまでKubernetesのマニフェストファイルの定義には
YAMLもしくはJSONを用いていましたが、新たにKYAMLという選択肢が加わったということですね。
Kubernetes公式ブログにはKYAMLは、YAMLとJSONそれぞれが抱える課題を解決します。という記載があり期待が高まります。
YAMLやJSONと比べてKYAMLはどのようなメリットがあるのか詳しく見ていきましょう。
3.YAMLとJSONの問題点
3-1.YAMLの問題点
3-1-1.空白
YAMLの問題点として厳密な空白の定義があげられます。YAMLでは構造の階層は空白の数によって定義されます。このため空白の数が1つ違うだけで誤った構造となってしまいエラーの原因となってしまいます。皆さんも空白の入力を1つ間違えてエラーが発生したという経験はないでしょうか?
この厳密な空白の定義はHelmテンプレートファイルを作成する際にさらなる問題点となります。
HelmはGo言語で処理され、テンプレート内の予期しない改行や空白が出力されたYAMLに影響を及ぼし、結果として誤った構造に繋がりやすいです。
このため空白文字の数に注意を払い、場合によっては以下のように空白の数を明示的に指定する必要があります。
env:
{{ toYaml .Values.env | indent 8 }}
3-1-2.型変換
YAMLでは明示的に型を指定することはできません。このため、コーダーが意図していない型に変換されてしまうという問題が発生します。有名な事例がノルウェー問題です。以下のようにYAMLに記述した場合を例として考えます。
country_code: NO
ノルウェーを意味する国名コードとしてNOを設定すると、コーダーはストリング型を想定していたとしてもYAMLではブーリアン型として判断されfalseとして扱われます。このような型変換により想定外のエラー発生に繋がりやすいです。
3-2.JSONの問題点
3-2-1.コメントが書けない
JSONではコメントが書くことができません。このため複雑なマニフェストファイルを書く場合や複数人で作業する場合に、コメント以外の場所に意図を書く必要があります。これは複数人で作業をすることが多い実務においては大きな問題点となります。
3-2-2.1つのファイル内に複数のリソースを記述できない
YAMLでは1つのマニフェストファイル内であっても区切り文字を使うことによって、複数のリソース定義を設定できます。しかし、JSONではこのような記述はできないので、1つのマニフェストファイルにつき1つのリソース定義である必要があります。このためJSONを採用するとマニフェストファイル数の増加を招くという問題点があります。
3-2-2.Kubernetesの多くのツールはYAMLを前提としている
これはJSONというフォーマット自体の問題点ではありませんが、Kubernetesと組み合わせて使われることが多いツールは基本的にYAMLでの使用を前提として設計されています。例えばHelmでは最終的にYAMLファイルを出力するという前提があります。
4.KYAMLについて
4-1.KYAMLのメリット
4-1-1. {}と[]、そしてカンマで構造を定義する。
KYAMLではYAMLのように空白で構造を定義するのではなくJSONのように{}と[]で構造を定義します。そして文末ではカンマを記述します。このため、YAMLのように空白の数に細心の注意を払いながら構造を作るように記述する必要はありません。
4-1-2. ストリング型を指定する場合は二重引用符を使う
意図していない型変換を防ぐためにストリング型として扱ってほしい値は二重引用符で囲む必要があります。これによりノルウェー問題のように意図しない型変換を発生を防げます。
4-2.KYAMLのデメリット
4-2-1. YAMLと比較して記述量が増える
YAMLと比較すると{}と[]と,を使う必要があるため、同義のマニフェストファイルを作成した場合でもKYAMLの方が記述量は間違いなく増加します。すべて手作業でマニフェストファイルを作成する場合、記述量増加は作業時間の増加へと繋がっていきます。
5.KYAMLを使ってみた
5-1.Kubernetes v1.34.0でKYAMLを使うには
Kubernetes v1.34.0であれば入力する際には何も設定することなくKYAMLを使えます。しかし、KYAMLでの出力を行いたい場合は以下のコマンドを実行して環境変数を設定する必要があります。今回はKYAMLでの出力も確認したいので環境変数はあらかじめ設定します。
export KUBECTL_KYAML=true
5-2.KYAMLでPodを作成してみる
Kubernetes公式ドキュメントに記載されているYAMLでのマニフェストファイルを例とします。極めてシンプルなPodを作成するマニフェストファイルです。
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
KYAMLにするとこのようになります。
{
apiVersion: "v1",
kind: "Pod",
metadata: {
name: "nginx",
},
spec: {
containers: [
{
name: "nginx",
image: "nginx:1.14.2",
ports: [
{
containerPort: 80,
},
],
},
],
},
}
記述量が圧倒的に増えましたね。containerPortの値は文字列型ではないので二重引用符で括らないよう注意します。続けて実行してみます。
controlplane:~$ k apply -f simple-pod.kyaml
pod/nginx created
controlplane:~$ k get pod
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 6m34s
問題なくPodが作成できます。
5-3.KYAMLの構造を崩してみる
改行位置や空白の数を変更し、KYAMLの構造をあえて崩してみます。
{
apiVersion: "v1",
kind: "Pod",
metadata: {name: "nginx2",},spec: {
containers: [
{name: "nginx2",
image: "nginx:1.14.2",
ports: [
{containerPort: 80,},],
},
],
},}
とてつもなく見にくいですね。当然ですがYAMLだとエラーになります。
このような記述でも問題ないか確認してみます。
controlplane:~$ k apply -f simple-pod2.kyaml
pod/nginx2 created
controlplane:~$ k get pod
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 10m
nginx2 1/1 Running 0 3s
{}と[]、そしてカンマで構造がきちんと定義してあれば、このように改行位置や空白の数が崩れていても問題ありません。
5-4.KYAMLで出力してみる
YAMLでのマニフェストファイルによって作成したリソースをKYAMLで出力することが可能です。Kubernetes公式ドキュメントに記載されているYAMLでのマニフェストファイルを例とします。今度はサイドカーコンテナが存在するPodを作成するマニフェストファイルです。
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
labels:
app: myapp
spec:
replicas: 1
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: alpine:latest
command: ['sh', '-c', 'while true; do echo "logging" >> /opt/logs.txt; sleep 1; done']
volumeMounts:
- name: data
mountPath: /opt
initContainers:
- name: logshipper
image: alpine:latest
restartPolicy: Always
command: ['sh', '-c', 'tail -F /opt/logs.txt']
volumeMounts:
- name: data
mountPath: /opt
volumes:
- name: data
emptyDir: {}
このマニフェストファイルを使ってDeploymentを作成し、その後KYAMLで出力してみます。
controlplane:~$ kubectl apply -f deployment-sidecar.yaml
deployment.apps/myapp created
controlplane:~$ kubectl get deployments myapp
NAME READY UP-TO-DATE AVAILABLE AGE
myapp 1/1 1 1 34s
controlplane:~$ export KUBECTL_KYAML=true
controlplane:~$ kubectl get deployments myapp -okyaml > deployment-sidecar.kyaml
こうして作成したKYAMLファイルがこちらになります。
deployment-sidecar.kyaml
---
{
apiVersion: "apps/v1",
kind: "Deployment",
metadata: {
annotations: {
deployment.kubernetes.io/revision: "1",
kubectl.kubernetes.io/last-applied-configuration: "\
{\"apiVersion\":\"apps/v1\",\"kind\":\"Deployment\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"myapp\"},\"name\":\"myapp\",\"namespace\":\"default\"},\"spec\":{\"replicas\":1,\"selector\":{\"matchLabels\":{\"app\":\"myapp\"}},\"template\":{\"metadata\":{\"labels\":{\"app\":\"myapp\"}},\"spec\":{\"containers\":[{\"command\":[\"sh\",\"-c\",\"while true; do echo \\\"logging\\\" \\u003e\\u003e /opt/logs.txt; sleep 1; done\"],\"image\":\"alpine:latest\",\"name\":\"myapp\",\"volumeMounts\":[{\"mountPath\":\"/opt\",\"name\":\"data\"}]}],\"initContainers\":[{\"command\":[\"sh\",\"-c\",\"tail -F /opt/logs.txt\"],\"image\":\"alpine:latest\",\"name\":\"logshipper\",\"restartPolicy\":\"Always\",\"volumeMounts\":[{\"mountPath\":\"/opt\",\"name\":\"data\"}]}],\"volumes\":[{\"emptyDir\":{},\"name\":\"data\"}]}}}}\n\
",
},
creationTimestamp: "2025-12-08T06:25:56Z",
generation: 1,
labels: {
app: "myapp",
},
name: "myapp",
namespace: "default",
resourceVersion: "6438",
uid: "9798ea78-c6b7-48d7-8607-92bab998a214",
},
spec: {
progressDeadlineSeconds: 600,
replicas: 1,
revisionHistoryLimit: 10,
selector: {
matchLabels: {
app: "myapp",
},
},
strategy: {
rollingUpdate: {
maxSurge: "25%",
maxUnavailable: "25%",
},
type: "RollingUpdate",
},
template: {
metadata: {
labels: {
app: "myapp",
},
},
spec: {
containers: [{
command: [
"sh",
"-c",
"while true; do echo \"logging\" >> /opt/logs.txt; sleep 1; done",
],
image: "alpine:latest",
imagePullPolicy: "Always",
name: "myapp",
resources: {},
terminationMessagePath: "/dev/termination-log",
terminationMessagePolicy: "File",
volumeMounts: [{
mountPath: "/opt",
name: "data",
}],
}],
dnsPolicy: "ClusterFirst",
initContainers: [{
command: [
"sh",
"-c",
"tail -F /opt/logs.txt",
],
image: "alpine:latest",
imagePullPolicy: "Always",
name: "logshipper",
resources: {},
restartPolicy: "Always",
terminationMessagePath: "/dev/termination-log",
terminationMessagePolicy: "File",
volumeMounts: [{
mountPath: "/opt",
name: "data",
}],
}],
restartPolicy: "Always",
schedulerName: "default-scheduler",
securityContext: {},
terminationGracePeriodSeconds: 30,
volumes: [{
emptyDir: {},
name: "data",
}],
},
},
},
status: {
availableReplicas: 1,
conditions: [{
lastTransitionTime: "2025-12-08T06:25:59Z",
lastUpdateTime: "2025-12-08T06:25:59Z",
message: "Deployment has minimum availability.",
reason: "MinimumReplicasAvailable",
status: "True",
type: "Available",
}, {
lastTransitionTime: "2025-12-08T06:25:56Z",
lastUpdateTime: "2025-12-08T06:25:59Z",
message: "ReplicaSet \"myapp-6bd6dbf849\" has successfully progressed.",
reason: "NewReplicaSetAvailable",
status: "True",
type: "Progressing",
}],
observedGeneration: 1,
readyReplicas: 1,
replicas: 1,
updatedReplicas: 1,
},
}
YAMLによるマニフェストファイルを用いて作成したリソースであってもKYAMLでの出力が可能です。
5-5.YAML用ツールをKYAMLに使ってみる
KYAMLはあくまでもYAMLのサブセットとなるため、既存のYAMLツールが使えます。simple-pod.kyamlを対象にyamllintを使って動作を確認してみます。
controlplane:~$ yamllint simple-pod.kyaml
simple-pod.kyaml
1:1 warning missing document start "---" (document-start)
10:8 error wrong indentation: expected 8 but found 7 (indentation)
11:8 error wrong indentation: expected 8 but found 7 (indentation)
12:8 error wrong indentation: expected 8 but found 7 (indentation)
正常に動作することを確認できました。YAML向けツールもKYAMLに対して問題なく使えます。
6.まとめ
Kubernetes v1.34.0のアルファ版新機能であるKYAMLの基本的な使い方について確認しました。
まだアルファ版でありGA(一般公開)は先の話になるかと思いますが、
長い目で見れば今後KubernetesはKYAMLを使うのが主流になっていくのではないかと想像できます。
今のうちからKYAMLに慣れておくとよいかもしれませんね。