← 一覧へ戻る

Claude が書いた記事

veth と bridge でコンテナに「線」を引く ── 仮想ネットワークの配線の底

前回 ip netns で作った red / blue を実際につなぐ veth と bridge を、ネットワーク屋の頭で掘った学習ログ。(1)veth = NIC (インターフェース)。必ずニコイチで作り、2 つのもの (netns や bridge) をつなぐ。『ケーブル』の比喩はどこで壊れるかまで。(2)3 台目をつなぐには bridge=仮想 L2 スイッチを挟む。物理スイッチの 48+4 と違いポートは動的・最大 1024・ソフト転送。(3)netns / veth / bridge は全部カーネル機能だが作った時期も人もバラバラ。bridge が最古で元は物理 L2 スイッチ用。Linus はほぼ出てこない。

俺の最初の疑問

前回、ip netnsredblue という箱を 2 つ作って、veth でつないで ping を通した (Linux の namespace となんやねん? の続き)。namespace の概念は腹に落ちた。

で、今回は手を動かした先にある「配線」を掘る。今回潰すのは 3 つ。

  1. veth ってつまり何なんや?
  2. 3 つ目 (green) を足したいとき、どうつなぐ? → bridge
  3. netns / veth / bridge って誰がいつ作ったんや? Linux カーネルの機能なんか?

まず一言でいうと

  • vethNIC (インターフェース)。必ずニコイチ (2 個 1 組) で作り、2 つのもの (netns や bridge) をつなぐ
  • bridge = 仮想の L2 スイッチ。複数の netns から veth を 1 本ずつ挿して、同じ Subnet の複数ホストにする。

ネットワーク屋の語彙に落とすと、veth ≒ 必ずペアで生える NICbridge ≒ Subnet のスイッチ役。Docker が勝手に作る docker0 は、この bridge そのもの。

veth は「ニコイチで生える NIC」

veth を「仮想 LAN ケーブル」と説明されることが多い。挙動はそれで合っている (2 つのものをつないで通信させる)。でもこの比喩は突き詰めると壊れるので、先に正体を押さえておく。

前回 ip link add veth-red type veth peer name veth-blue で作られるのは、veth-redveth-blue という 2 つの NIC (インターフェース)だ。これが実体のすべてで、この 2 枚が中で直結している (片方が送ったものをもう片方が受け取る)。ip link に出てくるし、MAC を持てる・IP を振れる・up / down もできる ── つまりケーブルの端っこ (受け身のプラグ) ではなく、本物の NIC

要点はこれだけ。veth = NIC。必ずニコイチで生える。2 つのもの (netns や bridge) をつなぐ。 そして大事なのは、つなぐ相手は両方が netns でなくてもいいこと ── 片割れを netns に入れ、もう片割れを bridge に挿す、という使い方がこのあと出てくる。

3 つ目をどうつなぐ? → bridge

veth は 2 つしかつなげない。red ↔ blue はつながった。でも veth 1 組では、3 つ目 green をつなぐ先が無い。

素直にやると red↔bluered↔greenblue↔green総当たりで結線することになる。3 台で 3 組、4 台で 6 組、5 台で 10 組……これは物理ネットワークでも昔通った道だ。ホストを直結で総当たりにすると破綻する。だからスイッチが生まれた。

仮想でも答えは同じ。間に bridge (仮想 L2 スイッチ) を置く。各 netns から veth を 1 組ずつ生やして、その片割れを全部 bridge に挿す。

veth 1 組は 2 つしかつなげないので 3 つ目をつなぐ先が無い。bridge を挟み、各 netns から veth を 1 組ずつ生やして片割れを bridge に集めると、同じ Subnet にぶら下がった複数ホストになる。bridge を使っても veth は消えない ── bridge は『みんなが挿すスイッチ』、veth は『ホストとの配線役の NIC』で役割が違う。

bridge に何台ぶら下げられる? ── 物理の「48+4」との差

ここで物理屋の頭が一番うずく問い。物理スイッチは「48+4」みたいにポート数が固定されている。あれは何で決まっていたか思い出すと差が見える。筐体の面板 (フェイスプレート) の都合だ。RJ45 を 48 個並べる物理スペース、+4 は上位向け SFP のアップリンク。ハードとコストで先に決め打ちされた固定数で、空きポートは「物理的に存在するけど挿していない穴」だった。

bridge には面板が無い。ここが効く。

  • ポートは動的に生えたり消えたりするip link set veth-red master br0 を叩いた瞬間にポートが 1 個増える。抜けば消える。「空きポート」という概念がそもそも無い。
  • だから上限は物理の「48」みたいな数やなくて、カーネルの上限になる。それが 1 つの bridge あたり 1024 ポート (カーネル定数 BR_MAX_PORTS = 1024)。

ただし現実には 1024 まで挿す前に別の壁が来る。bridge はソフトウェアでパケットを転送しているので、ぶら下げる数が増えるほど転送の CPU 負荷がそのホストに集中する。物理スイッチが ASIC でワイヤスピードに殴るのとは違う。

物理スイッチLinux bridge
ポート数48+4 など面板で固定動的 (挿した瞬間+1、抜けば-1)
「空きポート」物理の穴として存在概念なし。必要な数だけ生やす
転送ASIC でワイヤスピードソフト転送 → 数が増えると CPU 負荷が集中
上限筐体で決まるカーネル定数 1024 / bridge

だから本番 (Docker / k8s) は「1 個の巨大 bridge に全コンテナ」をやらない。ノードごとに bridge を分ける、VLAN で割る、overlay に逃がす ── 1 個の bridge に集中させない設計にする。Azure で「1 個の Subnet / NSG に全部ぶら下げず分割する」のと同じ判断が、ここでも効く。

これ、誰がいつ作ったんや?

netns / veth / bridge は全部 Linux カーネルの機能だ。ただし面白いのは、3 つとも作られた時期も作った人もバラバラだということ。一気に「コンテナ用」として設計されたんやなくて、10 年かけて別々のピースが積み上がった結果だった。

時期何が入ったか主な作り手
~2000 (2.4 系)bridge (Linux を L2 スイッチにする)Lennert Buytenhek
2002 (2.4.19)mount namespace (最初の namespace)
2006 (2.6.19)UTS / IPC namespace
2008 (2.6.24)network namespace + veth (同時)Eric W. Biederman + OpenVZ 勢
2013 (3.8)user namespace (最後発)
2013(カーネル外)Docker が上の全部を束ねて世に出すdotCloud / Solomon Hykes

効くポイントが 2 つ。

① bridge だけ飛び抜けて古い (2000 年)。 これはコンテナの話が始まるずっと前から存在した。当時の動機はシンプルで「Linux マシンを L2 スイッチ代わりに使いたい」── 物理 NIC を 2 枚以上挿した Linux マシンが、その間でフレームを L2 転送する、というもの。ぶら下がっていたのは veth やなくて本物の物理ポートだった。証拠が 1 つあって、Linux bridge は STP (スパニングツリー / IEEE 802.1D) を実装している。STP は純粋にスイッチの世界の話なので、「本気で L2 スイッチをやるための機能」として作られた何よりの裏付けだ。そして bridge のコードはぶら下がるものが物理 (eth0) でも仮想 (veth) でも同じ。だから後年 veth が出てきたとき、新しく仮想スイッチを発明する必要が無く、物理用の bridge をそのまま流用できた。

② network namespace と veth は 2008 年に同時に来た。これは偶然ちゃう。 前回見たとおり、できたての netns は中身が lo だけの孤立状態で、それ単体では外とつながれない。だから「netns を外につなぐ専用のケーブル」として veth が同時に発明された。veth は netns のために生まれた相方だ。だから 2 つはセットで 2.6.24 に入った。

混乱しやすいポイント:Linus はほぼ出てこない

「Linux = Linus が全部書いた」と思いがちだが、実際は違う。Linus がやったのは 1991 年に最初のカーネルを書いたことと、今も全マージの最終ゲートキーパーであること。設計の趣味と「それはダメだ」と却下する権限は握っている。でも個々のサブシステム (bridge・namespace・veth) を自分で書くことはほぼ無い。今の Linux は数千人が書いた数千万行で、各分野に専任の maintainer がいる。ネットワーク分野はとくに専任が強く、David S. Miller (“DaveM”) が約 20 年ネットワークサブシステムの maintainer を務めた。

だから流れはこうなる。

書いた人(Buytenhek / Biederman ら)
      ↓ パッチを投げる
ネットワーク maintainer(DaveM)がレビュー・net ツリーにマージ
      ↓ pull request
Linus が mainline に取り込む(最終ゲート)

Linus の指紋は全部に間接的に付いている (彼が取り込んだから mainline にある) が、直接の作者ではない。今の Linus の役割は「機能の作者」やなくて「最後に Yes / No を言う統合者 + 設計の番人」。コードは書かんけど全 PR の最終承認を握っているアーキテクト、みたいなポジションだ。

たとえ話

veth は「双子の NIC」。 必ず 2 枚セットで生まれ、互いに直結している。片方だけでは存在できない。1 枚を red に、もう 1 枚を blue に置けば、red と blue がつながる。(「1 本のケーブル」と呼ぶと「じゃあ両端は? 1 個なん 2 個なん?」と混乱する。最初から「2 枚の NIC」と数えると迷子にならない。)

bridge は「電源タップ (OA タップ)」。 機器を直結で総当たりにすると破綻するので、みんなが挿す 1 個のタップを置く。各 netns は自分の veth の片割れをそのタップに挿す。タップ (bridge) と挿す側 (veth) は役割が違うので、bridge を置いても veth は要る。

そして Docker は「この配線キットを箱詰めして売った会社」 であって、NIC やタップを発明したわけではない。部品は 10 年前からカーネルに転がっていた。

出てきた言葉の変換表

出てくる言葉つまり何の話?
vethNIC (インターフェース)。必ずニコイチで生え、2 つのもの (netns や bridge) をつなぐ
bridge仮想 L2 スイッチ。複数 netns を同じ Subnet にぶら下げる箱。docker0 がこれ
docker0Docker が自動で作る bridge。手組みの br0 を自動化したもの
STPスパニングツリー。ループ防止のスイッチ用プロトコル。bridge が L2 スイッチ起源である証拠
BR_MAX_PORTS1 つの bridge にぶら下げられる上限ポート数のカーネル定数 (1024)
maintainerカーネルの各分野の責任者。ネットワークは長年 David Miller。Linus は最終統合役
深掘りメニュー 次におすすめのトピック

今回で「線を引く」までは底が見えた。次は この土台の上に機能を足していく段で、ここがネットワーク屋の本領 (Azure の守備範囲) にそのままつながる。

  • netns に router を立てる ── 別 Subnet どうしをつなぐには、netns を 1 個ルータ役にして ip forward を有効化し、ルーティングテーブルを書く。Azure の UDR (ユーザー定義ルート) を手で組むのと同じ構造が見える。次はこれ。
  • NAT を効かせて外に出す ── 内側 netns から物理 NIC 経由でインターネットに出るには iptables/nftables の masquerade (SNAT)。Docker コンテナが外に通信できる仕組みの底。
  • パケットフィルタ (ACL / firewall) を挟む ── netns 間の通信を iptables/nftables で許可・拒否する。NSG (ネットワークセキュリティグループ) の中身を生で 1 回触ると、ルールの評価順や戻り通信の扱いが腹に落ちる。
  • Pod の IP が 1 個しかない理由 ── 「Pod = 複数コンテナで network namespace を 1 個共有する単位」を pause コンテナで追うと、今回の veth / bridge が k8s のどこに化けるかが一気につながる。