Skip to main content

8. Docker Overlay Networking

Mục lục


Phần 7 đã dựng xong 1 swarm và biết cách khai báo service với --replicas — nhưng cố tình bỏ qua 1 câu hỏi: các task của cùng 1 service, nằm rải trên nhiều node khác nhau, gọi được tới nhau bằng cách nào? Phần này trả lời đúng câu hỏi đó.

1. Overlay network là gì?

Networking cho container ngày càng quan trọng, nhất là trong môi trường production — container cần giao tiếp được với nhau đáng tin cậy và an toàn, kể cả khi nằm trên các host khác nhau, thuộc các network vật lý khác nhau. Đây chính là vấn đề overlay networking giải quyết: tạo ra 1 network layer 2 phẳng, an toàn, trải rộng qua nhiều host — container kết nối vào network này giao tiếp trực tiếp với nhau, bất kể hạ tầng network vật lý bên dưới ra sao. Docker cung cấp sẵn overlay networking dạng native, cấu hình đơn giản và an toàn theo mặc định.

Phía sau, Docker networking được xây trên libnetwork (bản hiện thực chuẩn của Container Network Model — CNM) và các driver — thành phần pluggable hiện thực từng công nghệ/topology networking khác nhau. Docker có sẵn driver native như overlay driver, và bên thứ 3 cũng có thể cung cấp driver riêng.

💡 Hình dung: overlay network giống 1 hệ thống điện thoại nội bộ công ty — dù các chi nhánh nằm ở nhiều thành phố khác nhau, dùng hạ tầng viễn thông khác nhau, gọi nhau qua số nội bộ 4 chữ số vẫn liền mạch như đang ở chung 1 tòa nhà. "Mạng vật lý" bên dưới (dây điện thoại, tổng đài của từng thành phố) là chuyện của nhà mạng lo, người gọi không cần biết gì về nó.

Overlay: 1 network layer 2 phẳng trên nhiều host vật lý khác nhauHost A — network 172.31.1.0/24Host B — network 192.168.1.0/24Container C1Container C2overlay 10.0.0.0/24 (network ảo)2 host khác network vật lý, nhưng C1 và C2 "thấy" nhau như đang chung 1 switch

2. Dựng và test overlay network trong swarm

Chuẩn bị: swarm 2 node

Dùng lại đúng quy trình docker swarm init/docker swarm join đã nói kỹ ở Phần 7 để dựng 1 swarm 2 node đơn giản — node1 làm manager (chạy swarm init với --advertise-addr/--listen-addr trỏ về 172.31.1.5:2377), node2 join làm worker bằng token lấy từ docker swarm join-token worker. Ví dụ dưới đây giả định đã có sẵn swarm 2 node này.

Tạo overlay network

$ docker network create -d overlay demo-net

Chỉ với 1 lệnh, bạn đã có 1 overlay network mới, khả dụng cho mọi host trong swarm, với control plane được mã hóa TLS mặc định. Muốn mã hóa luôn cả data plane, thêm cờ -o encrypted.

$ docker network ls
# Output minh họa
NETWORK ID NAME DRIVER SCOPE
9f904ee27bf5 demo-net overlay swarm
a09f7e6ee180 bridge bridge local
c1a7fe8c7c17 host host local

(Output minh họa — cột SCOPE: swarm là dấu hiệu cho biết đây là overlay network trải rộng toàn swarm, không chỉ 1 host.)

Network demo-net xuất hiện với SCOPE: swarm. Điều thú vị: nếu chạy docker network ls trên node2, bạn sẽ không thấy demo-net — vì overlay network mới chỉ được "lộ diện" trên worker node nào đang thực sự chạy container gắn với nó, nhằm giảm phạm vi của gossip protocol (giao thức lan truyền thông tin theo kiểu "tin đồn" — mỗi node định kỳ chia sẻ những gì mình biết cho vài node lân cận ngẫu nhiên, cứ thế lan dần ra toàn swarm, thay vì 1 node trung tâm phải gửi riêng tới từng node — nhờ vậy vẫn hoạt động tốt dù swarm có hàng trăm node) và giúp hệ thống dễ scale hơn.

Gắn service vào overlay network

$ docker service create --name test \
--network demo-net \
--replicas 2 \
ubuntu sleep infinity
# Output minh họa
u8mzq30dva9d
overall progress: 2 out of 2 tasks
verify: Service converged

Lệnh này tạo service test, gắn vào demo-net, và tạo 2 container (replica) — vì swarm có đúng 2 node, mỗi node chạy 1 container. Khi swarm khởi động container gắn với 1 overlay network, nó tự động mở rộng network đó tới đúng node chạy container — lúc này demo-net sẽ xuất hiện luôn trên node2.

$ docker service ps test
# Output minh họa
NAME IMAGE NODE DESIRED STATE CURRENT STATE
test.1 ubuntu:latest node1 Running Running 30 seconds ago
test.2 ubuntu:latest node2 Running Running 30 seconds ago

(Output minh họa — đúng 1 task trên mỗi node, khớp với swarm 2 node + 2 replica.)

Test kết nối giữa 2 container

Kiểm tra subnet của overlay network:

$ docker network inspect demo-net
# Output minh họa (rút gọn)
[
{
"Name": "demo-net",
"Scope": "swarm",
"Driver": "overlay",
"IPAM": {
"Config": [{ "Subnet": "10.0.0.0/24" }]
}
}
]

(Output minh họa — rút gọn; field cần chú ý là Subnet bên trong IPAM.Config, chính là dải IP ảo của overlay network.)

Ví dụ subnet trả về là 10.0.0.0/24không hề trùng với bất kỳ network vật lý bên dưới nào (172.31.1.0/24 hay 192.168.1.0/24).

Lấy IP của từng container (chạy trên cả 2 node):

$ docker container ls
# Output minh họa
CONTAINER ID IMAGE COMMAND STATUS NAMES
a3f8c9d1e2b4 ubuntu:latest "sleep infinity" Up 2 minutes test.1.xk2m...

$ docker container inspect \
--format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' <container-id>
# Output minh họa
10.0.0.4

(Output minh họa — IP 10.0.0.4 là địa chỉ trên overlay network demo-net, không phải IP thật của host.)

Container trên node1 hoàn toàn ping được container trên node2 qua địa chỉ overlay (ví dụ 10.0.0.4), dù 2 host nằm trên 2 network layer 2 vật lý tách biệt:

$ docker container exec -it <container-id> bash
root@...:/# ping 10.0.0.4
64 bytes from 10.0.0.4: icmp_seq=1 ttl=64 time=1.06 ms
...

Dùng thêm traceroute/tracert sẽ chỉ thấy đúng 1 hop — bằng chứng 2 container đang giao tiếp trực tiếp qua overlay network, hoàn toàn "không biết" và không quan tâm gì tới các network vật lý bên dưới mà gói tin thực sự phải đi qua.


3. VXLAN — cơ chế đứng sau overlay network

Docker overlay networking dùng VXLAN tunnel để tạo ra network layer 2 ảo. Ở tầm nhìn cao nhất, VXLAN cho phép tạo 1 network layer 2 ảo chồng lên trên hạ tầng layer 3 sẵn có — như ví dụ trên, tạo ra network layer 2 mới 10.0.0.0/24 chồng lên trên hạ tầng IP layer 3 vốn gồm 2 network layer 2 riêng biệt (172.31.1.0/24192.168.1.0/24).

Điểm hay của VXLAN: đây là công nghệ đóng gói (encapsulation) — router và hạ tầng network hiện có chỉ nhìn thấy gói tin IP/UDP thông thường và xử lý bình thường, không cần biết gì về VXLAN.

💡 Hình dung: encapsulation giống việc bỏ 1 lá thư nội bộ (frame layer 2 gốc) vào trong 1 phong bì chuyển phát nhanh quốc tế (gói IP/UDP mới). Nhân viên bưu điện quốc tế (router/hạ tầng underlay) chỉ cần đọc đúng địa chỉ trên phong bì ngoài để chuyển đúng chặng, hoàn toàn không cần biết bên trong phong bì là thư gì — chỉ khi tới đúng nơi nhận (VTEP đích), phong bì mới bị bóc ra để lấy lại đúng lá thư gốc.

Để tạo network layer 2 ảo, 1 VXLAN tunnel được dựng xuyên qua hạ tầng IP layer 3 bên dưới (thường gọi là underlay network). Mỗi đầu tunnel được kết thúc bởi 1 VXLAN Tunnel Endpoint (VTEP) — chính VTEP đảm nhiệm việc encapsulate/de-encapsulate và mọi "phép màu" cần thiết để mọi thứ hoạt động.

Cơ chế dựng bên trong

Với ví dụ 2 host + 2 container ở trên, thực tế bên dưới diễn ra như sau:

  1. Mỗi host tạo 1 network namespace mới — giống 1 container, nhưng thay vì chạy ứng dụng, nó chạy 1 network stack cô lập riêng, tách biệt khỏi network stack của chính host.
  2. Bên trong namespace đó, 1 virtual switch (hay virtual bridge) tên Br0 được tạo ra.
  3. 1 VTEP cũng được tạo, 1 đầu cắm vào Br0, đầu còn lại cắm vào network stack của host — đầu này nhận 1 IP trên underlay network của host, và bind vào UDP socket cổng 4789.
  4. 2 VTEP trên 2 host tạo thành overlay qua 1 VXLAN tunnel.
  5. Mỗi container nhận 1 virtual Ethernet (veth) adapter riêng, cũng cắm vào Br0 cục bộ.
Bên trong 1 host: namespace → Br0 → VTEPnetwork namespace (host)Br0 (virtual switch)ContainervethVTEPUDP :4789Underlay networkcủa hostMọi container trên host này cắm vào chung Br0 — VTEP là "cổng ra" duy nhất tới host khác

4. Container giao tiếp qua VXLAN như thế nào?

Giả sử container C1 (trên node1) ping container C2 (trên node2, IP overlay 10.0.0.4):

  1. C1 tạo gói ping, gửi qua interface veth của nó, tới Br0.
  2. Br0 chưa có entry ARP tương ứng IP đích, nên flood gói tin ra mọi port. Interface VTEP (đang cắm vào Br0) biết cách forward frame này nên trả lời bằng chính MAC của nó (proxy ARP reply) — Br0 học được cách forward, cập nhật ARP table ánh xạ 10.0.0.4 → MAC của VTEP. (VTEP biết về C2 vì thông tin network của mọi container mới khởi động luôn được lan truyền tới các node khác trong swarm qua gossip protocol built-in.)
  3. Từ giờ, mọi gói gửi tới C2 được Br0 chuyển thẳng tới VTEP. VTEP encapsulate frame: thêm VXLAN header (chứa VXLAN Network ID — VNID, dùng để map frame VLAN ↔ VXLAN, đảm bảo cách ly network giữa các overlay khác nhau), rồi bọc tiếp trong 1 gói IP/UDP với địa chỉ IP của VTEP bên node2 làm đích, cổng UDP 4789.
  4. Gói tin tới node2, kernel thấy đích là UDP port 4789, biết có 1 VTEP interface đang bind ở socket đó, nên chuyển gói cho VTEP. VTEP đọc VNID, de-encapsulate, rồi chuyển vào đúng Br0 cục bộ ứng với VNID đó — cuối cùng gói tin tới được container C2.
Gói tin sau khi VTEP encapsulate: 3 lớp lồng nhauIP/UDP Header — src/dst = IP của 2 VTEP, cổng UDP 4789VXLAN Header — VNID (Network ID)Original FrameC1 MAC → C2 MAC (ping payload)Underlay router chỉ thấy đúng lớp ngoài cùng (IP/UDP) — 2 lớp trong là "hộp đen" với nó

Nhờ cơ chế encapsulate/de-encapsulate này, dữ liệu truyền được qua underlay network mà bản thân underlay network không cần biết gì về VXLAN.

Docker overlay network còn hỗ trợ routing layer 3 ngay trong cùng 1 overlay network — ví dụ tạo 1 overlay network với 2 subnet (docker network create --subnet=10.1.1.0/24 --subnet=11.1.1.0/24 -d overlay prod-net), Docker sẽ tự lo việc routing giữa 2 subnet đó (tạo ra 2 virtual switch Br0/Br1 bên trong network namespace, routing giữa chúng diễn ra mặc định).

Theo kinh nghiệm vận hành thực tế, phần lớn sự cố networking khó debug nhất trên swarm không nằm ở tầng ứng dụng mà nằm đúng ở đây — nhầm lẫn giữa IP overlay (10.0.0.x) và IP thật của host, hoặc quên rằng docker network ls trên 1 worker không thấy network nào đó chỉ vì chưa có container nào của worker đó tham gia. Nhớ đúng 1 nguyên tắc: container chỉ cần biết địa chỉ overlay của nhau — mọi việc mã hóa, đóng gói, định tuyến qua nhiều host vật lý là việc của VTEP, không phải việc của ứng dụng.

Đã đi qua kiến trúc, image, container, swarm, và networking — phần cuối cùng của series sẽ gom mọi mảnh ghép này lại dưới góc nhìn bảo mật: những cơ chế nào (namespace, cgroup, TLS trong swarm...) thực sự đứng ra bảo vệ hệ thống, và bảo vệ tới mức nào là đủ.


Danh sách lệnh chính

LệnhCông dụng
docker network create -d overlay <tên>Tạo network mới; -d chỉ định driver (thường là overlay); thêm -o encrypted để mã hóa cả data plane
docker network lsLiệt kê network — ở swarm mode, worker chỉ thấy overlay network nào nó đang có container tham gia
docker network inspect <network>Xem chi tiết: scope, driver, subnet, VXLAN network ID, trạng thái mã hóa
docker network rm <network>Xóa network

Nguồn tham khảo

Nguồn gốc: Bài viết lấy cảm hứng từ sách "Docker Deep Dive" — tác giả Nigel Poulton (bản phát hành version 4, 3/10/2017), Part 2: The Technical Stuff — chapter 10 "Docker overlay networking". Nội dung được viết lại theo cách hiểu và ví dụ riêng, không sao chép nguyên văn hay dịch sát nguyên bản. Muốn đọc bản đầy đủ hoặc ủng hộ tác giả, có thể mua sách tại leanpub.com/dockerdeepdive.

Đã verify qua research (Mode R) — vẫn đúng với Docker hiện tại, không cần đính chính: