Mạng Linux & Container/Nối container bằng veth — ping qua 2 netns
9/21
Bài 9 / 21~13 phútContainer NetworkingMiễn phí lượt xem

Nối container bằng veth — ping qua 2 netns

veth pair nối 2 network namespace: gán IP mỗi đầu, route, ping thành công. Đây là cách Docker nối container vào bridge — bước trước khi dựng bridge đầy đủ.

TL;DR: Hai network namespace cô lập tuyệt đối cho đến khi có cáp L2 giữa chúng. veth pair là cáp đó: tạo bằng ip link add … type veth, move mỗi đầu vào một netns, lo + veth UP, gán IP cùng subnet (hoặc route tường minh hai chiều) → ping chéo. Docker bridge mode làm đúng pipeline này: eth0 trong container là một đầu veth; đầu host gắn bridge. Pitfall: một đầu DOWN, subnet lệch, hoặc chỉ “sửa” một chiều route khi topology không còn on-link.

Code review: PR “service mesh lab” — hai netns appdb đã ip netns add, lo UP, junior dán screenshot ping fail và bảo “iptables mặc định DROP”. Bạn hỏi: “có veth giữa hai ns chưa?” ip -br link trong mỗi ns chỉ còn lo. Không có peer → không có path. Thêm pair, gán 10.10.0.1/24 / 10.10.0.2/24, up hai đầu → ping sống trước khi đụng firewall. Không phải policy — thiếu cáp.

Bài 01 — Network namespaces chứng minh isolation. Bài này kéo cáp bằng veth (cơ chế đã học ở M1 — Virtual network devices) và implement ping hai chiều — bước trước mini-challenge bridge.

1. Analogy — Hai căn hộ, một cáp nhảy

Nhớ bài 01: mỗi netns là một căn hộ với tủ mạng riêng. Hai căn không dùng chung chuông 127.0.0.1. Muốn nói chuyện:

  1. Thợ kéo một sợi cáp nhảy (veth pair) — hai đầu, không phải “Wi‑Fi xuyên tường”.
  2. Cắm đầu 1 vào tủ căn A, đầu 2 vào tủ căn B (move netns).
  3. Gán số máy trong cùng dải LAN (hoặc ghi rõ “đi qua gateway nào” = route).
  4. Bật cổng (admin UP) cả hai đầu — rút một đầu = im cả đường.
Đời thườngLinux
Cáp nhảy hai đầuveth pair
Cắm vào tủ căn A / Bip link set … netns nsA / nsB
Số máy /24 cùng LANIP on-link, connected route tự sinh
Bản đồ “đi subnet kia qua đâu”Route tường minh (khi không còn cùng prefix)
Rút một đầu cápMột đầu veth DOWN hoặc bị xoá
💡 Cách nhớ

Netns = máy logic. veth = cáp. IP/route = địa chỉ + bản đồ. Thiếu bất kỳ mảnh nào → “cô lập” trông như bug app/firewall.

2. Cơ chế: veth pair xuyên hai netns

Module 1 đã định nghĩa veth: hai netdev kernel peer — TX đầu A thành RX đầu B. Điểm mới của Module 2: mỗi đầu có thể thuộc netns khác nhau.

flowchart LR
    subgraph NS1["Netns ns1"]
        LO1["lo"]
        E1["veth1s / eth0"]
    end
    subgraph NS2["Netns ns2"]
        LO2["lo"]
        E2["veth2s / eth0"]
    end
    E1 -->|"peer TX/RX"| E2

Thứ tự object (đủ để debug):

  1. ip link add veth1s type veth peer name veth2s — pair sinh trong netns hiện tại (thường init/host).
  2. ip link set veth1s netns ns1 — object netdev chuyển ownership sang ns1 (biến mất khỏi ip link host).
  3. Tương tự veth2sns2.
  4. Trong mỗi ns: rename (tuỳ chọn) → lo UP → veth UP → ip addr add … → (nếu cần) ip route add ….
  5. Kernel ARP + L2 trên pair; L3 dùng IP bạn gán.

Vì sao không “chỉ gán IP trên host eth0”?
Host eth0 nằm init netns. Process trong ns1 không thấy iface đó. Container runtime không cướp NIC host cho mỗi container (bridge mode) — nó tạo veth và move một đầu vào netns container.

Lệnh / bướcÝ nghĩa
type veth peer name …Tạo cặp, không phải một iface lẻ
set … netnsĐổi netns của một đầu
ip link set … upAdmin state theo đầu — kiểm cả hai
ip addr add x.x.x.x/24Connected route /24 trên iface đó (on-link)
ip link del một đầuPair hỏng / đầu kia biến mất
📌 Recall Module 1

Chi tiết peer semantics, pitfall một đầu DOWN, so sánh tun/tap: Virtual network devices — veth, tun, tap. Bài này không lặp tun/tap — chỉ implement veth giữa hai netns.

3. Worked lab — dựng pair và ping một chiều (ns1 → ns2)

Chạy trên Linux có netns + sudo. Nếu còn ns1/ns2 sót từ bài 01, dọn trước (cuối bài có cleanup).

3.1 Tạo hai netns + loopback

# Fresh namespaces (isolated stacks — no link yet)
sudo ip netns add ns1
sudo ip netns add ns2

sudo ip netns exec ns1 ip link set lo up
sudo ip netns exec ns2 ip link set lo up

# Prove: still no path between them
sudo ip netns exec ns1 ip -br link
sudo ip netns exec ns2 ip -br link

Mỗi ns gần như chỉ lo. Ping chéo chưa có đích L3 hợp lệ qua cáp — đúng isolation bài 01.

3.2 Tạo veth pair trên host rồi move

# Create pair in init netns, then move each end
sudo ip link add veth1s type veth peer name veth2s

# Optional: see both on host before move
ip -br link | grep veth || true

sudo ip link set veth1s netns ns1
sudo ip link set veth2s netns ns2

# Host should no longer list veth1s/veth2s
ip -br link | grep veth || echo "host: pair ends moved (expected)"

3.3 Cấu hình đầy đủ phía ns1 + peer đủ để trả lời (worked một chiều demo)

Mục tiêu worked: ping từ ns1 tới IP của ns2 thành công. Cần cả hai đầu UP và cả hai có IP (peer phải có địa chỉ để ICMP echo reply). Phần “một chiều” ở đây là bạn theo worked script tới lệnh ping ns1→ns2; chiều ngược và biến thể route là faded ở §4.

# Inside ns1: name optional; use veth1s as-is
sudo ip netns exec ns1 ip link set veth1s up
sudo ip netns exec ns1 ip addr add 10.10.0.1/24 dev veth1s

# Inside ns2: peer must be UP + addressed for reply path
sudo ip netns exec ns2 ip link set veth2s up
sudo ip netns exec ns2 ip addr add 10.10.0.2/24 dev veth2s

# Connected routes appear automatically for 10.10.0.0/24
sudo ip netns exec ns1 ip route
sudo ip netns exec ns2 ip route
Thử đoán

Trước khi chạy ping: từ ns1, ping -c 2 10.10.0.2 kỳ vọng success, fail, hay chỉ ARP timeout? Viết một dự đoán (không liệt kê mọi khả năng). Rồi chạy lệnh dưới.

# Worked direction: ns1 -> ns2
sudo ip netns exec ns1 ping -c 3 10.10.0.2

Kỳ vọng: 0% packet loss (cùng /24, pair UP, lo không cần cho đích remote — nhưng lo UP là thói quen sạch). Cơ chế: ICMP echo request ra veth1s → peer RX veth2s trong ns2 → stack ns2 reply → TX ngược pair → ns1.

# Counters: TX ns1 ~ RX ns2 (and reverse on reply)
sudo ip netns exec ns1 ip -s link show veth1s
sudo ip netns exec ns2 ip -s link show veth2s

4. Faded — hoàn thiện chiều ngược và route đối xứng

4.1 Ping chiều ngược (faded nhẹ)

Worked đã có IP hai đầu. Trước khi nhìn đáp án, tự chạy và giải thích:

# Faded: you run and interpret — reverse direction
# sudo ip netns exec ns2 ping -c 3 10.10.0.1
Tự làm trước khi xem đáp án

Chạy ping ns2 → 10.10.0.1. Nếu fail: checklist (1) veth2s/veth1s state, (2) ip addr đủ hai phía, (3) có còn pair không (ip link trong từng ns). Không thêm iptables. Viết vì sao cùng /24 on-link thì chiều ngược không cần ip route add thêm.

Đáp án / mechanism: Với 10.10.0.1/2410.10.0.2/24 trên hai đầu pair, mỗi ns có connected route 10.10.0.0/24 dev veth tương ứng. Reply path L3 đối xứng trên cùng link → ping ns2→ns1 cũng OK nếu cả hai đầu vẫn UP. Lệnh:

sudo ip netns exec ns2 ping -c 3 10.10.0.1

4.2 Faded nặng hơn — topology khác subnet (cần route hai chiều)

Đôi khi lab (hoặc misconfig) gán hai prefix khác nhau trên hai đầu pair. Khi đó không còn on-link chung: phải có route (hoặc đổi lại mask). Đây là chỗ fade chính: worked một chiều route, bạn tự điền chiều ngược.

Dọn địa chỉ cũ rồi gán lệch prefix (lab cố ý):

# Remove old addresses (ignore errors if already clean)
sudo ip netns exec ns1 ip addr flush dev veth1s
sudo ip netns exec ns2 ip addr flush dev veth2s

# Different subnets on the same L2 pair (still one cable)
sudo ip netns exec ns1 ip addr add 10.10.1.1/24 dev veth1s
sudo ip netns exec ns2 ip addr add 10.10.2.1/24 dev veth2s

# Ensure links still UP
sudo ip netns exec ns1 ip link set veth1s up
sudo ip netns exec ns2 ip link set veth2s up

Worked — chiều ns1 → ns2: ns1 phải biết “tới 10.10.2.0/24 ra iface nào”. Trên pair point-to-point, lab dùng on-link route (dev veth…, không cần via — tránh chicken-and-egg “via IP chưa reach được”):

# Worked: route from ns1 to ns2's subnet out the local veth end
sudo ip netns exec ns1 ip route add 10.10.2.0/24 dev veth1s

# Try ping (often still fails until YOU add the reverse route)
sudo ip netns exec ns1 ping -c 2 10.10.2.1 || true
Tự điền — reverse route

Echo request có thể tới ns2, nhưng reply từ ns2 về 10.10.1.1 cần ns2 biết path tới 10.10.1.0/24.
TODO của bạn: một lệnh ip route add … trong ns2 — cùng pattern on-link (10.10.1.0/24 ra dev veth2s). Viết ra trước, rồi mới mở đáp án dưới. Sau đó ping hai chiều.

Đáp án reverse route:

# Faded answer — reverse path ns2 -> ns1 subnet (on-link via local veth)
sudo ip netns exec ns2 ip route add 10.10.1.0/24 dev veth2s

sudo ip netns exec ns1 ping -c 3 10.10.2.1
sudo ip netns exec ns2 ping -c 3 10.10.1.1

Mechanism: ICMP là hai gói. Một chiều route chỉ “bắn tên đi”; nếu phía nhận không có route về source, reply drop trong netns nhận (hoặc không ra được iface) — triệu chứng hay bị đổ oan iptables. Production: policy route / missing return path cùng class với “half-NAT”.

4.3 Trả lab về mô hình Docker-like (cùng /24)

Mini-challenge sau dùng một subnet + bridge. Trả pair về /24 chung cho sạch:

sudo ip netns exec ns1 ip addr flush dev veth1s
sudo ip netns exec ns2 ip addr flush dev veth2s
sudo ip netns exec ns1 ip route flush dev veth1s 2>/dev/null || true
sudo ip netns exec ns2 ip route flush dev veth2s 2>/dev/null || true

sudo ip netns exec ns1 ip addr add 10.10.0.1/24 dev veth1s
sudo ip netns exec ns2 ip addr add 10.10.0.2/24 dev veth2s

sudo ip netns exec ns1 ping -c 2 10.10.0.2
sudo ip netns exec ns2 ping -c 2 10.10.0.1

5. Docker / container map — veth là gì trong docker run?

Bridge mode cổ điển (rút gọn; bridge thật ở lab 03):

flowchart LR
    P["Process container"] --> NS["Container netns"]
    NS --> ETH["eth0 = veth end"]
    ETH -->|"peer"| HV["veth on host"]
    HV --> BR["bridge docker0"]
    BR --> W["other containers / NAT"]
Bạn thấyObject bài này
eth0 trong containerMột đầu veth đã move vào netns + rename
vethXXXX trên hostĐầu peer — thường master docker0 (bài sau)
IP 172.17.0.2/16ip addr add trong netns + gateway qua bridge IP
Hai container ping nhauKhông bắt buộc pair thẳng container–container: thường mỗi container một pair lên bridge, bridge L2 chuyển giữa các port

Bài này cố ý point-to-point (ns1—veth—ns2) để bạn cảm cáp trước khi thêm switch ảo. Mini-challenge: nhiều netns, mỗi ns một veth lên một bridge — mô hình docker0.

6. Pitfall tổng hợp

Nhầm 1: “Hai netns trên cùng host thì tự route được.”
✅ Không link L2/L3 → không path. Fail là đúng; sửa bằng veth/bridge, không phải chỉ iptables -P ACCEPT.

Nhầm 2: “Chỉ up và gán IP một đầu.”
✅ Peer DOWN hoặc không IP → ARP/ICMP im. Checklist: ip link + ip addr trong từng netns.

Nhầm 3: “Ping một chiều OK = mạng xong.”
✅ Thiếu return route (topology lệch subnet, policy routing, firewall một chiều) → tool một chiều pass, chiều kia/timeout app fail. Luôn thử hai chiều khi lab “nối hai stack”.

Nhầm 4: “Subnet không khớp nhưng cùng veth thì IP stack tự hiểu.”
✅ Mask/prefix quyết định on-link hay cần route. 10.10.1.1/24 không coi 10.10.2.1 là local subnet.

Nhầm 5: “Xoá veth1s chỉ mất một iface.”
✅ Xoá một đầu phá pair. Tạo lại pair + move + IP.

# Cleanup this lesson
sudo ip netns del ns1 2>/dev/null || true
sudo ip netns del ns2 2>/dev/null || true
# Deleting netns removes moved veth ends with it
ip netns list
⚠️ Return path — half-success

Triệu chứng: tcpdump thấy echo request vào ns2, không thấy reply ra; hoặc reply ra nhưng ns1 không nhận vì route/ns sai. Root cause hay gặp: chỉ có route đi. Fix: route (hoặc connected prefix) đối xứng, hai đầu UP, đúng dev.

📚 Deep Dive — man page

📚 Deep Dive — tài liệu gốc

Spec / reference:

Ghi chú: Production container ns thường anonymous (PID) — cùng veth move; debug bằng nsenter -t <pid> -n (bài 06).

Liên hệ các bài khác

Tóm tắt

  • Hai netns cần veth pair (hoặc tương đương) để có L2 path.
  • Pipeline: ip link add type vethset netns mỗi đầu → lo/veth UP → IP → (nếu lệch subnet) route hai chiều.
  • Cùng /24 on-link: connected route đủ cho ping hai chiều khi pair lành.
  • Lệch prefix: thiếu return route = half-success — class lỗi hay đổ oan firewall.
  • Docker: mỗi container một đầu veth; đầu host thường gắn bridge (lab 03), không phải lúc nào cũng pair thẳng hai container.
  • Pitfall: một đầu DOWN, quên dọn netns, xoá một đầu pair, chỉ test một chiều.

7. Tự kiểm tra

Tự kiểm tra
Q1
Hai netns chỉ có lo UP, chưa có veth. Vì sao iptables -P ACCEPT trên host không giúp ping chéo?
Không có netdev/link nối hai netns thì không có L2/L3 path — isolation mặc định. Policy ACCEPT trên init netns không tạo cáp ảo. Cần veth (hoặc shared netns / move iface). Fail là đúng mô hình.
Q2
Liệt kê thứ tự tối thiểu để ping được giữa ns1 và ns2 bằng một veth pair (cùng /24).
Tạo ns1/ns2; ip link add … type veth peer name …; move mỗi đầu vào một ns; trong mỗi ns: lo up (thói quen), veth up, gán IP cùng subnet (ví dụ 10.10.0.1/24 và 10.10.0.2/24); ping hai chiều. Thiếu up hoặc IP một phía → fail.
Q3
Vì sao xoá một đầu veth làm hỏng cả 'cáp', trong khi down một đầu chỉ tạm ngắt?
veth là pair kernel: lifecycle gắn peer. ip link del một đầu phá object pair. Admin DOWN một đầu giữ object nhưng ngắt TX/RX logic (cáp rút một phía). Fix down: up lại; fix delete: tạo pair mới + gán lại netns/IP.
Q4
ns1 có route tới subnet ns2, ping một chiều timeout. tcpdump trong ns2 thấy echo request. Chẩn đoán ưu tiên?
Ưu tiên return path: ns2 thiếu route (hoặc filter) về source ns1. Request tới được chứng tỏ chiều đi và peer L2 tạm ổn. Thêm route đối xứng (hoặc cùng connected prefix), kiểm UP hai đầu, rồi mới đào deep iptables.
Q5
Trong Docker bridge mode cổ điển, eth0 của container tương ứng object nào trong lab veth+netns?
Một đầu của veth pair đã được move vào netns container (thường rename eth0), gán IP và up. Đầu peer nằm host netns, thường gắn bridge docker0 — không phải tun. Lab point-to-point là rút gọn; multi-container dùng nhiều pair lên cùng bridge.
Q6
Gán 10.10.1.1/24 trên ns1 và 10.10.2.1/24 trên ns2 cùng một pair. Vì sao cần route tường minh?
Prefix /24 khác nhau: địa chỉ peer không nằm connected subnet của iface local, stack không coi đích là on-link cùng LAN logic. Phải route (via peer hoặc on-link dev) — và cả hai chiều nếu muốn ICMP request+reply.
Q7
Bạn ip link set veth1s netns ns1 nhưng quên move veth2s. ip -br link trên host còn thấy gì, và ping từ ns1 đi đâu?
Đầu veth2s còn trên host (init netns); ns1 chỉ có một đầu pair. Traffic từ ns1 ra veth1s xuất hiện trên veth2s host, không vào ns2. Muốn hai container/ns nói chuyện, cả hai đầu phải ở đúng netns (hoặc một đầu host nếu topology host–container).

Bài tiếp theo: Mini-challenge — dựng Docker bridge network from scratch

Bài này đáng gửi cho bạn học cùng?

Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.

Bài này có giúp bạn hiểu bản chất không?

Hỏi đáp về bài này

Chưa có câu hỏi

Đặt câu hỏi

Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).

Đặt câu hỏi đầu tiên

Bài tiếp theo

Mini-challenge — dựng Docker bridge network from scratch