- はじめに
- 動画
- 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申込時のアンケートでいただいたものも含めています。