SONiC ZTPを仮想マシン環境で試してみた
はじめに
SONiC 201911 ブランチが作成されてから半年近く経ち、各機能が安定して動作するようになってきました。本ブランチの個々の機能の開発状況については、201911ブランチ のトラッキングにて管理されています。以下がSONiC 201911ブランチにて開発された主な機能となります。
- VRF
- ZTP
- NAT
- sFlow
SONiC ZTPの紹介および本記事のシナリオ
SONiC ZTPのHigh Level Design(仕様および実装方法の概略)はこちらにて公開されています。SONiCのZTPは、SONiCを初期起動したときに、以下の初期設定・操作を行うことを可能にします。
補足になりますが、ZTP JSONでは01などの番号を付与することで、実行する順序を明示することが可能です。また、上記の"03-configdb-json"ではダウンロードするconfig_db.jsonは装置毎に変えたいため、dynamic-urlを使いHost nameによって別のファイルをダウンロードできるように制御しています。例えばHost NameがLeaf1の場合は、Leaf1_config_db.jsonをダウンロードします。これらのZTP JSONファイルの記述ルールは前述のZTPのHigh Level Designにて説明されています。
上記のZTP JSONファイルを前提に、SONiC ZTPの試験環境を用意し、動作確認を行っていきます。
- 運用で使用するSONiCイメージの再インストール
- 装置の設定(config_db.jsonのインストール)
- shellスクリプトによる任意の初期設定・操作
- pingによる起動確認
図1: GNS3上におけるZTPの試験構成
また、ZTP JSONの例として以下を使用します。{
"ztp": {
"01-firmware": {
"install": {
"url": "http://192.168.1.1/sonic-vs-ztp.bin"
},
"reboot-on-success": true
},
"02-provisioning-script": {
"plugin": {
"url":"http://192.168.1.1/apt_install.sh"
}
},
"03-configdb-json": {
"dynamic-url": {
"source": {
"prefix": "http://192.168.1.1/",
"identifier": "hostname",
"suffix": "_config_db.json"
}
}
},
"04-connectivity-check": {
"ping-hosts": [ "192.168.1.1" ]
},
"05-provisioning-script": {
"plugin": {
"url":"http://192.168.1.1/upload_log.sh"
}
}
}
}
このZTP JSONファイルの場合、以下の制御を順番に行います。
01-firmware | sonic-vs-ztp.binをインストールし、成功したらリブート |
02-provisioning-script | aptで必要なツール(tftp client)をインストール |
03-configdb-json | 装置毎のconfig_db.jsonをインストール |
04-connectivity-check | 管理ポートのGatewayへpingを実行 |
05-provisioning-script | ZTPのログをProvisioningサーバにtftpにてアップロード |
上記のZTP JSONファイルを前提に、SONiC ZTPの試験環境を用意し、動作確認を行っていきます。
ZTPが有効なSONiCイメージの作成
SONiCのZTPを使用するためにはZTPを有効にしたSONiCイメージを作成する必要があります。もしZTPが有効ではないSONiCイメージを起動した場合、SONiCイメージに埋め込まれているconfig_db.jsonを初期設定ファイルとして起動します。
SONiCイメージのビルド方法の詳細はこちらを参照ください。今回はGNS3上で動作確認をしたいため、Platformはvsとなります。今回の試験では、Ubuntu 16.04 Server、docker-ce (version 19.03.9)を使ってビルドしました。また、試験実施時に使用したSONiC 201911ブランチのcommit numberは7b5fb95fc7505b592b173769bb2fd089b4723e0eです。 以下のように順番にコマンドを実行し、SONiCのイメージを作成しました。(サーバ環境によりますが、フルビルドに4時間程度かかります)
SONiCイメージのビルド方法の詳細はこちらを参照ください。今回はGNS3上で動作確認をしたいため、Platformはvsとなります。今回の試験では、Ubuntu 16.04 Server、docker-ce (version 19.03.9)を使ってビルドしました。また、試験実施時に使用したSONiC 201911ブランチのcommit numberは7b5fb95fc7505b592b173769bb2fd089b4723e0eです。 以下のように順番にコマンドを実行し、SONiCのイメージを作成しました。(サーバ環境によりますが、フルビルドに4時間程度かかります)
sudo modprobe overlay
git clone https://github.com/Azure/sonic-buildimage
cd sonic-buildimage
git checkout 201911
git submodule update --init --recursive
make init
make configure PLATFORM=vs ENABLE_ZTP="y"
make all PLATFORM=vs ENABLE_ZTP="y"
なお、今回のZTPの試験は、最初に起動するSONiCイメージと、その後にZTPにてダウンロードするSONiCイメージの二つが必要になります。ですので、上記にてSONiCイメージを二回ビルドして、一方のSONiCイメージをGNS3に登録(こちらの記事を参照ください)し、もう一方のSONiCイメージはGNS3内のZTPサーバに保存しました。ZTPサーバの準備
GNS3にて、図1の通り、ZTPを有効にしたSONiCを配置し、ZTPサーバを用意します。本試験ではGNS3上にUbuntu 18.04 serverを起動し、それをZTPサーバとして設定します。本試験では、一台のZTPサーバにDHCP、NAT、TFTP、Provisioningの4つの役割を持たせますが、複数のサーバに役割を分けることも可能です。
まずはDHCPの設定を行います。DHCPは"apt-get install isc-dhcp-server"にてインストールしました。DHCPにて配布する情報を設定するために、GNS3にてLeaf1、Leaf2、SpineのMACアドレスを確認します。本試験では以下のMACアドレスの割り当てになっていました。
・apt_install.sh
・upload_log.sh
これらのshellスクリプトを実行するためには、aptとtftpを動作させる必要があります。SONiCからaptにてツールのインストールを可能にするためにZTPサーバにNATを設定します。/etc/sysctl.conf にnet.ipv4.ip_forward=1を追加し、sudo sysctl –pを実行することで、IPv4 forwardingを有効にします。そのうえで、iptablesにてNATを有効にするため、sudo iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE を実行します。次にTFTPサーバをsudo -E apt install tftpd-hpa にてインストールします。また、クライアントからファイルをTFTPにてアップロードできるように、/etc/default/tftpd-hpaに、TFTP_OPTIONS="--secure --create"を追記し、sudo /etc/init.d/tftpd-hpa restart にて設定を反映します。
以上で、ZTPサーバの準備が完了しました。
まずはDHCPの設定を行います。DHCPは"apt-get install isc-dhcp-server"にてインストールしました。DHCPにて配布する情報を設定するために、GNS3にてLeaf1、Leaf2、SpineのMACアドレスを確認します。本試験では以下のMACアドレスの割り当てになっていました。
図2: GNS3における各SONiCスイッチのMACアドレス
そこで、/etc/dhcp/dhcpd.conf に以下を追加します。この設定では、MACアドレス毎に異なるIPアドレスとHost nameを割り当てます。また、DHCP Option 67にてZTP JSONのURL(http://192.168.1.1/ztp.json)をホワイトボックススイッチにアナウンスしています。subnet 192.168.1.0 netmask 255.255.255.0 {
option domain-name "sonic-test.com";
option routers 192.168.1.1;
}
host Leaf1 {
hardware ethernet 0c:a6:61:d7:8b:00;
fixed-address 192.168.1.11;
option host-name "Leaf1";
#option 67
option bootfile-name "http://192.168.1.1/ztp.json";
}
host Leaf2 {
hardware ethernet 0c:a6:61:4a:a4:00;
fixed-address 192.168.1.12;
option host-name "Leaf2";
#option 67
option bootfile-name "http://192.168.1.1/ztp.json";
}
host Spine {
hardware ethernet 0c:a6:61:c0:be:00;
fixed-address 192.168.1.13;
option host-name "Spine";
#option 67
option bootfile-name "http://192.168.1.1/ztp.json";
}
これで、SONiCが初期起動したときに、DHCPによって自身のHost nameを設定した上で、ZTP JSONも自動的にダウンロードします。そのためにはhttpサーバをZTPサーバにて起動しておき、ZTP JSONファイルを配置しておく必要があります。また、その後も、ZTP JSONに記載しているSONiCイメージやconfig_db.json、shellスクリプト類もhttpにてダウンロードできるようにしておく必要があります。httpサーバのインストールはsudo apt install apache2 にて行いました。その後に/var/www/htmlにダウンロード用の以下のファイルを配置します。
- ztp.json
- sonic-vs-ztp.bin (ZTPを有効にした再インストール用のSONiC イメージ)
- Leaf1_config_db.json
- Leaf2_config_db.json
- Spine_config_db.json
- apt_install.sh
- upload_log.sh
・apt_install.sh
#! /bin/bash
sudo apt update
sudo apt install -y tftp-hpa
このshellスクリプトにて、aptを使いtftpのクライアントツールをインストールしています。・upload_log.sh
#! /bin/bash
sudo show ztp status --verbose > /tmp/ztp.log
tftp 192.168.1.1 -m binary -c put /tmp/ztp.log ztp_status_${HOSTNAME}.log
tftp 192.168.1.1 -m binary -c put /var/log/ztp.log ztp_log_${HOSTNAME}.log
このshellスクリプトにて、後述するshow ztp statusの結果をztp.logに保存し、それをtftpサーバにアップロードします。また、SONiC ZTP機能の実行ログ(/var/log/ztp.log)も、tftpサーバにアップロードします。これらのshellスクリプトを実行するためには、aptとtftpを動作させる必要があります。SONiCからaptにてツールのインストールを可能にするためにZTPサーバにNATを設定します。/etc/sysctl.conf にnet.ipv4.ip_forward=1を追加し、sudo sysctl –pを実行することで、IPv4 forwardingを有効にします。そのうえで、iptablesにてNATを有効にするため、sudo iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE を実行します。次にTFTPサーバをsudo -E apt install tftpd-hpa にてインストールします。また、クライアントからファイルをTFTPにてアップロードできるように、/etc/default/tftpd-hpaに、TFTP_OPTIONS="--secure --create"を追記し、sudo /etc/init.d/tftpd-hpa restart にて設定を反映します。
以上で、ZTPサーバの準備が完了しました。
SONiC ZTPの実行結果
GNS3上にて、SONiCのZTPの動作試験をしていきます。今回の試験では、図1の試験構成の中でLeaf1, Spineは既にZTPによる初期設定が完了している状態で、Leaf2を新たに接続して、ZTPにて初期設定をしたときの結果を示します。Leaf2を起動するとコンソール画面にZTP関連のログが出力されました。ログの量が多いため、一部のログは"..."にて省略しております。
ZTP service started.
Downloading provisioning data from http://192.168.1.1/ztp.json to /var/run/ztp/ztp_data_opt67.json
Starting ZTP using JSON file /var/run/ztp/ztp_data_opt67.json at 2020-05-22 02:28:45 UTC.
Checking running configuration to load ZTP configuration profile.
…
Processing configuration section 01-firmware at 2020-05-22 02:29:18 UTC.
firmware: Downloading file 'http://192.168.1.1/sonic-vs-ztp.bin'.
firmware: Version SONiC-OS-201911.0-7b5fb95f is already installed and is operational.
Processed Configuration section 01-firmware with result SUCCESS, exit code (0) at 2020-05-22 02:29:18 UTC.
ZTP is rebooting the device as reboot-on-success flag is set.
…
ZTP service started.
Starting ZTP using JSON file /host/ztp/ztp_data.json at 2020-05-22 02:28:45 UTC.
Checking running configuration to load ZTP configuration profile.
…
Processing configuration section 02-provisioning-script at 2020-05-22 02:31:08 UTC.
…
Processed Configuration section 02-provisioning-script with result SUCCESS, exit code (0) at 2020-05-22 02:31:08 UTC.
Processing configuration section 03-configdb-json at 2020-05-22 02:31:18 UTC.
configdb-json: Downloading config_db.json file from 'http://192.168.1.1/Leaf2_config_db.json'.
configdb-json: Configuration change detected. Removing ZTP configuation from Config DB.
configdb-json: Stopping ZTP discovery on interfaces.
configdb-json: Reloading config_db.json to Config DB.
…
configdb-json: Copying downloaded config_db.json to startup configuration.
Processed Configuration section 03-configdb-json with result SUCCESS, exit code (0) at 2020-05-22 02:31:18 UTC.
Processing configuration section 04-connectivity-check at 2020-05-22 02:32:29 UTC.
connectivity-check: Attempting to connect to IPv4 hosts ['192.168.1.1'].
connectivity-check: Pinging host '192.168.1.1’.
…
Processed Configuration section 04-connectivity-check with result SUCCESS, exit code (0) at 2020-05-22 02:32:29 UTC.
Processing configuration section 05-provisioning-script at 2020-05-22 02:32:33 UTC.
Processed Configuration section 05-provisioning-script with result SUCCESS, exit code (0) at 2020-05-22 02:32:33 UTC.
Checking configuration section 01-firmware result: SUCCESS, ignore-result: False.
Checking configuration section 02-provisioning-script result: SUCCESS, ignore-result: False.
Checking configuration section 03-configdb-json result: SUCCESS, ignore-result: False.
Checking configuration section 04-connectivity-check result: SUCCESS, ignore-result: False.
Checking configuration section 05-provisioning-script result: SUCCESS, ignore-result: False.
ZTP successfully completed at 2020-05-22 02:32:34 UTC.
ログをご覧いただくと最初にztp.jsonをダウンロードして、それ以降はzpt.jsonに記載された01から05の操作が順番に実行されていることが分かると思います。また、以下のようにSONiCのコマンドから、ZTPの状態を確認することが可能です。以下の例は、ZTPが完了した後の結果のため、全ての項目のStatusがSUCCESSになっていますが、ZTPが動作中に本コマンドを実行すると、中間状態を確認することも可能です。
$ sudo show ztp status --verbose
Command: ztp status --verbose
========================================
ZTP
========================================
ZTP Admin Mode : True
ZTP Service : Inactive
ZTP Status : SUCCESS
ZTP Source : dhcp-opt67 (eth0)
Runtime : 03m 51s
Timestamp : 2020-05-22 02:32:34 UTC
ZTP JSON Version : 1.0
ZTP Service is not running
----------------------------------------
01-firmware
----------------------------------------
Status : SUCCESS
Runtime : 16s
Timestamp : 2020-05-22 02:29:34 UTC
Exit Code : 0
Ignore Result : False
----------------------------------------
02-provisioning-script
----------------------------------------
Status : SUCCESS
Runtime : 10s
Timestamp : 2020-05-22 02:31:18 UTC
Exit Code : 0
Ignore Result : False
----------------------------------------
03-configdb-json
----------------------------------------
Status : SUCCESS
Runtime : 01m 11s
Timestamp : 2020-05-22 02:32:29 UTC
Exit Code : 0
Ignore Result : False
----------------------------------------
04-connectivity-check
----------------------------------------
Status : SUCCESS
Runtime : 04s
Timestamp : 2020-05-22 02:32:33 UTC
Exit Code : 0
Ignore Result : False
----------------------------------------
05-provisioning-script
----------------------------------------
Status : SUCCESS
Runtime : 01s
Timestamp : 2020-05-22 02:32:34 UTC
Exit Code : 0
Ignore Result : False
また、今回のztp.jsonでは最後にupload_log.shを実行して、TFTPサーバにZTP関連のログをアップロードするようにしていましたが、以下の通り、ZTPサーバにZTP関連ログがアップロードされていることも確認できました。
$ ls -l /var/lib/tftpboot
-rw-rw-rw- 1 tftp tftp 13732 May 22 11:32 ztp_log_Leaf2.log
-rw-rw-rw- 1 tftp tftp 1605 May 22 11:32 ztp_status_Leaf2.log
これでZTPによりLeaf2が初期設定された上で起動しました。以下のように、Leaf1、SpineからBGPにてルート情報を取得できており、またLeaf1配下のIPサブネットに対してLeaf2からのpingが成功することを確認しました。
Leaf2# show ip route
Codes: K - kernel route, C - connected, S - static, R - RIP,
O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
F - PBR, f - OpenFabric,
> - selected route, * - FIB route, q - queued route, r - rejected route
S>* 0.0.0.0/0 [200/0] via 192.168.1.1, eth0, 00:12:12
B>* 1.1.1.1/32 [20/0] via 10.1.2.1, Ethernet0, 00:00:41
C>* 2.2.2.2/32 is directly connected, Loopback0, 00:12:06
B>* 3.3.3.3/32 [20/0] via 10.1.2.1, Ethernet0, 00:00:41
C>* 10.1.2.0/24 is directly connected, Ethernet0, 00:12:05
B>* 10.11.0.0/24 [20/0] via 10.1.2.1, Ethernet0, 00:00:41
C>* 10.22.0.0/24 is directly connected, Vlan1001, 00:12:05
C>* 192.168.1.0/24 is directly connected, eth0, 00:12:14
Leaf2#
Leaf2# ping 10.11.0.1
PING 10.11.0.1 (10.11.0.1) 56(84) bytes of data.
64 bytes from 10.11.0.1: icmp_seq=1 ttl=63 time=1.51 ms
64 bytes from 10.11.0.1: icmp_seq=2 ttl=63 time=1.35 ms
64 bytes from 10.11.0.1: icmp_seq=3 ttl=63 time=1.25 ms
^C
--- 10.11.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 1.254/1.373/1.514/0.115 ms
Leaf2#
最後に
今回はSONiC ZTPの動作確認結果を共有させていただきました。今回はGNS3にてZTPの動作確認をしましたが、ハードウェアに依存する要素がないため、ハードウェアでも同様な手順でSONiCのZTPを動作させることができると考えます。SONiC 201911ブランチは他にも重要な機能が開発されておりますので、引き続き、動作確認した結果を共有していきたいと思います。
補足:使用したconfig_db.json (SONiC設定ファイル)
本試験では、Leaf1, Leaf2, Spineには、以下のconfig_db.jsonを使用しました。
・Leaf1
・Leaf1
{
"BGP_NEIGHBOR": {
"10.1.1.1": {
"asn": "65100",
"holdtime": "180",
"keepalive": "60",
"local_addr": "10.1.1.2",
"name": "Spine1",
"nhopself": "0",
"rrclient": "0"
}
},
"DEVICE_METADATA": {
"localhost": {
"bgp_asn": "65110",
"hostname": "Leaf1",
"hwsku": "Force10-S6000",
"mac": "52:54:00:12:34:01",
"platform": "x86_64-kvm_x86_64-r0",
"type": "LeafRouter"
}
},
"MGMT_INTERFACE": {
"eth0|192.168.1.11/24": {
"gwaddr": "192.168.1.1"
}
},
"INTERFACE": {
"Ethernet0|10.1.1.2/24": {}
},
"VLAN_INTERFACE": {
"Vlan1001|10.11.0.1/24": {}
},
"LOOPBACK_INTERFACE": {
"Loopback0|1.1.1.1/32": {}
},
"PORT": {
"Ethernet0": {
"admin_status": "up",
"alias": "fortyGigE0/0",
"index": "0",
"lanes": "25,26,27,28",
"mtu": "9100",
"speed": "40000"
},
"Ethernet4": {
"admin_status": "up",
"alias": "fortyGigE0/4",
"index": "1",
"lanes": "29,30,31,32",
"mtu": "9100",
"speed": "40000"
}
},
"TELEMETRY": {
"gnmi": {
"port": "8080"
}
},
"VLAN": {
"Vlan1001": {
"members": [
"Ethernet4"
],
"vlanid": "1001"
}
},
"VLAN_MEMBER": {
"Vlan1001|Ethernet4": {
"tagging_mode": "untagged"
}
}
}
・Leaf2
{
"BGP_NEIGHBOR": {
"10.1.2.1": {
"asn": "65100",
"holdtime": "180",
"keepalive": "60",
"local_addr": "10.1.2.2",
"name": "Spine1",
"nhopself": "0",
"rrclient": "0"
}
},
"DEVICE_METADATA": {
"localhost": {
"bgp_asn": "65120",
"hostname": "Leaf2",
"hwsku": "Force10-S6000",
"mac": "52:54:00:12:34:01",
"platform": "x86_64-kvm_x86_64-r0",
"type": "LeafRouter"
}
},
"MGMT_INTERFACE": {
"eth0|192.168.1.12/24": {
"gwaddr": "192.168.1.1"
}
},
"INTERFACE": {
"Ethernet0|10.1.2.2/24": {}
},
"VLAN_INTERFACE": {
"Vlan1001|10.22.0.1/24": {}
},
"LOOPBACK_INTERFACE": {
"Loopback0|2.2.2.2/32": {}
},
"PORT": {
"Ethernet0": {
"admin_status": "up",
"alias": "fortyGigE0/0",
"index": "0",
"lanes": "25,26,27,28",
"mtu": "9100",
"speed": "40000"
},
"Ethernet4": {
"admin_status": "up",
"alias": "fortyGigE0/4",
"index": "1",
"lanes": "29,30,31,32",
"mtu": "9100",
"speed": "40000"
}
},
"TELEMETRY": {
"gnmi": {
"port": "8080"
}
},
"VLAN": {
"Vlan1001": {
"members": [
"Ethernet4"
],
"vlanid": "1001"
}
},
"VLAN_MEMBER": {
"Vlan1001|Ethernet4": {
"tagging_mode": "untagged"
}
}
}
・Spine
{
"BGP_NEIGHBOR": {
"10.1.1.2": {
"asn": "65110",
"holdtime": "180",
"keepalive": "60",
"local_addr": "10.1.1.1",
"name": "Leaf1",
"nhopself": "0",
"rrclient": "0"
},
"10.1.2.2": {
"asn": "65120",
"holdtime": "180",
"keepalive": "60",
"local_addr": "10.1.2.1",
"name": "Leaf2",
"nhopself": "0",
"rrclient": "0"
}
},
"DEVICE_METADATA": {
"localhost": {
"bgp_asn": "65100",
"hostname": "Spine",
"hwsku": "Force10-S6000",
"mac": "52:54:00:12:34:01",
"platform": "x86_64-kvm_x86_64-r0",
"type": "LeafRouter"
}
},
"MGMT_INTERFACE": {
"eth0|192.168.1.13/24": {
"gwaddr": "192.168.1.1"
}
},
"INTERFACE": {
"Ethernet0|10.1.1.1/24": {},
"Ethernet4|10.1.2.1/24": {}
},
"LOOPBACK_INTERFACE": {
"Loopback0|3.3.3.3/32": {}
},
"PORT": {
"Ethernet0": {
"admin_status": "up",
"alias": "fortyGigE0/0",
"index": "0",
"lanes": "25,26,27,28",
"mtu": "9100",
"speed": "40000"
},
"Ethernet4": {
"admin_status": "up",
"alias": "fortyGigE0/4",
"index": "1",
"lanes": "29,30,31,32",
"mtu": "9100",
"speed": "40000"
}
},
"TELEMETRY": {
"gnmi": {
"port": "8080"
}
}
}