Network namespaces — vì sao container thấy mạng riêng
Tạo network namespace, mỗi netns có stack mạng độc lập (interface, route, iptables riêng). Đây là isolation layer Docker/Kubernetes dùng cho mỗi container/pod.
TL;DR: Network namespace (netns) là cơ chế kernel Linux tách stack mạng thành các “máy logic” trên cùng host: mỗi netns có interface, bảng route, iptables/nft ruleset, và socket riêng. Container “có mạng riêng” vì runtime đưa process vào một netns — không phải vì Docker viết stack TCP mới. Bạn tạo netns bằng ip netns add, chạy lệnh bên trong bằng ip netns exec, và chứng minh isolation bằng ip addr / ip route / iptables khác host. Pitfall số một: netns mới chỉ có lo và lo mặc định DOWN — ping 127.0.0.1 fail cho đến khi ip link set lo up.
On-call: junior báo “container không listen được localhost:8080 — trên host curl 127.0.0.1:8080 vẫn thấy service cũ”. Họ đã ss -lntp trên host và thấy process app… nhưng app chạy trong netns container. 127.0.0.1 của host ≠ 127.0.0.1 của container. Bạn ip netns identify <pid> (hoặc nsenter/docker exec) rồi ss bên trong netns — bind đúng chỗ, chỉ đang nhìn nhầm stack. Không phải firewall, không phải app “im lặng” — sai namespace.
Module 1 đã dạy stack, veth, bridge, netfilter trên một view host. Bài này mở chiều isolation: cùng kernel, nhiều stack mạng song song. Không có netns, “container network” không tồn tại; không hiểu netns, mỗi lần debug bạn chỉ restart daemon.
Bài này implement netns bằng ip netns và chứng minh stack độc lập — trước khi nối hai netns bằng veth.
1. Analogy — Nhiều căn hộ, mỗi căn một tủ điện mạng
Hình dung một toà nhà (host Linux) có nhiều căn hộ (network namespace):
- Mỗi căn có tủ mạng riêng: cổng RJ45 ảo (interface), sơ đồ dây (route), nội quy ra vào (firewall rules).
127.0.0.1là chuông cửa trong căn — bấm chuông căn 5 không mở cửa căn 7.- Hành lang chung (kernel) mang điện/nước (CPU, memory) cho mọi căn — nhưng không tự nối LAN căn này với căn kia.
- Muốn hai căn nói chuyện: thợ (runtime) phải kéo cáp nhảy (veth pair) giữa hai tủ — bài sau.
| Đời thường | Network namespace |
|---|---|
| Căn hộ | Một netns |
| Tủ điện / switch trong căn | Interface list trong netns |
| Bản đồ “đi đâu ra ngoài” | Route table của netns |
| Nội quy ra vào | iptables/nft của netns |
Chuông localhost | lo / 127.0.0.1 chỉ trong netns đó |
| Cáp giữa hai căn | veth pair (bài 02) |
Netns không tạo kernel mới — nó tạo view mạng riêng. Cùng softirq/sk_buff/netfilter Module 1, nhưng device, route, rule, socket gắn theo netns. Debug sai view = đọc đúng lệnh trên nhầm tủ điện.
2. Network namespace cô lập những gì?
Namespace Linux là cơ chế partition tài nguyên kernel theo process. Có nhiều loại (pid, mnt, uts, user…). Network namespace (CLONE_NEWNET) tách riêng phần networking:
| Tài nguyên | Có tách theo netns? | Ý nghĩa debug |
|---|---|---|
Network interfaces (ip link) | Có | Host không thấy eth0 của container (và ngược lại), trừ khi share/move |
| Địa chỉ IP | Có | Cùng 10.0.0.5 có thể tồn tại ở hai netns khác nhau |
| Route table | Có | Default gateway container ≠ host |
| iptables / nftables ruleset | Có | Rule NAT host không “tự” là rule trong netns container |
| Socket / port listen | Có | Hai process hai netns cùng bind :80 được |
/proc/net/*, ss, netstat | Có (view theo netns) | Phải exec/nsenter đúng netns mới thấy |
| CPU, RAM, filesystem (trừ mount ns) | Không (netns không lo) | Netns chỉ là lớp mạng |
flowchart TB
subgraph HOST["Host / init netns"]
H_IF["eth0, docker0, veth..."]
H_RT["route table host"]
H_FW["iptables host"]
end
subgraph NS1["Netns container A"]
A_IF["lo, eth0"]
A_RT["route table A"]
A_FW["iptables A"]
end
subgraph NS2["Netns container B"]
B_IF["lo, eth0"]
B_RT["route table B"]
B_FW["iptables B"]
end
HOST -.->|"no auto link"| NS1
HOST -.->|"no auto link"| NS2
NS1 -.->|"isolated"| NS2Cơ chế bên dưới (mức đủ để debug): process gắn một network ns (file descriptor /proc/<pid>/ns/net). Mọi lời gọi socket, ip, netfilter lookup của process đó resolve trong ns đó. Kernel vẫn một bản; object mạng (netdev, fib route, xtables) mang con trỏ “thuộc netns nào”. Vì vậy ip addr trên host không liệt kê iface chỉ nằm trong netns con — không phải “Docker che giấu”, mà object không thuộc view hiện tại.
Vì sao container cần netns riêng?
Nếu mọi container share netns host:
- Cùng port
8080→ conflict bind. iptables -Ftrong một “container đặc quyền” có thể phá rule host.- Không có ranh giới rõ cho multi-tenant / least privilege mạng.
Netns cho phép: mỗi container (bridge mode) một stack, IP private riêng, port riêng; host giữ bridge/NAT ở init netns. Kubernetes pod thường một netns cho cả pod (các container trong pod share network — chi tiết Module 3); mô hình vẫn là “một stack logic / một netns”, không phải “mỗi process một TCP stack user-space”.
PID namespace, mount namespace, user namespace… khác network namespace. Bài này chỉ netns. Container runtime thường kết hợp nhiều namespace; khi debug mạng, vào đúng net ns (ip netns exec, nsenter -t <pid> -n, docker exec).
3. Tạo và vào netns bằng ip netns
iproute2 quản lý netns có tên qua thư mục (thường /var/run/netns/): mỗi tên là một bind-mount tới file ns. Đây là cách lab và nhiều script ops dùng — không cần Docker.
Chạy trên Linux (VM/WSL2/host có netns), cần sudo. Copy-paste từng khối, đọc output thật trên máy bạn.
# 1) Create a named network namespace
sudo ip netns add ns1
# 2) List named namespaces (ns1 should appear)
ip netns list
# 3) What interfaces exist *inside* ns1? (expect lo only, often DOWN)
sudo ip netns exec ns1 ip link
sudo ip netns exec ns1 ip addr
# 4) Bring loopback up — classic first step in a fresh netns
sudo ip netns exec ns1 ip link set lo up
# 5) Re-check addresses / state
sudo ip netns exec ns1 ip addr
sudo ip netns exec ns1 ip route
# 6) Loopback should answer only *inside* this netns
sudo ip netns exec ns1 ping -c 2 127.0.0.1
Kết quả kỳ vọng:
Lệnh (trong ns1) | Trước lo up | Sau lo up |
|---|---|---|
ip link | lo … DOWN | lo … UP |
ip addr | lo không usable / chưa có traffic local | 127.0.0.1/8 trên lo (thường kèm ::1) |
ping 127.0.0.1 | fail / unreachable | 0% packet loss |
ip route | rỗng hoặc gần rỗng (chưa default gw) | vẫn không có route ra Internet — đúng |
So sánh cùng lệnh trên host (không netns exec):
# Host (init netns) — your real NIC still here
ip -br link
ip route | head
# Inside ns1 — still isolated; no eth0 of the host
sudo ip netns exec ns1 ip -br link
Host vẫn thấy eth0/ens*/wlan*; trong ns1 không thấy NIC vật lý host (trừ khi bạn move device vào netns — hiếm cho lab container; container dùng veth thay vì cướp NIC host).
ip netns exec làm gì?
ip netns exec ns1 <cmd> roughly:
- Mở ns file của
ns1. setns()(hoặc tương đương) sang network namespace đó cho process con.- Chạy
<cmd>trong context đó (và thường mount/sys//etcnetns nếu cấu hình).
Tương đương ngắn với nhiều lệnh ip:
# Same view as: sudo ip netns exec ns1 ip link
sudo ip -n ns1 link
sudo ip -n ns1 addr
Process lâu dài (server) cần start trong netns hoặc join ns — không phải chỉ chạy một exec rồi thoát là “app mãi trong ns” trừ khi bạn giữ process đó.
Trước khi lo up, bạn chạy sudo ip netns exec ns1 ping -c 1 127.0.0.1. Kết quả là success, fail ngay, hay treo? Viết dự đoán — rồi chạy. Không gán IP thêm trên lo trước khi quan sát.
Mechanism: lo DOWN → stack local coi interface không sẵn sàng; ping loopback fail (hoặc không usable). Sau ip link set lo up, địa chỉ 127.0.0.1/8 hoạt động chỉ trong netns đó. Host ping 127.0.0.1 vẫn đập vào lo host — hai loopback độc lập.
4. Chứng minh stack độc lập — iface, route, iptables
Isolation không chỉ là “có lo riêng”. Ba trục bạn phải tự chứng trên lab (worked):
4.1 Interface list khác nhau
echo "=== HOST ==="
ip -br link
echo "=== NS1 ==="
sudo ip netns exec ns1 ip -br link
Host: nhiều iface. ns1: gần như chỉ lo (sau khi up).
4.2 Route table khác nhau
# Optional: add a dummy route only inside ns1 (does not touch host table)
sudo ip netns exec ns1 ip route add 198.51.100.0/24 via 127.0.0.1 dev lo
echo "=== HOST routes (should NOT show 198.51.100.0/24 via lo ns1) ==="
ip route | grep 198.51.100 || echo "host: no 198.51.100 route (expected)"
echo "=== NS1 routes ==="
sudo ip netns exec ns1 ip route
Route thêm trong ns1 không xuất hiện trên host. Xoá test:
sudo ip netns exec ns1 ip route del 198.51.100.0/24 || true
4.3 iptables ruleset khác nhau
# Count rules / list filter table in each view (nft backend may show via iptables-nft)
echo "=== HOST filter (first lines) ==="
sudo iptables -L -n 2>/dev/null | head -n 15 || echo "iptables not available on host"
echo "=== NS1 filter (fresh netns often empty/default) ==="
sudo ip netns exec ns1 iptables -L -n 2>/dev/null | head -n 15 || echo "iptables not available in ns1"
Netns mới thường không kế thừa rule NAT DOCKER của host. Đó là lý do publish port / MASQUERADE sống ở host netns (bài SNAT/DNAT), không “tự copy” vào container.
flowchart LR
CMD["ip netns exec ns1 ..."] --> VIEW["Kernel objects in ns1"]
VIEW --> IF["netdev list"]
VIEW --> RT["FIB / routes"]
VIEW --> FW["netfilter tables"]
VIEW --> SK["sockets"]4.4 Hai netns không nối = không ping chéo
sudo ip netns add ns2
sudo ip netns exec ns2 ip link set lo up
# Still no veth between ns1 and ns2 — isolation is total
sudo ip netns exec ns1 ping -c 1 127.0.0.1 # OK: own lo
# There is no "ns2 loopback address" reachable from ns1 without a link
Không có cáp ảo, không có L3 path giữa hai netns. Fail là thiết kế, không phải thiếu iptables -P ACCEPT. Bài 02 — veth mới kéo cáp.
5. Container = một network namespace — Docker map thế nào?
Runtime container (Docker, containerd, CRI-O…) khi tạo container bridge mode cổ điển roughly:
- Tạo (hoặc lấy) network namespace cho container/pod.
- Tạo veth pair; move một đầu vào netns → rename
eth0, gán IP,lo up, default route qua gateway bridge. - Đầu còn lại gắn bridge host (
docker0/ CNI bridge) — Module 1 + bài bridge lab. - Host netns:
ip_forward, SNAT/MASQUERADE, DNAT publish port — bài 04/05 module này.
flowchart LR
CTR["Process in container"] --> NS["Container netns"]
NS --> ETH0["eth0 = veth end"]
ETH0 -->|"peer"| HV["veth on host"]
HV --> BR["bridge docker0"]
BR --> NAT["iptables NAT host"]
NAT --> PHY["physical NIC"]| Thứ bạn thấy | Object thật |
|---|---|
docker run “có IP” | IP gán trong netns container trên eth0 |
docker exec … ip addr | ip netns exec (hoặc nsenter) vào netns đó |
localhost trong container | lo của netns container |
docker run -p 8080:80 | DNAT trên host netns, không “mở lỗ” trong netns app một mình |
| Pod K8s nhiều container | Thường share một netns — cùng localhost trong pod |
Bạn không cần Docker để hiểu mô hình: ip netns add + (bài sau) veth + bridge = cùng plumbing daemon script hoá.
Liên hệ Module 1
- Stack RX/TX (Linux network stack) chạy per netns context cho object mạng.
- veth là cáp giữa netns; netns là hai đầu máy.
- bridge / iptables trên host gom nhiều netns container thành LAN ảo + NAT.
6. Pitfall tổng hợp
❌ Nhầm 1: “Quên ip link set lo up.”
✅ Netns mới: lo DOWN mặc định. App bind 127.0.0.1, healthcheck curl localhost, ping 127.0.0.1 đều fail trông như “mạng hỏng”. Checklist đầu tiên trong netns trống: lo UP.
❌ Nhầm 2: “curl 127.0.0.1 trên host = test service trong container.”
✅ 127.0.0.1 là per-netns. Service listen trong container chỉ trả lời loopback của container. Từ host: dùng IP veth/bridge, publish port, hoặc docker exec/nsenter vào đúng ns.
❌ Nhầm 3: “Hai container cùng host thì tự thấy nhau qua eth0 host.”
✅ Không share netns → không tự share iface host. Cần bridge/veth/CNI path. Ping fail giữa hai netns “trống” là đúng.
❌ Nhầm 4: “Xem iptables -L trên host là đủ cho rule trong container.”
✅ Ruleset tách. NAT Docker chủ yếu host netns; một số CNI/policy chèn rule chỗ khác. Debug: xác định packet đang ở netns nào rồi mới iptables/nft đúng view.
❌ Nhầm 5: “Xoá container / reboot là cách dọn netns lab.”
✅ Named netns từ ip netns add sống đến khi ip netns del. Lab sót ns1/ns2 → đụng tên lần sau.
# Cleanup this lesson's namespaces
sudo ip netns del ns1 2>/dev/null || true
sudo ip netns del ns2 2>/dev/null || true
ip netns list
Triệu chứng: netns mới, ip addr thấy lo nhưng ping/curl localhost fail; junior nghi DNS/iptables. Root cause: admin state DOWN. Fix một dòng: ip link set lo up (qua ip netns exec). Runtime container thường up lo giúp bạn; script tự tạo netns hay quên.
📚 Deep Dive — man page & kernel
Spec / reference:
ip-netns(8)—add/del/list/exec/identify,/var/run/netns.network_namespaces(7)— isolation boundaries (devices, stacks,/proc/net).nsenter(1)— join net ns by PID (-n), nền debug production (bài 06).
Ghi chú: Lab dùng ip netns (named). Production container thường anonymous ns gắn PID — nsenter -t <pid> -n hoặc ip netns identify. Cùng cơ chế kernel.
Liên hệ các bài khác
- 00 — Tổng quan Module 2 — netns là bước 1 trên pipeline container network.
- 02 — Nối container bằng veth — kéo cáp giữa hai netns; không veth = mãi isolated.
- 03 — Mini-challenge bridge from scratch — nhiều netns + bridge như
docker0. - 06 — Debug nsenter / tcpdump — vào đúng netns khi production hỏng.
- Module 1 — Linux Network Internals — stack, veth, bridge, iptables trên một view; bài này nhân bản view.
- M1 — Virtual network devices — veth là peer cáp; netns là hai “máy” hai đầu cáp.
Tóm tắt
- Network namespace = stack mạng độc lập (iface, route, iptables, socket) trên cùng kernel.
- Tạo/vào:
ip netns add <name>→ip netns exec <name> <cmd>(hoặcip -n <name> …). - Netns mới: chủ yếu
lo, vàloDOWN cho đến khiip link set lo up. - Container ≈ 1 netns (pod K8s: share netns trong pod); Docker script veth + bridge + NAT trên host.
- Hai netns không link = cô lập tuyệt đối — nối bằng veth ở bài sau.
- Pitfall: nhầm
localhosthost/container; đọc iptables sai netns; quên dọnip netns del.
7. Tự kiểm tra
Q1Network namespace cô lập những object mạng nào? Kể ít nhất bốn và một thứ netns không tách.▸
/proc/net. Không tách (netns không phải cgroup/pid): CPU, RAM — netns chỉ partition networking. Filesystem/PID là namespace loại khác.Q2Bạn vừa ip netns add ns1 và ip netns exec ns1 ping -c 1 127.0.0.1 thì fail. Chẩn đoán khả dĩ nhất và lệnh sửa tối thiểu?▸
lo nhưng admin state thường DOWN. Ping loopback fail không có nghĩa kernel hỏng. Sửa tối thiểu: sudo ip netns exec ns1 ip link set lo up rồi ping lại. Nếu vẫn fail, kiểm ip link/ip addr trong đúng ns1 (không phải host).Q3Vì sao curl 127.0.0.1:8080 trên host không chứng minh service trong container đang listen?▸
127.0.0.1 là địa chỉ loopback của netns hiện tại. Host và container là hai netns: listen trên lo container không xuất hiện trên lo host. Phải exec/nsenter vào netns container, hoặc dùng IP gắn veth/bridge, hoặc publish port (DNAT) trên host.Q4ip netns exec ns1 ip addr khác ip addr trên host ở điểm cơ chế nào — 'Docker che output' hay object khác?▸
setns vào netns ns1 chỉ thấy netdev/route thuộc ns đó. NIC host nằm init netns — không list trong ns1 trừ khi move device. Cùng binary ip, khác kernel network view.Q5Trong Docker bridge mode cổ điển, 'một container một netns' nghĩa là gì cho port 80? Hai container cùng host bind :80 được không?▸
:80 trên eth0/lo của mình được. Conflict xảy ra khi share netns (host network mode, hoặc cùng pod netns) hoặc khi publish cùng port host qua DNAT.Q6Hai netns ns1 và ns2 chỉ có lo UP, không veth. Vì sao ping từ ns1 sang 'IP của ns2' không phải lỗi iptables thiếu ACCEPT?▸
Q7Bạn thêm route 198.51.100.0/24 trong ns1. Vì sao ip route trên host không thấy route đó — và điều đó chứng minh gì?▸
ip route del trong đúng ns1, không phải trên host.Bài tiếp theo: Nối container bằng veth — ping qua 2 netns
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