Carpe Diem

備忘録

ip netnsコマンドで学ぶNetwork Namespace

概要

Linuxには名前空間(Namespace)というカーネルの機能が提供されています。
これは1つのプロセスが1つのリソースセットを参照し、別のプロセスが異なるリソースセットを参照するようにカーネルリソースを分割する機能です。

その中の1つであるネットワーク名前空間(Network Namespace)の機能を学んでみます。

環境

ip netnsを使ってみる

初期状態

デフォルトのUbuntuでは以下のように2つのNICが存在します。

  • lo
  • enp0s3

コマンドで確認します。

$ ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 02:b7:1f:33:e9:24 brd ff:ff:ff:ff:ff:ff

図では以下のような状態です。

ネットワーク名前空間を作る

host1というネットワーク名前空間を作ります。

ip netns addで作ります。

$ sudo ip netns add host1
$ ip netns l
host1

ここで作成された名前空間にはループバックインタフェースのみが存在します。

ネットワーク名前空間の中に入ってみる

ip netns execで中に入ることができます。

vagrant@ubuntu-bionic:~$ sudo ip netns exec host1 bash
root@ubuntu-bionic:~#

プロンプトが変わっていることが分かりますね。中のNICを確認すると、先ほどの図の通りループバックのloが存在します。

root@ubuntu-bionic:~# ip l
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

ただ初期状態ではDOWNしているのでifconfigでは見えません
先ほどの図でloが点線だったのはそのためです。

root@ubuntu-bionic:~# ifconfig
root@ubuntu-bionic:~#

pingを打ってもunreachableが返ります。

root@ubuntu-bionic:~# ping 127.0.0.1
connect: Network is unreachable

なのでNICをUP状態にします。

root@ubuntu-bionic:~# ip link set lo up
root@ubuntu-bionic:~# ping 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.018 ms

pingも返るようになりました。

ホストLinuxと疎通させる

このままだと何も通信できないので、ホストLinuxと疎通できるようにします。
ホストLinux側で以下のようにip link addvethを用意します。

$ sudo ip link add name veth1 type veth peer name host1-veth1
$ ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 02:b7:1f:33:e9:24 brd ff:ff:ff:ff:ff:ff
3: host1-veth1@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether c6:fc:67:d3:45:bc brd ff:ff:ff:ff:ff:ff
4: veth1@host1-veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 76:40:af:6f:eb:67 brd ff:ff:ff:ff:ff:ff

name veth1peer name host1-veth1はオプションなので省略しても問題ありません。今回はveth1host1-veth1といった分かりやすい名前を付けたかったのでオプションで指定しています。

図で表すとこの状態です。

vethのピアをネットワーク名前空間

先ほど作ったvethのピアhost1-veth1host1へバインドします。

$ sudo ip link set host1-veth1 netns host1

するとホストLinuxからNICが消えます。

$ ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 02:b7:1f:33:e9:24 brd ff:ff:ff:ff:ff:ff
4: veth1@if3: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 76:40:af:6f:eb:67 brd ff:ff:ff:ff:ff:ff link-netnsid 0

host1に入って確認してみると、先ほどのNICが存在しています。

$ sudo ip netns exec host1 bash
root@ubuntu-bionic:~# ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
3: host1-veth1@if4: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether c6:fc:67:d3:45:bc brd ff:ff:ff:ff:ff:ff link-netnsid 0

図で表すと以下の状態です。

IPアドレスを付与

では最後にIPアドレスを付与してNICをUPさせます。

互いに通信できるよう、

  • 同一サブネットにする
  • お互いのIPは重複しないようにする

の2点に注意してください。

今回はサブネットを10.0.0.0/24として付与していきます。

ホストLinux

IPアドレス10.0.0.1を付与。サブネットマスク/24(255.255.255.0)で。

$ sudo ip addr add 10.0.0.1/24 dev veth1

NICをUP。

$ sudo ip link set veth1 up
$ 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: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 02:b7:1f:33:e9:24 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3
       valid_lft 83214sec preferred_lft 83214sec
    inet6 fe80::b7:1fff:fe33:e924/64 scope link
       valid_lft forever preferred_lft forever
4: veth1@if3: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state LOWERLAYERDOWN group default qlen 1000
    link/ether 76:40:af:6f:eb:67 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.0.0.1/24 scope global veth1
       valid_lft forever preferred_lft forever

ネットワーク名前空間host1

IPアドレス10.0.0.2を付与。サブネットマスク/24(255.255.255.0)で。

# ip addr add 10.0.0.2/24 dev host1-veth1

NICをUP。

# ip link set host1-veth1 up
# 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
3: host1-veth1@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether c6:fc:67:d3:45:bc brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.0.0.2/24 scope global host1-veth1
       valid_lft forever preferred_lft forever
    inet6 fe80::c4fc:67ff:fed3:45bc/64 scope link
       valid_lft forever preferred_lft forever

図で表すと以下です。

動作確認

ホストLinuxからhost1へのping

10.0.0.1から10.0.0.2です。

vagrant@ubuntu-bionic:~$ ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.020 ms

疎通できてますね。

host1からホストLinuxへのping

10.0.0.2から10.0.0.1です。

root@ubuntu-bionic:~# ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.028 ms

こちらも疎通できてますね。

その他

ネットワーク名前空間はホストLinuxを再起動すると消えてなくなります。
なので再起動しても同じような環境を用意したい場合、ブート時に名前空間を作成するスクリプトを用意する必要があります。

unix.stackexchange.com

まとめ

ip netnsコマンドを使ってネットワーク名前空間について検証してみました。
これに加えてbridgeやiptablesを用いたNATなどを駆使するとdockerと同じような状態を構築することが可能です。

ソース