目錄
Docker 網路教學:bridge、自訂網路與容器互連
當應用程式需要連到資料庫、快取等其他容器時,就要了解 Docker 的網路。最重要的一句話:把要互相溝通的容器放進同一個自訂網路,就能用容器名稱互相連線。
網路驅動程式
剛安裝好的 Docker 有三個內建網路:
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
a74caef4acb9 bridge bridge local
b9a49b39804b host host local
87a8a7fd2cd9 none null local
| 驅動程式 | 說明 |
|---|---|
bridge | 預設值。容器有自己的 IP,位於一個虛擬網段中 |
host | 容器直接使用主機的網路,不做隔離(Linux 上效果最完整) |
none | 沒有網路 |
overlay | 跨多台主機(Docker Swarm) |
macvlan | 讓容器像實體機器一樣出現在區域網路上 |
預設 bridge 的限制
沒有指定網路時,容器會連到名為 bridge 的預設網路。在這個網路裡,容器之間不能用名稱連線:
$ docker run -d --name web1 nginx:1.30
b1d3fb9024c112c5990d9ac85012cd3f5e38c114ab4409a06f960c6d8cdb496e
$ docker run --rm alpine:3.23 ping -c 1 -W 2 web1
ping: bad address 'web1'
$ docker inspect --format '{{.NetworkSettings.IPAddress}}' web1
template parsing error: template: :1:18: executing "" at <.NetworkSettings.IPAddress>: map has no entry for key "IPAddress"
$ docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' web1
172.18.0.2
$ docker run --rm alpine:3.23 ping -c 1 172.18.0.2
PING 172.18.0.2 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.058 ms
--- 172.18.0.2 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.058/0.058/0.058 ms
$ docker rm -f web1
web1
- 用名稱
web1找不到(bad address),用 IP172.18.0.2可以連到。 - 但 IP 是 Docker 自動分配的,容器重新建立後可能改變,不能寫死在設定裡。
Docker 29 的變化:舊版可以用
{{.NetworkSettings.IPAddress}}取得容器 IP,Docker 29 已經移除這個欄位(上面的template parsing error)。請改用{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}。
所以實務上幾乎都會建立自訂網路。
自訂網路:用名稱互連
docker network create appnet
docker run -d --name db --network appnet -e POSTGRES_PASSWORD=secret postgres:18
$ docker network create appnet
fadfeb6028cb78335fa94cfd7536265256a914cc461caae1026b6ebb21e9d7a0
$ docker run -d --name db --network appnet -e POSTGRES_PASSWORD=secret postgres:18
227033e27591e6a1c195a6d63a141aaf4486c6285477792fe3080e1d4deeb2c0
$ docker run --rm --network appnet alpine:3.23 ping -c 2 db
PING db (172.19.0.2): 56 data bytes
64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.069 ms
64 bytes from 172.19.0.2: seq=1 ttl=64 time=0.092 ms
--- db ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.069/0.080/0.092 ms
$ docker run --rm --network appnet -e PGPASSWORD=secret postgres:18 psql -h db -U postgres -c "SELECT version();"
version
--------------------------------------------------------------------------------------------------------------------
PostgreSQL 18.6 (Debian 18.6-1.pgdg13+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit
(1 row)
$ docker network inspect appnet --format "{{range .Containers}}{{.Name}} {{.IPv4Address}}{{println}}{{end}}"
db 172.19.0.2/16
- 在
appnet裡,db這個名稱被 Docker 內建的 DNS 解析成資料庫容器的 IP。 - 用 PostgreSQL 的用戶端以
-h db連線成功,查到了資料庫版本(PostgreSQL 18.6)。 - 資料庫容器沒有用
-p,主機和外部都連不到它,只有同一網路裡的容器可以,比較安全。
應用程式的連線設定也一樣寫容器名稱,例如:
docker run -d --name app --network appnet -p 3000:3000 \
-e DATABASE_URL=postgres://postgres:secret@db:5432/postgres \
myapp
注意:
- 容器之間連線用的是容器內的連接埠(5432),和
-p無關。 - 只有需要讓主機或外部存取的服務(例如網站)才需要
-p。 - 容器名稱避免使用
web、app這類剛好也是頂級網域的單字當作連線目標;在同一網路內沒問題,但在網路外查詢時會解析到奇怪的位址,除錯時容易誤判。
一個容器加入多個網路、網路別名
$ docker network create appnet
77fadbc5633854c9e75fd9aa65325d0155ad33f5de453abac7f15f6766402bee
$ docker run -d --name app --network appnet nginx:1.30
25b4c1d92caeef5ca8765daf2f3ad6247982cacfad4914000ded8a0a455df851
$ docker network create frontend
45a9ab78fddb9f1b47a22b9ce156144286888c42eadc2b4f589986fe1f98cee0
$ docker network connect frontend app
$ docker inspect --format '{{range $k, $v := .NetworkSettings.Networks}}{{$k}} {{$v.IPAddress}}{{println}}{{end}}' app
appnet 172.19.0.2
frontend 172.20.0.2
$ docker run -d --name search --network appnet --network-alias es nginx:1.30
43b07713e7ec032d21e37fdf690064b389f798ed26b2715e7ea18c15840647af
$ docker run --rm --network appnet alpine:3.23 getent hosts es
172.19.0.3 es es
$ docker rm -f app search
app
search
$ docker network rm appnet frontend
appnet
frontend
docker network connect把執行中的容器加入另一個網路,app現在同時有兩個 IP。常用來做隔離:前端網路放 Nginx 與應用程式,後端網路放應用程式與資料庫,Nginx 就連不到資料庫。--network-alias es讓同一網路的其他容器也可以用es這個名稱連到search。多個容器使用相同別名時,DNS 會回傳多個 IP。
管理網路
docker network create appnet
docker network ls
docker network inspect appnet # 看有哪些容器、IP
docker network connect appnet 容器名稱 # 把執行中的容器加入網路
docker network disconnect appnet 容器名稱
docker network rm appnet
docker network prune # 刪除未使用的網路
連接埠對應:程式要監聽 0.0.0.0
-p 8000:8000 會讓 Docker 把送到主機 8000 的流量,轉給容器網路介面上的 8000。所以容器裡的程式必須監聽 0.0.0.0(所有介面),不能只監聽 127.0.0.1(容器自己的 localhost)。用 Python 內建的網頁伺服器實際比較:
$ docker run -d --name localonly -p 8000:8000 python:3.14-slim python -m http.server 8000 --bind 127.0.0.1
246edb96a9affefaab188ac7d5ceec2fb103b511bdac352cca6ed3a80ae6764a
$ curl -sS http://localhost:8000
curl: (52) Empty reply from server
$ docker rm -f localonly
localonly
$ docker run -d --name allif -p 8000:8000 python:3.14-slim python -m http.server 8000 --bind 0.0.0.0
438fa44edf6aec790a7e6b2bfcdb7af33c16a14c661c50b8d9564ff52844e5e6
$ curl -s http://localhost:8000 | head -n 3
<!DOCTYPE HTML>
<html lang="en">
<head>
$ docker rm -f allif
allif
- 只監聽
127.0.0.1時,連接埠明明有對應,curl卻得到Empty reply from server:流量進到了容器,但程式只在容器自己的 localhost 上等待,收不到。 - 改成監聽
0.0.0.0就正常了。
很多開發伺服器預設只監聽 localhost,在容器裡執行時要特別指定:
flask run --host 0.0.0.0
uvicorn main:app --host 0.0.0.0
vite --host 0.0.0.0
從容器連到主機
想讓容器存取主機上的服務(例如主機上跑的資料庫),使用特殊名稱 host.docker.internal:
$ docker run --rm alpine:3.23 getent hosts host.docker.internal
$ docker run --rm --add-host=host.docker.internal:host-gateway alpine:3.23 getent hosts host.docker.internal
172.18.0.1 host.docker.internal host.docker.internal
- Docker Desktop(Windows、macOS):內建支援
host.docker.internal。 - Linux:預設沒有這個名稱(第一個指令沒有任何輸出),需要加上
--add-host=host.docker.internal:host-gateway,它會被解析成主機在 Docker 網路上的位址。
主機上的服務也必須監聽容器能連到的介面,而不只是 127.0.0.1。
host 網路與 none 網路
docker run -d --network host nginx:1.30
容器直接使用主機的網路介面,nginx 監聽 80 就是主機的 80,不需要 -p(寫了也會被忽略)。效能最好,但失去隔離,也可能和主機上的程式搶連接埠。在 Docker Desktop 上的行為與 Linux 不完全相同。
--network none 則完全沒有網路,只有本機回環介面:
$ docker run --rm --network none alpine:3.23 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
適合處理不需要網路、或不應該連網的工作(例如處理不受信任的檔案)。
疑難排解
容器之間用 localhost 連不到
每個容器都有自己的 localhost。在 app 容器裡連 localhost:5432,連到的是 app 自己,而不是資料庫容器。請改用容器名稱,例如 db:5432。
主機連不進容器
依序檢查:
docker ps看PORTS欄位是否有0.0.0.0:8080->80/tcp。- 容器內的程式是否監聽
0.0.0.0(上面的Empty reply from server就是典型症狀)。 - 主機防火牆或雲端安全群組是否允許該連接埠。
容器裡無法連上網際網路
通常是 DNS 問題。可以在 /etc/docker/daemon.json(Docker Desktop 在 Settings → Docker Engine)指定 DNS,修改後重新啟動 Docker:
{ "dns": ["1.1.1.1", "8.8.8.8"] }
使用 Docker Compose 時,Compose 會自動為專案建立一個網路,服務之間直接用服務名稱就能連線。
重點回顧
- 預設 bridge 網路不提供名稱解析,容器之間要用自訂網路。
- 自訂網路中用容器名稱或
--network-alias互連。 - 程式要監聽
0.0.0.0,連接埠對應才有效。 - 容器連主機用
host.docker.internal。
動手練習
練習 1:建立網路 lab,啟動一個名為 cache 的 redis:8-alpine 容器加入它,再用另一個容器 redis-cli -h cache ping 測試。
答案
docker network create lab
docker run -d --name cache --network lab redis:8-alpine
docker run --rm --network lab redis:8-alpine redis-cli -h cache ping
輸出 PONG。網路指令見 docker network。
常見問題
容器裡用 localhost 為什麼連不到另一個容器?
每個容器有自己的網路,localhost 指的是容器自己。要連另一個容器,請把兩者放在同一個自訂網路中,用容器名稱連線。
容器怎麼連到主機上執行的服務?
Docker Desktop 用 host.docker.internal;Linux 上加 --add-host host.docker.internal:host-gateway,見本章「從容器連到主機」。
Docker 29 怎麼取得容器的 IP?
{{.NetworkSettings.IPAddress}} 已移除,改用 docker inspect -f "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" 容器。
更新於 2026年10月14日