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 app và db đã 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:
- 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”.
- Cắm đầu 1 vào tủ căn A, đầu 2 vào tủ căn B (move netns).
- Gán số máy trong cùng dải LAN (hoặc ghi rõ “đi qua gateway nào” = route).
- Bật cổng (admin UP) cả hai đầu — rút một đầu = im cả đường.
| Đời thường | Linux |
|---|---|
| Cáp nhảy hai đầu | veth pair |
| Cắm vào tủ căn A / B | ip link set … netns nsA / nsB |
| Số máy /24 cùng LAN | IP 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áp | Một đầu veth DOWN hoặc bị xoá |
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"| E2Thứ tự object (đủ để debug):
ip link add veth1s type veth peer name veth2s— pair sinh trong netns hiện tại (thường init/host).ip link set veth1s netns ns1— object netdev chuyển ownership sang ns1 (biến mất khỏiip linkhost).- Tương tự
veth2s→ns2. - Trong mỗi ns: rename (tuỳ chọn) →
loUP → veth UP →ip addr add …→ (nếu cần)ip route add …. - 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 … up | Admin state theo đầu — kiểm cả hai |
ip addr add x.x.x.x/24 | Connected route /24 trên iface đó (on-link) |
ip link del một đầu | Pair hỏng / đầu kia biến mất |
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
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
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/24 và 10.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
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ấy | Object bài này |
|---|---|
eth0 trong container | Mộ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/16 | ip addr add trong netns + gateway qua bridge IP |
| Hai container ping nhau | Khô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
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
Spec / reference:
veth(4)— peer pair lifecycle.ip-link(8)—type veth,netns, state.ip-netns(8)—exec/ named ns (bài 01).ip-route(8)— connected vsvia, debug return path.
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
- 01 — Network namespaces — isolation trước khi có cáp; bài này nối hai stack.
- 03 — Mini-challenge bridge from scratch — nhiều veth + một bridge như Docker.
- 04 — SNAT ra Internet — sau khi có path lên host/bridge, rewrite source ra ngoài.
- 06 — Debug nsenter / tcpdump — bắt gói đúng đầu veth khi production đứt.
- M1 — Virtual network devices — định nghĩa veth/tun/tap; bài này implement veth × netns.
- M1 — Linux bridge — bước kế: đầu host veth thành port switch ảo.
Tóm tắt
- Hai netns cần veth pair (hoặc tương đương) để có L2 path.
- Pipeline:
ip link add type veth→set netnsmỗi đầu →lo/veth UP → IP → (nếu lệch subnet) route hai chiều. - Cùng
/24on-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
Q1Hai netns chỉ có lo UP, chưa có veth. Vì sao iptables -P ACCEPT trên host không giúp ping chéo?▸
Q2Liệt kê thứ tự tối thiểu để ping được giữa ns1 và ns2 bằng một veth pair (cùng /24).▸
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.Q3Vì sao xoá một đầu veth làm hỏng cả 'cáp', trong khi down một đầu chỉ tạm ngắt?▸
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.Q4ns1 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?▸
Q5Trong Docker bridge mode cổ điển, eth0 của container tương ứng object nào trong lab veth+netns?▸
Q6Gá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?▸
Q7Bạ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?▸
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
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