← 一覧へ戻る

Claude が書いた記事

コマンドで見ていく Linux network namespace の世界

netns を『繋ぐ』のではなく『見分ける』側の話。ip / lsns で root netns と空っぽの red を並べ、ネットワーク屋の道具のまま一段下の OS レイヤに降りる学習ログ。

コマンドで見ていく Linux network namespace の世界

俺の最初の疑問

ラボで network namespace (netns) を触り始めた。前に red / blue という箱を veth でつないで ping を通した回 (veth と bridge でコンテナに線を引く / Linux の namespace となんやねん?) の続きや。ただし前回は箱を繋ぐ (配線) 側やった。今回は箱を見る・見分ける側に回る。

readlinkiplsns をひたすら叩いてるうちに、2 つ引っかかった。

  1. これは Linux の世界を覗いてるのか? 何をやってることになるんや? ── readlink /proc/self/ns/netip route を叩いてる、この手応えの正体。
  2. red や blue の前に、root netns を先に理解したい。 基準点がわからんまま空っぽの箱を見ても、何が普通と違うのか掴めん。

まず一言でいうと

  • netns = ネットワークスタック丸ごと 1 セット (カーネル内の struct net)。NIC・ルート表・ARP 表・ソケット・firewall、全部ひとまとめで 1 個の独立した世界。
  • root netns (= init_net) = その原点。起動時にカーネルが最初に作る、ただ 1 つの「本家」。普段 ip a で見てる世界はこれで、本物の NIC もルートも ARP も全部入ったぎっしりの箱
  • ip netns add red で作る red / blue = 同じ間取りの空っぽの箱。器は root と全く同じ struct net やけど、中身は lo が 1 本あるだけのがらんどう。
  • ip コマンドはカーネルを直接触ってないnetlink という窓越しに、カーネルが持つ本物のデータの写しを読み、「こう変えろ」と申請してるだけ。

ネットワーク屋の道具 (ip route / ip neigh / ss) はそのまま効く。新しいのは「netns という箱」と「窓越しに見てる」という 2 つの感覚だけや。

何と比べるとわかるか ── root (中身ぎっしり) と red (空っぽ) を並べる

root netns は、ログインシェルが普段いる素の世界そのもの。だから何も指定せず普段どおり ip route などを叩けば、それがそのまま root netns の情報になる。

$ ip -br addr
lo    UNKNOWN  127.0.0.1/8 ::1/128
eno1  UP       172.16.1.6/24 fe80::223:24ff:feeb:57ef/64

$ ip route
default via 172.16.1.1 dev eno1 onlink
172.16.1.0/24 dev eno1 proto kernel scope link src 172.16.1.6

$ ip neigh
172.16.1.1 dev eno1 lladdr 00:a0:de:cb:6f:17 REACHABLE

本物の NIC (eno1) がいて、外へのデフォルトルートがあり、ゲートウェイの MAC まで学習済み ── これが「外と繋がった完成品」や。ip routeip neigh も、ネットワークの障害調査で普段から叩く道具そのまま。新しいのは「どの netns で叩いてるか」だけ。

同じ 3 つを red で見たい時は ip netns exec red を付けてコマンド叩けばオーケー。ただし中身はこうなる。

# 頭に ip netns exec red を付けて、red の中で叩く
ip netns exec red ip -br addr   # → lo  DOWN だけ (IP 無し)
ip netns exec red ip route      # → (何も出ない)
ip netns exec red ip neigh      # → (何も出ない)

この ip netns exec red はネットワーク屋の感覚で言うと ── show ip route vrf RED でその VRF のルート表を見るのと同じや。何も指定しなければグローバル (default) テーブル、vrf RED を付ければその VRF のテーブルが出る。netns はそれを、ルート表だけやなく NIC・ARP・firewall 含めた「ネットワーク一式」に広げた版。

この中身ぎっしり vs 空っぽのギャップが、「netns は独立したスタック、red は生まれたての空っぽの箱」の正体や。先に root のぎっしり詰まった中身を目に焼き付けるから、red の空白が際立つ ── 「root を先に理解したい」という判断が正しかったのはここ。比較対象なしに red だけ見ても「で、これ普通と何が違うん?」が出てこない。

図で見る ① ── 中身ぎっしり vs 空っぽ

同じ 3 コマンドを root と red で叩くだけで差が出る。root は本物の NIC・ルート・ARP が揃ってぎっしり、red は lo が 1 本あるだけのがらんどう。違いは中身が詰まってるか空っぽか、そして先に在るか後付けか ── それだけで、器そのもの (netns = struct net) は同じ。

root netns はなんで特別なんや? ── init_net という原点

正式名は init_net (initial network namespace)。起動時にカーネルが最初に作る、唯一の元祖や。red / blue は後からこの隣に生やした対等な兄弟で、違いは、「最初から在る / 後付け」と「中身ぎっしり / 空っぽ」だけ。構造はどれも同じ struct net。押さえどころは 4 つ。

① netns は名前やなく inode 番号で一意。

$ readlink /proc/self/ns/net
net:[4026531840]
readlink ──

シンボリックリンク (ショートカット) が指してる先を表示するコマンド/proc/self/ns/net は中身が net:[...] という文字列を指すリンクになってて、readlink でその指し先 ── つまり今いる netns の指紋 (inode) を読み出してる。

netns には人間が付けた名前やなく、inode 番号という一意な ID が振られる。同じ番号なら同じ箱。そして 4026531840 (= 16 進 0xF0000000) は init_net 固定の予約値で、どの Linux でも同じ。だから「この番号を見たら root netns」と断定できる。red / blue は作るたびに別の番号が振られる。

やさしいことばで 小学生にわかるように説明すると

inode 番号は、ものに付けるそのもの専用の番号。名前やなくて、番号で見分ける。

学校のげた箱で考えると

クラスに「たかし」が 2 人いても、げた箱は名前やなくて番号で決まってる。3 番のげた箱は、ずっと 3 番のげた箱。名前はかぶるし、あだ名を付け替えてもいいけど、番号はその箱だけの、ずっと変わらへん目印や。

  • 名前 ── 後から付ける・変える・かぶることもある (red とか blue とか)
  • 番号 (inode) ── その箱だけの、ずっと同じ目印。同じ番号なら、同じ箱

じゃあ 4026531840 って?

これは「1 番のげた箱は、どの学校でも保健室のもの」みたいに、最初から場所が決まってる特別な番号。Linux やったら、この番号のげた箱は必ず「本家 (root netns)」。だから番号を見ただけで「あ、これが本家や」とすぐ分かる。

つまり ── inode 番号 = もの専用の番号。名前はかぶっても番号はかぶらへん。同じ番号なら同じもの。

② root netns は「無名の原点」だから ip netns list に出ない。

veth の記事で ip netns add red した red は ip netns list に出る。でも root netns はそこに出てこない。消えてるんやない ── root には名前付きの「取っ手」が無いだけやip netns add <名> は、その netns を /run/netns/<名> というファイルに bind mount して「名前の取っ手」を作る操作で、root は起動時からある原点なので、その取っ手を持たない。取っ手の有無に関係なく全部見たいときは lsns -t net を使う。

/run ──

起動してからの「実行中の状態」を置く一時ディレクトリ。tmpfs (メモリ上) なので再起動で中身は消える。動いてるプロセスの PID ファイル・ソケット・ロックなどが置かれ、ip netns add が作る netns の取っ手 (/run/netns/<名>) もここに住む。だから取っ手の有る無しが、そのまま ip netns list に出る出ないになる。

③ netns = プロセスの集合が見るネットワーク世界。

$ lsns -t net
        NS TYPE NPROCS  PID USER COMMAND
4026531840 net      27 1333 nobi /usr/bin/...
lsns ──

“list namespaces” の略で、システム上の namespace を一覧するコマンド。-t net で net (ネットワーク名前空間) だけに絞ってる。この記事で要るのは 2 つの理由から ── ① ip netns list と違って取っ手の無い root netns も出る (NS 列が inode = 指紋)、② NPROCS 列でその箱の住人数が分かる (root は 27、red は 0)。「箱を見分ける」道具やから何度も出てくる。

lsnsNPROCS 27 は「この netns を共有してるプロセスが 27 個」という意味や。ログインシェルも systemd もブラウザも、デフォルトで全員この root netns を共有してる。だから普段 ip a で見えるのは全部この箱の中身。red / blue を作っても、中で何かを exec するまで NPROCS は 0 ── 箱はあるが住人ゼロ。つまり netns とは「あるプロセスの集合に見えるネットワーク世界」のことや、と分かる。

④ 本物の NIC は root netns の持ち物。

物理インターフェース (eno1) は、起動時に root netns に配属される。red / blue を作っても eno1 はそっちには見えない。だから red を外に出したければ、veth で root netns 経由で中継するか、物理 IF を netns へ move するしかない ── これがコンテナが外部通信する仕組みそのものや (その配線は netns の島から外に出る (NAT / masquerade) でやった)。

整理すると、root と red / blue はこう並ぶ。

root netns (init_net)red / blue
作られる時起動時に 1 個だけ、自動ip netns add で後から
inode4026531840 (固定・予約値)作るたび別番号が振られる
取っ手無し (ip netns list に出ない)/run/netns/<名> に bind mount
中身lo + 本物の NIC + 既存ルート全部lo だけ・DOWN・IP 無し
消せるか不可 (システムの土台)ip netns del で消せる
誰が居るsystemd 含む全プロセス (27 個)誰も居ない (exec した時だけ)

要は root netns = ホスト本体のネットワーク世界。red / blue の実験は、この原点の隣に空っぽの兄弟を建てて veth で繋ぐ遊び、という地図でいい。

これは Linux の世界を覗いてるのか? ── 答えは「窓越しに」

最初の引っかかりに答える。yes、ただし窓越しに、や。

Linux はユーザー空間とカーネルを分離してる。NIC もルート表も ARP も、全部カーネルの中のデータ構造で、ユーザー空間からは直接は触れない。だからカーネルは「覗き窓」を用意してる。今叩いてるコマンドは、その窓を開けてるだけや。

  • ip コマンド → 内部で netlink という専用ソケットを開く。 ip route の出力は、カーネルが持つ本物のルート表のスナップショット (写し)。ip addr add で書き換えるときも同じ netlink で「こう変えろ」と命令を送る。読みも書きも、窓越しのカーネル対話や。
  • /proc/self/ns/net → カーネルが netns の正体を仮想ファイルの姿で見せてる窓。 readlink で覗いた net:[4026531840] は、ディスク上のファイルやなくカーネル状態の射影。だから ls で中身を読むような普通のファイルとは違う。
  • lsns/proc を舐めて、どのプロセスがどの netns に属すかを集計してる。
netlink ──

ユーザー空間のプログラムとカーネルが、ネットワーク設定をやり取りするための専用ソケット。普通のソケットは相手が別マシンやけど、netlink は相手がカーネルipss はこれを開いて「ルート表ちょうだい」「この IP 足して」とメッセージを送り、カーネルが返事する。図②の窓①の正体がこれ。

/proc ──

ディスク上のファイルやなく、procfs という仮想ファイルシステム。中身はカーネルが読まれた瞬間に作って見せるだけで、保存された実体は無い ── これが「射影」。/proc/self は「今コマンドを叩いてる自分自身」への近道で、/proc/self/ns/net はその netns の指紋を見せる。ネットワーク屋なら、ルーターの show コマンドに近い (cat /proc/net/routeshow ip route)。保存された config やなく、いまの状態をその場で出してるだけ。

ネットワーク屋の実感に繋ぐと、ip route でルートを見て ip neigh で ARP を見て ss で待ち受けを見る、その普段の動作はすべて「カーネルが管理してるネットワーク世界の現在の姿を、用意された窓から読み出す」作業やった。netns は、そこに「どの世界の窓を開けるか」を一段足しただけや。

図で見る ② ── 窓越しのカーネル対話

本物のデータ (ルート表・NIC・ARP) はカーネルの中。コマンドはそこを直接触らず、netlink ソケット (読みも書きも双方向) と /proc の仮想ファイル (カーネル状態の射影) という窓越しに、写しを読み・変更を申請する。

たとえ話

root netns は、元から人が住んで家具も配線も全部入った本家ip netns add red は、同じ間取りで隣に建てた空っぽの新築inode は名前でなく地番にあたる。同じ地番なら同じ物件で、4026531840 は本家の地番で全国共通や (どの Linux でも本家はこの番号)。

そして netlink役所の窓口。中の台帳 (ルート表) は直接めくれんが、窓口越しに「今の登記を見せて」と照会し、「住所を 1 つ足して」と申請する。ip route がその照会で、ip addr add がその申請。台帳のある部屋には入れんが、窓口を通せば読めるし変えられる。

出てきた言葉の変換表

出てきた言葉つまり何の話?
netns (network namespace)ネットワークスタック丸ごと 1 セット。カーネル内の struct net
root netns / init_net起動時の原点。本物の NIC・ルートが入ってぎっしりの本家。普段の ip a の世界
inode 4026531840netns の指紋 (一意な ID)。0xF0000000init_net 固定の予約値
取っ手 (/run/netns/<名>)ip netns add が作る名前付きの bind mount。root は持たない=ip netns list に出ない
NPROCSその netns を共有してるプロセス数。netns = プロセスの集合が見る世界
netlinkip がカーネルと話す専用ソケット。読み (照会) も書き (申請) も双方向
/proc/self/ns/net今いる netns の指紋を見せる仮想ファイル。カーネル状態の射影
次に答える、設計者の問い で、それがなんやねん?

root (中身ぎっしり) と空っぽの箱を見分けられると、k8s の Pod が外と通信できなくなったとき、どこを覗くかを切り分けられる。問いはこれや ── 不通の原因は、ホスト側 (root netns の eno1・ルート・NAT) にあるのか、それとも Pod 自身の netns の中 (veth・IP・ルート) にあるのか?

責任境界で言うと、どこまでが root netns = ホストの仕事で、どこからが Pod の netns の話か。中身がぎっしり詰まっているべき root と、配線済みであるべき Pod の箱、どちらの期待値が崩れてるかで、ip netns exec / nsenter でどっちを覗くかが決まる。これに切り分けられると強い ── 「ネットワークが死んだ」を「どの netns の、どの層か」に翻訳して詰められる側に立てる。

深掘りメニュー 次におすすめのトピック