跳至主要內容

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:5432
  • api 可以連線 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
# 正常運作

從外部存取容器

使用端口映射(-pports:):

# 格式:主機端口:容器端口
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 中,服務之間用服務名稱互連,端口映射只用於需要對外暴露的服務。善用多網路隔離,可以大幅提升容器化應用的安全性。

分享這篇文章