2020-05-01 06:28 PM 2020-12-22 01:39 PM 更新
■現実的なACIの構成管理における自動化を実現するために
ACIはAPI-Firstで設計されており、Ansibleを用いた構成管理とも非常に相性が良い実装となっています。APICのGUIを通じた操作はバージョンアップに伴い改善が続けられており、最初に構成を実施してみる手段としては適していますが、大量のパラメータを構成管理する手段としては最適とは言えません。特に、繰り返し構成作業が発生する様な事項については、自動化する仕組みを用意することによって、作業工数を削減するとともに、操作ミスなどの発生を抑制することができます。
Ansibleはネットワークエンジニアも親しんでいるYAML形式で構成を定義し、Playbookとして構成を実行することで定義した構成を実現することができます。
Ansible 2.4〜2.9ではACIの管理に利用することができるモジュールが標準で含まれているため、Ansibleを導入するだけでACIの管理に利用することができます。Ansible 2.9では、65を超えるACIモジュールと30を超えるMulti-Site Orchestrator (MSO)モジュールが用意されており、日常的なACIの運用管理において必要とする構成の大半をパラメータの定義だけで非常に簡単に構成できるようになっています。
本ページでは、Ansible自体に対する解説は含めていません。Ansibleウェブサイト等の情報を合わせてご確認下さい。
■ Ansibleを通じたACIの構成管理方法
Ansibleを通じたACIの構成管理は、AnsibleからACIモジュールを通じてPythonによるcurlを持ちいたREST API操作となります。以下では、実行先のhostsとして "apic" を指定していますが、実際にはinventoryにおいては以下のようにlocalhostを指定しています。
[apic] localhost
実際のAPIC IPの指定は、モジュール内の host パラメータで指定します。つまり、AnsibleからAPICに対する処理は、実際にはAnsibleホスト自身で実行されており、その結果としてREST/APIがAPICに対してPOST/GETされます。ネットワークとしてProxyを利用している環境では、APICに対する接続ではProxyを経由しないようno_proxyでAPIC IPを指定して下さい。
■Ansible 2.10〜 cisco.aci コレクションとしての提供
Ansible 2.10以降では Ansible 本体の配布ファイル肥大化を防止することやレポジトリとしての管理を分離するために、Collection機能を利用してAnsible本体のレポジトリから基本機能を除く全てのモジュールがコレクションとして分離されました。
ACI用のモジュールについても、cisco.aci コレクションとしてとして分離されたため、Ansible 2.10以降でACIを対象としてAnsibleを利用したい場合には、以下のようにansibleとは別にansible-galaxy コマンドを用いてcisco.aci コレクションの導入が必要となります。
pipを用いたAnsible 2.10.3の導入例
(ansible) root@tsmgmt2:~/ansible# pip install ansible Collecting ansible Downloading ansible-2.10.3.tar.gz (28.0 MB) (中略) Successfully built ansible ansible-base PyYAML MarkupSafe Installing collected packages: MarkupSafe, jinja2, PyYAML, six, pycparser, cffi, cryptography, pyparsing, packaging, ansible-base, ansible Successfully installed MarkupSafe-1.1.1 PyYAML-5.3.1 ansible-2.10.3 ansible-base-2.10.3 cffi-1.14.3 cryptography-3.2.1 jinja2-2.11.2 packaging-20.4 pycparser-2.20 pyparsing-2.4.7 six-1.15.0 (ansible) root@tsmgmt2:~/ansible# ansible --version ansible 2.10.3 config file = /etc/ansible/ansible.cfg configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules'] ansible python module location = /root/.local/share/virtualenvs/ansible--ycPIPLU/lib/python3.9/site-packages/ansible executable location = /root/.local/share/virtualenvs/ansible--ycPIPLU/bin/ansible python version = 3.9.0 (default, Nov 18 2020, 07:53:34) [GCC 9.3.0] (ansible) root@tsmgmt2:~/ansible# (ansible) root@tsmgmt2:~/ansible# pip freeze ansible==2.10.3 ansible-base==2.10.3 cffi==1.14.3 cryptography==3.2.1 Jinja2==2.11.2 MarkupSafe==1.1.1 p1==1.0.1 packaging==20.4 pycparser==2.20 pyparsing==2.4.7 PyYAML==5.3.1 RPi.GPIO==0.7.0 six==1.15.0
ansible-galaxyを用いたcisco.aciコレクション1.1.0の導入例
(ansible) root@tsmgmt2:~/ansible# ansible-galaxy collection install cisco.aci Starting galaxy collection install process Process install dependency map Starting collection install process Installing 'cisco.aci:1.1.0' to '/root/.ansible/collections/ansible_collections/cisco/aci' Downloading https://galaxy.ansible.com/download/cisco-aci-1.1.0.tar.gz to /root/.ansible/tmp/ansible-local-30990gdisz9rb/tmp8jtzyibt cisco.aci (1.1.0) was installed successfully
ansible-galaxyを用いたcisco.aciコレクション2.0.0へのアップグレード例
(ansible) root@tsmgmt2:~/ansible# ansible-galaxy collection install cisco.aci -f Starting galaxy collection install process Process install dependency map Starting collection install process Installing 'cisco.aci:2.0.0' to '/root/.ansible/collections/ansible_collections/cisco/aci' Downloading https://galaxy.ansible.com/download/cisco-aci-2.0.0.tar.gz to /root/.ansible/tmp/ansible-local-1121264su0xhbff/tmpylan67oc cisco.aci (2.0.0) was installed successfully (ansible) root@tsmgmt2:~/ansible# ansible-galaxy collection list # /root/.ansible/collections/ansible_collections Collection Version ---------- ------- cisco.aci 2.0.0
■Ansible利用における認証方式
Ansibleを通じてACIを利用する場合にも認証が必要です。認証方式としてはパスワードを用いる方法と、署名された公開鍵認証を用いる方法が提供されていますが、パスワード認証の方法を利用した場合、大量の処理を一度にAPICに対して実行するとAPICのDoS対策機能によって接続が遮断(HTTP 503 errorが返される)されてしまう可能性があるため、運用環境においてACIの構成管理をAnsibleを用いて利用したい場合には署名方式を用いることが推奨されます。
署名方式を利用するための手順は以下のとおりです。
1. 鍵の生成(公開鍵・秘密鍵)
Ansibleノード側でRSA鍵を生成します。
opensslコマンドによる生成例
$ openssl req -new -newkey rsa:2048 -days 36500 -nodes -x509 -keyout admin_ansible.key -out admin_ansible.crt -subj '/CN=Admin/O=Cisco Systems/C=JP'
2. APICアカウントへの公開鍵の登録
署名された公開鍵をAPICの管理者権限を持ったアカウントに登録します。登録は様々な方法で実行可能ですが、APIC GUIを通じて行う方法と、Ansible Playbookを用いて行う方法を紹介します。
APIC GUIを通じた公開鍵の登録例
(1) APIC GUIにおいて、公開鍵を紐付けるアカウントを選択します([Admin] - [AAA] - [Users])。なお、署名形式の認証を利用できるアカウントはAPICのローカルアカウントに限られます。
(2) [User Certificates] 欄において、[+]をクリックし、名前をつけた上で生成した公開鍵を貼り付けます。
Ansible Playbookを使った公開鍵の登録例
※このPlaybook自体はパスワード形式での認証が必要です。
※実行ホストとして "apic" を指定していますが、APICに対する操作はAPI経由での実行となるため、実際の実行先はlocalhostです。そのため、自分自身にSSHする必要性はないため、"connection: local"を付加して実行します。
※APICのデフォルトの証明書は自己署名証明書となっているため、"gather_facts: no" を指定しないと接続に失敗します。
--- - name: ACI Sample - aci_aaa_user_certificate hosts: apic connection: local gather_facts: no tasks: - name: Add Certificate to APIC admin cisco.aci.aci_aaa_user_certificate: host: apic_url_or_ip username: admin password: password aaa_user: admin certificate_name: acihot_ansible certificate: "{{ lookup('file', 'admin_ansible.crt') }}" validate_certs: no delegate_to: localhost register: query_result - name: debug debug: var: query_result
ACI Collection 2.0.0から、cisco.aci.aci_aaa_user_certificate のパラメータ "certificate_name" は "name" に変更されました。そのため、上記 1.x 向けのPlaybookを実行すると以下のようにエラーとなります。
(ansible) root@tsmgmt2:~/ansible# ansible-playbook -i inventory.ini acilab-add-user-certificate.yaml PLAY [aci_aaa_user_certificate] ****************************************************************************************************************************************************************************************** TASK [Add Certificate to admin of APIC] ********************************************************************************************************************************************************************************** fatal: [localhost]: FAILED! => {"changed": false, "msg": "state is present but all of the following are missing: name"} PLAY RECAP *************************************************************************************************************************************************************************************************************** localhost : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
cisco.aci コレクション 2.0.0 以降を利用する場合は以下の様に certificate_name を name に書き換える必要があります。
--- - name: ACI Sample - aci_aaa_user_certificate hosts: apic connection: local gather_facts: no tasks: - name: Add Certificate to APIC admin cisco.aci.aci_aaa_user_certificate: host: apic_url_or_ip username: admin password: password aaa_user: admin name: acihot_ansible certificate: "{{ lookup('file', 'admin_ansible.crt') }}" validate_certs: no delegate_to: localhost register: query_result - name: debug debug: var: query_result
上記Playbookの実行例
(ansible) root@tsmgmt2:~/ansible# ansible-playbook -i inventory.ini acilab-add-user-certificate.yaml PLAY [aci_aaa_user_certificate] ****************************************************************************************************************************************************************************************** TASK [Add Certificate to admin of APIC] ********************************************************************************************************************************************************************************** changed: [localhost] TASK [debug] ************************************************************************************************************************************************************************************************************* ok: [localhost] => { "query_result": { "changed": true, "current": [ { "aaaUserCert": { "attributes": { "annotation": "", "data": "-----BEGIN CERTIFICATE-----(略)-----END CERTIFICATE-----", "descr": "", "dn": "uni/userext/user-admin/usercert-acilab_ansible", "name": "acilab_ansible", "nameAlias": "", "ownerKey": "", "ownerTag": "", "userdom": ":all:common:" } } } ], "failed": false, "mo": { "aaaUserCert": { "attributes": { "data": "-----BEGIN CERTIFICATE-----(略)-----END CERTIFICATE-----", "dn": "uni/userext/user-admin/usercert-acilab_ansible", "name": "acilab_ansible" } } } } } PLAY RECAP *************************************************************************************************************************************************************************************************************** localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
いずれの方法で登録した場合でも、APICローカルアカウントに正しく公開鍵が紐付けられたことを確認して下さい。
Ansibleから署名形式の認証を利用する場合には、実際にREST/API経由でAPICに対して接続する際に鍵の署名をCookieにセットして通信を行います。
■Ansibleモジュールを用いたPlaybookサンプル
署名方式の認証を利用し、AnsibleのACIモジュールを使って全てのテナント情報を取得するAnsible Playbookです。各変数については別途Inventoryファイルなどで指定してください。
aci-query-tenant-sample.yaml
--- - name: ACI Tenant Query hosts: apic connection: local gather_facts: no tasks: - name: Query All Tenants cisco.aci.aci_tenant: host:"{{ apic_address }}" username:"{{ apic_username }}" certificate_name:"{{ apic_aaa_certificate }}" private_key:"{{ apic_aaa_private_key }}" state: query output_level: debug validate_certs: no delegate_to: localhost register: query_result - name: debug debug: var: query_result
cisco.aci コレクション 1.1.0 以降では、Playbookの中で環境変数を定義することによってタスク毎に認証情報を指定する必要性がなくなり、より簡潔でわかりやすいPlaybookを書くことが可能となりました。環境変数を利用することのメリットは、もちろんタスクの項目数を減らせることですが、モジュール側に指定があればそれを優先して利用しますので汎用性と厳密性を両立させられそうです。
以下が環境変数名とオプションの対比表となります。
項目 | 環境変数名 | モジュールオプション | 補足 |
ホスト名 | ACI_HOST | host | |
ポート | ACI_PORT | port | |
ユーザ名 | ACI_USERNAME | username | |
パスワード | ACI_PASSWORD | password | パスワード認証時のみ |
証明書名 | ACI_CERTIFICATE_NAME | certificate_name (〜1.x.x) name (2.0.0〜) |
鍵認証時のみ |
秘密鍵ファイル名 | ACI_PRIVATE_KEY | private_key | 鍵認証時のみ |
接続時の証明書の検証有無 | ACI_VALIDATE_CERTS | validate_certs | |
SSL/TLS利用 | ACI_USE_SSL | use_ssl | |
プロキシ利用 | ACI_USE_PROXY | use_proxy | |
タイムアウト | ACI_TIMEOUT | timeout |
環境変数を利用して上記Playbookを書き直すと、以下の通りとなります。得られる結果は同一です。
--- - name: ACI Tenant Query hosts: apic connection: local gather_facts: no environment: ACI_HOST: "{{ apic_address }}" ACI_USERNAME: "{{ apic_username }}" ACI_CERTIFICATE_NAME: "{{ apic_aaa_certificate }}" ACI_PRIVATE_KEY:"{{ apic_aaa_private_key }}" ACI_VALIDATE_CERTS: no tasks: - name: Query All Tenants cisco.aci.aci_tenant: state: query output_level: debug delegate_to: localhost register: query_result - name: debug debug: var: query_result
上記ではDebugを含めているので行数が増えてしまっていますが、タスク内のモジュールでは必要最小限のパラメータのみを指定すれば良くなるため、格段にPlaybookが簡潔となり解りやすくなります。
■aci_restモジュールを用いたPlaybookサンプル
ACIの運用管理において必要となる大半の操作対象に紐づくモジュールが提供されていますが、ACIモジュールが全てのパラメータの構成をサポートしてないケースや、そもそもACIモジュールが用意されていない項目も多く存在します。そうした場合には、aci_restモジュールを使用することで、AnsibleからAPICに対するあらゆるパラメータの構成管理を行うことが可能です。
ただし、冪等性の確保が通常のモジュールを利用する場合と比較して制限されるため、aci_restモジュールの利用は可能な限り最小限とすることを推奨します。
L3outをACIモジュールを使って構成し、OSPF基本パラメータの定義をaci_restモジュールを使って行う例
--- - name: ACILAB - L3out hosts: apic connection: local gather_facts: no tasks: - name: Create new L3out cisco.aci.aci_l3out: host: "{{ apic_address }}" username: "{{ apic_username }}" certificate_name: "{{ apic_aaa_certificate }}" private_key: acihot_ansible.key state: present tenant: "{{ tenant_name }}" vrf: "{{ vrf_name }}" name: "{{ l3out_name }}" domain: "{{ l3out_domain }}" l3protocol: ospf route_control: export validate_certs: no delegate_to: localhost - name: Modify L3out OSPF parameters cisco.aci.aci_rest: host: "{{ apic_address }}" username: "{{ apic_username }}" certificate_name: "{{ apic_aaa_certificate }}" private_key: acihot_ansible.key method: post path: "/api/node/mo/uni/tn-{{ tenant_name }}/out-{{ l3out_name }}/ospfExtP.json" content: { "ospfExtP": { "attributes": { "dn": "uni/tn-{{ tenant_name }}/out-{{ l3out_name }}/ospfExtP", "areaId": "0", "areaType": "regular" }, "children": [] } } validate_certs: no delegate_to: localhost
上記例では、ACI L3out自体は "aci_l3out" モジュールを使って構成していますが、aci_l3out モジュールでは 2020/04時点では使用するルーティングプロトコルの指定(この例では "ospf")は出来ても、そのパラメータとして Area ID や Area Type の指定ができないため、その範囲を "aci_rest" モジュールを使って構成しています。
パラメータとして利用する path や content の構造については、APIC GUI に用意されている API Inspector を利用することによってGUI操作を通じて簡単に確認することが可能です。他のパラメータと同様に、これらの構文の一部に変数を利用することが可能ですので、これによって汎用性を確保することができます。
上記 aci_rest 作成のためにAPIC GUI で実行した操作を API Inspector で取得した例
timestamp: 12:03:15 DEBUG method: POST url: https://apic_url/api/node/mo/uni/tn-Tenant10/out-L3out_OSPF/ospfExtP.json payload{"ospfExtP":{"attributes":{"dn":"uni/tn-Tenant10/out-L3out_OSPF/ospfExtP","areaId":"0","areaType":"regular","status":"created"},"children":[]}} response: {"totalCount":"0","imdata":[]}
API Inspector の使用方法については、ACI How To - API Inspectorの活用 等を参照してください。
■参考情報
ACIとAnsibleを組合せた構成管理の詳細については、Cisco DevNet, Ansible Documentサイトなどを参照して下さい。
また、Cisco Liveセッションでも以下のセッションにおいて ACI と Ansible を組み合わせた運用管理ついて扱っています。
Cisco Liveで行われた ACI のAPI活用に関するハンズオンのガイドが公開されており、Internet経由で参照可能となっていますが、こちらのBonus ContentsとしてAnsible連携についても解説されています。
LTRACI-3225 ハンズオンガイド
以下に有益な情報が公開されています。
検索バーにキーワード、フレーズ、または質問を入力し、お探しのものを見つけましょう
シスコ コミュニティをいち早く使いこなしていただけるよう役立つリンクをまとめました。みなさんのジャーニーがより良いものとなるようお手伝いします