- はじめに
- 動画
- ansible-navigator とは
- 準備
- TUI による Playbook 実行
- Ansibleの実行環境コンテナの利用
- Playbook 実行以外にも
- おわりに
- Part37にむけて
はじめに
2022/03/12 に、YouTube Live で「つまずき Ansible 【Part36】ansible-navigator」という配信をしました。
これまで Playbook の実行といえば、ansible-playbook
コマンドでした。 最近 ansible-navigator
という新たなツールが出てきました。TUI または CLI のPlaybook 実行ツールです。今回はこれをさわりました。
動画
ansible-navigator とは
個人的に感じている特徴は以下の通りです。
- TUI を備えている
- Ansibleの実行環境コンテナ(Execution Environment)を利用できる
- Playbook 実行だけなくドキュメントの参照などもできる
準備
インストール
今回は pip でインストールします。
pip install ansible-navigator
ansible-runner
などの依存パッケージがインストールされます。ansible-navigator
から ansible-runner
を呼び出すような関係になっています。
$ pip list Package Version ------------------- ------- ansible-navigator 1.1.0 ansible-runner 2.1.2 ...(略)...
ansible
や ansible-core
はインストールされません。
TUI による Playbook 実行
まずは実行
Playbook の実行にはサブコマンド run
を指定します。その後、Playbookファイル名とインベントリファイルを指定します。
$ ansible-navigator run debug.yml -i inventory
以下のような画面になります。これはPlayの一覧です。
PLAY NAME OK CHANGED UNREACHABLE FAILED SKIPPED IGNORED IN PROGRESS TASK COUNT PROGRESS 0│localhost 1 0 0 0 0 0 0 1 COMPLETE
左にある番号を入力すると、そのPlay内のタスクの一覧が表示されます。
更に、タスクの番号を入力するとそのタスクの実行結果の詳細が表示されます。
動画を見ていただくほうが、分かりやすいと思います。
このTUIのモードは、設定上は interactive
モードと呼ばれています。
ansible-playbook
コマンドと同じ形式でもOK
デフォルトは interactive
モードですが、ansible-playbook
コマンド実行時と同じ形式の stdout
モードにも変更できます。
指定は設定ファイル(後述)で行うか、ansible-navigator
コマンドで --mode stdout
を指定します。
$ ansible-navigator run debug.yml -i inventory --mode stdout PLAY [localhost] *************************************************************** TASK [debug] ******************************************************************* ok: [localhost] => { "msg": "sakana sakana sakana" } PLAY RECAP ********************************************************************* localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
アーティファクト(ログ)の保存とリプレイ機能
ansible-navigator
にはアーティファクト保存機能があります。JSON形式で保存されます。
アーティファクトの例
以下は、ios_command でshow ip route
を実行して debug
表示するPlaybookの実行したときのアーティファクトです。
▼クリックして開く
{ "version": "1.0.0", "plays": [ { "playbook": "/home/admin/stumble/ios_show.yml", "playbook_uuid": "6b70c8f8-4e21-4288-8fe0-53bc29178723", "play": "ios", "play_uuid": "a29dee92-8fae-871d-4141-000000000007", "play_pattern": "ios", "name": "ios", "pattern": "ios", "uuid": "a29dee92-8fae-871d-4141-000000000007", "__play_name": "ios", "tasks": [ { "playbook": "/home/admin/stumble/ios_show.yml", "playbook_uuid": "6b70c8f8-4e21-4288-8fe0-53bc29178723", "play": "ios", "play_uuid": "a29dee92-8fae-871d-4141-000000000007", "play_pattern": "ios", "task": "exec show commands", "task_uuid": "a29dee92-8fae-871d-4141-000000000009", "task_action": "cisco.ios.ios_command", "task_args": "", "task_path": "/home/admin/stumble/ios_show.yml:6", "host": "ios01", "uuid": "8db1e3c2-ab2f-4ff7-917b-cd5bab31cfdb", "__host": "ios01", "__result": "OK", "__changed": false, "__duration": "1s", "__number": 0, "__task": "exec show commands", "__task_action": "cisco.ios.ios_command", "remote_addr": "ios01", "res": { "changed": false, "stdout": [ "Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP\n D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area \n N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2\n E1 - OSPF external type 1, E2 - OSPF external type 2\n i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2\n ia - IS-IS inter area, * - candidate default, U - per-user static route\n o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP\n a - application route\n + - replicated route, % - next hop override, p - overrides from PfR\n\nGateway of last resort is not set\n\n 192.168.1.0/24 is variably subnetted, 2 subnets, 2 masks\nC 192.168.1.0/24 is directly connected, GigabitEthernet0/0\nL 192.168.1.11/32 is directly connected, GigabitEthernet0/0" ], "stdout_lines": [ [ "Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP", " D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area ", " N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2", " E1 - OSPF external type 1, E2 - OSPF external type 2", " i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2", " ia - IS-IS inter area, * - candidate default, U - per-user static route", " o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP", " a - application route", " + - replicated route, % - next hop override, p - overrides from PfR", "", "Gateway of last resort is not set", "", " 192.168.1.0/24 is variably subnetted, 2 subnets, 2 masks", "C 192.168.1.0/24 is directly connected, GigabitEthernet0/0", "L 192.168.1.11/32 is directly connected, GigabitEthernet0/0" ] ], "invocation": { "module_args": { "commands": [ "show ip route" ], "match": "all", "retries": 10, "interval": 1, "wait_for": null, "provider": null } }, "ansible_facts": { "discovered_interpreter_python": "/usr/libexec/platform-python" }, "_ansible_no_log": false }, "start": "2022-03-12T10:48:19.084551", "end": "2022-03-12T10:48:21.031434", "duration": 1.946883, "event_loop": null }, { "playbook": "/home/admin/stumble/ios_show.yml", "playbook_uuid": "6b70c8f8-4e21-4288-8fe0-53bc29178723", "play": "ios", "play_uuid": "a29dee92-8fae-871d-4141-000000000007", "play_pattern": "ios", "task": "debug show commands", "task_uuid": "a29dee92-8fae-871d-4141-00000000000a", "task_action": "ansible.builtin.debug", "task_args": "", "task_path": "/home/admin/stumble/ios_show.yml:12", "host": "ios01", "uuid": "4ee85d46-5d0b-4a99-a806-4be244eb1fed", "__host": "ios01", "__result": "OK", "__changed": false, "__duration": "0s", "__number": 1, "__task": "debug show commands", "__task_action": "ansible.builtin.debug", "remote_addr": "ios01", "res": { "msg": [ "Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP", " D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area ", " N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2", " E1 - OSPF external type 1, E2 - OSPF external type 2", " i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2", " ia - IS-IS inter area, * - candidate default, U - per-user static route", " o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP", " a - application route", " + - replicated route, % - next hop override, p - overrides from PfR", "", "Gateway of last resort is not set", "", " 192.168.1.0/24 is variably subnetted, 2 subnets, 2 masks", "C 192.168.1.0/24 is directly connected, GigabitEthernet0/0", "L 192.168.1.11/32 is directly connected, GigabitEthernet0/0" ], "_ansible_verbose_always": true, "_ansible_no_log": false, "changed": false }, "start": "2022-03-12T10:48:21.039585", "end": "2022-03-12T10:48:21.544976", "duration": 0.505391, "event_loop": null } ] } ], "stdout": [ "", "PLAY [ios] *********************************************************************", "", "TASK [exec show commands] ******************************************************", "\u001b[0;32mok: [ios01]\u001b[0m", "", "TASK [debug show commands] *****************************************************", "\u001b[0;32mok: [ios01] => {\u001b[0m", "\u001b[0;32m \"msg\": [\u001b[0m", "\u001b[0;32m \"Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP\",\u001b[0m", "\u001b[0;32m \" D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area \",\u001b[0m", "\u001b[0;32m \" N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2\",\u001b[0m", "\u001b[0;32m \" E1 - OSPF external type 1, E2 - OSPF external type 2\",\u001b[0m", "\u001b[0;32m \" i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2\",\u001b[0m", "\u001b[0;32m \" ia - IS-IS inter area, * - candidate default, U - per-user static route\",\u001b[0m", "\u001b[0;32m \" o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP\",\u001b[0m", "\u001b[0;32m \" a - application route\",\u001b[0m", "\u001b[0;32m \" + - replicated route, % - next hop override, p - overrides from PfR\",\u001b[0m", "\u001b[0;32m \"\",\u001b[0m", "\u001b[0;32m \"Gateway of last resort is not set\",\u001b[0m", "\u001b[0;32m \"\",\u001b[0m", "\u001b[0;32m \" 192.168.1.0/24 is variably subnetted, 2 subnets, 2 masks\",\u001b[0m", "\u001b[0;32m \"C 192.168.1.0/24 is directly connected, GigabitEthernet0/0\",\u001b[0m", "\u001b[0;32m \"L 192.168.1.11/32 is directly connected, GigabitEthernet0/0\"\u001b[0m", "\u001b[0;32m ]\u001b[0m", "\u001b[0;32m}\u001b[0m", "", "PLAY RECAP *********************************************************************", "\u001b[0;32mios01\u001b[0m : \u001b[0;32mok=2 \u001b[0m changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 " ], "status": "successful", "status_color": 10 }
リプレイ機能で再現表示
そのまま表示するのも良いですが、リプレイ機能が便利です。
ansible-navigator replay アーティファクトファイル名
このように指定して実行すると、ansible-navigator run
実行を再現したような表示になります。Ansible Tower / Automation Controller でいうと過去のジョブ実行結果を後から表示するイメージです。
ansible-navigator run
を interactive
モードで実行したときのアーティファクトを stdout
モードで表示することもできますし、その逆もできます。
設定は ansible-navigator.yml
ansible-navigator
の挙動を指定する設定ファイルは ansible-navigator.yml
というファイルです。
厳密には、YAML でも JSON でも構いません。YAML の場合は、拡張子 は .yml
または .yaml
です。
- 環境変数
ANSIBLE_NAVIGATOR_CONFIG
で指定したパスのファイル - カレントディレクトリの
ansible-navigator.yml
~/.ansible-navigator.yml
ansible.cfg
と似てますね。
この設定ファイルでは、利用するイメージの指定やモード、アーティファクトファイルの保存パスの指定などができます。設定項目の一覧は、公式ドキュメントに掲載されています。
ansible-navigator.readthedocs.io
なお、配信中にいじっていた設定ファイルは以下のようなものです。
--- ansible-navigator: # ansible: # inventories: # - inventory execution-environment: container-engine: podman enabled: true # image: quay.io/ansible/creator-ee:v0.2.0 image: quay.io/ansible/ansible-runner:stable-2.12-latest # image: ghcr.io/akira6592/my-ee:latest pull-policy: missing # mode: stdout playbook-artifact: enable: True save-as: "artifacts/{playbook_name}-{ts_utc}.json"
Ansibleの実行環境コンテナの利用
利用イメージの定義
利用するイメージは、ansible-navigator.yml
では以下のように image
で指定します。
--- ansible-navigator: execution-environment: container-engine: podman enabled: true image: quay.io/ansible/ansible-runner:stable-2.12-latest
たとえば、このイメージに入っていないコレクションのモジュールを使おうとするとエラーになります。
イメージは、できあいの物を利用するか ansible-builder
というツールを使って作成したものを利用します。
コンテナを使わなくてもOK
コンテナの実行環境を使わないで、ansible-navigator
を実行している自身の環境の Ansible を利用できます。
--- ansible-navigator: execution-environment: enabled: false # ここで無効を指定
コマンドのオプションで指定する場合は --ee false
です。
Playbook 実行以外にも
ansible-navigator
のサブコマンドを run
以外に切り替えることによって、ドキュメントの参照やコンフィグの参照などする機能もあります。
ansible-navigator
のサブコマンドと、既存の ansible-*
コマンドとの対応を以下のページにまとめられています。
ansible-navigator.readthedocs.io
ドキュメントの参照
ansible-doc
コマンドに相当します。
モジュールを指定する場合、コマンド書式は以下のとおりです。
$ ansible-navigator doc モジュール名
おわりに
簡単ですが、ansible-navigator
の紹介をしました。
コンテナによる実行環境が主流になってくると、コマンドとしては、ansible-navigator
を使う機会も増えてくるかも知れません。
stdout
モードが使えるのは気が利いているなと思います。
アーティファクトの保存とリプレイ機能も便利だと感じました。
参考
ansible-navigator
公式ドキュメントansible-navigator
リポジトリ- Ansible もくもく会 ネットワーク編コンテンツ
- https://aap2.demoredhat.com/exercises/ansible_network/
- 最近のコンテンツでは Playbook の実行は
ansible-playbook
ではなくansible-navigator
になってます。
Part37にむけて
以下のネタを検討中です。気が向いたものをやります。 connpass申込時のアンケートでいただいたものも含めています。