さくらのクラウドでSONiCの検証環境を作ってみる

さくらのアドベントカレンダー2022 8日目の記事です。

https://qiita.com/advent-calendar/2022/sakura

この記事は、SONiCをつかった検証環境が急に欲しくなった人向けに、「さくらのクラウドだけで作れるよ！」という内容の~~ネタ~~記事です。

# SONiCとは
紹介記事などがたくさんあるため、詳しい説明は他の解説記事を参照していただきたいのですが、SONiCはホワイトボックススイッチ向けのオープンソースなOSです。基本的にはホワイトボックススイッチにインストールして利用しますが、検証用として仮想マシン上でも利用が可能です。

https://github.com/sonic-net/SONiC


# さくらのクラウドとSONiC
さくらのクラウドはIaaSなクラウドサービスなので、一般的にはサーバーOSをインストールして利用することが多いかと思いますが、今回はこのさくらのクラウド上にSONiCをデプロイし、SONiCの検証環境を構築することを目指します。

# やってみる

## SONiCイメージのダウンロード

SONiCのビルド済みイメージは以下のサイトにて一覧でまとめられており、ダウンロードが可能です。

https://sonic.software/

様々なプラットフォーム向けのイメージがありますが、`sonic-vs.img.gz` が仮想マシン用イメージですので、これをダウンロードします。今回は ブランチ `202205` を利用しました。


## SONiCイメージの変換

ダウンロードしたSONiCのイメージはgzipで圧縮されているので、まずはgunzipで伸長します。しかしながら配布されているイメージはqcow2フォーマットなので、そのままではさくらのクラウドにインポートできません。そのため `qemu-img` コマンドを使ってrawフォーマットに変換します。

```shell:イメージの変換
gunzip sonic-vs.img.gz 
qemu-img convert -f qcow2 -O raw sonic-vs.img sonic-vs.raw
```

## さくらのクラウドでアーカイブを作成する

さくらのクラウドでは、rawイメージをアップロードしてクラウドインスタンスとして実行することができる機能がありますので、以下ドキュメントを参考にrawイメージのアップロードを行います。rawイメージは16GB程度あったので、今回は余裕をもって40GBのブランクアーカイブを作成しました。

https://manual.sakura.ad.jp/cloud/design-pattern/tips/qcow2.html

コントロールパネル上でブランクのアーカイブを作成するとcurlコマンドのサンプルが表示されるので、このままファイル名だけ変更してコピペすると簡単にアップロードできます。
![アーカイブ作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36522/d695f95b-288f-b976-fa15-f69f73d98575.png)

## サーバーを作成する

SONiCイメージのアーカイブができたら、サーバーを作成します。今回は2Core 2GBプランで作成しました。
作成したSONiCのアーカイブは、「マイアーカイブ」で選択できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36522/8fc3c420-dd84-3408-fbc3-70ca1d42af61.png)


### サーバー作成するときに注意点

NICの準仮想化モードを無効化しておきます。準仮想化モードでもSONiC上でインターフェースはみえるのですが、スピード表示がおかしくなったりするので無効化するのが良いと思います。

またNICはサーバー作成後に追加するので、とりあえず「切断」しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36522/e88bd4ea-959e-3ee7-f366-c8eba82298cf.png)



もう1点、デフォルトでは「その他のオプション」の箇所に「作成後すぐに起動」のチェックボックスが有効になっていますが、前述した通りサーバー作成後にNICを追加するのでチェックを外しておきます。（サーバー起動中はNICの追加ができないため）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36522/03e84de6-228c-aaa3-435d-70a79e179361.png)

## スイッチの作成

SONiCサーバー（さくらのクラウドにデプロイされたものということで、あえてサーバーと呼びます）に接続するためのスイッチを作成します。さくらのクラウドにおいては、サーバーとサーバーを直結することができないため、SONiCサーバー同士を直結したいのであれば、直結したいリンク数分のスイッチを作成しておきます。

スイッチの作成では難しいところはありません。

https://manual.sakura.ad.jp/cloud/network/switch/router-switch.html

## NICの追加

作成したSONiCサーバーにNICを追加し、前項で作成したスイッチの接続を行います。サーバーへのNIC追加についても難しいところは無いと思います。

https://manual.sakura.ad.jp/cloud/server/nic.html?highlight=nic


### NIC追加における注意点

仮想マシンのSONiCイメージでは、先頭のNICがマネジメントポート(`eth0`)として認識されます。そのため、さくらのクラウドでNICを作成する場合でもその点を念頭においた上でNICを追加してください。

例えば以下のようにNICを追加した場合、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36522/72f00edd-177c-758b-054c-8f63f510c475.png)

SONiC内部での認識名との対応は以下のようになります。
| SONiCでの認識名 | クラウドコンパネで表示される番号 | 
|:-----------|------------:|
| eth0       | 0        | 
| Ethernet0     | 1      |
| Ethernet4   | 2        |
| Ethernet8  | 3          |
| Ethernet12  |4       |
| Ethernet16 | 5     |



# 起動
NICが追加できたら起動です！「電源操作」ボタンからサーバーを起動しましょう。起動後はコンソールからSONiCの操作ができます。なおデフォルトのユーザー名とパスワードは以下のドキュメントに記載していますので参考にしてください。
https://github.com/sonic-net/SONiC/blob/master/doc/SONiC-User-Manual.md

## 起動後
あとは自由にSONiCを使うだけです！... というわけにはいかず、さくらのクラウドで利用するためにはSONiCでいくつかの設定が必要です。


## MACアドレスの罠
さくらのクラウドでは、NICを追加すると固定のMACアドレスが割り振られます。

https://manual.sakura.ad.jp/cloud/support/technical/network-settings.html#support-network-settings-nic-macaddr-static

SONiCのデフォルトでは、 `eth0` のMACアドレスが `Ethernet0` や `Ethernet4` といったインターフェースのMACアドレスとして利用されるようなのですが、前述した通り `eth0` は先頭のNIC、`Ethernet0` は先頭から2番めのNICですので、送信元MACアドレスがさくらのクラウドによってアサインされたものと異なることになってしまい、結果パケットがSONiCサーバーから出ていけません。

まとめると、起動直後の初期状態では以下のようになります。

| SONiCでの認識名 | クラウドコンパネで表示される番号 | MACアドレス | 
|:-----------|------------|-----:|
| eth0 | 0 |クラウドコンパネ番号に応じたMACアドレス |
| eth1 | 1 |クラウドコンパネ番号に応じたMACアドレス|
| eth2 | 2 |クラウドコンパネ番号に応じたMACアドレス|
| Ethernet0 | 1 | eth0と同じMACアドレス | 
| Ethernet4 | 2 | eth0と同じMACアドレス | 

### MACアドレスを変更しよう

さくらのクラウドで利用するにあたっては、  `Ethernet*** `インターフェースのMACアドレスがマズいことがわかりましたので、これを変更しましょう。MACアドレスの変更は、 `config` コマンドでは行えないようなので、`/etc/sonic/config_db.json` を直接編集する必要があります。

```:設定例
$ cat /etc/sonic/config_db.json
（途中略）
    "INTERFACE": {
        "Ethernet0": {
            "mac_addr": "12:34:56:78:90:ab"
        },
        "Ethernet4": {
            "mac_addr": "11:22:33:44:55:66"
        },
        "Ethernet8": {
            "mac_addr": "aa:bb:cc:dd:ee:ff"
        }
    },
（後略）
```

設定後はリロードして反映させます。
```
$ sudo config reload -y
```

設定反映後 `ip link` コマンド等でMACアドレスを確認すると、変更されたことが確認できるかと思います。

## さらなるMACアドレスの罠
EthernetインターフェースのMACアドレス変更により、パケットがSONiCから出ていけるようになりました！
あとは自由にSONiCを使うだけです！ ... というわけにもいかず、なんだか挙動がおかしい状態となっています。


### pingを実行してみよう
この状態で、シンプルに以下のように接続し、pingを実行してみます。

```
 ┌────────────────┐               ┌────────────────┐
 │ SONiC          │               │ Ubuntu         │
 │ 192.168.1.1/24 ├───────────────┤ 192.168.1.2/24 │
 └────────────────┘               └────────────────┘
```

Ubuntu側から実行すると2つ応答があります。おかしいですね、直結しているはずなのに。
```
ubuntu@ubuntu:~$ ping 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=1.34 ms
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=1.34 ms (DUP!)
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=0.695 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=0.748 ms (DUP!)
64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=0.580 ms
```

ためしにUbuntu側から1つだけ投げ、SONiC側でパケットキャプチャしてみると、
```
ubuntu@ubuntu:~$ ping -c1 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=6.79 ms

--- 192.168.1.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 6.793/6.793/6.793/0.000 ms
ubuntu@ubuntu:~$
```

確かにSONiC側から2つのreplyが返されているようです。おかしいですね。
```
admin@sonic:~$ sudo tcpdump -nei Ethernet0 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on Ethernet0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
08:00:39.254735 9c:a3:ba:29:e7:33 > 9c:a3:ba:2b:79:20, ethertype IPv4 (0x0800), length 98: 192.168.1.1 > 192.168.1.2: ICMP echo reply, id 36, seq 1, length 64
08:00:39.254923 9c:a3:ba:2b:79:20 > 9c:a3:ba:29:e7:33, ethertype IPv4 (0x0800), length 98: 192.168.1.2 > 192.168.1.1: ICMP echo request, id 36, seq 1, length 64
08:00:39.254947 9c:a3:ba:29:e7:33 > 9c:a3:ba:2b:79:20, ethertype IPv4 (0x0800), length 98: 192.168.1.1 > 192.168.1.2: ICMP echo reply, id 36, seq 1, length 64
```

これは `eth1` でキャプチャしてみても同様です。
```
admin@sonic:~$ sudo tcpdump -nei eth1 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
08:00:57.316927 9c:a3:ba:2b:79:20 > 9c:a3:ba:29:e7:33, ethertype IPv4 (0x0800), length 98: 192.168.1.2 > 192.168.1.1: ICMP echo request, id 37, seq 1, length 64
08:00:57.317258 9c:a3:ba:29:e7:33 > 9c:a3:ba:2b:79:20, ethertype IPv4 (0x0800), length 98: 192.168.1.1 > 192.168.1.2: ICMP echo reply, id 37, seq 1, length 64
08:00:57.317407 9c:a3:ba:29:e7:33 > 9c:a3:ba:2b:79:20, ethertype IPv4 (0x0800), length 98: 192.168.1.1 > 192.168.1.2: ICMP echo reply, id 37, seq 1, length 64
```

この挙動、pingだけならまだしも？、たとえばBGPで接続しようとしたときに、SONiC側からackとrstが同時に返ってくるみたいな動きになります。こわい！

### 回避策

結論からいうと、上記の場合 `Ethernet0` に対応した `eth1` インターフェースのMACアドレスを手動で変更すると改善します。SONiCのインターフェースを介してのethインターフェースのMACアドレス変更方法は不明だったため、`ip link` コマンドで変更しました。

```:MACアドレスを変更する
$ sudo ip link set dev eth1 address ff:ee:dd:cc:bb:aa
```

`eth1` のMACアドレスを変更することで、 `Ethernet0` からの応答が1つになりました。

```
ubuntu@ubuntu:~$ ping -c 1 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=1.20 ms

--- 192.168.1.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.202/1.202/1.202/0.000 ms
```

```
admin@sonic:~$ sudo tcpdump -nei Ethernet0 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on Ethernet0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
08:02:09.401079 9c:a3:ba:2b:79:20 > 9c:a3:ba:29:e7:33, ethertype IPv4 (0x0800), length 98: 192.168.1.2 > 192.168.1.1: ICMP echo request, id 38, seq 1, length 64
08:02:09.401170 9c:a3:ba:29:e7:33 > 9c:a3:ba:2b:79:20, ethertype IPv4 (0x0800), length 98: 192.168.1.1 > 192.168.1.2: ICMP echo reply, id 38, seq 1, length 64
```

`eth1` でキャプチャしても1つで見えます。

```
admin@sonic:~$ sudo tcpdump -nei eth1 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
08:02:17.292052 9c:a3:ba:2b:79:20 > 9c:a3:ba:29:e7:33, ethertype IPv4 (0x0800), length 98: 192.168.1.2 > 192.168.1.1: ICMP echo request, id 39, seq 1, length 64
08:02:17.292469 9c:a3:ba:29:e7:33 > 9c:a3:ba:2b:79:20, ethertype IPv4 (0x0800), length 98: 192.168.1.1 > 192.168.1.2: ICMP echo reply, id 39, seq 1, length 64
```

どうも IPアドレスをアサインした `Ethernet0` に加えて `eth1` からも応答を返しているように見えなくもない挙動ですが、実際のところ原因はわかりません。もし詳しい方がいらっしゃいましたら教えてください...。



# ともあれ
MACアドレスの罠はあったものの、これでさくらのクラウド上でSONiCが利用できるようになりました！
さくらのクラウド上でSONiCによるIP Closな検証環境もちゃんと作れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36522/b62de524-87b0-cc3c-f908-4166a917270f.png)



ただ、普通にGNS3とかeve-ngとか使うほうが私はいいと思います。