Mini-challenge — dựng Docker bridge network from scratch
Tự dựng lại bridge network kiểu Docker: 2 netns + veth + bridge + subnet + gateway, hai container ping nhau được — không cần Docker.
TL;DR: Docker bridge network không phải ma thuật — bạn tự dựng được bằng tay: hai network namespace (hai “container”), mỗi cái một veth cắm vào một Linux bridge, gán IP cùng subnet, đặt gateway trên bridge, bật mọi iface → ping chéo thành công. Lab này không cần Docker daemon, không đụng SNAT/DNAT (ra Internet và publish port là bài 04 / 05). Xong lab, bạn ghép được plumbing Module 1 + 2 thành đúng mô hình docker0.
🎯 Đề bài
Bạn có một máy Linux (VM, bare metal, hoặc WSL2 có netns đầy đủ) với quyền sudo. Không cần Docker đang chạy.
Mục tiêu: dựng một Docker-style bridge network từ scratch sao cho hai “container” (netns) ping nhau được.
Yêu cầu chức năng (acceptance):
| # | Kiểm tra | Kỳ vọng |
|---|---|---|
| 1 | Hai netns (tên tự chọn, ví dụ c1 / c2) | Tồn tại, list được bằng ip netns list |
| 2 | Một bridge (tên tự chọn, ví dụ br0) | UP, có IP gateway trên bridge |
| 3 | Mỗi netns | Một đầu veth + IP cùng subnet với gateway |
| 4 | Đầu host của mỗi veth | Gắn master bridge, UP |
| 5 | ip netns exec c1 ping -c1 <IP-c2> | success (và nên thử chiều ngược) |
| 6 | Không dùng Docker daemon | Chỉ ip (iproute2) |
| 7 | Dọn lab | Document cleanup: xoá netns + bridge |
Phạm vi lab (HARD):
- ✅ netns + veth + bridge + subnet + gateway + ping L2/L3 trong LAN ảo
- ❌ Không SNAT / MASQUERADE /
ip_forwardra Internet - ❌ Không DNAT / publish port
- ❌ Không bắt buộc
docker network create
Đây là bài fadeLevel: independent: skeleton có TODO — bạn điền lệnh thật trước khi mở §Lời giải. §Gợi ý chỉ định hướng, không chốt plan IP đầy đủ.
🔍 Phân tích I-P-O
| Mô tả | |
|---|---|
| Input | Linux + sudo + iproute2 (ip); 0 Docker; 2 “container” logic cần cùng LAN |
| Process | Tạo netns → tạo bridge + IP gateway → mỗi container một veth pair → move peer vào netns → host-end master bridge → UP + gán IP trong netns → verify ping |
| Output | Hai netns ping chéo OK; topology tương đương “hai container cùng bridge network” |
Điều kiện làm đúng:
- Chạy trên Linux kernel có netns (macOS / Docker Desktop host không thay thế được view
ip netnstrên Linux). - Dọn rác lab cũ (
ip netns list, bridge cũ) trước khi bắt đầu — tránh trùng tên iface. - Mở một terminal root/
sudođủ; nếu debug, terminal thứ hai chạyip -br link/bridge fdb show.
flowchart TB
subgraph C1["Netns c1"]
E1["c1-eth0 + IP"]
end
subgraph C2["Netns c2"]
E2["c2-eth0 + IP"]
end
subgraph HOST["Init / host netns"]
V1["veth-c1"]
V2["veth-c2"]
BR["bridge br0 + gateway IP"]
end
E1 -->|"veth peer"| V1
E2 -->|"veth peer"| V2
V1 --> BR
V2 --> BR📦 Concept mapping (Module 1 + Module 2)
Lab không dạy concept mới — chỉ ghép mảnh đã học:
| Kiến thức cần | Bài gốc | Dùng ở đây |
|---|---|---|
| Network namespace = stack mạng riêng | M2 — Network namespaces | Hai “container”: c1, c2 |
| veth pair = cáp L2 hai đầu | M2 — Nối container bằng veth + M1 — Virtual devices | Mỗi container một pair; peer vào netns, host-end lên bridge |
| Linux bridge = switch L2 ảo | M1 — Linux bridge | br0 thay docker0; host-end veth = port (master) |
| Gateway trên bridge | Cùng bài bridge (IP trên br0/docker0) | Host làm default gateway của LAN ảo (lab chỉ cần cùng subnet để ping peer; gateway chuẩn bị cho bài SNAT) |
| Không cần iptables NAT | M1 — iptables/netfilter (context) | Ping trong LAN bridge = L2/L3 on-link — chưa SNAT |
Vì sao không pair thẳng c1—veth—c2 như bài 02?
Bài 02 chứng minh cáp. Production Docker: N container cùng network → một switch nhiều port, không mesh N(N−1)/2 pair. Lab này bắt đúng mô hình đó.
▶️ Starter — skeleton (điền TODO)
Copy skeleton. Đừng paste đáp án từ §Lời giải. Thay mỗi # TODO bằng lệnh thật, chạy từng bước, đọc stderr.
#!/usr/bin/env bash
# lab-bridge-from-scratch.sh
# Requires: Linux + sudo + iproute2
# Scope: 2 netns + bridge + veth + same-subnet IPs + ping
# NO SNAT / NO DNAT / NO docker daemon
set -euo pipefail
# --- 0) Optional: cleanup previous run (uncomment if needed) ---
# sudo ip netns del c1 2>/dev/null || true
# sudo ip netns del c2 2>/dev/null || true
# sudo ip link del br0 2>/dev/null || true
# --- 1) Two container namespaces ---
# TODO: create netns c1 and c2
# --- 2) Bridge (the virtual switch) ---
# TODO: create bridge device
# TODO: bring bridge UP
# TODO: assign gateway IP/prefix on the bridge
# --- 3) veth for c1 ---
# TODO: create veth pair (host end + peer for c1)
# TODO: move peer into netns c1
# TODO: attach host end as port on the bridge (master)
# TODO: bring host end UP
# TODO: inside c1: lo UP, peer iface UP, assign container IP/prefix
# --- 4) veth for c2 ---
# TODO: repeat the same pattern for c2 with a different container IP
# --- 5) Optional but recommended: default route via bridge gateway ---
# TODO: inside each netns, add default route via the bridge IP
# (Not strictly required for on-link peer ping if same /24; useful for next lab.)
# --- 6) Verify ---
# TODO: ping from c1 to c2 IP
# TODO: ping from c2 to c1 IP
echo "Lab verify done — check ping output above."
Checklist tự chấm trước khi xem lời giải:
-
ip netns listthấy đủ 2 ns -
ip -br link show master br0(hoặc tên bridge bạn chọn) thấy hai port veth host - Trong mỗi ns:
ip -br addrcó IP đúng prefix - Ping chéo 1 gói thành công
- Chưa chèn rule
iptables -t nat/ chưa bật SNAT
💡 Gợi ý
§Gợi ý không chốt plan IP cụ thể hay “dùng đúng dải X với gateway .1”. Bạn tự chọn một private
/24(hoặc/16kiểu Docker) và gán gateway + hai host. Chỉ mở §Lời giải sau khi đã thử.
Gợi ý 1 — Topology đúng mô hình Docker:
Không nối thẳng netns–netns bằng một veth pair. Mỗi “container” có một pair riêng; đầu host của mọi pair cùng gắn một bridge. Bridge = switch; container = máy cắm vào switch.
Gợi ý 2 — Thứ tự object:
Tạo bridge và UP bridge trước khi gắn port thường dễ debug hơn. Tạo pair → set … netns peer → set … master <bridge> host-end → UP host-end → trong netns: lo + peer UP + ip addr add.
Gợi ý 3 — Cùng subnet:
IP gateway trên bridge và IP hai container phải cùng prefix (ví dụ cùng /24). Khi đó kernel coi peer on-link — không cần route tường minh giữa hai container cho ping chéo. (Route default via <gateway> chuẩn bị cho bài ra Internet, không bắt buộc để pass acceptance ping peer.)
Gợi ý 4 — Checklist khi ping fail:
Trong từng netns: iface peer UP chưa? IP đúng chưa? Trên host: bridge UP? Hai host-end master bridge và UP? Một đầu DOWN = “cáp rút một phía”. Xem thêm bài 02 pitfall.
Gợi ý 5 — Công cụ quan sát (không lộ đáp án):
ip -br link, ip -br addr, ip netns exec c1 ip route, bridge link, bridge fdb show (sau khi có traffic ARP/ICMP). FDB có MAC sau ping/ARP là tín hiệu L2 qua bridge đang học.
Gợi ý 6 — Phạm vi:
Đừng “fix” bằng cách thêm MASQUERADE hay docker network create. Acceptance chỉ cần ping trong LAN bridge.
✅ Lời giải
Chỉ mở sau khi đã tự dựng. Plan IP dưới đây là một phương án chuẩn lab (private, không đụng dải Docker mặc định
172.17.0.0/16nếu máy bạn đang chạy Docker).
Plan IP (author solution)
| Object | Name | Address / role |
|---|---|---|
| Bridge | br0 | 10.10.0.1/24 (gateway LAN ảo) |
Netns c1 | peer c1-eth0 | 10.10.0.10/24 |
Netns c2 | peer c2-eth0 | 10.10.0.11/24 |
| Host ends | veth-c1, veth-c2 | master br0, không cần IP trên host-end |
Lệnh đầy đủ (đã thiết kế theo man ip-netns / bridge driver model)
# requires root on Linux — NO docker, NO SNAT/DNAT
# 0) Clean slate (ignore errors if absent)
sudo ip netns del c1 2>/dev/null || true
sudo ip netns del c2 2>/dev/null || true
sudo ip link del br0 2>/dev/null || true
# 1) Namespaces
sudo ip netns add c1
sudo ip netns add c2
# 2) Bridge + gateway
sudo ip link add br0 type bridge
sudo ip link set br0 up
sudo ip addr add 10.10.0.1/24 dev br0
# 3) veth for c1
sudo ip link add veth-c1 type veth peer name c1-eth0
sudo ip link set c1-eth0 netns c1
sudo ip link set veth-c1 master br0
sudo ip link set veth-c1 up
sudo ip netns exec c1 ip link set lo up
sudo ip netns exec c1 ip link set c1-eth0 up
sudo ip netns exec c1 ip addr add 10.10.0.10/24 dev c1-eth0
# optional: default via gateway (prep for SNAT lab; not required for peer ping)
sudo ip netns exec c1 ip route add default via 10.10.0.1 || true
# 4) veth for c2
sudo ip link add veth-c2 type veth peer name c2-eth0
sudo ip link set c2-eth0 netns c2
sudo ip link set veth-c2 master br0
sudo ip link set veth-c2 up
sudo ip netns exec c2 ip link set lo up
sudo ip netns exec c2 ip link set c2-eth0 up
sudo ip netns exec c2 ip addr add 10.10.0.11/24 dev c2-eth0
sudo ip netns exec c2 ip route add default via 10.10.0.1 || true
# 5) Verify L2/L3 on the virtual LAN
sudo ip netns exec c1 ping -c 1 10.10.0.11
sudo ip netns exec c2 ping -c 1 10.10.0.10
Kỳ vọng: mỗi ping in 1 received (hoặc 0% packet loss).
Vì sao các bước này đủ (cơ chế)
- Hai netns = hai stack: route table / ARP / iface riêng — đúng isolation container.
- Bridge = switch L2: frame từ port
veth-c1forward sangveth-c2theo MAC (FDB). - Mỗi veth = một cáp: peer trong netns (
c1-eth0) ↔ host port trên bridge. - Cùng
/24→ connected route on-link; ARP resolve IP peer → MAC → bridge học FDB → ICMP echo/reply. - IP trên
br0= gateway Docker-like (docker0cũng mang IP). Ping peer không bắt buộc default route; default route chuẩn bị bài 04 SNAT.
flowchart LR
subgraph NS1["c1"]
A["10.10.0.10 c1-eth0"]
end
subgraph NS2["c2"]
B["10.10.0.11 c2-eth0"]
end
subgraph H["host"]
P1["veth-c1"]
P2["veth-c2"]
BR["br0 10.10.0.1/24"]
end
A --> P1 --> BR
B --> P2 --> BRQuan sát nhanh sau khi ping
# Ports on bridge
ip -br link show master br0
# FDB may show MACs after ARP/ICMP
bridge fdb show br br0 | head
# Routes inside a container ns
sudo ip netns exec c1 ip route
🧹 Cleanup
Luôn dọn sau lab (tránh sót netns/bridge khi làm bài SNAT):
# Delete namespaces first (removes moved veth peers with them)
sudo ip netns del c1 2>/dev/null || true
sudo ip netns del c2 2>/dev/null || true
# Host-end veth often vanish with peer; delete bridge last
sudo ip link del br0 2>/dev/null || true
# If a dangling host veth remains:
# sudo ip link del veth-c1 2>/dev/null || true
# sudo ip link del veth-c2 2>/dev/null || true
ip netns list
ip -br link | grep -E 'br0|veth-c' || echo "clean"
Lab bắt buộc Linux (ip netns, bridge kernel). Máy Mac chỉ chạy Docker Desktop không thay được: bạn không thấy cùng netns/veth/bridge trên host Darwin. Dùng VM Linux, multipass, cloud instance, hoặc remote lab.
🔎 Mở rộng — so sánh với docker network inspect
Nếu máy có Docker, tạo network bridge thật và đối chiếu object (không thay lab bằng Docker):
# Optional — only if docker is installed
docker network create --driver bridge labnet-compare
docker network inspect labnet-compare
Trong JSON inspect, tìm các field map 1-1 với lab tay:
Docker (inspect / runtime) | Lab tay (bài này) |
|---|---|
Driver: "bridge" | ip link add br0 type bridge |
IPAM.Config[].Subnet / Gateway | Prefix + IP trên br0 |
Container Endpoints / eth trong container | Peer veth trong netns + IP |
Host veth + bridge port (thấy bằng ip link / docker network) | veth-c* master br0 |
| Tên network | Tên bridge logic (Docker đặt tên bridge kiểu br-… theo network ID) |
# After: docker run -d --name cA --network labnet-compare alpine sleep 3600
# docker run -d --name cB --network labnet-compare alpine sleep 3600
# docker exec cA ping -c1 <IP-cB>
#
# On Linux host (not Docker Desktop Mac VM opaque layer):
# ip -br link | grep -E 'br-|veth'
# docker network inspect labnet-compare --format '{{json .IPAM.Config}}'
Đọc ra: docker network create + attach container ≈ script bạn vừa viết. Daemon thêm IPAM, naming, iptables chain — core L2/L3 path vẫn là netns + veth + bridge.
Dọn Docker (nếu đã thử):
docker rm -f cA cB 2>/dev/null || true
docker network rm labnet-compare 2>/dev/null || true
Thử ngẫmproduction của bạn có 5 network Docker khác nhau cho 5 nhóm service. Dưới lớp docker network create, đó là 5 bridge riêng hay 1 bridge chia VLAN? Cách bạn vừa dựng tay gợi ý gì?
⚠️ Pitfall hay gặp trong lab này
❌ Pair thẳng hai netns, bỏ bridge — pass ping nhưng không phải mô hình Docker multi-container.
✅ Mỗi container một veth lên cùng bridge.
❌ Gán IP trên host-end veth thay vì trên bridge / trong netns — lệch mô hình docker0 + eth0 container.
✅ IP container trên peer trong netns; gateway trên bridge; host-end thường không cần IP.
❌ Quên master bridge hoặc quên UP port — ARP không qua switch ảo.
✅ ip link set <veth-host> master br0 + UP bridge và port.
❌ Subnet lệch giữa c1 và c2 — mất on-link, cần route (dễ half-success như bài 02).
✅ Một prefix chung cho gateway + mọi container trên network đó.
❌ Thêm SNAT “cho chắc” — ra ngoài scope; che lỗi L2.
✅ Ping peer không cần NAT. NAT = bài tiếp theo.
Liên hệ các bài khác
- 01 — Network namespaces — isolation; lab này nhân isolation × 2 rồi nối bằng switch.
- 02 — Nối container bằng veth — một cáp point-to-point; lab này nhiều cáp + bridge.
- M1 — Linux bridge — FDB /
master; lab áp dụng full topology container. - 04 — Container ra Internet (SNAT) — bước kế: private IP trên bridge LAN → host
ip_forward+ MASQUERADE ra WAN. Giữ topology lab này, thêm NAT — đừng xoá bridge trước khi làm bài 04 nếu bạn muốn tái dùngc1/br0. - 05 — Publish port (DNAT) — chiều inbound sau khi đã có path host ↔ container.
- 06 — Debug nsenter / tcpdump — khi lab fail: bắt gói đúng veth/bridge.
Tóm tắt
- Docker bridge network = script: netns + veth + bridge + IP/gateway — bạn vừa implement không cần daemon.
- Acceptance: 2 netns, 1 bridge có gateway, mỗi ns một veth lên bridge, ping chéo, cleanup documented.
- Cùng subnet on-link qua bridge L2; SNAT/DNAT cố ý để bài sau.
docker network inspectchỉ là view có cấu trúc của cùng object kernel.
Bài tiếp theo: Container ra Internet — SNAT và MASQUERADE
Bài này có giúp bạn hiểu bản chất không?
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.
Nối container bằng veth — ping qua 2 netns
Bài tiếpContainer ra Internet — SNAT và MASQUERADE
Thảo luận & góp ý
Hỏi đáp về bài này