Linux の Namespace を学んだときの自分用ログ。実施しているきっかけは、Kubernetes のネットワーク周りをしっかり勉強しようと思ったのだけど、そのためには Docker のネットワークをしっかり学ぶ必要があり、Docker といえば Namespace だよね!ということで、壮大なヤクの毛狩りの結果、ちゃんと根本から勉強しようと腹をくくることにしました。
実際は超素晴らしい ip netnsコマンドの使い方(ネットワークの実験の幅が広がるなぁ~を WSL2 で流して実験するだけなので、素晴らしい元記事を読んでいいねしてくださるとよいと思います。
基本的には、ip
コマンドを使って、Namespace を使ったり、ネットワークを作ってみたりします。
ip コマンド
私の Linux の知識が古すぎて ifconfig
とばかり思っていましたが、現在では ip
コマンドです。様々なことが出来るので見ていきましょう。
$ ip help
Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }
ip [ -force ] -batch filename
where OBJECT := { link | address | addrlabel | route | rule | neigh | ntable |
tunnel | tuntap | maddress | mroute | mrule | monitor | xfrm |
netns | l2tp | fou | macsec | tcp_metrics | token | netconf | ila |
vrf | sr | nexthop }
OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |
-h[uman-readable] | -iec | -j[son] | -p[retty] |
-f[amily] { inet | inet6 | mpls | bridge | link } |
-4 | -6 | -I | -D | -M | -B | -0 |
-l[oops] { maximum-addr-flush-attempts } | -br[ief] |
-o[neline] | -t[imestamp] | -ts[hort] | -b[atch] [filename] |
-rc[vbuf] [size] | -n[etns] name | -N[umeric] | -a[ll] |
-c[olor]}
環境
ただし、環境は、Windows 10 の WSL2 です。最近面倒なのでこの環境を使うことが多いですが、制限がないかちょっと心配。
$ cat /etc/os-release # or lsb-release
NAME="Ubuntu"
VERSION="20.04 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
オプション一覧
ip netns
がネットワーク関連のネームスペース関連のコマンドです。ネームスペースを指定することによって、プロセスや、ユーザーや、ネットワークを隔離して管理することができます。このネームスペースや、cgroupをうまく使っているのが Docker のようなコンテナ技術です。この記事がわかりやすかったです。第2回 コンテナの仕組みとLinuxカーネルのコンテナ機能[1]名前空間とは? そして 第3回 Linuxカーネルのコンテナ機能[2] ─cgroupとは?(その1)も cgroupの概要をつかむための良い記事でした。
$ ip netns help
Usage: ip netns list
ip netns add NAME
ip netns attach NAME PID
ip netns set NAME NETNSID
ip [-all] netns delete [NAME]
ip netns identify [PID]
ip netns pids NAME
ip [-all] netns exec [NAME] cmd ...
ip netns monitor
ip netns list-id [target-nsid POSITIVE-INT] [nsid POSITIVE-INT]
NETNSID := auto | POSITIVE-INT
ネームスペースの追加
WSL2 の環境では、sudo が必要でした。
$ sudo ip netns add test1
[sudo] password for ushio:
$ sudo ip netns add test2
ネームスペースのリスト
$ ip netns list
test2
test1
ネームスペースの削除
すべてのネームスペースの削除
$ sudo ip -all netns del
個別の削除
$ sudo ip netns delete test1
ネームスペース内でのプロセスの実行
ネームスペース内でbash
を実行してみます。ネットワークのインターフェイスも母艦とは別のものになっています。
$ sudo ip netns exec test1 bash# ps
PID TTY TIME CMD
31284 pts/3 00:00:00 sudo
31285 pts/3 00:00:00 bash
# ip link
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
2: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/sit 0.0.0.0 brd 0.0.0.0
プロセス関連のコマンド
ターミナル1で、bash を実行して、nc コマンドを実行してみます。nc はネットワークの調査で便利なコマンドで、下記のはポート11111でリッスンしています。-l
は、リッスンするオプションであり、他の場合は、メッセージを送信します。-k
は、コネクションがクローズした後も、次のコネクションを待ち受けます。
$ tty
/dev/pts/3
$ sudo ip netns exec test1 bash
# nc -kl 11111
違うターミナルからこのプロセスを見てみます。tty
は現在使っているターミナルに割り当てられているデバイスファイルを表示しています。ps -C nc
で、nc
コマンドを実行しているプロセスを表示します。別のネームスペースで実行しているプロセスでも、ps aux
等であれば見ることが出来ます。
$ tty
/dev/pts/5
$ ps -C nc
PID TTY TIME CMD
31343 pts/3 00:00:00 nc
ちなみにネームスペースから、aux コマンドを実行しても結果は同じです。この辺は docker と違いを感じます。
$ sudo ip netns exec test1 bash
# ps
PID TTY TIME CMD
31416 pts/5 00:00:00 sudo
31417 pts/5 00:00:00 bash
31424 pts/5 00:00:00 ps
# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 896 576 ? Sl Feb27 0:00 /init
root 11 0.0 0.0 896 88 ? S Feb27 0:00 /init
root 27172 0.3 2.5 9125368 156384 ? Sl Feb27 5:35 /usr/bin/dockerd -p /var/run/docker.pid
root 27194 0.3 0.9 2369808 56864 ? Ssl Feb27 5:37 containerd --config /var/run/docker/containerd/containerd.toml --log-level info
root 27677 0.0 0.0 904 88 ? Ss 07:14 0:00 /init
root 27678 0.0 0.0 904 96 ? S 07:14 0:00 /init
ushio 27679 0.0 0.0 2608 540 pts/1 Ss+ 07:14 0:00 sh -c "$VSCODE_WSL_EXT_LOCATION/scripts/wslServer.sh" 622cb03f7e070a9670c94bae1a45d78d7181fbd4 stable .vscode-server 0
ushio 27680 0.0 0.0 2608 1796 pts/1 S+ 07:14 0:00 sh /mnt/c/Users/tsushi/.vscode/extensions/ms-vscode-remote.remote-wsl-0.53.4/scripts/wslServer.sh 622cb03f7e070a9670c94bae1a45d78d7181fbd4 stable .vs
ushio 27685 0.0 0.0 2608 596 pts/1 S+ 07:14 0:00 sh /home/ushio/.vscode-server/bin/622cb03f7e070a9670c94bae1a45d78d7181fbd4/server.sh --port=0 --use-host-proxy --enable-remote-auto-shutdown --print
ushio 27687 0.1 1.2 1022480 76736 pts/1 Rl+ 07:14 0:23 /home/ushio/.vscode-server/bin/622cb03f7e070a9670c94bae1a45d78d7181fbd4/node /home/ushio/.vscode-server/bin/622cb03f7e070a9670c94bae1a45d78d7181fbd4/
ushio 27723 0.0 0.6 856768 40184 pts/1 Sl+ 07:14 0:00 /home/ushio/.vscode-server/bin/622cb03f7e070a9670c94bae1a45d78d7181fbd4/node /home/ushio/.vscode-server/bin/622cb03f7e070a9670c94bae1a45d78d7181fbd4/
ushio 27777 0.0 1.3 943272 83604 pts/1 Sl+ 07:14 0:08 /home/ushio/.vscode-server/bin/622cb03f7e070a9670c94bae1a45d78d7181fbd4/node /home/ushio/.vscode-server/bin/622cb03f7e070a9670c94bae1a45d78d7181fbd4/
ushio 27784 0.0 0.0 10128 5036 pts/3 Ss 07:14 0:00 /bin/bash
ushio 31210 0.0 0.0 10132 5328 pts/5 Ss 10:08 0:00 /bin/bash
root 31317 0.0 0.0 11008 4520 pts/3 S 10:14 0:00 sudo ip netns exec test1 bash
root 31318 0.0 0.0 8960 3908 pts/3 S 10:14 0:00 bash
root 31343 0.0 0.0 3252 764 pts/3 S+ 10:20 0:00 nc -kl 11111
ushio 31346 0.0 0.0 10236 5308 pts/6 Ss 10:21 0:00 /bin/bash
ushio 31395 0.0 0.0 8788 3924 pts/6 S+ 11:14 0:00 man nc
ushio 31405 0.0 0.0 7596 2232 pts/6 S+ 11:14 0:00 pager
root 31416 0.0 0.0 11008 4592 pts/5 S 11:19 0:00 sudo ip netns exec test1 bash
root 31417 0.0 0.0 8960 4000 pts/5 S 11:19 0:00 bash
root 31425 0.0 0.0 10600 3256 pts/5 R+ 11:19 0:00 ps aux
このプロセスIDがどのネームスペースに所属しているか見てみます。
$ sudo ip netns identify 31343
test1
Bash の実行状況を観ます。tty のパートでわかるように、ターミナル1で動いているbash は2つあって、一つが、ネームスペースで実行したものです。
$ ps -C bash -o comm,pid,lstart,tty
COMMAND PID STARTED TT
bash 27784 Sun Feb 28 07:14:05 2021 pts/3
bash 31210 Sun Feb 28 10:08:49 2021 pts/5
bash 31318 Sun Feb 28 10:14:36 2021 pts/3
bash 31346 Sun Feb 28 10:21:08 2021 pts/6
それは次の比較でわかります。
$ sudo ip netns identify 31318
test1
$ sudo ip netns identify 27784
ホスト、ルーターの環境構築
実際やりたかったのはこのパートです。WSL2 で動くかな?もともと CKA のコースを受けていた時に、このコマンドの紹介があったのですが、その通りに WSL2 で動かなかったので気になってここでやっている感じです。まずは元のブログ通りの環境を作ってみます。
ネームスペースの作成
$ sudo ip netns add host1
$ sudo ip netns add host2
$ sudo ip netns add router
$ sudo ip netns list
router
host2
host1
インターフェイスの作成
仮想のネットワークインターフェイスを作成して、対向のネットワークインターフェイスを指定します。もちろん最初なので、DOWN したままです。
$ sudo ip link add name veth-h1 type veth peer name veth1-rt
$ sudo ip link add name veth-h2 type veth peer name veth2-rt
$ sudo ip link | grep veth
76: veth1-rt@veth-h1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
77: veth-h1@veth1-rt: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
78: veth2-rt@veth-h2: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
79: veth-h2@veth2-rt: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
ネームスペースをネットワークインターフェイスに割り当てる
host
, host2
, router
にインターフェスを割り当てます。
$ sudo ip link set veth-h1 netns host1
$ sudo ip link set veth-h2 netns host2
$ sudo ip link set veth1-rt netns router
$ sudo ip link set veth2-rt netns router
IP アドレスの割り当て
$ sudo ip netns exec host1 ip addr add 192.168.100.10/24 dev veth-h1
$ sudo ip netns exec host2 ip addr add 192.168.200.10/24 dev veth-h2
$ sudo ip netns exec router ip addr add 192.168.100.20/24 dev veth1-rt
$ sudo ip netns exec router ip addr add 192.168.200.20/24 dev veth2-rt
インターフェイスの UP
$ sudo ip netns exec host1 ip link set veth-h1 up
$ sudo ip netns exec host2 ip link set veth-h2 up
$ sudo ip netns exec router ip link set veth1-rt up
$ sudo ip netns exec router ip link set veth2-rt up
確認すると、正しく設定されています。
$ sudo ip netns exec host1 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
link/sit 0.0.0.0 brd 0.0.0.0
77: veth-h1@if76: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether aa:9c:34:e9:11:2f brd ff:ff:ff:ff:ff:ff link-netns router
inet 192.168.100.10/24 scope global veth-h1
valid_lft forever preferred_lft forever
inet6 fe80::a89c:34ff:fee9:112f/64 scope link
valid_lft forever preferred_lft forever
$ sudo ip netns exec host2 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
link/sit 0.0.0.0 brd 0.0.0.0
79: veth-h2@if78: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether e6:d6:9a:c7:0e:60 brd ff:ff:ff:ff:ff:ff link-netns router
inet 192.168.200.10/24 scope global veth-h2
valid_lft forever preferred_lft forever
inet6 fe80::e4d6:9aff:fec7:e60/64 scope link
valid_lft forever preferred_lft forever
$ sudo ip netns exec router ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
link/sit 0.0.0.0 brd 0.0.0.0
76: veth1-rt@if77: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 2e:8d:45:ae:d7:b4 brd ff:ff:ff:ff:ff:ff link-netns host1
inet 192.168.100.20/24 scope global veth1-rt
valid_lft forever preferred_lft forever
inet6 fe80::2c8d:45ff:feae:d7b4/64 scope link
valid_lft forever preferred_lft forever
78: veth2-rt@if79: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 9e:80:da:c0:3c:2f brd ff:ff:ff:ff:ff:ff link-netns host2
inet 192.168.200.20/24 scope global veth2-rt
valid_lft forever preferred_lft forever
inet6 fe80::9c80:daff:fec0:3c2f/64 scope link
valid_lft forever preferred_lft forever
ルーティングテーブルの設定
この手順が前に試したときに抜けていたと思います。ip_forward
の設定をしないと、デフォルトでは、IPフォワードをしない設定になっています。
$ sudo ip netns exec host1 ip route add 192.168.200.0/24 via 192.168.100.20 dev veth-h1
$ sudo ip netns exec host2 ip route add 192.168.100.0/24 via 192.168.200.20 dev veth-h2
$ sudo ip netns exec router sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
Ping
きっちり動作しています。
$ sudo ip netns exec host1 ping 192.168.200.10
PING 192.168.200.10 (192.168.200.10) 56(84) bytes of data.
64 bytes from 192.168.200.10: icmp_seq=1 ttl=63 time=0.055 ms
64 bytes from 192.168.200.10: icmp_seq=2 ttl=63 time=0.065 ms
64 bytes from 192.168.200.10: icmp_seq=3 ttl=63 time=0.056 ms
64 bytes from 192.168.200.10: icmp_seq=4 ttl=63 time=0.057 ms
ルーター機能の確認
tcpdump を使ってルーター機能が正常に動いているか確認します。
$ sudo ip netns exec router tcpdump -i veth1-rt icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth1-rt, link-type EN10MB (Ethernet), capture size 262144 bytes
Ping を1回送ります。
$sudo ip netns exec host1 ping -c 1 192.168.200.10
しっかりと、tcpdump で観察できます。
$sudo ip netns exec router tcpdump -i veth1-rt icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth1-rt, link-type EN10MB (Ethernet), capture size 262144 bytes
11:54:38.197299 IP 192.168.100.10 > 192.168.200.10: ICMP echo request, id 31512, seq 1, length 64
次に、iptable を使って確認します。router の IPテーブルに icmp をフォワードします。
$ sudo ip netns exec router bash
# iptables -A FORWARD -p icmp
# iptables -nvL FORWARD
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 icmp -- * * 0.0.0.0/0 0.0.0.0/0
1回Pingを送ると、値が変わります。
$ sudo ip netns exec host1 ping -c 1 192.168.200.10
# iptables -nvL FORWARD
Chain FORWARD (policy ACCEPT 2 packets, 168 bytes)
pkts bytes target prot opt in out source destination
2 168 icmp -- * * 0.0.0.0/0 0.0.0.0/0
同じブログの方が素晴らしいブログを書いてくれています。iptable や tpcdump についても学ばねばですが、先にもとにやってたことに戻ります。ネットワークはマジ奥深い。コントロール配下に置きたいところ。