docker-compose 中 ports 和 expose 有什么区别?
ports 把容器端口发布到宿主机,可达性取决于绑定地址和网络策略;expose 仅声明端口,不控制访问。同网络服务通信不要求 expose。
一句话区别:ports 会把端口发布到宿主机**(宿主机端口:容器端口,可达性仍取决于绑定地址、路由和防火墙);expose 是内部端口声明,不创建防火墙,也不保证宿主机无法直达容器地址。数据库这类只供内部服务连的用 expose,需要浏览器/外部访问的才用 ports。**
ports:对外发布
services:
mysql:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?请先安全设置初始化密码}
ports:
- "3306" # 容器 3306 → 宿主机随机端口
docker compose ps 会看到 0.0.0.0:32769->3306/tcp——宿主机的 32769 被映射过去,未限制绑定与防火墙时可能对外开放。固定端口写法 "3306:3306"。
expose:声明内部端口
services:
mysql:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?请先安全设置初始化密码}
expose:
- "3306" # 端口声明,不是访问控制
app:
image: myapp
# app 里用 mysql:3306 连接
app 服务通过服务名 mysql:3306 正常连接;未发布到宿主3306并不等于任意路径都不可达;验证MySQL应使用mysql客户端或TCP探测,不能用HTTP curl。
一个常见误解
其实连接到共同网络的服务通常可访问对方正在监听且未受策略限制的端口(通过内部 DNS 和服务名),expose 更多是文档性声明+与老版 link 语义的兼容。真正的边界是:
- 要不要从宿主机/外部访问?要 →
ports - 只在服务间访问 → 什么都不写也行,写
expose表明意图
安全建议
- 数据库、Redis、消息队列默认不要用 ports 暴露到宿主机,尤其宿主机有公网 IP 时
- 调试期临时需要,可用
"127.0.0.1:3306:3306"只绑定回环地址
常见问题(FAQ)
Q:ports 写 "3306" 和 "3306:3306" 有什么区别?
A:前者宿主端口随机分配(每次启动可能变),后者固定映射到宿主 3306。
Q:expose 会影响容器间通信吗?
A:不会限制——即使不写 expose,同网络服务仍需目标进程监听、路由与策略允许;它主要起声明作用。
Q:生产环境数据库该暴露 ports 吗?
A:按访问需求决定,默认尽量只供内部应用;确需外部管理时限制绑定地址、网络来源并配置数据库认证。