隙あらば寝る

うぇぶのかいしゃではたらくえんじにあがかいています

netnsを触ってみる

netnsでネットワークの勉強

tl;dr

netnsというlinuxのネットワーク機能を紹介する。

netnsをコマンドレベルで触ってみて、基本的なネットワークの勉強をする。

netnsとは

linuxにNetwork Namespaces(netns)という仕組みがある。

Linux Namespacesと呼ばれるリソースの仮想化のための機能の一部で、名前の通りネットワークの機能を提供する。

docker等でも用いられている技術で、コンテナ毎に異なるネットワーク設定が行える部分で用いられている。

具体的にはプロセス毎に異なるIPアドレスを使って通信するといったことができるようになる。

docker使えば?

dockerや各種コンテナ技術がnetnsをうまく隠してくれているのに、わざわざnetnsを直接触る必要がないと思った方。

その通りで、ここでは勉強用に使うことを想定している。

(もちろん、理解した上でproductionで活用というのも十分あり得ると思うが)

というのも、最近はネットワーク周りの基本を知らなくてもあまり困らなくなっているとのこと。

dhcpなんか当たり前だし、クラウド環境はネットワーク設定済みだし、なるほどたしかに触る機会は少ないかもしれない。

なので手軽にネットワークを組んだりしながら基本的な動作を理解する環境として、netnsが使えないかと考えている。

まずは触ってみる

nsを作ってみる

まずtestnsという名前のnamespaceを作ってみる。

初期状態は以下で、ループバックのloと、外部と接続したens33があるよくある普通の構成。

f:id:yoru9zine:20161228225217p:plain

ipコマンドにnetnsサブコマンドがあり、さらにnetns以下のコマンドでnamespaceの操作ができる。

ip netns help で説明が出る。

新しくnamespaceを作る場合は add コマンドで、引数に作りたいnamespaceの名前を指定する。

$ sudo ip netns add testns
$ ip netns list # 一覧表示
testns

ここで作成されたtestnsにはループバックインタフェースのみが存在する以下のようなnamespaceになる。

f:id:yoru9zine:20161228225311p:plain

初期状態では外部とは一切接続されていない独立したネットワークとなっている。

nsの中に入ってみる

このままだとnsが存在しているだけで特に何も変わらない。

nsはプロセスに対して紐付ける事ができ、具体的にはあるプロセスを指定したnsで実行するということができる。

例えばbashをこのnsで起動するには以下のようなコマンドを実行する。

sudo ip netns exec testns bash

これでshellがns内で起動する。

vmではないので、ファイルシステムは実行前と全く同じ、ネットワークの設定だけ異なるbashが起動することになる。

最初はloすらdownの状態になっているため、127.0.0.1 にすら通信ができない。

# ping 127.0.0.1
connect: Network is unreachable
# ip link set lo up
# 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.031 ms
...
# exit

と、こんな感じで独立したネットワークを作っているのがわかる。(当然外部ホストには一切つながらない)

nsと通信してみる

このままではただ隔離されただけなので、元ホストと通信をしてみる。

今はloしかインタフェースが無いので、当然ながら追加する必要がある。

仮想的なインタフェースはip linkbrctlコマンドで新規に作ることができる。

今回はip linkでvethを作成する事にする。

linuxにおけるvethはインタフェースのペアであり、作成すると2つのインタフェースがL2で直結した状態で利用可能になる。

主には仮想化環境において親と子供で通信をするために用いられており、

今回もペアの片方をnetns内に持っていくことで相互通信を可能とする構成とする。イメージは以下。

f:id:yoru9zine:20161228225314p:plain

vethはip link addで作る事ができる。特に指定しなければveth0/veth1が作られ、これは相互に接続されている。

次にip link set vetn1 netns testnsを実行するとveth1がtestnsの中に出現し、ホスト側から消える。

この状態で片側がホストに、片側がnetnsに入ったことになる。

あとはホスト側のveth0とnetns内のveth1に同一サブネットでアドレスを付与すれば目標達成、通信可能になる。

$ sudo ip link add type veth   # veth0/veth1が作られる
$ sudo ip link set veth1 netns testns   # veth1をtestnsに入れる
$ sudo ip addr add 172.16.0.1/24 dev veth0 # veth0にアドレス付与
$ sudo ip link set veth0 up # veth0をup

$ sudo ip netns exec testns bash #netnsでbash起動
# ip addr add 172.16.0.2/24 dev veth1 # veth1にアドレス付与
# ip link set veth1 up # veth1をup
# ping 172.16.0.1
PING 172.16.0.1 (172.16.0.1) 56(84) bytes of data.
64 bytes from 172.16.0.1: icmp_seq=1 ttl=64 time=0.057 ms
...
# exit

まとめ

ざっとnetnsで簡素なネットワークを組んで疎通確認までを紹介した。

現実世界ではこんな単純なもので足りるケースはすくないので、このあとブリッジの設定やnatの設定等が必要になると思う。

そのあたりはまた今後整理して紹介していきたい。