1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Ansible: 変数定義の中で別の変数を参照する

Last updated at Posted at 2021-09-02

はじめに

Ansibleでちょっと困ったことがあって、なんとかならないか探したり試行錯誤したりしてみました。

下記、Undocumentedな動作かも知れませんのでバージョンを明記します。
ansible-playbook 2.9.24 で試した結果になります。

変数定義の値のベタ書きがとてもつらい

Ansible、便利ですよね。roleやtaskにあわせて構造化された変数を定義しておいてansible-playbookコマンドで複数ホストに一度に設定を流し込めます。でもちょっと困ったことが起きたのです。

ふたつの環境で変数定義をある程度共有したい

ふわっとした例を書くと、たとえばDatabase serverとWeb serverの2つを用意して、互いのNICを接続するとします。実環境と仮想環境を用意して、仮想環境でテストした結果を実環境で、みたいなことを考えます。

変数定義のイメージとしてはこんなふう。

db.yml
apt:
  name: mysql-server

network_interfaces:
  - name: ens4
    ip_addresses:
     - 192.168.0.10/24
web.yml
apt:
  name: nginx

network_interfaces:
  - name: ens4
    ip_addresses:
     - 192.168.0.20/24

仮想環境が完全に実環境と同じにできればいいのですが、たとえばNICの名前が違ってたりすることは十分ありえます。たとえばこんなふうです。

環境 Database Server Web server
仮想環境 ens4 ens4
実環境 enp1f1 enp2f1

上記YAMLファイルでいうところのens4の部分を変数にして、別の場所で定義した値で置き換えたい、ということです。

変数定義の中で同一識別子を繰り返し利用する場合、まとめて書き換えたい

上で説明した内容との合わせ技ですが、変数定義の中で特定ワードが繰り返し使用されるケースがあります。たとえば、あるインタフェースにIPアドレスを設定してACLを設定してNATを設定して、のようなパターンです。(それっぽく変数定義を書いてるだけで、これを読めるroleがあるというわけではありません、ねんのため)

nathost.yml
network_interfaces:
  - name: ens4
    ip_addresses:
      - 192.168.0.10/24
acl:
  tables:
   - name: Filter
     stage: ingress
     interfaces:
      - ens4
  rules:
   - name: Rule1
     priority: 100
     l4_port: 80
     action: forward

nat:
  static:
    - global_ip: 192.168.0.10
      local_ip: 172.21.0.1
      nat_type: dnat
      interface: ens4

これらの例のようにホスト数や変数が少なくぱっと見てどこを書き換えればいいようなケースであれば、ファイルまるごとコピーして中身をちょこちょこ書き換えるのでも十分です。が、10も20もホストがある場合それぞれ50行とか100行とかある定義の中から書き換える場所を漏らさず見つけて書き換えるのはしんどいです。

YAMLアンカーを試してみた

YAMLアンカーは、ある値を定義してる箇所に &名前 のアンカーをつけて、*名前 でその値を引っ張ってくるというものです。

web.yml
liesn_if: &lif ens4

apt:
  name: nginx

network_interfaces:
  - name: *lif
    ip_addresses:
     - 192.168.0.20/24

定義してる名前の他にアンカーの名前を書くのが煩わしいですが、この例は正しく*lifens4に置き換わります。

が。

YAMLアンカーはどうやら同一ファイル内でしか機能しないようでした。つまり繰り返しの例のように何度も同じワードが出てくる場合、ファイルの先頭付近で定義しておけば後ろを書き換えなくて済むものの、別ファイルに定義をまとめたりはできないということです。

Jinja2的な参照

ネットの海を漂っても、テンプレートファイルを書いて置き換えるかtaskの中で変数を参照する例ばかり。できないのかなあとあきらめかけてたのですが、こんな書き方が変数定義の中でできるという記述が。

web.yml
liesn_if: ens4

apt:
  name: nginx

network_interfaces:
  - name: "{{ listen_if }}"
    ip_addresses:
     - 192.168.0.20/24

やってみたら、できました。しかもこれ、ファイルが分かれていても問題なく動きます。Web serverを3台建てるようなケースで下記のようにできます。

group_vars/web
apt:
  name: nginx

network_interfaces:
  - name: "{{ listen_if }}"
    ip_addresses:
     - "{{ listen_ip }}"
host_vars/web0
listen_if: ens4
listen_ip: 192.168.0.10/24
host_vars/web1
listen_if: enp3f1
listen_ip: 192.168.0.11/24
host_vars/web0
listen_if: eno1
listen_ip: 192.168.0.12/24

これは、便利……!

制約

なんでもかんでもできるわけではありません。

{% ... %} は使えない

試した限りですが、エラーになってしまいます。forとか使いたかったのですが、残念です。

キーの置き換えはできない

error.yml
# これは失敗する例
listen_if: ens4

route:
  bgp:
    neighbors:
      "{{ listen_if }}":
        remote_as: ....

置き換わらずに {{ listen_if }} というネイバーとして扱われ、最終的にエラーになります。roleが自作であれば、構造を変更して下記のように書けば置換されます。

valid.yml
listen_if: ens4

route:
  bgp:
    neighbors:
      - name: "{{ listen_if }}"
        remote_as: ....

置換結果は常に文字列

あるroleを使いVLANを設定しようとしました。変数定義は次のとおりです。

vlan.yml
second_vlan: 20

interfaces:
  - name: vlan10
    vlan_id: 10
  - name: vlan20
    vlan_id: "{{ second_vlan }}"

直接書いたときは問題ないのに、置換すると正しく動作しないことがありました。上記例でいうとvlan10は無事作成される(作成済みなら何もしない)のに、vlan20を削除するという動作になりました。

roleの中でwhen: による判定があったのですが、factsで現在設定されているvlan_idを参照すると値の型はintで、上記で置換された値はstr型だったため比較に失敗していた、というオチでした。"{{ second_vlan | int }}"と書いたりしましたが無駄でした。roleは自作だったので、数字の比較と文字列の比較両方やることでなんとか回避しました。

"{{ vars }}" は置換前の情報

デバッグのとき変数を参照するため msg: "{{ vars }}" を書いたりしますが、これで表示される内容は置換前のままです。よしあしはわかりませんが、ご注意ください。

Undocumented?

探し方が悪いのか、このようなことができることを公式ドキュメントで見つけることができませんでした。仕様として明記されてなければ、将来できなくなる可能性もありますし、どのバージョンからこれができるのかも調べてみないとわかりません。便利ですが利用にはご注意を。

さいごに

変数定義の範囲でできることは相当少ないですが、少しでもメンテナンスが楽になればと思います。

ところで、こんなことができるって皆知ってて黙ってたの? 知らなかったのぼくだけ? 早く言ってよー。って感じで、半日くらい費やしました。

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?