Mạng Linux & Container
10/21
Bài 10 / 21~40 phútContainer NetworkingMiễn phí… lượt xem

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 traKỳ vọng
1Hai netns (tên tự chọn, ví dụ c1 / c2)Tồn tại, list được bằng ip netns list
2Một bridge (tên tự chọn, ví dụ br0)UP, có IP gateway trên bridge
3Mỗi netnsMột đầu veth + IP cùng subnet với gateway
4Đầu host của mỗi vethGắn master bridge, UP
5ip netns exec c1 ping -c1 <IP-c2>success (và nên thử chiều ngược)
6Không dùng Docker daemonChỉ ip (iproute2)
7Dọn labDocument 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_forward ra 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ả
InputLinux + sudo + iproute2 (ip); 0 Docker; 2 “container” logic cần cùng LAN
ProcessTạ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
OutputHai 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 netns trê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ạy ip -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ầnBài gốcDùng ở đây
Network namespace = stack mạng riêngM2 — Network namespacesHai “container”: c1, c2
veth pair = cáp L2 hai đầuM2 — Nối container bằng veth + M1 — Virtual devicesMỗi container một pair; peer vào netns, host-end lên bridge
Linux bridge = switch L2 ảoM1 — Linux bridgebr0 thay docker0; host-end veth = port (master)
Gateway trên bridgeCù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 NATM1 — 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.

Bash
#!/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 list thấ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 addr có 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 /16 kiể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/16 nếu máy bạn đang chạy Docker).

Plan IP (author solution)

ObjectNameAddress / role
Bridgebr010.10.0.1/24 (gateway LAN ảo)
Netns c1peer c1-eth010.10.0.10/24
Netns c2peer c2-eth010.10.0.11/24
Host endsveth-c1, veth-c2master br0, không cần IP trên host-end

Lệnh đầy đủ (đã thiết kế theo man ip-netns / bridge driver model)

Bash
# 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ế)

  1. Hai netns = hai stack: route table / ARP / iface riêng — đúng isolation container.
  2. Bridge = switch L2: frame từ port veth-c1 forward sang veth-c2 theo MAC (FDB).
  3. Mỗi veth = một cáp: peer trong netns (c1-eth0) ↔ host port trên bridge.
  4. Cùng /24 → connected route on-link; ARP resolve IP peer → MAC → bridge học FDB → ICMP echo/reply.
  5. IP trên br0 = gateway Docker-like (docker0 cũ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 --> BR

Quan sát nhanh sau khi ping

Bash
# 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):

Bash
# 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"
macOS / Docker Desktop

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):

Bash
# 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 / GatewayPrefix + IP trên br0
Container Endpoints / eth trong containerPeer veth trong netns + IP
Host veth + bridge port (thấy bằng ip link / docker network)veth-c* master br0
Tên networkTên bridge logic (Docker đặt tên bridge kiểu br-… theo network ID)
Bash
# 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ử):

Bash
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

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 inspect chỉ 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.

Thảo luận & góp ý

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

Bài tiếp theo

Container ra Internet — SNAT và MASQUERADE