在docker-compose.yml中通过ports字段直接声明host_port:container_port映射即可,支持单端口(如"8080:80")、端口范围(如"9000-9005:9000-9005")及动态分配(如":80"),各服务端口独立暴露、互不干扰,前提是容器内程序监听0.0.0.0而非127.0.0.1。
直接在 docker-compose.yml 的每个服务下用 ports 字段分别声明映射即可,不需要额外配置或共享网络——每个服务的端口是独立暴露、互不干扰的。
单个服务暴露一个或多个端口
这是最常见场景。语法为 HOST_PORT:CONTAINER_PORT,支持单端口和端口范围:
-
- "8080:80":宿主机 8080 → 容器 80 -
- "3306:3306":宿主机 3306 → 容器 3306(MySQL) -
- "9000-9005:9000-9005":宿主机 9000–9005 全部映射到容器对应端口(要求长度一致)
多个服务各自暴露不同端口
每个服务定义自己的 ports,Docker Compose 会自动为它们建立独立的 iptables DNAT 规则,互不影响:
- Web 服务用
- "8000:8000" - API 服务用
- "8001:8001" - Redis 服务用
- "6379:6379" - 前端调试端口可设为
- "3000:3000"
只要宿主机上这些端口未被占用,就能同时访问:curl http://localhost:8000、curl http://localhost:8001 等。
避免端口冲突的关键细节
暴露多服务时,真正容易出问题的是人为重复或绑定错误,不是 Compose 本身限制:
- 不要让两个服务都映射到宿主机同一个端口(如都写
"8080:80"),启动会失败 - 若想动态分配主机端口(比如测试时不想手动选号),可只写容器端口:
- "80",Docker 自动选一个空闲端口,之后用docker-compose port <service> 80</service>查看实际映射 - 显式写
"0.0.0.0:8080:80"不必要,且在 WSL2 或某些防火墙策略下可能异常;简写"8080:80"即默认监听所有接口
应用内部监听地址必须匹配
端口映射生效的前提,是容器内程序自己监听了正确的地址:
- ✅ 正确:
app.run(host="0.0.0.0", port=8000)(绑定所有网络接口) - ❌ 错误:
app.run(host="127.0.0.1", port=8000)(仅限容器内部访问,外部请求被拒绝)
尤其对 Python(Sanic/Flask)、Node.js(Express)、Go 等常见 Web 框架,这个细节常被忽略,导致“明明写了 ports 却访问不到”。











