YAML検証ツール

ブラウザ内で解析され、送信されることはありません。マニフェストにはシークレットやレジストリ、内部ホスト名が含まれることが多いためです。キーの重複やタブによるインデントは黙認せずエラーにし、複数ドキュメントはJSONの配列に変換します。

入力した内容が保存・記録・共有されることはありません。

YAMLを貼り付けて構文を検証し、JSONに変換します。方向を切り替えればJSONからYAMLへも変換できます。エラーは行と列を指して報告されます。解析はすべてブラウザ内で行われるため、シークレットを含むマニフェストが送信されることはありません。

使い方

  1. YAMLを入力欄に貼り付けます。
  2. 下部の判定を確認します。有効か、または理由と行・列が表示されます。
  3. JSONをコピーするか、「JSON → YAML」に切り替えて逆方向に変換します。

YAMLが見た目より難しい理由

YAMLの魅力は、単純なドキュメントならほとんど構文を必要としないことです。その代償として、パーサーは多くのことを推測しなければならず、YAMLのトラブルの大半は、意図とは違うものとして推測されることから生じます。

インデントは構造そのもので、スペースでなければなりません。タブは一度も許されたことがなく、タブを挿入する設定のエディタでは、どこも間違っていないように見えるのにエラーが出ます。またインデントは固定幅ではなく相対的なので、4スペースでインデントしたブロックと2スペースでインデントした兄弟要素が並ぶと、スタイルの不統一ではなく本物のエラーになります。

さらに、同じデータを何通りにも書けます。ブロック形式とフロー形式、引用符ありとなしのスカラー、nullの3通りの書き方。値がその隙間に落ちるまではどれも問題になりませんが、落ちたときには大問題になります。

暗黙の型変換の落とし穴

引用符のないスカラーは見た目の形で型が決まり、その形は人が文字列のつもりで書くものと重なっています。

典型例はバージョン番号です。引用符なしの1.10は浮動小数点数で、末尾のゼロは保持されないため1.1になり、依存関係の指定が知らないうちに別のバージョンを指してしまいます。先頭にゼロがあるものは逆方向に同じ問題を抱えます。YAML 1.2では0755は8進数のファイルモードではなく数値の755で、0o755と書く必要があります。電話番号や口座番号、IDなどの長い数字列は先頭のゼロを失い、16桁を超えると精度も失われます。

有名なのは真偽値です。YAML 1.1ではyes、no、on、off、y、nが真偽値として扱われ、これが「ノルウェー問題」の由来です。国コードの一覧にNO(ノルウェー)が含まれていると、falseを含む一覧に変わってしまいます。YAML 1.2 coreではtrueとfalseだけに絞られ、このツールもそれに従いますが、RubyのPsychや古いPyYAMLなど、今も1.1のパーサーを使うソフトウェアは多く残っています。文字列のつもりの値は引用符で囲む習慣をつけるのが安全です。

ほかにも知っておきたい点があります。nullはnull、~、または何も書かないことで表せるため、値が空のキーは空文字列ではなくnullになります。またYAML 1.1では22:22は時刻ではなく60進数として解釈されます。

複数行の文字列

ブロックスカラーの指示子は2つでほぼすべてのケースに対応でき、選び間違いは設定ファイルが壊れるよくある原因です。

パイプ(|)は改行を書いたとおりに保持します。シェルスクリプト、証明書、埋め込みの設定ファイルなど、行の構造に意味があるものにはこれを使います。大なり記号(>)は折り畳みます。単独の改行はスペースに、空行は改行になるため、ソース上では折り返したいけれど値としては1段落にしたい長い文章に向いています。

どちらも末尾の改行を制御するチョンピング指示子を付けられます。何も付けなければ改行を1つだけ残し、マイナス(-)はすべて取り除き、プラス(+)はすべて残します。トークンや鍵、コマンド引数など、末尾の改行があると受け取る側が壊れる場合はマイナスを、空白を正確に保持したい場合はプラスを使います。

内容はキーからの相対位置でインデントし、そのインデントは値から取り除かれます。他の行より深くインデントされた行は余分なスペースを保持するので、マニフェストに埋め込んだスクリプトも自分のインデント構造を保てます。

アンカー、エイリアス、マージキー

アンカーは&でノードに印を付け、エイリアスは*でそれを参照します。これにより、ドキュメント内で先に定義した値を再利用できます。CIの設定やcomposeファイルでは、ほぼ同じジョブやサービスの繰り返しをこれで取り除けます。JSONではなくYAMLで書く、数少ない本当に良い理由の1つです。

<<: *baseと書くマージキーはさらに踏み込み、アンカーを付けたマッピングのキーを現在のマッピングに取り込んで、一部だけを上書きできるようにします。ただしこれはYAML 1.2 coreの一部ではなく、別の仕様です。多くのパーサーが実装していますが、実装していないものもあるため、どこでも安全というわけではありません。

JSONに変換すると、これらはすべて必然的に消えます。エイリアスは変換前に実際の値へ展開されるので、3回参照されたアンカーは3つの参照ではなく3つのデータのコピーになります。JSONにはコメントがないので、コメントも失われます。アンカーを多用した設定をJSONに変換して戻すと、意味は同じでもずっと長いドキュメントになり、読みやすさの点では一方通行です。

引用符をいつ、どちらで使うか

引用符で囲まれていないスカラーは見た目の形から型が決まるので、これはテキストだと伝える手段が引用符です。単一引用符の中は、単一引用符2つで1つのアポストロフィを表すことを除けばすべてリテラルで、エスケープは一切処理されません。つまり 'C:\path\to' はそのままの文字列です。二重引用符はエスケープを処理するので、\n は改行になり、バックスラッシュそのものを書くには2つ重ねる必要があります。

面倒の大半を避けられる指針はこうです。既定では単一引用符を使い、エスケープシーケンスが必要なときだけ二重引用符にする。そして別の型として読まれかねないものはすべて引用符で囲む。バージョン番号、数字だけの値、0で始まる値、そして yes、no、on、off という語がこれに当たります。

値の中に現れるだけで引用が必須になる文字がいくつかあります。コロンに続く空白、空白に続くハッシュ記号、そして先頭の &、*、!、%、@、バッククォートです。波括弧や角括弧で始まる値はフロースタイルの開始と見なされるため、{{ .Values.name }} のような Jinja や Helm のテンプレートは引用符で囲まないと、パーサーに入れ子のマッピングとして読まれてしまいます。

KubernetesやCIのファイルを壊す定番のミス

まずリスト項目のインデントです。キーの下のシーケンスは字下げしてもキーと揃えても構わず、どちらも正しい書き方ですが、同じシーケンスの中の項目どうしは揃っていなければなりません。複数人が手を入れたファイルで両者が混在すると、報告される行番号がまるで別の場所を指すエラーになります。

次に複数ドキュメントのファイルです。ハイフン3つがドキュメントの区切りで、最初の1つしか読まないツールは区切り以降を黙って無視します。Kubernetesのマニフェストで Deployment だけが適用されて Service が適用されないのはこのためです。kubectl は複数ドキュメントを扱えますが、各言語の既定のロード関数を使っているスクリプトの多くは扱えず、load-all にあたる関数が必要です。

3つ目は空の値です。コロンの後に何も書かれていないキーの値は null であって、空文字列でも空のマップでもありません。そのため、設定の一部をコメントアウトしつつキーだけ残しておくと、利用する側がオブジェクトを期待している場所に null が渡ります。YAMLの上に作られたテンプレートシステムでは、null と空文字列の振る舞いはどれも異なります。

スキーマでYAMLを検証する

パーサーが教えてくれるのは、そのファイルが構文的に正しいかどうかだけです。中身に意味が通っているかを教えてくれるのはスキーマで、運用に関わるものならそちらのほうが役に立つ検査です。YAMLはJSONと同じデータモデルに写せるため、JSON Schemaをそのまま適用できます。多くのエディタがKubernetesのマニフェストやGitHub Actionsのファイルで補完やインラインのエラー表示を出せるのは、この仕組みによるものです。

パイプラインでは、汎用のスキーマより踏み込んだ専用のバリデータがあります。kubeconform は対象バージョンのKubernetes APIスキーマに照らしてマニフェストを検査し、yamllint はスタイルを強制してインデントや重複キーの問題を捕まえます。また、たいていのCIサービスは自分の設定ファイルをプッシュ前にローカルで検証する手段を用意しています。

重複キーは別途取り上げておく価値があります。ほとんどのパーサーはそれを受け入れて最後のものを採用し、先に書かれたほうを黙って捨てます。長いファイルの中で2度設定した値が無視されたように見えるのはこのためです。リンターは指摘してくれますが、言語のランタイムはたいてい何も言いません。

YAMLをテンプレートで生成するつらさ

Helm、Ansible、そして多くのCIサービスはテンプレートからYAMLを生成します。つまり、テキストのテンプレート言語で空白に敏感な形式を組み立てているわけです。結果は予想どおりです。挿入した値のインデントがずれる、複数行の文字列がブロックを潰す、条件分岐が行を消してキーだけが宙に浮く、といった具合です。

痛みを減らす習慣が2つあります。1つは適用する前にレンダリングすること。helm template や相当するドライランを実行し、出力をテンプレートではなくYAMLとして読むことです。もう1つは、テンプレートの中で空白を手で数えるのではなく、Helm の nindent のようにツールが用意しているインデント用のヘルパーを使うことです。

より根本的な代替案は、テキストからではなくデータからYAMLを生成することです。実際のプログラミング言語で構造を組み立ててシリアライズすれば、インデントはシリアライザの仕事になります。CDK8s、Pulumi、そしてCUEやDhallといった設定記述言語はまさにそのために存在しており、この方法は失敗の一分類をまるごと取り除いてくれます。

シークレット、コメント、そして変換で失われるもの

リポジトリにシークレットが紛れ込む場所として最も多いのがYAMLの設定ファイルで、たいていはコミット前に消すつもりだった値です。紛れ込ませないのはワークフローの問題で、環境変数、シークレットマネージャ、あるいは SOPS や sealed secrets による暗号化ファイルといった手段があります。すでに入り込んでいるものがないかリポジトリを一度スキャンしておく価値はあります。履歴には一度コミットされたものが残り続けることも忘れないでください。

もう1つ守る価値があるのがコメントです。多くの設定ファイルにとって唯一のドキュメントがコメントですが、JSONへの変換や、ファイルを読み込んで再度シリアライズするプログラムを通すと失われてしまいます。コメントを保ったままYAMLをその場で編集できるツールはあり、Pythonなら ruamel.yaml、コマンドラインでの編集なら yq がそれです。人が保守しているファイルをスクリプトで書き換えるなら、こうしたツールを選ぶのが正解です。

アンカーについても同じことが言えます。JSONに変換するとエイリアスはすべて実体のコピーに展開されるので、共有ブロックを軸に組み立てた設定は、はるかに長くなり、保守しやすさを支えていた構造を失った状態で返ってきます。機械にとっては同じ意味ですが、次にそのファイルを編集する人にとってはずいぶん意味が薄れます。

よくある質問

Kubernetesのマニフェストを貼り付けても安全ですか?
はい。パーサーはブラウザ内で動作し、ドキュメントがどこかへ送信されることはありません。サーバー側で処理するツールを勧めずにこのツールを用意しているのは、まさにそのためです。マニフェストにはイメージレジストリや内部ホスト名、base64のシークレットが日常的に含まれますが、それらは当サイトにもAIアシスタントにも渡りません。
バージョン番号が1.10から1.1に変わったのはなぜですか?
クォートしていない1.10は浮動小数点数であり、浮動小数点数に末尾のゼロは存在しないためです。これは実務で最も多いYAMLの落とし穴で、このツールはそれを隠しません。文字列として保ちたい場合は"1.10"とクォートしてください。ビルド番号や電話番号、先頭にゼロが付く識別子など、数値に見えて数値でない値すべてに同じことが当てはまります。
NOがfalseになる「ノルウェー問題」は残っていますか?
いいえ。それはYAML 1.1の挙動で、NO・yes・on・offがすべて真偽値として扱われていました。このツールはYAML 1.2 coreに従うため、これらは文字列のままです。先頭のゼロも変わり、0755は8進数ではなく10進数の755になります。ファイルモードを表したい場合は0o755と書いてください。
複数ドキュメントのファイルはどう扱われますか?
---で区切られたストリームは、ドキュメントごとに1要素のJSON配列へ変換されます。複数のルートドキュメントをJSONで表す方法はこれしかありません。末尾に内容のない---がある場合はnull要素にせず無視します。ほとんどのマニフェスト生成ツールが末尾に---を出力するためです。
エディタでは通るのに、なぜキーの重複がエラーになるのですか?
それを受け入れるツールは検証していないからです。YAMLはマッピングキーの重複を禁じていますが、多くのパーサーは最後の値だけを黙って残すため、最初の値が警告なく消えます。まさにそれこそが、あなたがここで見つけようとしているバグです。タブによるインデントを拒否する理由も同じで、YAMLはタブを一度も許可していません。
JSONへの変換で失われるものはありますか?
コメントとアンカーは失われます。JSONにはどちらも存在しないためです。アンカーとエイリアスは変換前に実際の値へ解決されるため、&baseと*baseは同じデータの複製2つになります。逆方向では、繰り返される値はエイリアスにせずそのまま書き出し、長い文字列を複数行に折り返すこともありません。

最終更新:

問題を報告

操作手順、期待した結果、実際の結果を記載してください。秘密情報や個人情報は削除してください。

関連ツール