Mạng Linux & Container/Mini-challenge — dựng Docker bridge network from scratch
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.

#!/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)

# 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

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

# 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)
# 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 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 đá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

Đặt 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

Bài tiếp theo

Container ra Internet — SNAT và MASQUERADE