SSD Nodes Learn 8GB 記憶體 — 每年 $66
指南 Matt Connor作者: Matt Connor · 已更新 2026-08-01

Docker Compose 網路設定與 DNS 連線指南

了解 Compose 預設 bridge 網路、依服務名稱解析 DNS、host 模式適用時機、跨專案共用網路,以及可能繞過 UFW 的 published port。

Compose 在應用程式啟動前建立的內容

Docker Compose 網路的運作始於一項規則:docker compose up 會為專案建立私有網路,將每個服務連接到該網路,並讓這些服務透過服務名稱互相連線。您不需要撰寫任何 networks: 行即可完成這項設定。許多 Compose 網路問題源自不了解預設網路本來就已存在。

以下是一個簡單的檔案。請將它以 compose.yaml 的檔名,儲存在名為 shop 的目錄中。

services:
  web:
    image: nginx:1.27
    ports:
      - "8080:80"
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example

啟動它,查看 Docker 建立的內容:

docker compose up -d
docker network ls

現在清單中會有一個名為 shop_default 的網路。Compose 將其命名為 <project>_default,而專案名稱預設為小寫的目錄名稱。您可以使用 docker compose -p myproject up -d,或在檔案的最上層設定 name: myproject 來覆寫此名稱。其驅動程式為 bridge,也就是主機內的虛擬交換器。每個容器都會在私有子網路上取得一個位址,對外連出的流量則會在離開時轉換為主機的位址。

docker compose down 會再次刪除該網路。這就是為什麼舊專案的過時容器可能會占用網路:Docker 會以 error while removing network: network shop_default has active endpoints 拒絕操作,解決方法是停止或移除仍連接至該網路的容器。

如果您剛開始使用 Compose,建議先閱讀 Compose 檔案配置與生命週期命令,因為以下內容都假設您能夠啟動和停止專案。

初學者容易忽略的是依服務名稱進行 DNS 解析

在任何使用者定義的網路上,Docker 都會執行內嵌 DNS 伺服器,每個容器都可在 127.0.0.11 存取該伺服器。它會將服務名稱解析為目前容器的位址。因此,web 不需任何設定,即可透過主機名稱 db,使用連接埠 5432 連線至資料庫。

docker compose exec web getent hosts db

該指令會輸出類似 172.18.0.2 db 的一行內容。如果沒有輸出,表示這兩個服務不在同一個網路上。

幾乎所有人都會犯一次的錯誤,是在應用程式設定中使用 localhost。在容器內,localhost 代表該容器本身,不是主機,也不是其他服務。Postgres 用戶端會清楚回報這項錯誤:

could not connect to server: Connection refused
	Is the server running on host "localhost" (127.0.0.1) and accepting
	TCP connections on port 5432?

連線字串應為 postgresql://postgres:example@db:5432/postgres。主機部分應使用服務名稱。

以下兩項細節可避免日後浪費時間。名稱會解析至目前正在執行的容器,因此 docker compose up -d --scale web=3 會以一個名稱對應三個位址;永遠快取 DNS 的用戶端則會固定連線至已失效的容器。此外,沒有使用 --network 的純 docker run 所使用的舊版 bridge 網路完全不提供名稱解析。因此,2016 年關於容器連結的建議,與您目前看到的行為不符。

您不需要 ports: 來連線兩項服務

ports: 會將容器連接埠發布到主機。它用於處理來自 Docker 外部的網路流量。它與服務對服務的流量無關,因為相同專案網路上的服務已可使用整個連接埠範圍彼此通訊。

因此,許多人加到資料庫服務中的 ports: - "5432:5432" 不僅沒有作用,還會造成實際危害:它會在伺服器的公開介面上暴露 Postgres。請刪除它。如果您要讓筆記型電腦在移轉期間存取該服務,請使用 "127.0.0.1:5432:5432" 將其繫結至 loopback,並透過 SSH tunnel 存取。Linux 上的連接埠與監聽服務運作方式說明了 listening socket、published port 與 firewall rule 之間的差異。

在 Compose 中,expose: 僅供文件說明使用。它不會開放任何項目,因為相同網路上的容器之間原本就沒有任何項目被關閉。

network_mode host 適用時機與代價

Host mode 會移除容器自身的 network namespace,讓程序直接使用主機的網路介面。

services:
  probe:
    image: alpine:3.20
    network_mode: host
    command: sleep infinity

使用 host mode 有實際需求。需要查看區域網路中 broadcast 或 multicast 流量的程序,例如媒體伺服器或家庭自動化 hub 的裝置探索,無法在 bridge 後方查看這些流量,因為 bridge 不會將其轉送至容器。需要讀取主機網路介面計數器的監控代理程式,也必須使用主機的網路介面。此外,host mode 可略過 address translation hop,這在封包速率很高時相當重要。

但它也有明確的代價。

ports: 會失效。Docker 警告,使用 host network mode 時會捨棄 published ports,容器會繫結其程序實際繫結的連接埠。兩個使用 host mode 的容器若都要使用連接埠 8080,便會發生衝突,第二個容器會因 bind: address already in use 而終止。

雙向的 service name 名稱解析都會失效。容器不在 project network 上,因此無法解析 db,其他服務也無法解析該容器。容器只能透過主機上 published 的連接埠連線至這些服務,通常位於 127.0.0.1

隔離也會消失。在 host mode 容器內繫結 0.0.0.0 的程序,會在伺服器的所有網路介面上監聽,包括 public interface,行為與使用 apt 安裝的套件完全相同。這也有一項優點:此流量會遵循一般的 input path,因此 UFW 規則會套用至此流量;published ports 則不會。

Host mode 是 Linux Docker Engine 的功能。Docker Desktop 僅從版本 4.34 起支援此功能,而且必須先啟用。除此之外,容器無法繫結主機 IP 位址,且只處理 TCP 和 UDP。如果團隊一半使用 Linux 伺服器,另一半使用 Docker Desktop,請預期同一份檔案會有不同的行為。

只有在需要主機網路介面時才使用 host mode。不要用它來修正連線問題,因為它通常只會將一個問題換成更難處理的問題。

透過外部網路連接兩個 Compose 專案

一個專案建立的網路,其他專案看不到。因此,proxy/compose.yaml 中的反向代理即使與 app/compose.yaml 位於同一部伺服器,也無法連線至其中的應用程式。解決方式是建立一個不由任何專案擁有的網路。

手動建立一次:

docker network create edge

接著在每個專案中將它宣告為外部網路。代理端:

services:
  proxy:
    image: traefik:v3.1
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - edge

networks:
  edge:
    name: edge
    external: true

應用程式端:

services:
  app:
    image: nginx:1.27
    networks:
      - edge
      - internal
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example
    networks:
      - internal

networks:
  edge:
    name: edge
    external: true
  internal:

external: true 會告訴 Compose 連接至現有網路,而不是建立新網路,並在 docker compose down 時保留該網路。獨立的 name: 設定比表面上更重要:沒有它時,Compose 會尋找名稱完全為 edge 的網路;有了它之後,您可以在檔案中使用一個名稱,在主機上使用另一個名稱。

如果網路不存在,Compose 會拒絕啟動,並回報該網路已宣告為外部網路,但找不到該網路。請先建立網路。

請注意應用程式檔案如何處理 internal。資料庫只位於該專案的本機網路上,因此代理無法連線至資料庫,只有 app 可以連線。將 internal: true 加入網路設定後,還會完全移除資料庫通往外部世界的路由。這是資料庫的良好預設設定,但在套用前必須了解一項代價:位於內部網路上的容器無法下載任何內容,因此啟動時執行 apt-get updatepip install 的 entrypoint 會卡住,最後因逾時而失敗。

如需包含路由規則與憑證的完整實作設定,請參閱 在單一 Traefik 執行個體後方執行多個應用程式

Published ports 會繞過 UFW

這是 Compose 網路設定演變成安全事件的部分。您公開一個連接埠,確認 UFW 已啟用且除了 SSH 外全部拒絕,但該服務仍可從網際網路存取。

sudo ufw status
curl http://203.0.113.10:8080

UFW 顯示該連接埠已封鎖。curl 仍會傳回頁面。一切運作正常。Docker 會直接將自己的位址轉譯與轉送規則寫入 iptables。傳送至已公開的容器連接埠之流量會被轉送至容器,而不是傳送至主機,因此不會經過 UFW 管理的本機目的流量鏈。Docker 的規則也會在 UFW 的規則之前比對。

簡易的修正方式是只在需要的位置公開連接埠:

    ports:
      - "127.0.0.1:8080:80"

這會將主機端繫結至 loopback,因此該連接埠可從伺服器本身及 SSH tunnel 存取,其他位置則無法存取。請將公開入口置於 reverse proxy 後方,並刻意公開 80 和 443。完整說明(包括必須篩選已公開連接埠時使用的 DOCKER-USER chain)請參閱為何 Docker 會直接繞過 UFW 公開連接埠,以及如何修正

如何使用 4 個命令進行偵錯

先確認每個容器實際連接的網路:

docker network inspect shop_default

Containers 區塊會列出所有已連接的容器及其位址。未出現在清單中的服務,可能位於其他網路、使用 host 模式,或尚未執行。

從連接至相同網路的一次性容器測試名稱解析,因此不需要在自己的映像中安裝任何工具:

docker run --rm --network shop_default busybox nslookup db
docker run --rm --network shop_default busybox nc -zv db 5432

nslookup 失敗表示名稱解析或網路成員資格有問題。nslookup 成功但 nc 失敗,表示服務正在執行,但未在該連接埠上接聽;或是在自己的容器中接聽 127.0.0.1,而不是 0.0.0.0。這在開發伺服器中很常見。修正方式是在應用程式的繫結位址中處理,而不是修改 Docker。

還有一種看似 Docker 錯誤的故障。如果容器彼此可以通訊,卻無法連線至辦公室或 VPN 網路中的機器,Docker 子網路可能與該網路重疊。Docker 預設會從 172.17.0.0/16 開始配置。請在 /etc/docker/daemon.json 中移動這個位址池:

{
  "default-address-pools": [
    { "base": "10.200.0.0/16", "size": 24 }
  ]
}

接著執行 sudo systemctl restart docker,並重新建立受影響的網路,因為現有網路會保留建立時所使用的子網路。

FAQ

為什麼我的容器無法透過服務名稱互相連線?

因為它們不在同一個網路上。Compose 會自動將每個服務放在 <project>_default 上,但只要您為服務加入 networks: 清單,該清單就會成為此服務的完整網路集合,不再自動套用預設網路。執行 docker network inspect <network>,確認兩個容器都出現在 Containers 區塊中。此外,也請確認兩個服務都未使用 network_mode: host,因為使用主機模式的容器不在任何 Docker 網路上,無法解析服務名稱。

一個服務要連線至另一個服務時,是否需要發布連接埠?

不需要。在 Compose 網路上,每個容器的所有連接埠都可供同一網路上的其他容器存取。ports: 僅用於讓 Docker 外部的流量存取容器,而 expose: 僅供文件說明使用。發布資料庫連接埠是常見且代價高昂的做法,因為這會讓資料庫暴露在伺服器的公開介面上。

bridge 與 host 網路有何差異?

bridge 會讓容器在虛擬交換器上使用自己的網路命名空間和位址,並提供容器間的自動名稱解析及經轉譯的對外流量。host 則會直接讓容器使用主機的網路堆疊:沒有獨立位址、無法透過服務名稱解析、不需要發布連接埠,也不會與主機上的其他監聽連接埠隔離。bridge 是預設選項;除非處理程序需要使用主機的網路介面,否則應選用 bridge。

如何連線來自 2 個不同 Compose 檔案的容器?

使用 docker network create edge 建立共用網路,然後在兩個檔案中使用 external: true 宣告該網路,並將需要互相通訊的服務連接至該網路。Compose 不會建立或刪除這個網路。如果略過建立步驟,Compose 會拒絕啟動,並回報該網路已宣告為外部網路但找不到。

為什麼 UFW 阻擋連接埠時,我的容器仍可從網際網路存取?

因為發布的連接埠是由 Docker 加入 iptables 的轉送規則處理。這些規則會在 UFW 的規則之前比對,而且轉送流量本來就不會經過 UFW 用來篩選流量的鏈結。使用 "127.0.0.1:8080:80" 將主機端繫結至迴路介面,並將所有公開服務放在連接埠 80 和 443 上的反向 Proxy 後方。