9.5 外部訪問容器
容器執行在自己的隔離網路環境中 (通常是 Bridge 模式)。為了讓外部網路訪問容器內的服務,我們需要將容器的連接埠對映到宿主機的連接埠。
9.5.1 為什麼要對映連接埠
容器的網路訪問規則如下:
- 容器之間:可以透過 IP 或容器名 (自定義網路) 互通。
- 宿主機訪問容器:原生 Linux 環境下可按網路設定訪問容器 IP;Docker Desktop 上不能依賴容器 IP,應透過連接埠對映、容器名(容器間)或
host.docker.internal等機制訪問。 - 外部網路訪問容器:❌ 預設無法直接訪問。
為了讓外部 (如你的瀏覽器、其他區域網路機器) 訪問容器內的服務,我們需要將容器的連接埠 對映 到宿主機的連接埠。
連接埠對映 (8080 -> 80)"] Proxy --> Container["容器 (Class B: 80)"]
9.5.2 連接埠對映方式
Docker 提供了多種方式來指定連接埠對映。
1. 指定對映
使用 -p <宿主機連接埠>:<容器連接埠> 格式:
## 將宿主機的 8080 連接埠對映到容器的 80 連接埠
$ docker run -d -p 8080:80 nginx
此時訪問 http://localhost:8080 即可看到 Nginx 頁面。
多種格式:
| 格式 | 含義 | 範例 |
|---|---|---|
ip:hostPort:containerPort |
繫結指定 IP 的特定連接埠 | -p 127.0.0.1:8080:80 (僅 IPv4 本機訪問) |
ip::containerPort |
繫結指定 IP 的隨機連接埠 | -p 127.0.0.1::80 |
hostPort:containerPort |
繫結所有地址(通常包括 0.0.0.0 和 [::])的特定連接埠 |
-p 8080:80 (預設) |
containerPort |
繫結所有地址的隨機連接埠 | -p 80 |
2. 隨機對映
如果不關心宿主機使用哪個連接埠,可以使用隨機對映。使用 -P (大寫) 引數,Docker 會把 Dockerfile 中 EXPOSE 指令暴露的所有連接埠發布到宿主機的隨機高位連接埠。具體落在哪個連接埠,取決於宿主機當前可用的臨時連接埠範圍。
$ docker run -d -P nginx
檢視對映結果:
$ docker ps
CONTAINER ID PORTS
abc123456 0.0.0.0:49153->80/tcp
此時 Nginx 被對映到了宿主機的一個隨機高位連接埠,例如 49153。
9.5.3 檢視連接埠對映
可以使用以下指令檢視容器的連接埠對映:
docker port
執行 docker port 可以檢視到指定容器的連接埠對映情況:
$ docker port mycontainer
80/tcp -> 0.0.0.0:8080
80/tcp -> [::]:8080
docker ps
執行 docker ps 可以檢視到所有容器的連接埠對映清單:
$ docker ps
CONTAINER ID IMAGE PORTS NAMES
abc123456 nginx 0.0.0.0:8080->80/tcp web
9.5.4 最佳實踐與安全
在設定連接埠對映時,需要注意以下安全事項:
1. 限制監聽 IP
預設情況下,-p 8080:80 會監聽所有可用地址,常見輸出包括 0.0.0.0:8080 和 [::]:8080。這意味著任何人只要能連線你的宿主機 IP,就能訪問該服務。
如果不希望對外暴露 (例如資料庫服務),應繫結到回環地址。IPv4 使用 127.0.0.1,IPv6 使用 [::1]:
## 僅允許本機訪問
$ docker run -d -p 127.0.0.1:3306:3306 mysql
$ docker run -d -p '[::1]:3306:3306' mysql
2. 避免連接埠衝突
如果宿主機 8080 已經被佔用了,容器將無法啟動。
解決:
- 更換宿主機連接埠:
-p 8081:80 - 讓 Docker 自動分配:
-p 80
3. UDP 對映
預設是 TCP 協定。如果要對映 UDP 服務 (如 DNS,Syslog):
$ docker run -d -p 53:53/udp dns-server
9.5.5 實現原理
Docker 使用 docker-proxy 程序 (使用者態) 或 iptables DNAT 規則 (核心態) 來實現連接埠轉發。
當流量到達宿主機連接埠時,iptables 規則將其目標地址修改為容器 IP 並轉發:
## 簡化的 iptables 邏輯
iptables -t nat -A DOCKER -p tcp --dport 8080 -j DNAT --to-destination 172.17.0.2:80
這也是為什麼你在容器內部看到的訪問來源 IP 通常是閘道器 IP (如 172.17.0.1),而不是真實的外部 Client IP (除非使用 host 網路模式)。