はじめに
本記事は、シスコの有志による Cisco Systems Japan Advent Calendar 2024 シリーズ 2 の25日目として投稿しています。
2017年版: https://qiita.com/advent-calendar/2017/cisco
2018年版: https://qiita.com/advent-calendar/2018/cisco
2019年版: https://qiita.com/advent-calendar/2019/cisco
2020年版: https://qiita.com/advent-calendar/2020/cisco
2020年版(2枚目): https://qiita.com/advent-calendar/2020/cisco2
2021年版: https://qiita.com/advent-calendar/2021/cisco
2021年版(2枚目): https://qiita.com/advent-calendar/2021/cisco2
2022年版(1,2): https://qiita.com/advent-calendar/2022/cisco
2023年版: https://qiita.com/advent-calendar/2023/cisco
2024年版: https://qiita.com/advent-calendar/2024/cisco
先日、Cisco Modeling Labs(CML)の最新バージョンCML 2.8がリリースされました。今回のリリースでは無償版(Free Tier)が公開され、より多くの方々がCMLの魅力を体験できるようになりました。
昨年、@kykanno さんの記事では、CML 2を用いてベータ版 Catalyst 9000vでEVPN VXLANの設定と検証を行う方法が紹介されました。本記事ではその内容の続編として、CML2でIOL-L2を使用してEVPN VXLANの設定と検証を行い、さらにAnsibleを用いてEVPN VXLANラボ展開の自動化を実現します。これにより、CML 2.8 Free Tierの可能性を最大限に引き出したいと思います。
事前準備
CML2.8 Free Tierのインストール
@ecodrive さんの記事ではCML2.8 Free Tierのインストール手順について詳しく説明されていますので、ぜひご参照ください
CML2.8 Free Tierサービスの起動
以下のサービスを起動します。
sudo systemctl enable --now ssh.service
sudo systemctl enable --now virl2-patty.service
Ansible実行環境の準備
今回、EVPN VXLAN Farbic構築の自動化を実現するため、Ansibleを使用します。
下記に、自動化シナリオで使用されるトポロジーを示します。
今回使用するAnsible playbookなどについては trustywolf/cml-free-evpn-vxlan をご参照ください。
実際のCML2のトポロジー図はこちらです:
AnsibleでCML2ラボを自動展開するため、手動での操作は不要です。
Ansible実行用のPCでは、以下の操作を実行し、実行環境の初期化を行います。
git clone https://github.com/trustywolf/cml-free-evpn-vxlan.git
cd cml-free-evpn-vxlan/dag
python3 -m venv .venv
source .venv/bin/activate
pip3 install -r requirements.txt
ansible-galaxy collection install cisco.cml
CML2ラボの自動展開
ラボを自動展開するため、以下のPlaybookを実行します。
ansible-playbook cisco.cml.build -e wait='yes' \
-e cml_host=<cml_host> \
-e cml_username=<cml_username> \
-e cml_password=<cml_password> \
-e cml_lab="cml-iol-evpn-vxlan" \
-e cml_lab_file="{{ lookup('env', 'PWD') }}/../cml-iol-evpn-vxlan.yaml"
下図に示すように、ラボが展開され、Power onになりました。
同様に、ラボを削除する際に、以下のPlaybookを実行します:
ansible-playbook cisco.cml.clean -t erase \
-e cml_host=<cml_host> \
-e cml_username=<cml_username> \
-e cml_password=<cml_password> \
-e cml_lab="cml-iol-evpn-vxlan" \
-e cml_lab_file="{{ lookup('env', 'PWD') }}/../cml-iol-evpn-vxlan.yaml"
下図に示すように、ラボが削除されました。
次に、Ansible CML Collectionのdynamic inventory pluginの使用し、ラボのinventory情報を取得します:
touch cml.yml
plugin: cisco.cml.cml_inventory
host: <cml_host>
username: <cml_username>
password: <cml_password>
validate_certs: "no"
lab: cml-iol-evpn-vxlan
group_tags: spine, leaf, host
% ansible-playbook cisco.cml.inventory -i cml.yml --limit=spine,leaf,host
SSL Verification disabled
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details
PLAY [cml_hosts] *********************************************************************************************************************************************************************
TASK [debug] *************************************************************************************************************************************************************************
ok: [S1] => {
"msg": "Node: S1(ioll2-xe), State: BOOTED, Address: cml-free.example.com:2201"
}
ok: [S2] => {
"msg": "Node: S2(ioll2-xe), State: BOOTED, Address: cml-free.example.com:2202"
}
ok: [L1] => {
"msg": "Node: L1(ioll2-xe), State: BOOTED, Address: cml-free.example.com:2203"
}
ok: [L2] => {
"msg": "Node: L2(ioll2-xe), State: BOOTED, Address: cml-free.example.com:2204"
}
ok: [H] => {
"msg": "Node: H(ioll2-xe), State: BOOTED, Address: cml-free.example.com:2205"
}
PLAY RECAP ***************************************************************************************************************************************************************************
H : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
L1 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
L2 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
S1 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
S2 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
最後に、疎通確認を行います:
% ansible -i cml.yml cml_hosts -m ping --limit=spine,leaf,host
SSL Verification disabled
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details
S2 | SUCCESS => {
"changed": false,
"ping": "pong"
}
L2 | SUCCESS => {
"changed": false,
"ping": "pong"
}
H | SUCCESS => {
"changed": false,
"ping": "pong"
}
S1 | SUCCESS => {
"changed": false,
"ping": "pong"
}
L1 | SUCCESS => {
"changed": false,
"ping": "pong"
}
ラボの各ノードがAnsibleのインベントリに追加され、疎通できるようになりました。
しかし、以下のコマンドを実行すると、
% ansible -i cml.yml cml_hosts --limit=spine,leaf -m cisco.ios.ios_command -a "commands='show ip int b'"
SSL Verification disabled
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details
S2 | FAILED! => {
"changed": false,
"msg": "ssh connection failed: ssh connect failed: Permission denied"
}
S1 | FAILED! => {
"changed": false,
"msg": "ssh connection failed: ssh connect failed: Permission denied"
}
L1 | FAILED! => {
"changed": false,
"msg": "ssh connection failed: ssh connect failed: Permission denied"
}
L2 | FAILED! => {
"changed": false,
"msg": "ssh connection failed: ssh connect failed: Permission denied"
}
SSH接続不可のエラーが出てしまいました。今回のラボ構成では、外部からラボのノードへSSH接続するため、PATty Toolを使用しPATの変換を行っていましたが、これが原因と考えられます。そこで、PAT変換の設定を削除し、CMLサーバをJump Hostとして利用することで、Ansibleからラボのノードへ接続を実現します。
group_vars/all.yml
に以下の内容を追加します:
ansible_ssh_common_args: '-o ProxyCommand="ssh -W %h:%p -q <cml_username>@<cml_host> -p 1122"'
疎通確認およびAnsibleの実行確認を行います:
% ansible -i inventory.yml all -m ping
S1 | SUCCESS => {
"changed": false,
"ping": "pong"
}
L2 | SUCCESS => {
"changed": false,
"ping": "pong"
}
L1 | SUCCESS => {
"changed": false,
"ping": "pong"
}
S2 | SUCCESS => {
"changed": false,
"ping": "pong"
}
% ansible -i inventory.yml all -m cisco.ios.ios_command -a "commands='show cdp nei'"
L1 | SUCCESS => {
"changed": false,
"stdout": [
"Capability Codes: R - Router, T - Trans Bridge, B - Source Route Bridge\n S - Switch, H - Host, I - IGMP, r - Repeater, P - Phone, \n D - Remote, C - CVTA, M - Two-port Mac Relay \n\nDevice ID Local Intrfce Holdtme Capability Platform Port ID\nH.lab Eth 0/3 152 R S I Linux Uni Eth 0/1\nS2.lab Eth 0/2 178 R S I Linux Uni Eth 0/1\nS1.lab Eth 0/1 154 R S I Linux Uni Eth 0/1\n\nTotal cdp entries displayed : 3"
],
"stdout_lines": [
[
"Capability Codes: R - Router, T - Trans Bridge, B - Source Route Bridge",
" S - Switch, H - Host, I - IGMP, r - Repeater, P - Phone, ",
" D - Remote, C - CVTA, M - Two-port Mac Relay ",
"",
"Device ID Local Intrfce Holdtme Capability Platform Port ID",
"H.lab Eth 0/3 152 R S I Linux Uni Eth 0/1",
"S2.lab Eth 0/2 178 R S I Linux Uni Eth 0/1",
"S1.lab Eth 0/1 154 R S I Linux Uni Eth 0/1",
"",
"Total cdp entries displayed : 3"
]
]
}
L2 | SUCCESS => {
"changed": false,
"stdout": [
"Capability Codes: R - Router, T - Trans Bridge, B - Source Route Bridge\n S - Switch, H - Host, I - IGMP, r - Repeater, P - Phone, \n D - Remote, C - CVTA, M - Two-port Mac Relay \n\nDevice ID Local Intrfce Holdtme Capability Platform Port ID\nH.lab Eth 0/3 174 R S I Linux Uni Eth 0/2\nS2.lab Eth 0/2 153 R S I Linux Uni Eth 0/2\nS1.lab Eth 0/1 154 R S I Linux Uni Eth 0/2\n\nTotal cdp entries displayed : 3"
],
"stdout_lines": [
[
"Capability Codes: R - Router, T - Trans Bridge, B - Source Route Bridge",
" S - Switch, H - Host, I - IGMP, r - Repeater, P - Phone, ",
" D - Remote, C - CVTA, M - Two-port Mac Relay ",
"",
"Device ID Local Intrfce Holdtme Capability Platform Port ID",
"H.lab Eth 0/3 174 R S I Linux Uni Eth 0/2",
"S2.lab Eth 0/2 153 R S I Linux Uni Eth 0/2",
"S1.lab Eth 0/1 154 R S I Linux Uni Eth 0/2",
"",
"Total cdp entries displayed : 3"
]
]
}
S2 | SUCCESS => {
"changed": false,
"stdout": [
"Capability Codes: R - Router, T - Trans Bridge, B - Source Route Bridge\n S - Switch, H - Host, I - IGMP, r - Repeater, P - Phone, \n D - Remote, C - CVTA, M - Two-port Mac Relay \n\nDevice ID Local Intrfce Holdtme Capability Platform Port ID\nL1.lab Eth 0/1 152 R S I Linux Uni Eth 0/2\nL2.lab Eth 0/2 141 R S I Linux Uni Eth 0/2\n\nTotal cdp entries displayed : 2"
],
"stdout_lines": [
[
"Capability Codes: R - Router, T - Trans Bridge, B - Source Route Bridge",
" S - Switch, H - Host, I - IGMP, r - Repeater, P - Phone, ",
" D - Remote, C - CVTA, M - Two-port Mac Relay ",
"",
"Device ID Local Intrfce Holdtme Capability Platform Port ID",
"L1.lab Eth 0/1 152 R S I Linux Uni Eth 0/2",
"L2.lab Eth 0/2 141 R S I Linux Uni Eth 0/2",
"",
"Total cdp entries displayed : 2"
]
]
}
S1 | SUCCESS => {
"changed": false,
"stdout": [
"Capability Codes: R - Router, T - Trans Bridge, B - Source Route Bridge\n S - Switch, H - Host, I - IGMP, r - Repeater, P - Phone, \n D - Remote, C - CVTA, M - Two-port Mac Relay \n\nDevice ID Local Intrfce Holdtme Capability Platform Port ID\nL1.lab Eth 0/1 169 R S I Linux Uni Eth 0/1\nL2.lab Eth 0/2 164 R S I Linux Uni Eth 0/1\n\nTotal cdp entries displayed : 2"
],
"stdout_lines": [
[
"Capability Codes: R - Router, T - Trans Bridge, B - Source Route Bridge",
" S - Switch, H - Host, I - IGMP, r - Repeater, P - Phone, ",
" D - Remote, C - CVTA, M - Two-port Mac Relay ",
"",
"Device ID Local Intrfce Holdtme Capability Platform Port ID",
"L1.lab Eth 0/1 169 R S I Linux Uni Eth 0/1",
"L2.lab Eth 0/2 164 R S I Linux Uni Eth 0/1",
"",
"Total cdp entries displayed : 2"
]
]
}
ご覧の通り、Ansible実行環境の整備が問題なく完了しました。
EVPN-VXLAN Farbicの自動構築
Underlayのプロビジョニング
ループバックインターフェースと物理インターフェースの設定、スパインおよびリーフデバイス間のIS-ISおよびPIMプロトコルの設定を行います。その結果、スパインとリーフスイッチ間で完全なIP到達性が確立され、アンダーレイネットワークがマルチキャストルーティングをサポートするようになります。
ラボの各ノードのUnderlay設定は以下のファイルにより定義されます:
% ls host_vars/node_vars/
L1.yml L2.yml S1.yml S2.yml
以下のPlaybookを実行することで、preview_files
フォルダの下に<hostname>-underlay.txt
のファイルが生成され、各ノードに適用するコンフィグを確認することが可能です。
ansible-playbook -i inventory.yml playbook_underlay_preview.yml
% ls preview_files/
L1-underlay.txt L2-underlay.txt S1-underlay.txt S2-underlay.txt
各ファイルの内容を確認し、問題がない場合は、以下のPlaybookを実行することで、各ノードへコンフィグが適用されます。
ansible-playbook -i inventory.yml playbook_underlay_commit.yml
CMLの各ノードでshowコマンドを使用し、状態を確認します。
L1#show isis neighbors
Tag UNDERLAY:
System Id Type Interface IP Address State Holdtime Circuit Id
S1 L2 Et0/1 172.16.13.1 UP 22 02
S2 L2 Et0/2 172.16.23.2 UP 22 02
L1#show ip pim neighbor
PIM Neighbor Table
Mode: B - Bidir Capable, DR - Designated Router, N - Default DR Priority,
P - Proxy Capable, S - State Refresh Capable, G - GenID Capable,
L - DR Load-balancing Capable
Neighbor Interface Uptime/Expires Ver DR
Address Prio/Mode
172.16.13.1 Ethernet0/1 00:00:57/00:01:16 v2 1 / S P G
172.16.23.2 Ethernet0/2 00:00:57/00:01:16 v2 1 / S P G
ご覧の通り、Underlayのプロビジョニングが無事完了しました。
Overlayのプロビジョニング
Overlay設定は以下のファイルにより定義されます:
% cat group_vars/overlay_db.yml
以下のPlaybookを実行することで、Overlay設定のチェックを行います:
% ansible-playbook -i inventory.yml playbook_yml_validation.yml
また、以下のPlaybookを実行することで、Overlay設定を投入する前に、各NodeのUnderlayのチェックを行います:
% ansible-playbook -i inventory.yml playbook_overlay_precheck.yml
ここで、version_license_check
のエラーが出ましたが、今回のラボ環境では特に影響がないため、無視しても問題ありません。
最後に、先ほどと同様に、各ノードへのコンフィグの確認および適用を行います。
% ansible-playbook -i inventory.yml playbook_overlay_preview.yml
% ls preview_files/ | grep overlay
L1-overlay.txt
L2-overlay.txt
S1-overlay.txt
S2-overlay.txt
% ansible-playbook -i inventory.yml playbook_overlay_commit.yml
アクセスポートのプロビジョニング
最後に、Leafスイッチのアクセスポートのプロビジョニングを行います。
アクセスポートの設定は、以下のファイルで定義されています。
% ls host_vars/access_intf/
L1.yml L2.yml
% ansible-playbook -i inventory.yml playbook_access_add_preview.yml
% ansible-playbook -i inventory.yml playbook_access_add_commit.yml
% ls preview_files/ | grep add
L1-add-intf.txt
L2-add-intf.txt
% ansible-playbook -i inventory.yml playbook_access_add_commit.yml
これにより、2台のLeafスイッチのEthernet0/3
ポートを、アクセスポートVLAN102に設定しました。
疎通確認
今回のラボでは、CML2.8 Free Tierの台数制限により、ホストを模擬する機器は1台しか用意できませんでした。そのため、ホスト模擬スイッチでVRFを使用することで、疑似的に2台のホストを作成します。
ホスト模擬スイッチに以下のコンフィグを投入します。
vrf definition H1
!
address-family ipv4
exit-address-family
!
address-family ipv6
exit-address-family
!
vrf definition H2
!
address-family ipv4
exit-address-family
!
address-family ipv6
exit-address-family
!
default interface Ethernet0/1
interface Ethernet0/1
no switchport
vrf forwarding H1
ip address 10.1.102.201 255.255.255.0
no shutdown
!
default interface Ethernet0/2
interface Ethernet0/2
no switchport
vrf forwarding H2
ip address 10.1.102.202 255.255.255.0
no shutdown
!
interface Ethernet0/3
shutdown
!
end
初期状態では、VRF H1
のARPテーブルには VRF 'H2'所属のEthernet0/2
のアドレスとMACアドレスが存在しません。
H#show arp vrf H1
Protocol Address Age (min) Hardware Addr Type Interface
Internet 10.1.102.201 - aabb.cc00.6210 ARPA Ethernet0/1
pingコマンドで疎通確認を行います:
H#ping vrf H1 10.1.102.202 source Ethernet0/1
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 10.1.102.202, timeout is 2 seconds:
Packet sent with a source address of 10.1.102.201
.!!!!
Success rate is 80 percent (4/5), round-trip min/avg/max = 2/2/3 ms
pingで疎通確認後、VRF H1
のARPテーブルにはVRF 'H2'所属のEthernet0/2
のアドレスとMACアドレスが無事に学習できました。
H#show arp vrf H1
Protocol Address Age (min) Hardware Addr Type Interface
Internet 10.1.102.201 - aabb.cc00.6210 ARPA Ethernet0/1
Internet 10.1.102.202 0 aabb.cc00.6220 ARPA Ethernet0/1
これにより、EVPN-VXLAN Farbicの正常性確認ができました。
まとめ
本記事では、Cisco Modeling Labs (CML) 2.8 Free Tierを活用し、IOL-L2を用いたEVPN VXLANのラボ環境構築と、Ansibleによる自動化について解説しました。これらの手順を通して、CML 2.8 Free Tierのポテンシャルを最大限に引き出し、EVPN-VXLANラボの構築から自動化までの一連の流れを、実践的に学んでいただけると考えています。CML 2.8 Free Tier、EVPN-VXLAN、Ansibleに関心のある方々にとって、少しでも役立つ情報となれば幸いです。
参考資料
- BGP EVPN VXLAN Configuration Guide, Cisco IOS XE 17.15.x (Catalyst 9300 Switches)
- Campus EVPN Fabric Based on Cisco Catalyst 9000 Switches
- Cat9kEVPN/cat9k-evpn-ansible
- CiscoDevNet/ansible-cml
- How to Use Ansible with CML
- Cisco CML 2.8 Free Tier上にSWを自動配置してAnsibleで設定したい!
- IOS-XE Catalyst 9k(CML) で EVPN-VXLANを構築してみた
- Intel N97のミニPCを買った話(ついでにCML2.8 Free Tierをベアメタルにインストール)
- Ansibleを使ってCMLでラボ構築
免責事項
本サイトおよび対応するコメントにおいて表明される意見は、投稿者本人の個人的意見であり、シスコの意見ではありません。本サイトの内容は、情報の提供のみを目的として掲載されており、シスコや他の関係者による推奨や表明を目的としたものではありません。各利用者は、本Webサイトへの掲載により、投稿、リンクその他の方法でアップロードした全ての情報の内容に対して全責任を負い、本Web サイトの利用に関するあらゆる責任からシスコを免責することに同意したものとします。