この記事は、Calendar for Ansible | Advent Calendar 2021 - Qiitaの23日目の記事になります。

半年前くらいからansible-runnerをansible/ansible-playbookの代替として使っているのですが、 Ansible Runnerの理解が不足していて、あまりメリットを教授できていないので、改めてAnsible Runnerに入門しようと思います。 理解を深めるにあたって、実際に環境を用意してAnsible Runnerのdemoを元に実行し、その結果を確認します。

今回の実行環境の構築方法については、こちらを参照ください。

ansible-runnerについて

“Ansible Runner is a tool and python library that helps when interfacing with Ansible directly
or as part of another system whether that be through a container image interface, as a standalone tool,
or as a Python module that can be imported.”

  1. Ansible RunnerからAnsibleを直接実行するツールであり、
  2. 別のシステムがAnsible Runnerを仲介してAnsibleを実行するためのツールでもあり、
  3. PythonがAnsible Runnerを仲介してAnsibleを実行するためのライブラリでもある

という理解です。 間違えていたら、教えていただけるととてもありがたいです。

“The goal is to provide a stable and consistent interface abstraction to Ansible.”

最終的なゴールは、Ansibleの実行を、安定して、一貫性のある抽象化されたインターフェースとして提供すること?
英語も日本語も難しい。。。

本家のドキュメントはこちら。
Githubのソースコードはここ。

実際に動かして動きを見ていく

Ansible Runnerの基本的な実行方法

ansible-runner run private_data_dir -p playbook

今回のdemo環境でのprivate_data_dirは、demoディレクトリになります。
Ansible Runnerの仕様として、単一のディレクトリを指定する必要があるようです。
そのディレクトリ配下に実行時の情報や実行結果やエラー等の情報を保存されます。(./demo/artifacts)

また、ドキュメントには"There are 3 primary ways of interacting with Runner"とあり、以下の3つが記載されています。

  1. “A standalone command line tool (ansible-runner) that can be started in the foreground or run in the background asynchronously”
  2. “A reference container image that can be used as a base for your own images and will work as a standalone container or running in Openshift or Kubernetes”
  3. “A python module - library interface”

本記事では、“A python module - library interface"を覗いた2つについて、実際に動かしつつ動きを見ていきます。

1つ目スタンドアロンツールとして実行する方法

“can be started in the foreground or run in the background asynchronously” とあるので、

  • フォアグランドで実行する方法
  • 非同期でバックグランドで実行する方法

があるようです。

ドキュメントは、この部分

Ansible Runnerのリポジトリをクローンしている前提で進めていきます。 リポジトリ直下にdemoのディレクトリがあります。 カレントのディレクトリは以下の通りです。

$ tree ./demo/ -L 2
./demo/
├── artifacts
│   ├── 85c14b0a-7d54-4def-9066-969522d2408c
│   └── fb8b36b4-e1c4-472f-9430-be27d34591df
├── daemon.log
├── env
│   ├── envvars
│   ├── extravars
│   ├── passwords
│   ├── settings
│   └── ssh_key
├── inventory
│   └── hosts
└── project
    ├── roles
    └── test.yml

7 directories, 8 files

フォアグランドで実行

ansible-runner run ./demo -p test.yml

私はいつもこの方法で、ansible-playbookの代替として使用しています。

ansibleコマンドの代替として使う場合は、

ansible-runner run ./demo --hosts localhost -m ping

バックグランドで実行

バックグランドで実行するには、“run"のオプションの代わりに"start”,“stop”,“is-alive"を使うようです。

  • バックグランド処理の開始
ansible-runner start ./demo -p test.yml
  • 停止
ansible-runner stop ./demo -p test.yml
  • ステータス確認
ansible-runner is-alive ./demo -p test.yml

使いどころは、ドキュメントに非同期とあるのですが、個人的にはちょっと長めのデプロイ作業とかですかね。 sleepのコマンドを実行して、試してみます。

test.ymlに以下のタスクを追加します。

+    - name: sleep
+      command: sleep 100

start後、is-alive実行しても何も出力されません。 ただ、プロセスを確認すると、実行はされているようです。

$ ansible-runner start ./demo -p test.yml

$ ps aux | grep runner
user1       69946  6.0  0.0  60788 37220 ?        S    01:37   0:00 /usr/bin/python3 /home/user1/.local/bin/ansible-runner start ./demo -p test.yml
user1       69947 21.6  0.0 142080 44684 pts/2    Rsl+ 01:37   0:00 /usr/bin/python3 /home/user1/.local/bin/ansible-playbook -i /home/user1/ghq/github.com/ansible/ansible-runner/demo/inventory -e @/home/user1/ghq/github.com/ansible/ansible-runner/demo/env/extravars test.yml
user1       70055  5.0  0.0 144524 41384 pts/2    S+   01:37   0:00 /usr/bin/python3 /home/user1/.local/bin/ansible-playbook -i /home/user1/ghq/github.com/ansible/ansible-runner/demo/inventory -e @/home/user1/ghq/github.com/ansible/ansible-runner/demo/env/extravars test.yml
user1       70078  0.0  0.0  18720  2572 pts/1    S+   01:37   0:00 grep --color=auto runner

$ ansible-runner is-alive ./demo -p test.yml -j

$ echo $?
0

start後今度はstopを実行してみます。

$ ansible-runner start ./demo -p test.yml
$ ps aux | grep runner
user1       70294 12.0  0.0  60792 37208 ?        S    01:40   0:00 /usr/bin/python3 /home/user1/.local/bin/ansible-runner start ./demo -p test.yml
user1       70295 29.0  0.0 142076 44956 pts/2    Rsl+ 01:40   0:00 /usr/bin/python3 /home/user1/.local/bin/ansible-playbook -i /home/user1/ghq/github.com/ansible/ansible-runner/demo/inventory -e @/home/user1/ghq/github.com/ansible/ansible-runner/demo/env/extravars test.yml
user1       70403 10.0  0.0 144632 41808 pts/2    S+   01:40   0:00 /usr/bin/python3 /home/user1/.local/bin/ansible-playbook -i /home/user1/ghq/github.com/ansible/ansible-runner/demo/inventory -e @/home/user1/ghq/github.com/ansible/ansible-runner/demo/env/extravars test.yml
user1       70427  0.0  0.0  18720  2556 pts/1    S+   01:40   0:00 grep --color=auto runner


$ ansible-runner stop ./demo -p test.yml
$ ps aux | grep runner
user1       18288  1.0  0.2 2708580 99272 ?       Sl   12月23   3:12 /usr/lib/virtualbox/VBoxHeadless --comment ansible-runner-demo_default_1640173248513_65011 --startvm 5c914744-fa5c-4612-b608-f5663ec49607 --vrde config
user1       70295 12.8  0.0 142076 44956 ?        Ssl  01:40   0:01 /usr/bin/python3 /home/user1/.local/bin/ansible-playbook -i /home/user1/ghq/github.com/ansible/ansible-runner/demo/inventory -e @/home/user1/ghq/github.com/ansible/ansible-runner/demo/env/extravars test.yml
user1       70403  0.9  0.0 144632 41808 ?        S    01:40   0:00 /usr/bin/python3 /home/user1/.local/bin/ansible-playbook -i /home/user1/ghq/github.com/ansible/ansible-runner/demo/inventory -e @/home/user1/ghq/github.com/ansible/ansible-runner/demo/env/extravars test.yml
user1       70434  0.0  0.0  18720  2496 pts/1    S+   01:40   0:00 grep --color=auto runner

stop実行後、プロセスはいなくなっていました。 こちらは意図した動きをしています。

2つ目のコンテナイメージを使用して、Ansibleを実行する方法

ドキュメントは、この部分

--process-isolationを指定して実行するようです。

$ ansible-runner run --process-isolation ./demo -p test.yml
Unable to find process isolation executable: podman

ドキュメントにも記載がありましたが、デフォルトではpodmanが実行されるようです。 私はdockerを使用しており、podmanは使用したことがないので、dockerで実行するようにします。

そのためには、--process-isolation-executableオプションに実行したいコンテナランタイムを指定すると良さそうです。

$ ansible-runner run --process-isolation --process-isolation-executable docker ./demo -p test.yml
Unable to find image 'quay.io/ansible/ansible-runner:devel' locally
devel: Pulling from ansible/ansible-runner
a1d0c7532777: Already exists 
0051e33d76d8: Pull complete 
3fb241a85b00: Pull complete 
014e39b4ae38: Pull complete 
442efd345444: Pull complete 
79831b585400: Pull complete 
27b3a0cd32c9: Pull complete 
a0432b3d84b6: Pull complete 
93e0336932fc: Pull complete 
1d30d58bb25f: Pull complete 
e5e64cd0b5b0: Pull complete 
af375d1ebe7d: Pull complete 
b919b20b21c3: Pull complete 
797daf249345: Pull complete 
Digest: sha256:d466b07100dc56fbb939d228cd4391ba53166b98e4084d181a4088a9b3851772
Status: Downloaded newer image for quay.io/ansible/ansible-runner:devel
[WARNING]: You are running the development version of Ansible. You should only
run Ansible from "devel" if you are modifying the Ansible engine, or trying out
features under development. This is a rapidly changing source of code and can
become unstable at any point.
[WARNING]: You are running the development version of Ansible. You should only
run Ansible from "devel" if you are modifying the Ansible engine, or trying out
features under development. This is a rapidly changing source of code and can
become unstable at any point.

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

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [debug] *******************************************************************
ok: [localhost] => {
    "msg": "Test!"
}

TASK [sleep] *******************************************************************
changed: [localhost]

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

dockerが起動され、指定したplaybookが実行されました。
デフォルトのdocker imageはquay.io/ansible/ansible-runner:develのようです。

今度はこれをバックグランドで実行してみます。

$ ansible-runner start --process-isolation --process-isolation-executable docker ./demo -p test.yml
$ ansible-runner is-alive ./demo -p test.yml

うーんis-aliveは何も返さない。。。

実行時に取得したpsの結果は以下の通り。

user1      178612  1.4  0.0  60788 37372 ?        S    14:11   0:00 /usr/bin/python3 /home/user1/.local/bin/ansible-runner start --process-isolation --process-isolation-executable docker ./demo -p test.yml
user1      178626  0.1  0.1 1646180 47240 pts/3   Ssl+ 14:11   0:00 /usr/bin/docker run --rm --tty --interactive --workdir /runner/project -v /home/user1/ghq/github.com/ansible/ansible-runner/demo/:/runner/:Z --env-file /home/user1/ghq/github.com/ansible/ansible-runner/demo/artifacts/60b45ef9-3715-4745-82a6-c640753f21db/env.list --user=1000 --name ansible_runner_60b45ef9-3715-4745-82a6-c640753f21db quay.io/ansible/ansible-runner:devel ansible-playbook -i /runner/inventory/hosts -e @/runner/env/extravars test.yml
user1      178675  0.0  0.0   4232   912 ?        Ss   14:11   0:00 /usr/local/bin/dumb-init -- ansible-playbook -i /runner/inventory/hosts -e @/runner/env/extravars test.yml
user1      178738 14.9  0.1 374316 59128 pts/0    Ssl+ 14:11   0:02 /usr/bin/python3 /usr/local/bin/ansible-playbook -i /runner/inventory/hosts -e @/runner/env/extravars test.yml
user1      178808  0.6  0.1 378552 53248 pts/0    S+   14:11   0:00 /usr/bin/python3 /usr/local/bin/ansible-playbook -i /runner/inventory/hosts -e @/runner/env/extravars test.yml
user1      178819  0.0  0.0  12280  2844 pts/0    S+   14:11   0:00 /bin/sh -c /usr/bin/python3 /home/runner/.ansible/tmp/ansible-tmp-1640409110.922103-93-179463132539417/AnsiballZ_command.py && sleep 0
user1      178820  0.9  0.0  93412 17500 pts/0    S+   14:11   0:00 /usr/bin/python3 /home/runner/.ansible/tmp/ansible-tmp-1640409110.922103-93-179463132539417/AnsiballZ_command.py

実行時に指定しているprivate_data_dir/runner/project配下にmountして、docker runを実行しています。
dockerに渡す環境変数は、private_data_dir/artifacts/identify(default:UUID)/env.listが指定されています。

identifyはオプション-iで個別に指定することが可能で、指定がないとUUIDが使われます。
artifactsディレクイトリは、ansible-runner実行時の様々な情報が保存されるディレクトリのようです。
詳細はドキュメントを確認してください。

docker内で実行するコマンドは、ansible-playbookが指定されており、mountしたローカルのprivate_data_dirの配下の情報(スタンドアロンでの実行時に参照する情報と同じもの)が指定されています。

複数のタスクを実行する場合

複数のplaybookを同時に起動する場合、競合が発生し、ファイルを上書きする可能性があり、
--directory-isolation-base-pathを指定することで、projectのコピーを指定した場所配下にコピーして競合を回避するようです。

ここでは、実際にどういった状態になるのか、同時に実行してみます。

$ ansible-runner run --process-isolation --process-isolation-executable docker ./demo -p test.yml
$ ansible-runner run --process-isolation --process-isolation-executable docker ./demo -p test.yml

特に問題なく実行できる。

--directory-isolation-base-path指定時の動作確認

指定するフォルダを先に作成します。

mkdir /tmp/runner

/tmpで指定して、実行すると権限が不足しているいうエラーになりました。
File “/usr/lib/python3.8/shutil.py”, line 649, in _rmtree_safe_fd dirfd = os.open(entry.name, os.O_RDONLY, dir_fd=topfd)
PermissionError: [Errno 13] Permission denied: ‘systemd-private-b9d7fdc512c24467adf85d2973279bcb-systemd-timesyncd.service-p2JlXh’

--directory-isolation-base-pathに先程指定したパスを指定して実行します。

ansible-runner run --process-isolation --process-isolation-executable docker --directory-isolation-base-path /tmp/runner ./demo -p test.yml

実行時のpsの状態からは、指定ありとなしの違いがわかりませんでした。 唯一わかったのは、作成したディレクトリ/tmp/runnerが処理の終了後に削除されたということくらいです。 これは、仕様通りの動きではあるのですが、実行時にそのディレクトリ内を参照しても特に何もなかったです。 demoディレクトリが/tmp/runner/配下にコピーされることを、lsの結果から確認できる想定でしたが、結果、そうではなかったです。

$ ls -l /tmp/runner/
合計 0
$ ls -al /tmp/runner/
合計 16
drwxrwxr-x  2 t451 t451  4096 12月 25 15:54 .
drwxrwxrwt 32 root root 12288 12月 25 15:54 ..
$ ls -al /tmp/runner/
ls: '/tmp/runner/' にアクセスできません: そのようなファイルやディレクトリはありません

どういった場合に、競合となるか、その時にこのオプションを指定することで回避できるかどうかは、別の機会で調査したいと思います。

まとめ

今回は、Ansible Runnerについて、デモ環境を使って簡単な動作を実際に動かしながら確認することで、理解を深めることをゴールとして進めてきました。
内容については、公式のドキュメントをなぞったようなものですが、個人的にはAnsible Runnerに対する理解がかなり深まりました。
これは、今回やった内容というよりは、アウトプットするという行為のためのインプットを再度実施したことによるものと理解しています。
ドキュメントを読む視点がかわったり、Why?という言葉を常に頭の中でつぶやきながら動作確認したことで、より深く知ろうとすることができたと考えています。
Ansible Runnerはまだまだできることがあるので、その点については引き続きアウトプットしていこうと思います。