YAMLの重複記述を減らしDRYを保つためにエイリアス機能を使う
はじめに
僕が YAML ファイルを触りだしたのは、Ansible を使い始めてからでした。
そこから Docker Composes 、 Kubernetes ともはや YAML を触ることがないシステムはない状況となりました。
AWS や GCP 、 Azure といったクラウド環境でも YAML ファイルで設定するリソースは非常に多いですし、Rest API のインターフェイス定義として OpenAPI を YAML で記述します。
YAML ファイルの重要性が高まるにつれて、ファイルの内容はどんどん肥大化する傾向にあります。
同時に 1 つの YAML ファイルの中に記述する内容が重複することも多く(Kubernetes マニフェストのラベル名など)、なんとか DRY に記述できないかと悩むことがあります。
重複記述を回避するために "エイリアス" を使う
そこで有効なのが YAML の標準機能として提供されている エイリアス 機能です。
現在担当してるシステムのローカル開発環境は Docker Compose を使って構築していますが、エイリアス機能を使って docker-compose.yaml
ファイルの簡素化を行っています。
それでは、エイリアス機能について見ていきましょう。
エイリアス(別名)機能とは?
YAML のエイリアス機能とは、 単一の値 か ブロック(YAML のツリー構造) のどちらかに名前をつけて、あとから参照するための仕組みになります。
エイリアス機能を利用した例
値にエイリアスを設定し、参照する
値に名前をつけるというのが最もシンプルな使い方です。
&エイリアス名
で名前をつけ、 *エイリアス名
で値を参照します。
hello: &hello "hello"
greeting:
audience: "world"
hello: *hello # 'hello' という文字列となります
new_greeting:
audience: "room"
hello: *hello # 'hello' という文字列となります
正しく値が参照できているかを確認するために、 yq というコマンドラインから YAML ファイルを操作するツールを使ってみます。
yq
には YAML ファイル読み込み JSON フォーマットで出力する機能があるので、これで変換してみます。
# 先程のYAMLファイルを sample.yaml という名前で保存しておきます
$ yq e --tojson sample.yaml
{
"hello": "hello",
"greeting": {
"audience": "world",
"hello": "hello"
},
"new_greeting": {
"audience": "room",
"hello": "hello"
}
}
*hello
と記述したノードの値が 'hello'
になっているのがわかります。
ブロックにエイリアスを設定し、参照する
「値」に名前をつけるだけでなく、データ構造である ブロック に対して名前をつけることもできます。
これを使えば更に重複した記述を減らすことができます。
foo:
bar: &bar
qux: "quxqux"
baz: "bazbaz"
greeting:
audience: "world"
bar: *bar # greeting.bar は foo.bar と同義。greeting.bar.baz の値は 'bazbaz' となります。
yq
で JSON に変換した結果は以下のとおりです。
# 先程のYAMLファイルを sample.yaml という名前で保存しておきます
$ yq e --tojson sample.yaml
{
"foo": {
"bar": {
"qux": "quxqux",
"baz": "bazbaz"
}
},
"greeting": {
"audience": "world",
"bar": {
"qux": "quxqux",
"baz": "bazbaz"
}
}
}
ブロックにエイリアスを設定し、参照し、一部を書き換える
「殆どのブロックを参照したいが、一部だけ異なる値にしたい。これができないのであればしょうがないのでコピペしようか。」
という悩みに対処できます。
エイリアスでブロックを参照しつつ、一部を書き換えることができます。
bar: &bar
qux: "quxqux"
baz: "bazbaz"
greeting:
audience: "world"
bar:
<<: *bar # greeting.bar.qux の値は 'quxqux' となります。
baz: "notbaz" # greeting.bar.baz は引き継いだ値ではなく 'notbaz' となります。
yq
で JSON に変換した結果は以下のとおりです。
# 先程のYAMLファイルを sample.yaml という名前で保存しておきます
$ yq e --tojson sample.yaml
{
"bar": {
"qux": "quxqux",
"baz": "bazbaz"
},
"greeting": {
"audience": "world",
"bar": {
"qux": "quxqux",
"baz": "notbaz"
}
}
}
ブロックにエイリアスを設定し、参照し、一部を書き換えたものにエイリアスを設定
タイトルではもうなんのこっちゃですね。
こんなこともできるよ、というレベルの話ですが、あまりやりすぎると可読性が下がります。
使うことは少ないでしょう。
bar: &bar
qux: "quxqux"
baz: "bazbaz"
greeting:
audience: "world"
bar: &newalias
<<: *bar
baz: "notbaz"
new_greeting:
audience: "room"
bar: *newalias # new_greeting.bar.baz の値は 'notbaz' となります。
yq
で JSON に変換した結果は以下のとおりです。
# 先程のYAMLファイルを sample.yaml という名前で保存しておきます
$ yq e --tojson sample.yaml
{
"bar": {
"qux": "quxqux",
"baz": "bazbaz"
},
"greeting": {
"audience": "world",
"bar": {
"qux": "quxqux",
"baz": "notbaz"
}
},
"new_greeting": {
"audience": "room",
"bar": {
"qux": "quxqux",
"baz": "notbaz"
}
}
}
ひとこと
実際に運用で適用したのは "Ansible" と "Docker Compose" だけですが、 YAML の仕様として定義されている機能 ですので "Kubernetes" のマニフェストファイルにも利用できるはずです。
特に Kubernetes のマニフェストファイルはラベル名などの重複が多いので、うまく活用すれば軽量化、メンテナンス性の向上につながります。
ディスカッション
コメント一覧
まだ、コメントがありません