Ansibleのジョブからファイルを保存する

レッドハットの杉村です。Ansible のテクニカルサポートをしています。

テクニカルサポートは土日祝日年末年始は全員休みというわけにはいかず、平日よりは少ない体制で問い合わせに対応するようにしています。今回の土日は当番になりました。空き時間ができましたのでブログを書いています。

twitter の #ansiblejp で最近少々話題になっていたのですが、Ansible Automation Platform (AAP) のジョブからファイルを保存したいときにどうするのがいいのかというお話がありました。AAP 2 からは実行環境がコンテナになったため、以前の Ansible Tower 時代とはアーキテクチャが異なっています。

まず大前提としては、原則としてはコントローラ上にはそういうファイルを置くべきではないというのがあります。ジョブの実行時に一時的にファイルを作成することはあっても、実行が終わったら消えるというのが想定した使い方です。

コントローラでは複数のユーザがさまざまなジョブを実行します。AAP のユーザ権限を細かく決めることができるというのに、ファイルは共通して自由に読み書きできるということができてしまってはその権限設定の意味がなくなってしまうこともあります。また、コントローラが稼働するOS上にファイルを置きますと、コントローラや実行ノードが分散しているときにはスケールしないこともあります。ストレージが溢れないように監視する必要があるかもしれませんし、バックアップやリストアも求められることがあるかもしれません。基本的に手間が増えるだけでいいことがないのです。

それでもどうしてもファイルを置きたいんですという要望も確かにありまして、どうすればできるのかを少々考えてみようと思います。AAP 2.2 (RHEL 8.6 / ansible-core 2.13) で動作検証しています。

こちらに詳しく書かれていますので、あわせて参照してください。この著者の Phil Griffiths とも金曜にこの件で少々話をしたばかりです。

www.ansible.com

用意したディレクトリ

OS 上のディレクトリに置く例と、NFS に置く例を検証してみました。OS からは awx ユーザで書き込みできるようにしておいてください。NFS に置くことができれば、複数のコントローラや実行ノードがあっても、それぞれから読み書きできるようになることが期待されます。

# ls -ld /share /nfs
drwxrwxrwx. 2 root root 71  8月 21  2022 /nfs
drwxr-xr-x. 2 awx  awx   6  8月 21 18:02 /share
# mount | grep nfs
sunrpc on /var/lib/nfs/rpc_pipefs type rpc_pipefs (rw,relatime)
192.168.0.XXX:/share on /nfs type nfs4 (rw,relatime,vers=4.2,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=192.168.0.YYY,local_lock=none,addr=192.168.0.XXX)

実行したいプレイブック

ただ単にファイルを書いてみる例です。

---
- hosts: all
  gather_facts: false

  tasks:
    - name: store something into an external file (share)
      copy:
        content: "this is a pen, that is a book"
        dest: /share/result.txt
        mode: '0644'
      tags:
        - localshare

    - name: store something into an external file (NFS)
      copy:
        content: "this is a pen, that is a book"
        dest: /nfs/result.txt
        mode: '0644'
      tags:
        - nfs

コマンドラインから実行

awx ユーザで実行してみました。それぞれ意図通りファイルを書き出しています。

$ whoami
awx
$ ansible-playbook playbook.yml -i localhost, -e ansible_connection=local -t nfs

PLAY [all] ***************************************************************************************************************************************************************************************

TASK [store something into an external file (NFS)] ***********************************************************************************************************************************************
changed: [localhost]

PLAY RECAP ***************************************************************************************************************************************************************************************
localhost                  : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

$ ansible-playbook playbook.yml -i localhost, -e ansible_connection=local -t localshare

PLAY [all] ***************************************************************************************************************************************************************************************

TASK [store something into an external file (share)] *********************************************************************************************************************************************
changed: [localhost]

PLAY RECAP ***************************************************************************************************************************************************************************************
localhost                  : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

$ ls -l /nfs/ /share/
/nfs/:
合計 4
-rw-r--r--. 1 awx awx 29  8月 21 18:08 result.txt

/share/:
合計 4
-rw-r--r--. 1 awx awx 29  8月 21 18:08 result.txt

AAP から実行

プロジェクトとジョブテンプレートを作成しました。ジョブテンプレートでは実行時に tag を入力して処理を切り替えられるようにしています。

実行してみると、このように書き出すディレクトリを見つけられずにエラーになります。実行環境はコンテナの中で動いており、そこにはこのディレクトリはないからです。

Settings > Jobs setting > Paths to expose to isolated jobs の設定で、コンテナがマウントするディレクトリを設定することができます。初期値はこのようになっています。

これは podman の -v (--volume) オプションとして実行時に渡されます。このように :rw をつけて定義することで、コンテナからも読み書きできるディレクトリとして認識させることができます。

[
  "/etc/pki/ca-trust:/etc/pki/ca-trust:O",
  "/nfs:/nfs:rw",
  "/share:/share:rw",
  "/usr/share/pki:/usr/share/pki:O"
]

この設定で、ローカルディレクトリもNFSも両方とも正しく書き込むことができました。

コントローラから見ても正しく保存されていることがわかります。

$ ls -lZ /nfs /share
/nfs:
合計 4
-rw-r--r--. 1 awx awx system_u:object_r:nfs_t:s0 29  8月 21 18:27 result.txt

/share:
合計 4
-rw-r--r--. 1 awx awx system_u:object_r:container_file_t:s0:c98,c728 29  8月 21 18:27 result.txt

$ cat /share/result.txt 
this is a pen, that is a book

$ cat /nfs/result.txt 
this is a pen, that is a book

podman のオプションについては man podman-run を見てください。

設定の注意点

このようにローカルファイルをマウントしてコンテナから読ませるようにする設定は AAP 2.1.2 から導入されたのですが、2.1.x ではこのオプションには :O と :z のどちらかしか設定できないという制限があります。

  • O (大文字のO) : 一時的なディレクトリを作って読み書きし、実行が終わったら削除する
  • z (小文字のz) : SELinux のラベルを付け替える

もしこのオプションをつけないときには、安全のため :z が初期値として設定されます。NFS のディレクトリでは SELinux のラベルを付け替えることはできないため、例えば今回の例で /nfs:/nfs:z と設定するとエラーになります。:rw で使いたい場合には、AAP 2.2 を使ってください。

:O を設定したときはジョブの動作はしますが、処理が終わりますと削除されるため、後でコントローラ上のOSコマンドでディレクトリを見てもファイルは見つかりません。

他の方法

今回はローカルディレクトリにデータを書いてみるという例でしたが、他の方法としては Amazon S3 などのクラウドストレージに書く方法もあります。

docs.ansible.com

もしデータが小さいようであれば、以前紹介した fact に保存する方法も利用できます。fact は AAP のデータベース上にホストごとのデータとして保存するものです。

rheb.hatenablog.com

要件にあわせていろいろな方法を検討してみてください。

まとめ

最近いくつか話題になったファイルの書き出しについて検証してみました。AAP 2.1.2 から少しずつ改善され、2.2 ではより自由度も増しました。問い合わせを受けてサポートエンジニアから開発チームに伝えることもよくありますので、もしお困りの際はご相談ください。

Ansible Automation Platform の評価ライセンスリクエストはこちらからご利用いただけます。60日間利用できます。

www.redhat.com

Happy Automation!

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。