Docker Network 容器間通訊完全指南
Docker Network 容器間通訊完全指南
很多人學 Docker 的時候,對於網路這塊總是一知半解:容器之間怎麼連線?為什麼有時候 localhost 可以用有時候不行?-p 和容器網路的關係是什麼?這篇文章要把這些問題一次說清楚。
Docker 網路的基本概念
每個 Docker 容器都有自己的網路命名空間,就像一台獨立的虛擬機器。預設情況下,容器有自己的網路界面(通常是 eth0)和 IP 位址。
Docker 提供幾種網路驅動:
- bridge:預設模式,容器連接到一個虛擬交換機
- host:容器直接使用主機的網路
- none:容器沒有網路
- overlay:跨主機的容器網路(用於 Swarm)
- macvlan:讓容器有自己的 MAC 位址
Bridge 網路
預設 Bridge 網路
# 查看所有網路
docker network ls
# 查看 bridge 網路詳情
docker network inspect bridge預設的 bridge 網路有一個重要的限制:容器之間不能用名稱互相解析,只能用 IP,而 IP 是動態分配的,每次重啟可能改變。
# 啟動兩個容器
docker run -d --name container1 nginx
docker run -d --name container2 alpine sleep 3600
# container2 無法透過名稱連線 container1
docker exec container2 ping container1
# 失敗!預設 bridge 不支援 DNS
# 必須用 IP
docker inspect container1 | grep IPAddress
docker exec container2 ping 172.17.0.2 # 用 IP 才行自訂 Bridge 網路(推薦)
自訂 Bridge 網路解決了 DNS 解析問題:
# 建立自訂網路
docker network create my-network
# 啟動容器並加入網路
docker run -d --name web --network my-network nginx
docker run -d --name api --network my-network node:20-alpine node server.js
docker run -d --name db --network my-network postgres:16
# 現在可以用名稱互相連線
docker exec web ping api # 成功!
docker exec api ping db # 成功!自訂網路提供自動 DNS 解析,讓容器可以透過名稱找到彼此。
Docker Compose 的網路
Docker Compose 預設會為每個 project 建立一個自訂 bridge 網路,同一 compose 文件中的服務可以直接用服務名稱互相連線:
# docker-compose.yml
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "80:80" # 主機:容器
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- api
api:
build: ./api
environment:
DATABASE_URL: postgresql://postgres:secret@db:5432/myapp
REDIS_URL: redis://cache:6379
# 不需要 ports,因為只有 nginx 需要從外部存取
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: secret
POSTGRES_DB: myapp
volumes:
- postgres_data:/var/lib/postgresql/data
cache:
image: redis:7-alpine
command: redis-server --appendonly yes
volumes:
postgres_data:在這個設定中:
nginx可以連線http://api:3000(容器 port,不是主機 port)api可以連線postgresql://db:5432api可以連線redis://cache:6379- 只有
nginx的 80 port 暴露給外部
多網路隔離
你可以讓不同容器加入不同的網路,實現安全隔離:
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
networks:
- frontend # nginx 只在 frontend 網路
api:
build: ./api
networks:
- frontend # 連接兩個網路
- backend
db:
image: postgres:16
networks:
- backend # db 只在 backend 網路,nginx 無法直接存取
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # 無法連接外部網路(安全性更高)這樣 nginx 就無法直接存取 db,必須透過 api,提升了安全性。
Host 網路模式
在某些情況下,容器需要直接使用主機的網路:
docker run --network host nginx使用 host 網路的容器直接監聽主機的端口,不需要端口映射。注意:host 網路模式在 macOS 和 Windows 上行為與 Linux 不同,因為 Docker 在這些平台上本身就跑在 VM 裡。
連線外部網路
容器存取外部服務
預設情況下,容器可以存取外部網路(通過 NAT):
docker exec my-container curl https://api.github.com
# 正常運作從外部存取容器
使用端口映射(-p 或 ports:):
# 格式:主機端口:容器端口
docker run -p 8080:80 nginx # 主機 8080 → 容器 80
docker run -p 127.0.0.1:8080:80 nginx # 只綁定 localhost
docker run -p 80 nginx # 隨機主機端口除錯技巧
# 查看容器的網路設定
docker inspect --format='{{json .NetworkSettings.Networks}}' my-container | jq
# 進入容器測試網路
docker exec -it my-container sh
# 在容器內
ping other-container
curl http://api:3000/health
wget -qO- http://db:5432 # 測試 TCP 連通性
# 在主機查看容器的網路命名空間
docker inspect my-container | grep -i pid
nsenter -t <PID> -n ip addr
# 使用 nicolaka/netshoot 除錯工具容器
docker run --rm -it --network my-network nicolaka/netshoot
# 包含 tcpdump, curl, nslookup, traceroute 等工具實際案例:微服務架構
version: '3.8'
services:
gateway:
image: nginx:alpine
ports:
- "443:443"
networks:
- public
user-service:
build: ./user-service
networks:
- public
- services
order-service:
build: ./order-service
networks:
- public
- services
user-db:
image: postgres:16
environment:
POSTGRES_DB: users
networks:
- services
order-db:
image: postgres:16
environment:
POSTGRES_DB: orders
networks:
- services
message-queue:
image: rabbitmq:3-management
networks:
- services
networks:
public:
driver: bridge
services:
driver: bridge
internal: true總結
Docker 網路看似複雜,但核心概念很清晰:預設 bridge 網路不支援 DNS,自訂 bridge 網路支援。在 Docker Compose 中,服務之間用服務名稱互連,端口映射只用於需要對外暴露的服務。善用多網路隔離,可以大幅提升容器化應用的安全性。
分享這篇文章