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 となんやねん?) の続きや。ただし前回は箱を繋ぐ (配線) 側やった。今回は箱を見る・見分ける側に回る。
readlink や ip や lsns をひたすら叩いてるうちに、2 つ引っかかった。
- これは Linux の世界を覗いてるのか? 何をやってることになるんや? ──
readlink /proc/self/ns/netやip routeを叩いてる、この手応えの正体。 - 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 route も ip 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 空っぽ
root netns はなんで特別なんや? ── init_net という原点
正式名は init_net (initial network namespace)。起動時にカーネルが最初に作る、唯一の元祖や。red / blue は後からこの隣に生やした対等な兄弟で、違いは、「最初から在る / 後付け」と「中身ぎっしり / 空っぽ」だけ。構造はどれも同じ struct net。押さえどころは 4 つ。
① netns は名前やなく inode 番号で一意。
$ readlink /proc/self/ns/net
net:[4026531840]
シンボリックリンク (ショートカット) が指してる先を表示するコマンド。/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 を使う。
起動してからの「実行中の状態」を置く一時ディレクトリ。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/...
“list namespaces” の略で、システム上の namespace を一覧するコマンド。-t net で net (ネットワーク名前空間) だけに絞ってる。この記事で要るのは 2 つの理由から ── ① ip netns list と違って取っ手の無い root netns も出る (NS 列が inode = 指紋)、② NPROCS 列でその箱の住人数が分かる (root は 27、red は 0)。「箱を見分ける」道具やから何度も出てくる。
lsns の NPROCS 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 で後から |
| inode | 4026531840 (固定・予約値) | 作るたび別番号が振られる |
| 取っ手 | 無し (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 は相手がカーネル。ip や ss はこれを開いて「ルート表ちょうだい」「この IP 足して」とメッセージを送り、カーネルが返事する。図②の窓①の正体がこれ。
ディスク上のファイルやなく、procfs という仮想ファイルシステム。中身はカーネルが読まれた瞬間に作って見せるだけで、保存された実体は無い ── これが「射影」。/proc/self は「今コマンドを叩いてる自分自身」への近道で、/proc/self/ns/net はその netns の指紋を見せる。ネットワーク屋なら、ルーターの show コマンドに近い (cat /proc/net/route ≒ show ip route)。保存された config やなく、いまの状態をその場で出してるだけ。
ネットワーク屋の実感に繋ぐと、ip route でルートを見て ip neigh で ARP を見て ss で待ち受けを見る、その普段の動作はすべて「カーネルが管理してるネットワーク世界の現在の姿を、用意された窓から読み出す」作業やった。netns は、そこに「どの世界の窓を開けるか」を一段足しただけや。
図で見る ② ── 窓越しのカーネル対話
たとえ話
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 4026531840 | netns の指紋 (一意な ID)。0xF0000000 は init_net 固定の予約値 |
取っ手 (/run/netns/<名>) | ip netns add が作る名前付きの bind mount。root は持たない=ip netns list に出ない |
| NPROCS | その netns を共有してるプロセス数。netns = プロセスの集合が見る世界 |
| netlink | ip がカーネルと話す専用ソケット。読み (照会) も書き (申請) も双方向 |
/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 の、どの層か」に翻訳して詰められる側に立てる。
- veth と bridge で箱を繋ぐ ── 見分けた空っぽの箱に、今度は線を引く。配線編 → veth と bridge でコンテナに線を引く
- 島から外に出る (NAT / masquerade) ── 空っぽの red を物理 NIC 経由で外へ出す。root netns が主役になる回 → netns の島から外に出る
- Pod = netns / CNI ── ここまでの手作業を k8s が自動でやってる正体。手作業を Go に翻訳したのが CNI → ネットワーク屋が降りる Kubernetes / Linux の namespace となんやねん?
- netns 以外の namespace (pid / mount / user) ── コンテナ=それらを全部まとった 1 プロセス。net はその 1 種でしかない、という上の地図