ansible gather_facts チューニング

前回、chefからansibleへの移行記事をまとめたが、その際にさらっとgather_factsの高速化に時間を使ったと触れた。そもそも素でansibleを使うとデフォルトで処理前段にgather_factsの処理が入る。これが何をしているかというと、各ノードのOSやスペック、ネットワーク情報などの収集だ。ansibleはサーバサイド・プッシュな動きをするので、ansible-playbookを実行すると対象ノード全てに処理を行う。gather_factsの処理も同様で対象ノード数に比例して処理時間が伸びていく。1ノードならまだしも、10ノードを超えだすとその待ち時間は耐え難いものに。chefサーバの場合はサーバプロセスが存在したので、その中でこの手の情報をキャッシュしてくれていたのだろうが、ansibleにはサーバプロセスが存在しないため、毎回律儀に各ノードから取得する羽目になる。

gather_factsで取得されるような情報は、原則的に固定値というか殆ど変化がないような値ばかりだ。それを毎回毎回時間かけて取りにいってしまうというのは無駄でしかない。序盤の検証段階では特にplaybookを回し続ける事になるので、このコストを効率化できないなら使う気なくすレベル。gather_factsをスキップさせるだけならそれほど難しくなく、playbookファイルに『gather_facts: false』と記述すればよいだけ。あるいはansible.cfg内で『gathering = implicit』となっている設定を『gathering = explicit』に変更する。これでgather_factsがデフォルト無効となる。

---
- hosts: web
  gather_facts: no
  roles:
  - web

ただし、fact情報がまったくないので、OS情報やネットワーク情報がない前提でplaybookを作ることになってしまう。これではansibleのメリットは半減する。そこでfact情報のキャッシュを考える。単純に考えれば1度取得したデータをファイルキャッシュさえしてくれれば、それが再利用可能なはずである。ansible.cfg内を覗いてみると、おあつらえ向きに『fact_caching = memory』という設定が見つかる。実行時にしか起動しないansibleプロセスでメモリキャッシュってどういう意味なのかわからないが、処理プロセス内ではキャッシュが効くということ?コメントを見ると、キャッシュ先をredisに設定出来るようだが、この程度のキャッシュを障害点増やすような方法でやる必要はなく、ファイルで充分。

ファイルで実現する方法がないかをぐぐってみると、『fact_caching = jsonfile』という設定がある事を発見。キャッシュファイルのパスとタイムアウトも併せて、以下のように設定した。

vi /etc/ansible/ansible.cfg
    :
#fact_caching = memory
fact_caching = jsonfile
fact_caching_connection = /etc/ansible/facts
fact_caching_timeout = 8640000

この設定を加えた上で、1度gather_factsを実行すると/etc/ansible/factsディレクトリ配下にfact情報をキャッシュしたjsonファイルが生成される。これさえ出来てしまえば、あとはgather_facts処理を無効化しても、playbook処理内でfact情報にアクセスする事ができる。fact情報を利用しながら、gather_factsのコストをゼロに出来る訳だ。

ついでに設定値の解説をしておく。キャッシュ先はどこでもよいが、自分は/etc/ansible配下をgit管理しているので、そこにキャッシュファイルそのものを加えてしまった。この方法であれば、git cloneさえすれば既にキャッシュファイルがある状態を実現できる。ありがちなclone後は1度キャッシュしないといけない、みたいな運用を回避出来る。また、キャッシュのタイムアウトも100日と大きい数字を指定した。この設定は環境の変更頻度にもよると思うので、お好みで。ただ、数日くらいだと大してfact情報に変化がないにもかかわらず、期限切れになる度にgather_factsを実行する必要が出てくるので自分は手間に感じた。

さて、実際の運用ではたまに起きる期限切れのときだけgather_factsを実行したり、新しいノード追加の際にgather_factsを実行したりしたくなる。そのため、対象がallとなるようなplaybookを以下のように設定した。

vi base.yml
    :
---
- hosts: all
  gather_facts: "{{ exec_gather_facts | default(false) }}"
  roles:
  - base

まず、gather_factsが不要なときは普通に動かせばよい。

ansible-playbook base.yml

キャッシュの期限切れで、gather_factsをし直すときは以下のように実行する。

ansible-playbook base.yml -e exec_gather_facts=true

ノード追加時など、任意のノードのみgather_factsを実行したいときは以下。

ansible-playbook -l ns2 base.yml -e exec_gather_facts=true

以上のような使い方でgather_factsのコストを最小限に抑える事が出来た。自分は無駄な処理が嫌いなので、こうした効率化が出来なければansibleの利用は見合わせたかもしれない。逆に言えば、こういった問題にきちんと解決方法があったからこそ、好感が持てたのかもしれない。まだまだ知らない事は多いが、これからも積極的に使っていってみたい。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)