Docker có bốn kiểu mạng bạn gặp hằng ngày: bridge (mặc định), host, none và overlay. Chúng khác nhau ở đường gói tin đi, và đường đi đó tốn tiền.
Tôi đo bằng iperf3 trong 5 giây, container nói chuyện với container:
| Mạng | Thông lượng | Độ trễ (ping, trung bình) |
|---|---|---|
host |
130,91 Gbit/s | 0,020 ms |
| bridge mặc định | 107,26 Gbit/s | 0,034 ms |
| bridge tự tạo | 101,64 Gbit/s | 0,033 ms |
overlay (hai nút) |
50,90 Gbit/s | 0,183 ms |
Một lưu ý về phạm vi trước khi đọc tiếp: máy tôi là macOS nên Docker chạy trong máy ảo Linux, và mọi phép đo ở trên là trong cùng một máy — không có card mạng vật lý nào tham gia. Những con số này đo chi phí phần mềm của từng đường đi, không phải tốc độ mạng thật của bạn.
bridge: veth, NAT, và cái giá của nó
Mặc định mỗi container được gắn một đầu của cặp veth, đầu kia cắm vào cầu docker0. Gói tin đi qua cầu, qua netfilter, có thể qua NAT.
Chi phí đo được so với host: mất khoảng 18% thông lượng và thêm 70% độ trễ. Nghe nhiều, nhưng 0,034 ms so với 0,020 ms là chênh 14 micro giây. Một truy vấn CSDL tầm thường mất 200 micro giây; một lời gọi HTTP nội bộ mất hàng mili giây. Chi phí của bridge nằm dưới ngưỡng bạn đo được trong ứng dụng thật.
Bridge mặc định và bridge tự tạo cho thông lượng như nhau (107 so với 102 Gbit/s, chênh lệch nằm trong dao động). Khác biệt thật giữa chúng không phải hiệu năng — mà là DNS và cách ly, phần sau sẽ đo.
Cách ly là thứ bridge tự tạo cho bạn
Container nằm trên netlab, thử ping từ ba nơi:
| Gọi từ | Kết quả |
|---|---|
cùng mạng netlab |
tới được |
mạng netlab2 |
không tới được |
| bridge mặc định | không tới được |
Đây là hàng rào thật, không phải quy ước. Muốn thông thì phải nối tường minh:
docker network connect netlab2 <container>
Sau lệnh đó container có hai địa chỉ IP, mỗi mạng một cái, và cả hai bên đều gọi tới được. Đó là cách dựng những kiến trúc kiểu "web nói được với cả CSDL lẫn cache, nhưng CSDL không nói được với cache".
host: nhanh nhất, và mất hết cách ly
--network host bỏ hẳn namespace mạng riêng — container dùng thẳng ngăn xếp mạng của máy chủ. Không veth, không NAT, không gì cả. Nhanh nhất trong bảng.
Đổi lại bạn mất mọi thứ:
- Không cách ly. Container thấy mọi giao diện mạng của máy chủ.
-pmất tác dụng. Không có gì để ánh xạ; ứng dụng nghe cổng nào là chiếm cổng đó trên máy chủ.- Đụng cổng. Hai container cùng nghe 8080 thì cái thứ hai chết.
- Không chạy trên Docker Desktop macOS/Windows đúng như bạn nghĩ, vì "host" ở đó là máy ảo Linux chứ không phải máy tính của bạn.
Đáng dùng khi bạn thật sự cần: thiết bị mạng, giám sát gói tin, hoặc một dịch vụ thông lượng cực cao mà 18% là số tiền lớn. Còn lại thì không.
none: có ngăn xếp mạng, nhưng đi đâu cũng không được
giao dien mang: lo tunl0@NONE gre0@NONE gretap0@NONE ...
ra Internet: HONG
loopback: OK
Chỉ có lo và mấy giao diện tunnel rỗng. Loopback vẫn chạy — nên một tiến trình vẫn tự nói chuyện với chính nó qua 127.0.0.1 được, chỉ là không ai bên ngoài vào được và nó không ra được đâu cả.
Hợp cho các job xử lý dữ liệu thuần tính toán: đọc từ volume, ghi ra volume, không cần mạng. Đây là lớp bảo vệ mạnh và rẻ — kết hợp với --read-only và --cap-drop=ALL (phần 31) thì container gần như không còn bề mặt tấn công nào.
overlay: cái giá của việc đi xuyên nhiều máy
Overlay cho container trên các máy khác nhau nói chuyện như cùng một mạng LAN. Nó bọc gói tin trong VXLAN.
Tôi dựng một swarm hai nút để đo, và so với bridge trong cùng một nút của chính swarm đó:
| Thông lượng | Độ trễ | MTU | |
|---|---|---|---|
| bridge (cùng nút) | 106,65 Gbit/s | 0,043 ms | 1500 |
| overlay (hai nút) | 50,90 Gbit/s | 0,183 ms | 1450 |
Overlay chỉ đạt 48% thông lượng và 4,3 lần độ trễ. Đây là chênh lệch duy nhất trong bài đủ lớn để bạn cảm thấy.
1450 mới là con số cần nhớ
Dòng MTU quan trọng hơn cả hai cột kia. VXLAN thêm 50 byte header, nên phần dữ liệu còn 1450.
Hậu quả rất khó chẩn đoán: ứng dụng nào tự đặt kích thước gói theo giả định MTU 1500, hoặc bật cờ "không phân mảnh", sẽ treo giữa chừng thay vì báo lỗi. Bắt tay TLS xong xuôi, request nhỏ chạy tốt, rồi một response lớn ngồi im tới khi hết giờ. Nếu bạn gặp "chỉ hỏng với payload lớn" trên overlay, hãy nghi MTU trước tiên.
Trên hạ tầng mà bản thân đường truyền đã bọc thêm một lớp nữa (VPN, một số mạng đám mây), MTU thực tế còn nhỏ hơn 1450 và bạn phải khai tay:
docker network create -d overlay --opt com.docker.network.driver.mtu=1400 ten-mang
Chọn cái nào
| Tình huống | Mạng |
|---|---|
| Gần như mọi trường hợp | bridge tự tạo |
| Job tính toán không cần mạng | none |
| Cần ngăn xếp mạng của máy chủ | host |
| Container trải trên nhiều máy | overlay |
Đừng dùng bridge mặc định cho việc thật. Nó không cho phân giải tên và không cách ly — hai thứ bạn được miễn phí khi tự tạo một mạng:
docker network create ung-dung
docker run -d --network ung-dung --name db postgres:16-alpine
docker run -d --network ung-dung --name web vi-du:1.0
Trong Compose thì bạn đã có sẵn: Compose tự tạo một mạng riêng cho mỗi project.
Thử ba mươi giây
docker network create thu
docker run -d --name may-chu --network thu alpine sleep 300
docker run --rm --network thu alpine ping -c3 may-chu # theo ten: chay
docker run --rm --network bridge alpine ping -c3 may-chu # bridge mac dinh: hong
docker run --rm --network thu alpine ip -o link show eth0 | grep -o 'mtu [0-9]*'
docker rm -f may-chu && docker network rm thu
Dòng thứ ba là bài kiểm tra nhanh nhất để biết bạn đang ở mạng nào — và phần sau sẽ đo kỹ chính cái DNS nội bộ làm cho dòng thứ hai chạy được, cùng cái bẫy cache khiến nó thỉnh thoảng trỏ sai.