Ansibleでホワイトボックススイッチの設定自動化(Cumulus Linux編)
今回ご紹介するのは、Ansibleを用いたネットワーク設定の管理および自動化です。
前回は、ZTPでホワイトボックススイッチの構築自動化でZTPを使用した初期構築の自動化を紹介しているのでこちらも参考下さい。
Ansibleを使用することで、ネットワークの構成管理をコード化できます。コード化によるメリットとしては、
私のホワイトボックススイッチのテスト環境では、よくNetworkOSを入れ替えて、構成を壊したり、再構築したりを繰り返したりしているのでAnsibleで構成管理しています。
その環境を紹介していきます。
Configなど設定情報の詳細については、Cumulus LinuxでVXLANルーティング構築を参考下さい。
今回の例では、タスクを直接記載せずに、全てのタスクをロールとして纏めています。
ロールとして纏めること再利用(設定追加など)が可能な事と、下記のとおりPlabookをシンプル化し、可読性が高くなるのでロールを使用しています。
テンプレートを用いてロールを作りこんでしまえば、以降の設定追加のメンテナンスは非常に簡単に出来ることが確認出来ました。閲覧頂いた方の参考になっていれば幸いです!
実機で試してみたい方必見!POCキャンペーン開催中!!
ホワイトボックススイッチに興味ある方はこちらへ
前回は、ZTPでホワイトボックススイッチの構築自動化でZTPを使用した初期構築の自動化を紹介しているのでこちらも参考下さい。
Ansibleを使用することで、ネットワークの構成管理をコード化できます。コード化によるメリットとしては、
- 人的ミスの削減、作業工数の削減
- テンプレート利用により、ロールで部品化し、簡単に再利用(設定変更/追加)が出来る
- 属人化の防止
私のホワイトボックススイッチのテスト環境では、よくNetworkOSを入れ替えて、構成を壊したり、再構築したりを繰り返したりしているのでAnsibleで構成管理しています。
その環境を紹介していきます。
ネットワーク構成
Cumulus Linuxで構築したEVPN-VXLAN構成を例に紹介していきます。以下に物理構成および論理構成を示します。Configなど設定情報の詳細については、Cumulus LinuxでVXLANルーティング構築を参考下さい。
Playbookの構成
- hosts # インベントリファイル
- deploy.yml # playbookの起点となるファイル。ファイル名は任意。
- ansible.cfg
- group_vars
all # テンプレート内で使用する変数を記載
- roles # 設定ロールを記載するフォルダ
- bgp-peer # BGP Unnumbered設定に関するロール
- tasks
main.yml
- exit # Exit01(外接LeafSW)に限定した設定に関するロール
- tasks
main.yml
- general # I/FやVLAN設定に関するロール
- tasks
main.yml
- leaf # LeafSWのEVPN-VXLAN設定に関するロール
- tasks
main.yml
- spine # SpineSWのEVPN-VXLAN設定に関するロール
- tasks
main.yml
hosts
インベントリに相当するファイルです。[xxx.chirdren]と記載することで、グルーピング化することもできます。[leaf]
leaf01 ansible_ssh_host=192.168.100.90
leaf02 ansible_ssh_host=192.168.100.91
[spine]
spine01 ansible_ssh_host=192.168.100.85
spine02 ansible_ssh_host=192.168.100.86
[exit]
exit01 ansible_ssh_host=192.168.100.94
[leafs:children]
leaf
exit
[spines:children]
spine
[exits:children]
exit
[all:vars]
ansible_ssh_port=22
ansible_ssh_user=cumulus
ansible_ssh_pass=CumulusLinux!
ansible_sudo_pass=CumulusLinux!
deploy.yml
hostsにて実行対象となる装置グループを指定し、rolesにて実行したいロールを指定します。今回の例では、タスクを直接記載せずに、全てのタスクをロールとして纏めています。
ロールとして纏めること再利用(設定追加など)が可能な事と、下記のとおりPlabookをシンプル化し、可読性が高くなるのでロールを使用しています。
---
- hosts:
- spines
user: cumulus
become: yes
become_method: sudo
roles:
- general
- bgp-peer
- spine
- hosts:
- leafs
user: cumulus
become: yes
become_method: sudo
roles:
- general
- bgp-peer
- leaf
- hosts:
- exits
gather_facts: False
user: cumulus
become: yes
become_method: sudo
roles:
- exit
group_vars/all
実際の設定情報を記載するファイルです。それぞれの設定項目は、後述するテンプレート内で変数として扱うことが可能です。Exit01を例に解説を記載したので参考下さい。nodes:
leaf01:
loopback: "10.0.0.11"
asn: 65011
vxlans:
10100:
vid: "100"
10200:
vid: "200"
104001:
vid: "4001"
vrf: "vrf1"
104002:
vid: "4002"
vrf: "vrf2"
neighbors:
swp49:
swp50:
svis:
100:
ipv4: "172.16.100.1/24"
vrf: "vrf1"
200:
ipv4: "172.16.200.1/24"
vrf: "vrf2"
4001:
vrf: "vrf1"
4002:
vrf: "vrf2"
ports:
swp1:
mode: Tagged
vlans:
- 100
- 200
swp49:
speed: 40000
swp50:
speed: 40000
leaf02:
loopback: "10.0.0.12"
asn: 65012
vxlans:
10100:
vid: "100"
10200:
vid: "200"
10101:
vid: "101"
10201:
vid: "201"
104001:
vid: "4001"
vrf: "vrf1"
104002:
vid: "4002"
vrf: "vrf2"
neighbors:
swp49:
swp50:
svis:
100:
ipv4: "172.16.100.1/24"
vrf: "vrf1"
200:
ipv4: "172.16.200.1/24"
vrf: "vrf2"
101:
ipv4: "172.16.101.1/24"
vrf: "vrf1"
201:
ipv4: "172.16.201.1/24"
vrf: "vrf2"
4001:
vrf: "vrf1"
4002:
vrf: "vrf2"
ports:
swp1:
mode: Tagged
vlans:
- 100
- 101
- 200
- 201
swp49:
speed: 40000
swp50:
speed: 40000
exit01: # ノードの名前
loopback: "10.0.0.41" # loopbackで設定するIPアドレス
asn: 65041 # AS番号
vxlans: # VXLAN <-> VLANのマッピング設定
104001: # VNI
vid: "4001" # マッピングするVLANのVID
vrf: "vrf1" # L3VNIの場合はVRFを指定
104002:
vid: "4002"
vrf: "vrf2"
neighbors: # 隣接情報
swp1: # BGPピア接続インターフェース(Unnumberedの場合はインターフェース名となる)
swp2:
svis: # VLANインターフェース設定
2001: # VID
ipv4: "10.10.10.0/31" # 付与するIPアドレス
vrf: "vrf1" # 所属するVRF
2002:
ipv4: "10.10.20.0/31"
vrf: "vrf2"
4001:
vrf: "vrf1"
4002:
vrf: "vrf2"
ports: # ポートの個別設定
swp3: # ポート名
mode: Tagged # 設定モード (現状Taggedのみ)
vlans: # Taggedの場合に所属するVIDを指定
- 2001
- 2002
external: # 外部ルータと接続する場合の設定
bgp: # BGPで接続する場合は、bgpとする(現状BGPのみ)
vrf1: # 関連するVRF
neighbors: # 隣接情報
- 10.10.10.1
networks: # 配布するネットワーク
- "172.16.100.0/24"
- "172.16.101.0/24"
evpn: # EVPN Type5で配布するネットワーク
default: # 名前
route_map: "DGW" # ルートマップ名を指定
vrf2:
neighbors:
- 10.10.20.1
networks:
- "172.16.200.0/24"
- "172.16.201.0/24"
evpn:
default:
route_map: "DGW"
route_maps: # ルートマップを作成
DGW: # 名前
prefix: "0.0.0.0/0" # ルートマップで指定する条件 (現状Prefixのみ)
spine01:
loopback: "10.0.0.1"
asn: 65020
neighbors:
swp48:
swp49:
swp50:
ports:
swp49:
speed: 40000
swp50:
speed: 40000
spine02:
loopback: "10.0.0.2"
asn: 65020
neighbors:
swp48:
swp49:
swp50:
ports:
swp49:
speed: 40000
swp50:
speed: 40000
roles
ロールは、roles配下に作成したフォルダの名前がロールの名前になります。ロール名のフォルダ配下に、tasks/main.ymlというファイルを作成することで、ロール内のタスクを設定可能です。bgp-peer/tasks/main.yml
template の部分でコマンドを設定しています。Ansible のテンプレートと同様に、Jinja2 の書式で記載可能なため、プログラマブルで柔軟なテンプレート設計が可能です。先述した通り、テンプレート内では、group_vars で設定した値を{{変数}}として利用可能です。これにより、タスク内容を変更することなく、設定の変更が可能になります。BGP Unnumbered設定に関するロールです。- name: configure fabric # タスク名
nclu: # モジュール名(Ansible公式のCumulus Linuxのモジュール NCLU を使用)
atomic: true # atomic=trueにすると、設定が即時コミットされる
description: "configure bgp peer for fabric"
template: | # コマンドのテンプレート
add loopback lo ip address {{ nodes[inventory_hostname].loopback }}/32
add bgp autonomous-system {{ nodes[inventory_hostname].asn }}
add bgp router-id {{ nodes[inventory_hostname].loopback }}
add bgp network {{ nodes[inventory_hostname].loopback }}/32
{% for iface in nodes[inventory_hostname].neighbors %}
add interface {{ iface }}
add interface {{ iface }} ipv6 nd ra-interval 10
del interface {{ iface }} ipv6 nd suppress-ra
{% endfor %}
add bgp bestpath as-path multipath-relax
add bgp neighbor FABRIC peer-group
add bgp neighbor FABRIC capability extended-nexthop
add bgp neighbor FABRIC remote-as external
{% for neighbor in nodes[inventory_hostname].neighbors %}
add bgp neighbor {{ neighbor }} peer-group FABRIC
{% endfor %}
exit/tasks/main.yml
Exit01(外接LeafSW)に限定した設定に関するロールです。- name: configure external route
nclu:
atomic: true
description: "configure external route"
template: |
{% if nodes[inventory_hostname].external is defined %}
{% if nodes[inventory_hostname].external.bgp is defined %}
{% for vrf in nodes[inventory_hostname].external.bgp %}
{% set conf=nodes[inventory_hostname].external.bgp[vrf] %}
add bgp vrf {{vrf}} autonomous-system {{nodes[inventory_hostname].asn}}
add bgp vrf {{vrf}} router-id {{nodes[inventory_hostname].loopback}}
{% for neighbor in nodes[inventory_hostname].external.bgp[vrf].neighbors %}
add bgp vrf {{vrf}} neighbor {{neighbor}} remote-as external
{% endfor %}
{% for network in nodes[inventory_hostname].external.bgp[vrf].networks %}
add bgp vrf {{vrf}} ipv4 unicast aggregate-address {{network}} summary-only
{% endfor %}
{% if nodes[inventory_hostname].external.bgp[vrf].evpn is defined %}
{% for evpn in nodes[inventory_hostname].external.bgp[vrf].evpn %}
{% if nodes[inventory_hostname].external.bgp[vrf].evpn[evpn].route_map is defined %}
{% set route_map = nodes[inventory_hostname].external.bgp[vrf].evpn[evpn].route_map %}
add bgp vrf {{vrf}} l2vpn evpn advertise ipv4 unicast route-map {{route_map}}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
{% endif %}
general/tasks/main.yml
IFやVLAN設定に関するロールです。- name: configure nodes
nclu:
atomic: true
description: "configure general settings"
template: |
{% if nodes[inventory_hostname].ports is defined %}
{% for port in nodes[inventory_hostname].ports %}
add interface {{port}}
{% if nodes[inventory_hostname].ports[port].mode is defined %}
{% if nodes[inventory_hostname].ports[port].mode == 'Tagged' %}
{% for vlan in nodes[inventory_hostname].ports[port].vlans %}
add interface {{port}} bridge vids {{vlan}}
{% endfor %}
{% endif %}
{% endif %}
{% if nodes[inventory_hostname].ports[port].speed is defined %}
add interface {{port}} link speed {{nodes[inventory_hostname].ports[port].speed}}
{% endif %}
{% endfor %}
{% endif %}
{% if nodes[inventory_hostname].route_maps is defined %}
{% for route_map in nodes[inventory_hostname].route_maps %}
add routing prefix-list ipv4 {{route_map}} permit {{nodes[inventory_hostname].route_maps[route_map].prefix}}
add routing route-map {{route_map}} permit {{loop.index}}0 match ip address prefix-list {{route_map}}
{% endfor %}
{% endif %}
leaf/tasks/main.yml
LeafSWのEVPN-VXLAN設定に関するロールです。- name: configure leaf
nclu:
atomic: true
description: "configure leaf"
template: |
add bgp l2vpn evpn neighbor FABRIC activate
add bgp l2vpn evpn advertise-all-vni
{% for vni in nodes[inventory_hostname].vxlans %}
{% set vid = nodes[inventory_hostname].vxlans[vni].vid %}
{% set vnid = 'vni-' + vni|string %}
add vxlan {{vnid}} vxlan id {{vni}}
add bridge bridge ports {{vnid}}
add bridge bridge vids {{vid}}
add bridge bridge vlan-aware
add vxlan {{vnid}} bridge access {{vid}}
add vxlan {{vnid}} bridge learning off
add vxlan {{vnid}} bridge arp-nd-suppress on
add vxlan {{vnid}} vxlan local-tunnelip {{ nodes[inventory_hostname].loopback }}
{% if nodes[inventory_hostname].vxlans[vni].vrf is defined %}
{% set vrf = nodes[inventory_hostname].vxlans[vni].vrf %}
add vrf {{vrf}} vni {{vni}}
{% endif %}
{% endfor %}
{% if nodes[inventory_hostname].svis is defined %}
{% for svi in nodes[inventory_hostname].svis %}
{% set vrf = nodes[inventory_hostname].svis[svi].vrf %}
{% if nodes[inventory_hostname].svis[svi].ipv4 is defined %}
add vlan {{svi}} ip address {{nodes[inventory_hostname].svis[svi].ipv4}}
{% endif %}
add vlan {{svi}} vrf {{vrf}}
{% endfor %}
{% endif %}
spine/tasks/main.yml
SpineSWのEVPN-VXLAN設定に関するロールです。- name: configure spine
nclu:
atomic: true
description: "configure spine"
template: |
add bgp l2vpn evpn neighbor FABRIC activate
Ansible Playbook実行
それでは、Playbookを実行します。「deploy.yml」の実行ログです。全てのタスクが"changed"となり、論理構成で示した設定が全て投入されました。# ansible-playbook deploy.yml
PLAY [spines] *********************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************
ok: [spine02]
ok: [spine01]
TASK [general : configure nodes] **************************************************************************************
changed: [spine01]
changed: [spine02]
TASK [bgp-peer : configure fabric] ************************************************************************************
changed: [spine01]
changed: [spine02]
TASK [spine : configure spine] ****************************************************************************************
changed: [spine01]
changed: [spine02]
PLAY [leafs] **********************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************
ok: [exit01]
ok: [leaf02]
ok: [leaf01]
TASK [general : configure nodes] **************************************************************************************
changed: [leaf01]
changed: [leaf02]
changed: [exit01]
TASK [bgp-peer : configure fabric] ************************************************************************************
changed: [leaf02]
changed: [leaf01]
changed: [exit01]
TASK [leaf : configure leaf] ******************************************************************************************
changed: [exit01]
changed: [leaf02]
changed: [leaf01]
PLAY [exits] **********************************************************************************************************
TASK [exit : configure external route] ********************************************************************************
changed: [exit01]
PLAY RECAP ************************************************************************************************************
exit01 : ok=5 changed=4 unreachable=0 failed=0
leaf01 : ok=4 changed=3 unreachable=0 failed=0
leaf02 : ok=4 changed=3 unreachable=0 failed=0
spine01 : ok=4 changed=3 unreachable=0 failed=0
spine02 : ok=4 changed=3 unreachable=0 failed=0
設定追加
設定追加してみます。Tenan2(VRF2)に新しくネットワーク VLAN202(VNI 10202)を追加します。設定追加するノードは、Leaf01とLeaf02の2台だけです。 group_vars/allに以下のとおり、設定情報を追加するだけです。一回テンプレートを利用してロールを作ってしまえば、その後のメンテナンスは非常に楽ですね~group_vars/all(抜粋)
nodes:
leaf01:
loopback: "10.0.0.11"
asn: 65011
vxlans:
10100:
vid: "100"
10200:
vid: "200"
10202: # 設定追加
vid: "202" # 設定追加
104001:
vid: "4001"
vrf: "vrf1"
104002:
vid: "4002"
vrf: "vrf2"
neighbors:
swp49:
swp50:
svis:
100:
ipv4: "172.16.100.1/24"
vrf: "vrf1"
200:
ipv4: "172.16.200.1/24"
vrf: "vrf2"
202: # 設定追加
ipv4: "172.16.202.1/24" # 設定追加
vrf: "vrf2" # 設定追加
4001:
vrf: "vrf1"
4002:
vrf: "vrf2"
ports:
swp1:
mode: Tagged
vlans:
- 100
- 200
- 202 # 設定追加
swp49:
speed: 40000
swp50:
speed: 40000
leaf02:
loopback: "10.0.0.12"
asn: 65012
vxlans:
10100:
vid: "100"
10200:
vid: "200"
10101:
vid: "101"
10201:
vid: "201"
10202: # 設定追加
vid: "202" # 設定追加
104001:
vid: "4001"
vrf: "vrf1"
104002:
vid: "4002"
vrf: "vrf2"
neighbors:
swp49:
swp50:
svis:
100:
ipv4: "172.16.100.1/24"
vrf: "vrf1"
200:
ipv4: "172.16.200.1/24"
vrf: "vrf2"
101:
ipv4: "172.16.101.1/24"
vrf: "vrf1"
201:
ipv4: "172.16.201.1/24"
vrf: "vrf2"
202: # 設定追加
ipv4: "172.16.202.1/24" # 設定追加
vrf: "vrf2" # 設定追加
4001:
vrf: "vrf1"
4002:
vrf: "vrf2"
ports:
swp1:
mode: Tagged
vlans:
- 100
- 101
- 200
- 201
- 202 # 設定追加
swp49:
speed: 40000
swp50:
speed: 40000
Playbook実行
それでは、Playbook実行します。設定の追加対象であるLeaf01、Leaf02のタスク「general」「leaf」のみ"changed"となっています。Ansibleの特徴である、冪等性がCumulus Linuxでもしっかり担保されていることが確認できました。# ansible-playbook deploy.yml
PLAY [spines] *********************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************
ok: [spine02]
ok: [spine01]
TASK [general : configure nodes] **************************************************************************************
ok: [spine01]
ok: [spine02]
TASK [bgp-peer : configure fabric] ************************************************************************************
ok: [spine01]
ok: [spine02]
TASK [spine : configure spine] ****************************************************************************************
ok: [spine01]
ok: [spine02]
PLAY [leafs] **********************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************
ok: [exit01]
ok: [leaf01]
ok: [leaf02]
TASK [general : configure nodes] **************************************************************************************
changed: [leaf01]
changed: [leaf02]
ok: [exit01]
TASK [bgp-peer : configure fabric] ************************************************************************************
ok: [leaf02]
ok: [exit01]
ok: [leaf01]
TASK [leaf : configure leaf] ******************************************************************************************
ok: [exit01]
changed: [leaf01]
changed: [leaf02]
PLAY [exits] **********************************************************************************************************
TASK [exit : configure external route] ********************************************************************************
ok: [exit01]
PLAY RECAP ************************************************************************************************************
exit01 : ok=5 changed=0 unreachable=0 failed=0
leaf01 : ok=4 changed=2 unreachable=0 failed=0
leaf02 : ok=4 changed=2 unreachable=0 failed=0
spine01 : ok=4 changed=0 unreachable=0 failed=0
spine02 : ok=4 changed=0 unreachable=0 failed=0
まとめ
Ansibleを用いた、ネットワーク構成管理のコード化、いかがでしたでしょうか。テンプレートを用いてロールを作りこんでしまえば、以降の設定追加のメンテナンスは非常に簡単に出来ることが確認出来ました。閲覧頂いた方の参考になっていれば幸いです!
実機で試してみたい方必見!POCキャンペーン開催中!!
ホワイトボックススイッチに興味ある方はこちらへ