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
⚠️ 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 đá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