最近アウトプットできてなかったけど、これはAnsible Advent Calendar 2021の13日目のエントリです。
個人的にあまりなじみがないfetch
モジュールについての備忘録です。
このモジュールはcopy
モジュールの逆で、マネージドノード上のファイルをコントロールノードへコピーします。システム構築の自動化でAnsaibleを使うようなシチュエーションだと割と使用頻度が低い(個人の感想)のですが、サービスの日々の運用で、各ホスト上の例えばログファイルを1か所へ集約したい場合に本領を発揮します(当社比)。
基本の使い方
tasks: - name: fetch file ansible.builtin.fetch: src: /var/log/messages dest: /var/tmp/fetch-test
モジュールの基本機能は、リモートホストのsrc
に指定したファイルを、Ansible実行ホストのdest
で指定したディレクトリ以下にリモートホスト名/リモート上のフルパス
というパスでコピーする。
このとき、dest
のディレクトリが無くても自動で作成される。
flatパラメタ
コピー時のリモートのパスやリモートホスト名が不要で、dest
に指定したパスに直接集めたい場合は、flat
パラメタを指定する。
- name: fetch file ansible.builtin.fetch: src: /var/log/messages dest: /var/tmp/fetch-test flat: true
flat
パラメタをtrue
にすると、dest
に指定したパスが転送先になる。
この例だと、リモートの/var/log/messages
ファイルは/var/tmp/fetch-test
というファイル名で転送される。
また、
- name: fetch file ansible.builtin.fetch: src: /var/log/messages dest: /var/tmp/fetch-test/ # '/' 追加 flat: true
このようにdest
の指定をディレクトリ(末尾/
)にすると、ファイル名は維持され転送先は/var/tmp/fetch-test/messages
になる。
ただしflat
を使う場合、処理対象のホストが複数ありファイル名が同一だとすべて同じファイルで転送されるため、処理順が最後のホスト以外のファイルは上書きされてしまうので注意。
flat
を使用しつつ、ファイル名にホスト名を付与したいのであれば、マジック変数のinventory_hostname
を使うなどしてパスやファイル名がホスト毎になるように調整する。
dest: "/var/tmp/fetch-test/{{ inventory_hostname }}/"
fail_on_missingパラメタ
デフォルトの動作は、リモートホスト上にsrc
で指定したファイルが無い場合は失敗となる。
要件によって「ファイルが無い場合は何もしない(正常完了)」したい場合は、fail_on_missing
パラメタにfalse
を指定する。
(ファイルが存在しない場合以外の転送失敗なども軒並み無視するignore_errors
などを使わないよーに)
- name: fetch file ansible.builtin.fetch: src: /var/log/messages dest: /var/tmp/fetch-test fail_on_missing: false
このオプションは、古いAnsibleバージョンだとデフォルト値が逆になっているので(バージョン塩漬けのような)環境によっては動作が異なるので注意。
ディレクトリ以下を再帰的にコピーするには…
fetchモジュールには再帰コピーの機能はない1ため、自力で処理する必要がある。
ファイルリストを作成するにはfind
モジュール辺りを使ったタスクを別途用意する。
- name: create file list ansible.builtin.find: paths: /var/log recurse: true register: register_file_stat - name: fetch files ansible.builtin.fetch: src: "{{ item }}" dest: /var/tmp/fetch-test loop: "{{ register_file_stat.files | map(attribute='path') }}"
ただしこのとき、ファイル数やサイズが大量で転送に時間がかかる場合でかつ、リモートホスト上のファイルの作成や削除が頻繁に発生するような環境の場合、リスト作成時点から転送までのタイムラグによってファイルの有無が変化すると転送失敗になったりするので注意が必要。
対象ディレクトリをarchive
モジュールでファイルに固めて転送する方が良い場合もあるので、要件によって検討する。
手順は増えるが、転送後のアーカイブファイルの展開と削除も必要に応じて行う。
- name: archive files community.general.archive: path: /var/log dest: /tmp/log.tar.gz - name: fetch file ansible.builtin.fetch: src: /tmp/log.tar.gz dest: /var/tmp/fetch-test
ちなみにarchive
モジュールは普段使わないから気付かなかったけど、アーカイブ対象に変化が無かったら実行ステータスはok
になって冪等性が担保されるように見えるけど、その場合でもアーカイブファイルのタイムスタンプが更新されてしまうため、その後のタスクの冪等性を担保するのはすこし難しいかも。
(ファイルの更新は最新のcommunity.general 4.1.0でも再現)
ただ、「定期実行でそのタイミングのスナップショット的にファイル構造を丸ごと保存したい」みたいな要件の場合は、アーカイブ式の方が扱いやすいかも。
環境
$ ansible --version ansible [core 2.11.3] config file = /home/zaki/.ansible.cfg configured module search path = ['/home/zaki/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules'] ansible python module location = /home/zaki/venv/ansible4.2.0/lib64/python3.9/site-packages/ansible ansible collection location = /home/zaki/.ansible/collections:/usr/share/ansible/collections executable location = /home/zaki/venv/ansible4.2.0/bin/ansible python version = 3.9.2 (default, Mar 5 2021, 01:49:45) [GCC 8.4.1 20200928 (Red Hat 8.4.1-1)] jinja version = 3.0.1 libyaml = True
-
現状のモジュールのドキュメントに「Recursive fetching may be supported in a later release.」とそのうち実装されるかも的な記述はあるけど。↩