YAMLとは?人間に優しいデータ言語
YAMLをわかりやすく解説
YAMLの正式名称は「YAML Ain't Markup Language」です。これは「YAMLはマークアップ言語ではない」と皮肉を込めて示す再帰的な頭字語です。HTMLのようにドキュメントをマークアップするためのものではありません。代わりに、YAMLは、マニュアルを別のタブで開くことなく、人間が読んで編集できるように設計されたデータシリアライズ形式です。2001年にClark Evans、Ingy döt Net、Oren Ben-Kikiによって作成されて以来、Kubernetes、Ansible、GitHub Actions、Docker Compose、Ruby on Railsなどの重要なツールのデフォルト設定言語となっています。 YAMLの核となるのは、キーと値のペアやリストのようなデータ構造をインデントを使って表現することです。山括弧や中括弧は使いません。簡単なDocker Composeファイルを見れば、これがよくわかります。 version: '3.9' services: web: image: nginx:latest ports: - '80:80' これと等価なJSONと比較すると、その魅力は明らかです。カンマも、キーを囲む引用符も、閉じ括弧の不一致もありません。その代償として、インデントが非常に重要になります。たった1スペースでも間違えると、ファイルがパースに失敗したり、さらに悪いことに、気づかないうちに全く別の意味になってしまうこともあります。読みやすさと厳格な構造というこの緊張関係が、YAMLの体験を定義しています。これを使いこなすことが、フォーマットをうまく活用する鍵となります。
YAMLのデータ構造:3つの構成要素
すべてのYAMLドキュメントは、スカラー、シーケンス、マッピングという同じ基本的な要素で構成されています。 スカラーとは、文字列、数値、真偽値、nullのような単一の値のことです。YAMLは型の推論が賢く、ほとんどの場合に役立ちますが、時には裏目に出ることもあります。古いYAML 1.1標準(PyYAMLやその他のレガシーツールで今も使われています)では、文字列「yes」は真偽値`true`としてパースされます。しかし、YAML 1.2では、これを正しくプレーンな文字列として扱います。このバージョンギャップが実際のプロダクション環境でバグを引き起こしてきたため、使用しているツールがどのパーサーを使っているかを確実に知っておく必要があります。 シーケンスは単なる順序付きリストです。先頭にダッシュとスペースを付けて記述します。 fruits: - apple - banana - mango マッピングはキーと値のペアの集合であり、設定ファイルで最も頻繁に目にするものです。マッピングは任意の深さまでネストでき、構造は一貫したインデントによってのみ定義されます。YAMLの仕様はこれについて明確です。タブではなく、必ずスペースを使用しなければなりません。 YAMLは、繰り返しを減らすための強力な機能も提供しています。アンカー(&)とエイリアス(*)を使うと、データのブロックを一度定義し、他の場所で再利用できます。これは、複数のジョブがまったく同じ環境変数ブロックを共有するCI/CDパイプラインでは非常に役立ちます。長い文字列の場合、YAMLには2つのブロックスタイルがあります。リテラルブロックスカラー(|)は改行を正確に保持し、折りたたみブロックスカラー(>)は改行をスペースにまとめます。これは、実際のコマンドに改行を追加せずに、ファイル内でコマンドを読みやすく保ちたい長いシェルコマンドに最適です。
YAMLが実際に使われている場所
YAMLはインフラストラクチャと開発者ツールの世界でその真価を発揮します。数字がそれを物語っています。2024年現在、GitHub Actionsは毎日1億回以上のワークフローを実行しており、そのすべてがプロジェクトの.github/workflowsディレクトリにある.ymlファイルによって動かされています。ほとんどのクラウドネイティブアプリケーションのエンジンであるKubernetesは、デプロイメント、サービス、ConfigMapなど、あらゆるリソースの定義にYAMLを使用しています。典型的なマイクロサービスアプリケーションでは、簡単に数百ものYAMLファイルが蓄積されることがあります。 Red Hatによると25,000以上の組織で使用されているIT自動化ツールであるAnsibleは、そのすべてのプレイブックにYAMLを使用しています。Ansibleプレイブックの標準的なタスクは次のようになります。 - name: Install nginx ansible.builtin.package: name: nginx state: present DevOpsの領域を超えて、Jekyllのような静的サイトジェネレーターでもYAMLが使われています。JekyllはYAMLフロントマターを使ってMarkdownファイルにメタデータを保存します。HoppscotchやInsomniaのようなAPIテストツールは、環境設定にそれを使用します。データサイエンスのパイプラインでさえ、DVC(Data Version Control)のようなツールはYAMLファイルで実験パラメーターを追跡します。 YAMLがあまり見られない場所の一つは、Webサービス間のデータ交換です。REST APIは、リクエストボディとレスポンスボディにほぼ例外なくJSONを使用します。なぜでしょうか?JSONパーサーはすべてのブラウザに組み込まれており、そのフォーマットの厳格さは曖昧さの余地がないからです。これが重要な区別です。YAMLはディスク上のファイルを人間が編集するためのものであり、JSONはネットワーク経由でデータを渡すマシン間のものです。このシンプルなルールを覚えておくことで、どちらのフォーマットを選ぶべきかという多くの混乱を防ぐことができます。
YAML vs. JSON vs. TOML:適切なフォーマットの選び方
設定フォーマットを選択する際、通常はYAML、JSON、TOMLのいずれかを選びます。それぞれに明確な個性があります。 JSON(JavaScript Object Notation)は最も厳格です。すべての文字列は引用符で囲む必要があり、すべてのリストとオブジェクトは明示的に閉じる必要があり、コメントのサポートはありません。この最後の点は、設定に注釈を付けたい開発者にとっては大きな不満の種です。しかし、JSONの強みは、その厳格で曖昧さのないパースにあります。2つの準拠パーサーは、同じ入力から同一のデータ構造を生成します。一般的な設定では、ファイルサイズは通常YAMLと同程度です。 TOML(Tom's Obvious, Minimal Language)は、JSONのコメントの欠如とYAMLの扱いにくい空白ルールを修正するために作成されました。INIスタイルの構文を使用し、RustのCargoパッケージマネージャーやPythonのpyproject.toml標準に採用されています。TOMLは、フラットな、または浅くネストされた設定には最適ですが、深くネストされたデータを表現する必要がある場合は、煩雑で冗長になります。 では、YAMLはどこに位置するのでしょうか?YAMLは、設定にかなりのネスト深度があり、アンカーとエイリアスを使用して繰り返しをなくせる場合、または非技術者がファイルを編集する必要がある場合に、明確な勝者となります。チームが常にインデントエラーと格闘している場合や、型推論が予期せぬ挙動を引き起こす場合(悪名高い「ノルウェー問題」のように、国コード「NO」が真偽値`false`としてパースされるなど)は、間違った選択です。 フォーマット間で変換する必要がある場合、CocoConvertは信頼性の高いYAMLからJSONへの変換とJSONからYAMLへの変換を提供します。ただし、TOMLは出力フォーマットとしてサポートしていません。YAMLからTOMLに変換する必要がある場合は、`yq`のようなコマンドラインツールを使う必要があります。このことは事前に知っておくべきです。
よくあるYAMLの間違いとその回避策
YAMLエラーの最も一般的な原因は、インデントの一貫性のなさです。CIパイプラインのデバッグに1時間も費やし、たった1つのスペースのずれを見つけた経験のある人なら、この苦痛を知っているでしょう。任意のインデント幅を受け入れるPythonとは異なり、YAMLは同じレベルの兄弟キーがまったく同じインデントを持つことを要求します。1つのファイルで2スペースと4スペースのインデントを混在させると、パースエラーが発生するか、さらに悪いことに、気づかないうちにデータ構造が再構築されてしまいます。安全な作業方法は、エディタをタブにスペースを使用するように設定し、一貫したインデントサイズを強制することです。2スペースはKubernetesとGitHub Actionsの事実上の標準です。 引用符で囲まれていない特殊文字も別の落とし穴です。コロンの後にスペースが続くのはキーと値の区切り文字なので、「http://example.com:8080」のような文字列は引用符で囲む必要があります。引用符を忘れると、パースエラーが発生します。同様に、`{`、`[`、`%`で始まる値も、YAML構文で特別な意味を持つため、引用符が必要です。 次に、型の強制変換による予期せぬ挙動があります。「no」が`false`になることはすでに述べました。しかし、先頭にゼロが付いた数値が8進整数としてパースされることをご存知でしたか?一般的なUnixファイルパーミッションである0755という値は、引用符で囲まないと10進数の493になります。日付も別の落とし穴です。引用符なしの2024-01-01は、文字列ではなく日付オブジェクトになるため、文字列を期待するツールを壊す可能性があります。 最善の防御策は、コミットする前にYAMLをLINTすることです。`yamllint`は、インデントエラー、末尾のスペース、その他の一般的な問題を検出する必須のコマンドラインツールです。CIパイプラインには必ず`yamllint`ステップを含めるべきです。簡単な一時的なチェックには、ファイルをCocoConvertのYAML-to-JSONコンバーターに貼り付けるのが優れた健全性チェックになります。結果のJSON構造が意図したものと異なる場合、YAMLに問題があります。
YAMLファイルの変換:CocoConvertでできること
CocoConvertは、最も一般的な2つの変換ニーズであるYAMLからJSONへ、JSONからYAMLへのツールを提供します。プロセスはシンプルです。コンテンツを貼り付けるか、.yamlファイルをアップロードし、ターゲット形式を選択して結果をダウンロードします。このコンバーターは、データのネストされた構造を正確に保持します。また、複数ドキュメントのYAMLファイル(ドキュメントが---で区切られているもの)も正しく処理し、それぞれを大きな配列内の個別のJSONオブジェクトに変換します。 YAMLをJSONに変換する場合、出力は標準の2スペースインデントでフォーマットされ、読みやすく、ほぼすべてのJSONツールと互換性があります。コンパクトな単一行のJSON文字列が必要な場合(環境変数用やペイロードサイズを削減するためなど)は、結果ページでminifyオプションが利用できます。 JSONからYAMLへの変換では、コンバーターは2スペースインデントを使用し、単一ドキュメントの場合はドキュメント開始マーカー(---)を省略します。入力に複数のJSONオブジェクトが含まれている場合、それぞれが`---`マーカーで区切られた個別のYAMLドキュメントになります。重要なのは、「true」や「1.0」のような文字列が真偽値、null、または数値として誤解される可能性がある場合に、自動的に文字列値を引用符で囲むため、問題が発生する心配がないことです。 制限事項について直接述べましょう。CocoConvertは変換中にYAMLコメントを保持しません。これはツールの欠陥ではなく、コメントはYAMLの正式なデータモデルの一部ではないため、パーサーによって削除されます。アンカーとエイリアスも解決されるため、最終的な出力には参照ではなく繰り返された値が含まれます。最後に、非常に大きなファイル(10 MBを超えるファイル)は無料プランではタイムアウトする可能性があります。そのような大きなジョブには、`yq`のようなコマンドラインツールがより良い選択肢です。
YAMLを使うべき時と避けるべき時
YAMLは、いくつかの条件が満たされている場合に適切なツールです。ファイルが人間によって読み書きされ、データに意味のあるネストされた構造があり、周囲のエコシステムがすでにYAMLを使用している場合です。Kubernetesのマニフェスト、CI/CDパイプライン、Ansibleプレイブックがその代表例です。これらのシナリオでJSONを使おうとすると、何のメリットもなく、ただ作業を難しくするだけです。 逆に、ファイルが常にマシンによってのみ触れられる場合、YAMLは間違った選択です。マシン間の通信には、JSONまたはMessagePackやProtocol Buffersのようなより効率的なバイナリ形式を使用してください。また、チームが常にインデントエラーに悩まされており、リンターを使う規律がない場合にも不適切な選択です。そのような状況では、TOMLのよりシンプルな構文、あるいは前処理されたJSONファイルの方が、プロダクションでのインシデントを減らすことにつながるでしょう。 PythonプロジェクトでYAMLを使用している場合、理解しておくべき重大なセキュリティリスクがあります。PyYAMLのデフォルトの`yaml.load()`関数は、YAMLファイルに埋め込まれた任意のコードを実行できます。信頼できないソースからYAMLをパースする場合は、必ず`yaml.safe_load()`を使用する必要があります。これは理論上の脆弱性ではありません。実際のサプライチェーン攻撃で悪用されてきました。 このフォーマットは20年以上前から存在しており、今後も使われ続けるでしょう。バージョン1.1の煩わしい型強制の問題のほとんどを修正したYAML 1.2仕様は、PyYAML 6.0+、js-yaml 4.x、Goのyaml.v3のようなモダンなパーサーで広くサポートされています。私の最も強い推奨事項は、YAMLに大きく依存するプロジェクトに取り組んでいる場合、1.2準拠のパーサーに移行することが、あなたが行える最も影響の大きいアップグレードだということです。設定を1行も変更することなく、繊細なバグのクラス全体を排除できます。