こんにちは。
株式会社クラスアクトインフラストラクチャ事業部の大塚と申します。
この記事では題名の通り、OVSとnamespaceを用いサーバ内部に仮想的なネットワークを構築していきたいと思います。
なぜOVSを触ってみるのか?
①弊社でProxmox VEを触る機会が増え、Proxmox VEはOVSが利用可能で、理解しておくと今後何かと便利かと考えたため
②個人的にopenstackの勉強をしていた時期があるのだが、その中でOVSの話があり、触りたいと思っていたため
OVSとは?
Open vSwicthの略。
ざっくりな理解で恐縮ですが、個人的には
「サーバ内に仮想的なbridgeやveth(Virtual ETHernet)を作成するためのもの」
という認識を持っています。
私はこのOVSという技術をopenstackで初めて知りました。
openstackだけではなく、dockerやkubernetes、KVM等でも使用できることから、今後の仮想化技術をより理解しるためには
避けては通れない道かと考えております。
namespaceとは?
これもざっくりな理解で恐縮ですが、個人的には
「1つのOS空間をいくつかに分断して、分断した空間同士でプロセス等を秘匿化(見えないようにすること)で、あたかも複数のサーバが稼働しているように見える」
という認識です。
dockerやコンテナ技術について1度でも見たり聞いたりしたことがある方は、それをイメージしてもらえればいいのかなと思っています。
※基本的にはnamespaceありきのdockerだと思いますので、本当であれば順序は逆だと思いますが・・・
例えば1つのOSを特に仮想的に分断しないでそのまま使用し、サーバ上に様々なアプリケーションが乱立していることを想像してみます。
この状態だと、プロセスもアプリケーションの数に比例して増えてしまう可能性があり、その結果OS環境がごちゃごちゃしてしまいます。
namespaceを使うことで、例えば各アプリケーションごとに分断した空間を提供することができ、結果ごちゃごちゃが解消され運用保守なども行いやすくなるでしょう。
※図に使用しているイラストはicons8になります
構築
今回はProxmox VE上にubuntu22.04でVMを立ち上げ、そこにプライベートネットワークを構築していきます。
HW構成は以下となります。
この記事では以下のネットワークをサーバ内に構築し、namespaceAとnamespaceC及びnamespaceBとnamespaceDの疎通確認まで行っていきたいと思います。
また、以下の記事を参考にしています。
図とコマンドが簡潔に表示されており、非常にわかりやすかったです。
この記事は参考記事をほぼ写経しているような形ですが、図を多めに配置し、
よりグラフィカルにイメージできるようにしてみました。主に私のために
まずVM上にovsをインストールしていきます。
インストールができましたら、start,enableも忘れずに行っておきましょう。
root@ovs-test:~# apt install -y openvswitch-switch
root@ovs-test:~# systemctl start openvswitch-switch
root@ovs-test:~# systemctl status openvswitch-switch
● openvswitch-switch.service - Open vSwitch
Loaded: loaded (/lib/systemd/system/openvswitch-switch.service; enabled; vendor preset: enabled)
Active: active (exited) since Mon 2023-03-27 21:38:47 UTC; 44s ago
Process: 3886 ExecStart=/bin/true (code=exited, status=0/SUCCESS)
Main PID: 3886 (code=exited, status=0/SUCCESS)
CPU: 1ms
Mar 27 21:38:47 ovs-test systemd[1]: Starting Open vSwitch...
Mar 27 21:38:47 ovs-test systemd[1]: Finished Open vSwitch.
root@ovs-test:~# systemctl enable openvswitch-switch
Synchronizing state of openvswitch-switch.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable openvswitch-switch
このタイミングでipコマンドを打っておき、出力の変化を確認できるようにしておきます。
root@ovs-test:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:d8:9a:43:a5:d6 brd ff:ff:ff:ff:ff:ff
altname enp0s18
inet 192.168.2.186/24 metric 100 brd 192.168.2.255 scope global dynamic ens18
valid_lft 348sec preferred_lft 348sec
inet6 fe80::50d8:9aff:fe43:a5d6/64 scope link
valid_lft forever preferred_lft forever
ブリッジを作成します。
ブリッジの名前は"ovsA"と"ovsB"としました。
ovs-vsctl showコマンドで作成したブリッジが表示されていることを確認します。
root@ovs-test:~# ovs-vsctl add-br ovsA
root@ovs-test:~# ovs-vsctl add-br ovsB
root@ovs-test:~# ovs-vsctl show
d9f37d67-9065-4bed-8741-30a72b02542c
Bridge ovsB
Port ovsB
Interface ovsB
type: internal
Bridge ovsA
Port ovsA
Interface ovsA
type: internal
ovs_version: "2.17.3"
併せてipコマンドもたたいてみます。
出力内容が増えていることが確認できるかと思います。
root@ovs-test:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:d8:9a:43:a5:d6 brd ff:ff:ff:ff:ff:ff
altname enp0s18
inet 192.168.2.186/24 metric 100 brd 192.168.2.255 scope global dynamic ens18
valid_lft 357sec preferred_lft 357sec
inet6 fe80::50d8:9aff:fe43:a5d6/64 scope link
valid_lft forever preferred_lft forever
3: ovs-system: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 06:ec:e2:4c:75:13 brd ff:ff:ff:ff:ff:ff
4: ovsA: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether b2:d7:65:fb:45:46 brd ff:ff:ff:ff:ff:ff
5: ovsB: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 76:e9:68:11:0b:4b brd ff:ff:ff:ff:ff:ff
この時の仮想ネットワークのイメージは以下となります。
ovsAとovsBというブリッジが作成されています。
次にnamespaceを作成していきます。
今回は"nsA","nsB","nsC","nsD"という名前のnamespaceを作成しています。
root@ovs-test:~# ip netns add nsA
root@ovs-test:~# ip netns add nsB
root@ovs-test:~# ip netns add nsC
root@ovs-test:~# ip netns add nsD
この時のイメージは以下です。
まだ、ethernetなどの設定をまったくやっていないため、存在しているだけの状態となります。
実際にvetheを作成していきます。
ここでは作成だけしており、ブリッジやnamespace等に紐づけてはおりません。
また、1つのコマンドで対になるvethを作成しているように見えます。
例えば以下のコマンドですと、"nsAovsA"という名前のvethの対として"ovsAnsA"というvethを併せて
作成しております。
root@ovs-test:~# ip link add name nsAovsA type veth peer name ovsAnsA
root@ovs-test:~# ip link add name nsBovsA type veth peer name ovsAnsB
root@ovs-test:~# ip link add name nsCovsB type veth peer name ovsBnsC
root@ovs-test:~# ip link add name nsDovsB type veth peer name ovsBnsD
ipコマンドをたたいてみますと、4つしかコマンドはたたいておりませんが、
計8つのvethが作成されていることがわかるかと思います。
root@ovs-test:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:d8:9a:43:a5:d6 brd ff:ff:ff:ff:ff:ff
altname enp0s18
inet 192.168.2.186/24 metric 100 brd 192.168.2.255 scope global dynamic ens18
valid_lft 341sec preferred_lft 341sec
inet6 fe80::50d8:9aff:fe43:a5d6/64 scope link
valid_lft forever preferred_lft forever
3: ovs-system: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 06:ec:e2:4c:75:13 brd ff:ff:ff:ff:ff:ff
4: ovsA: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether b2:d7:65:fb:45:46 brd ff:ff:ff:ff:ff:ff
5: ovsB: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 76:e9:68:11:0b:4b brd ff:ff:ff:ff:ff:ff
6: ovsAnsA@nsAovsA: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 76:2b:cc:69:d6:70 brd ff:ff:ff:ff:ff:ff
7: nsAovsA@ovsAnsA: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 1e:05:1c:48:c8:c4 brd ff:ff:ff:ff:ff:ff
8: ovsAnsB@nsBovsA: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 86:c6:5b:ae:eb:4b brd ff:ff:ff:ff:ff:ff
9: nsBovsA@ovsAnsB: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 62:55:e6:b5:f1:86 brd ff:ff:ff:ff:ff:ff
10: ovsBnsC@nsCovsB: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether ce:44:1e:be:b9:71 brd ff:ff:ff:ff:ff:ff
11: nsCovsB@ovsBnsC: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 1e:54:b0:70:6b:55 brd ff:ff:ff:ff:ff:ff
12: ovsBnsD@nsDovsB: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether b2:85:57:c4:8b:b4 brd ff:ff:ff:ff:ff:ff
13: nsDovsB@ovsBnsD: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether de:d5:00:a6:1a:78 brd ff:ff:ff:ff:ff:ff
ブリッジに作成したvethを割り当てていきます。
tagというオプションがVLANにあたるみたいです。
root@ovs-test:~# ovs-vsctl add-port ovsA ovsAnsA tag=10
root@ovs-test:~# ovs-vsctl add-port ovsA ovsAnsB tag=20
root@ovs-test:~# ovs-vsctl add-port ovsB ovsBnsC tag=10
root@ovs-test:~# ovs-vsctl add-port ovsB ovsBnsD tag=20
ovs-vsctl showの出力結果を確認します。
紐づいていることが確認できますね。
root@ovs-test:~# ovs-vsctl show
d9f37d67-9065-4bed-8741-30a72b02542c
Bridge ovsB
Port ovsBnsC
tag: 10
Interface ovsBnsC
Port ovsB
Interface ovsB
type: internal
Port ovsBnsD
tag: 20
Interface ovsBnsD
Bridge ovsA
Port ovsAnsA
tag: 10
Interface ovsAnsA
Port ovsA
Interface ovsA
type: internal
Port ovsAnsB
tag: 20
Interface ovsAnsB
ovs_version: "2.17.3"
OVSブリッジ同士を接続していきます。
併せてovs-vsctlコマンドで確認してみます。
root@ovs-test:~# ip link add name ovsAovsB type veth peer name ovsBovsA
root@ovs-test:~# ovs-vsctl add-port ovsA ovsAovsB
root@ovs-test:~# ovs-vsctl add-port ovsB ovsBovsA
root@ovs-test:~# ovs-vsctl show
d9f37d67-9065-4bed-8741-30a72b02542c
Bridge ovsB
Port ovsBnsC
tag: 10
Interface ovsBnsC
Port ovsBovsA
Interface ovsBovsA
Port ovsB
Interface ovsB
type: internal
Port ovsBnsD
tag: 20
Interface ovsBnsD
Bridge ovsA
Port ovsAnsA
tag: 10
Interface ovsAnsA
Port ovsA
Interface ovsA
type: internal
Port ovsAovsB
Interface ovsAovsB
Port ovsAnsB
tag: 20
Interface ovsAnsB
ovs_version: "2.17.3"
nsにvethを接続していきます。
root@ovs-test:~# ip link set nsAovsA netns nsA
root@ovs-test:~# ip link set nsBovsA netns nsB
root@ovs-test:~# ip link set nsCovsB netns nsC
root@ovs-test:~# ip link set nsDovsB netns nsD
これだけで接続できればいいのですが、vethやブリッジなどを稼働させなければなりません。
以下のコマンドを実行していきます。
root@ovs-test:~# ip link set ovsA up
root@ovs-test:~# ip link set ovsB up
root@ovs-test:~# ip link set ovsAovsB up
root@ovs-test:~# ip link set ovsBovsA up
root@ovs-test:~# ip link set ovsAnsA up
root@ovs-test:~# ip link set ovsAnsB up
root@ovs-test:~# ip link set ovsBnsC up
root@ovs-test:~# ip link set ovsBnsD up
root@ovs-test:~# ip netns exec nsA ip link set nsAovsA up
root@ovs-test:~# ip netns exec nsB ip link set nsBovsA up
root@ovs-test:~# ip netns exec nsC ip link set nsCovsB up
root@ovs-test:~# ip netns exec nsD ip link set nsDovsB up
root@ovs-test:~# ip netns exec nsA ip link set lo up
root@ovs-test:~# ip netns exec nsB ip link set lo up
root@ovs-test:~# ip netns exec nsC ip link set lo up
root@ovs-test:~# ip netns exec nsD ip link set lo up
この時のipコマンドの出力結果は以下です
state DOWN状態から変化していることが確認できると思います。
root@ovs-test:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:d8:9a:43:a5:d6 brd ff:ff:ff:ff:ff:ff
altname enp0s18
inet 192.168.2.186/24 metric 100 brd 192.168.2.255 scope global dynamic ens18
valid_lft 491sec preferred_lft 491sec
inet6 fe80::50d8:9aff:fe43:a5d6/64 scope link
valid_lft forever preferred_lft forever
3: ovs-system: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 06:ec:e2:4c:75:13 brd ff:ff:ff:ff:ff:ff
4: ovsA: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
link/ether b2:d7:65:fb:45:46 brd ff:ff:ff:ff:ff:ff
inet6 fe80::b0d7:65ff:fefb:4546/64 scope link
valid_lft forever preferred_lft forever
5: ovsB: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
link/ether 76:e9:68:11:0b:4b brd ff:ff:ff:ff:ff:ff
inet6 fe80::74e9:68ff:fe11:b4b/64 scope link
valid_lft forever preferred_lft forever
6: ovsAnsA@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master ovs-system state UP group default qlen 1000
link/ether 76:2b:cc:69:d6:70 brd ff:ff:ff:ff:ff:ff link-netns nsA
inet6 fe80::742b:ccff:fe69:d670/64 scope link
valid_lft forever preferred_lft forever
8: ovsAnsB@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master ovs-system state UP group default qlen 1000
link/ether 86:c6:5b:ae:eb:4b brd ff:ff:ff:ff:ff:ff link-netns nsB
inet6 fe80::84c6:5bff:feae:eb4b/64 scope link
valid_lft forever preferred_lft forever
10: ovsBnsC@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master ovs-system state UP group default qlen 1000
link/ether ce:44:1e:be:b9:71 brd ff:ff:ff:ff:ff:ff link-netns nsC
inet6 fe80::cc44:1eff:febe:b971/64 scope link
valid_lft forever preferred_lft forever
12: ovsBnsD@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master ovs-system state UP group default qlen 1000
link/ether b2:85:57:c4:8b:b4 brd ff:ff:ff:ff:ff:ff link-netns nsD
inet6 fe80::b085:57ff:fec4:8bb4/64 scope link
valid_lft forever preferred_lft forever
14: ovsBovsA@ovsAovsB: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master ovs-system state UP group default qlen 1000
link/ether e6:7e:aa:bb:58:e5 brd ff:ff:ff:ff:ff:ff
inet6 fe80::e47e:aaff:febb:58e5/64 scope link
valid_lft forever preferred_lft forever
15: ovsAovsB@ovsBovsA: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master ovs-system state UP group default qlen 1000
link/ether b6:80:8c:9c:80:c2 brd ff:ff:ff:ff:ff:ff
inet6 fe80::b480:8cff:fe9c:80c2/64 scope link
valid_lft forever preferred_lft forever
nsのvethにそれぞれIPアドレスを割り当てていきます。
ネットワークは192.168.2.11~14としました。
root@ovs-test:~# ip netns exec nsA ip addr add 192.168.2.11/24 dev nsAovsA
root@ovs-test:~# ip netns exec nsB ip addr add 192.168.2.12/24 dev nsBovsA
root@ovs-test:~# ip netns exec nsC ip addr add 192.168.2.13/24 dev nsCovsB
root@ovs-test:~# ip netns exec nsD ip addr add 192.168.2.14/24 dev nsDovsB
nsAからnsC、nsBからnsDに対してpingを実行していきます。
双方とも疎通できていることが確認できますね!
今回はここまでです!
root@ovs-test:~# ip netns exec nsA ping 192.168.2.13
PING 192.168.2.13 (192.168.2.13) 56(84) bytes of data.
64 bytes from 192.168.2.13: icmp_seq=1 ttl=64 time=1.95 ms
64 bytes from 192.168.2.13: icmp_seq=2 ttl=64 time=0.158 ms
64 bytes from 192.168.2.13: icmp_seq=3 ttl=64 time=0.112 ms
64 bytes from 192.168.2.13: icmp_seq=4 ttl=64 time=0.122 ms
64 bytes from 192.168.2.13: icmp_seq=5 ttl=64 time=0.105 ms
^C
--- 192.168.2.13 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4065ms
rtt min/avg/max/mdev = 0.105/0.489/1.952/0.731 ms
root@ovs-test:~# ip netns exec nsB ping 192.168.2.14
PING 192.168.2.14 (192.168.2.14) 56(84) bytes of data.
64 bytes from 192.168.2.14: icmp_seq=1 ttl=64 time=2.07 ms
64 bytes from 192.168.2.14: icmp_seq=2 ttl=64 time=0.116 ms
64 bytes from 192.168.2.14: icmp_seq=3 ttl=64 time=0.134 ms
64 bytes from 192.168.2.14: icmp_seq=4 ttl=64 time=0.094 ms
64 bytes from 192.168.2.14: icmp_seq=5 ttl=64 time=0.119 ms
^C
--- 192.168.2.14 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4056ms
rtt min/avg/max/mdev = 0.094/0.506/2.070/0.781 ms