Sau bài này bạn làm được: giải thích luồng traffic từ container ra internet và từ bên ngoài vào container; debug xung đột cổng khi chạy nhiều compose project; biết khi nào MTU là nguyên nhân gây kết nối kỳ lạ.


Docker thêm lớp network abstraction

Mỗi container mặc định có network namespace riêng — một không gian mạng cô lập với host và với container khác. Kernel tạo “interface ảo” nối container vào bridge.

[Host]
  ├── eth0: 10.0.1.50/24 → ra ngoài internet
  └── docker0 (bridge): 172.17.0.1/16
         ├── veth123 ←→ [Container A: eth0: 172.17.0.2]
         └── veth456 ←→ [Container B: eth0: 172.17.0.3]

docker0 là L2 bridge — các container cùng bridge thấy nhau như cùng LAN.


Bridge network — chế độ mặc định

Khi chạy docker run không chỉ định network, container gắn vào default bridge (docker0):

docker run -d nginx
docker inspect <id> | grep IPAddress
# "IPAddress": "172.17.0.2"

Container có thể nói chuyện với nhau qua IP nội bộ, nhưng không resolve được tên container — đây là hạn chế của default bridge.


Publish port: -p host_port:container_port

docker run -d -p 8080:80 nginx

Docker thêm rule iptables DNAT:

[Internet] → host:8080
    ↓  iptables DNAT
[Container: 172.17.0.2:80]

[Container ra ngoài]
    ↓  iptables MASQUERADE (SNAT)
[Host IP: 10.0.1.50] → Internet

Xung đột cổng:

docker run -p 8080:80 app1   # OK
docker run -p 8080:80 app2   # Error: address already in use

Mỗi (host_ip, host_port, protocol) chỉ có một process giữ. Dùng ss -lntp | grep 8080 để tìm ai đang giữ.


User-defined bridge — tốt hơn cho compose

Docker Compose tự tạo user-defined bridge riêng cho mỗi project:

# docker-compose.yml
services:
  api:
    image: my-api
  db:
    image: postgres
[my-project_default bridge]
  ├── api → có thể resolve "db" bằng DNS
  └── db  → có thể resolve "api" bằng DNS

Container trong cùng user-defined bridge resolve được tên service qua DNS nội bộ Docker — khác với default bridge.

Cô lập giữa compose project:

[project-a_default: 172.20.0.0/16]  ── cô lập ──  [project-b_default: 172.21.0.0/16]

Mặc định, container project A không reach được container project B trừ khi thêm shared network.


Network modes đặc biệt

# Host network: container dùng chung stack mạng với host
docker run --network host nginx
# nginx sẽ listen trực tiếp trên host:80, không qua NAT
# Lợi: không overhead NAT | Rủi ro: mất cô lập cổng

# None: container không có network interface (trừ loopback)
docker run --network none my-job
# Dùng cho build job cần hoàn toàn offline

MTU — điểm hay bị bỏ qua

Khi chạy Docker bên trong VPC hoặc qua VPN:

VPN tunnel MTU:     1400 byte
Docker default MTU: 1500 byte (kế thừa từ host Ethernet)

Kết quả: gói TCP lớn hơn 1400 byte bị drop hoặc fragment
Triệu chứng: ping nhỏ OK, curl download lớn treo / kết nối ngắt giữa chừng

Xem MTU hiện tại:

ip link show docker0
# 3: docker0: mtu 1500

Cấu hình MTU cho Docker daemon:

// /etc/docker/daemon.json
{
  "mtu": 1450
}

Inspect và debug network

# Liệt kê network
docker network ls

# Xem chi tiết: subnet, gateway, container kết nối
docker network inspect bridge

# Xem interface trong container
docker exec <container> ip addr
docker exec <container> ip route

# Test kết nối từ trong container
docker exec <container> nc -vz db 5432
docker exec <container> curl -v http://api:8080/health

Tóm tắt

  • Bridge + NAT là mô hình mặc định: container có IP riêng, ra internet qua SNAT.
  • Publish port = DNAT rule trên host; xung đột khi hai container cùng port.
  • User-defined bridge (Compose) cho DNS tên service — nên dùng thay default bridge.
  • MTU là nguyên nhân hay bị bỏ qua khi kết nối kỳ lạ trong VPN/VPC.

Câu hỏi hay gặp

Ping được IP nhưng curl http://tên-service không resolve trên default bridge — vì sao?

Trả lời: Default bridge không có DNS tên container như user-defined network. Dùng docker network create + attach container, hoặc link (lỗi thời), hoặc gọi bằng container name trên user-defined bridge / Compose.

-p 0.0.0.0:5432:5432 vs -p 127.0.0.1:5432:5432 khác nhau thế nào về bảo mật?

Trả lời: 0.0.0.0 publish ra mọi interface (mạng LAN/VPC có thể vào được). 127.0.0.1 chỉ localhost host — an toàn hơn nếu chỉ cần tunnel/port-forward cục bộ.

SSH vào container OK nhưng upload lớn treo (VPN MTU 1400) — kiểm tra gì đầu tiên?

Trả lời: MTU đường đi (host, bridge, VPN): thử giảm MTU Docker/daemon.json, hoặc kiểm tra PMTUD/black hole — triệu chứng điển hình của gói lớn bị drop.


Bài tiếp theo (Giai đoạn III): Mạng trong Kubernetes — container ở quy mô cluster với Service, Ingress và DNS riêng.