用 network_mode 讓容器共用另一個容器的網路。被依附的那個一重建,其他容器就綁在一個已經不存在的命名空間上,docker ps 完全看不出來。
發布於 2026-09-06・約 4 分鐘
我家裡那台機器上跑七八個服務,全部共用同一個網路入口容器。設定長這樣:
services: tailscale: image: tailscale/tailscale:latest container_name: homelab-tailscale stock: network_mode: service:tailscale threads: network_mode: service:tailscale gather: network_mode: service:tailscale
好處很實際:這些服務彼此之間直接用 127.0.0.1:8001 互連,對外只曝露一個節點位址,認證統一在最外層做一次就好,每個服務不必各自設一套。
127.0.0.1:8001
然後有一天我只重建了其中一個服務。
docker compose up -d gather
指令回 done。docker ps 七個容器全部 Up。而網站打不開,內網位址也連不上。容器日誌乾乾淨淨,沒有任何一行錯誤。
docker ps
Up
network_mode: service:X 的意思是「不要給我自己的網路,我要加進 X 那個容器的 network namespace」。namespace 屬於容器實體,不是屬於服務名稱。
network_mode: service:X
問題出在 Compose 的依賴推導。我下 up -d gather 時,它判斷 gather 依附 tailscale,於是把 tailscale 也一起重建了。重建 = 砍掉舊容器、建一個新的,新容器有一個全新的 network namespace。
up -d gather
剩下那些沒被點名的容器(stock、threads、writer…)沒有被重建,它們還活著,還綁在舊的那個已經消失的 namespace 上。
Linux 這時的行為不是報錯,是安靜地什麼都收不到。容器主行程還在跑,健康檢查如果只是檢查行程活著就會通過,docker ps 也照樣印 Up。從外面看一切正常,只有實際連線的人知道是死的。
這個坑的惡劣之處在於「重建了一個,壞掉的是另外五個」。你會盯著剛動過的那個服務找原因。
比對每個容器的 NetworkMode 跟入口容器的 ID:
NetworkMode
# 入口容器現在的 ID docker inspect homelab-tailscale --format '{{.Id}}' # 每個依附者實際綁在哪個容器上 docker inspect homelab-stock --format '{{.HostConfig.NetworkMode}}' docker inspect homelab-threads --format '{{.HostConfig.NetworkMode}}'
輸出會是 container:<64 位 ID>。那串 ID 跟第一條指令印出來的不一樣,就是壞的。
container:<64 位 ID>
一次全部掃過:
TS=$(docker inspect homelab-tailscale --format '{{.Id}}') for c in $(docker ps --format '{{.Names}}'); do m=$(docker inspect "$c" --format '{{.HostConfig.NetworkMode}}') case "$m" in container:*) [ "$m" = "container:$TS" ] && echo "OK $c" || echo "STALE $c → $m" ;; esac done
我把這段存成腳本,每次動過網路就跑一次。用眼睛比對兩串 64 個十六進位字元不是人幹的事,比對到第三個就會開始亂看。
重建的容器不要點名:
docker compose up -d --no-build
不指定服務名,Compose 會自己把整組依賴算完,需要重建的一起重建。改法簡單得有點可惜,但這正是問題所在——指定服務名是個看起來更謹慎、實際上更危險的動作。「我只想動這一個」的直覺,在共用 namespace 的架構下剛好是錯的。
已經壞掉的話沒有比較溫和的修法,就是把依附者全部重建一次,讓它們重新綁到現在這個 namespace。
Port 是共用的。 所有服務在同一個 port 空間裡,不能撞號。我的分配是 80 給反向代理、3000 給開發環境、8001 給股票、8002 給 Threads、8004 給抓取、8005 給編輯後台。加新服務時要先確認號碼沒被佔走,這件事沒有任何機制會幫你擋,撞到的症狀是後起的那個容器綁不上 port 然後退出。
用了 network_mode 就不能再宣告 ports 或 networks。 這兩個欄位跟它互斥,寫了 Compose 會直接拒絕啟動。要對外開 port 只能寫在入口容器上。這條至少會報錯,是這篇裡唯一一個不沉默的坑。
network_mode
ports
networks
會踩到這個,是因為選了共用 namespace 這條路。它換來的是「整組服務只有一個對外身分」,搬機器的時候 compose 檔直接跟著走、網路那段完全不用改。
代價就是這個:依賴關係從 Compose 檔看不出來,只存在於執行中的容器 ID 裡。 接受這個代價的前提是你有那條檢查指令。沒有的話,遲早會有一次「明明什麼都沒動它就壞了」,而且查不出來。
標籤:Docker、Compose、網路、自架服務、Tailscale