Docker 网络
容器默认拥有独立的网络空间。它可以主动访问外部网络,但外部不能仅凭容器内部端口访问它。本篇通过两个 Nginx 容器和一个临时客户端解释最常见的网络路径。
完成后,你将能够:
- 区分容器端口与宿主机端口;
- 使用
--publish对外提供服务; - 创建自定义 bridge 网络;
- 使用容器名进行 DNS 服务发现;
- 排查端口占用、连接拒绝和名称解析问题。
1. 先理解三种访问路径
容器访问互联网
bridge 网络中的容器通常可以通过宿主机转发访问互联网,例如拉取软件包或请求外部 API。能否访问仍取决于宿主机 DNS、代理和防火墙策略。
容器访问容器
两个容器加入同一个自定义网络后,可以通过容器名通信,不必发布端口,也不要依赖会变化的容器 IP。
宿主机访问容器
需要显式发布端口,例如 -p 8080:80:
- 左边
8080:宿主机监听端口; - 右边
80:容器内应用监听端口; - 请求
localhost:8080时,Docker 把流量转发到容器的 80 端口。
EXPOSE 80 只是镜像元数据,不会自动创建这条转发规则。
2. 查看默认网络
# 列出 Docker 已有网络;默认可见 bridge、host 和 none
docker network ls
# 查看默认 bridge 的子网、网关和已连接容器
docker network inspect bridge
常见网络驱动:
| 驱动 | 典型用途 | 新手需要知道的重点 |
|---|---|---|
bridge | 单台主机上的普通容器 | 优先创建自定义 bridge 网络 |
host | Linux 上直接共享宿主网络 | 无端口隔离;Docker Desktop 行为有限制 |
none | 完全禁用外部网络 | 只保留回环接口 |
overlay | Docker Swarm 的跨主机网络 | 单机入门暂不需要 |
3. 端口映射实验
3.1 发布固定端口
# 将宿主机 8080 端口转发到容器 80 端口
docker run --detach \
--name network-web \
--publish 8080:80 \
nginx:1.27-alpine
# 查看 Docker 记录的实际端口映射
docker port network-web
# 从宿主机验证;响应状态应为 HTTP/1.1 200 OK
curl --head http://127.0.0.1:8080
docker ps 的 PORTS 列可能显示 0.0.0.0:8080->80/tcp 和 [::]:8080->80/tcp,表示所有宿主机接口都可能接受连接。实际是否能从外部访问还取决于系统防火墙、云安全组和路由。
3.2 只允许本机访问
开发数据库、管理后台或由反向代理转发的服务,通常只绑定回环地址:
# 删除上一节容器,释放宿主机 8080 端口
docker rm --force network-web
# 显式绑定 127.0.0.1,局域网其他机器不能直接访问
docker run --detach \
--name network-web \
--publish 127.0.0.1:8080:80 \
nginx:1.27-alpine
# 输出应包含 127.0.0.1:8080
docker port network-web
3.3 让 Docker 随机选择端口
多个临时实例并行运行时,可以省略宿主机端口:
# 只写容器端口,Docker 会随机分配可用的宿主机端口
docker run --detach \
--name random-port-web \
--publish 80 \
nginx:1.27-alpine
# 查询随机分配的端口,例如 0.0.0.0:32768
docker port random-port-web 80
# 清理这个临时实例
docker rm --force random-port-web
端口默认使用 TCP。UDP 服务必须写明协议,例如 -p 5353:5353/udp。
4. 创建容器专用网络
现在让客户端容器通过名称访问 Nginx,不经过宿主机端口。
# 创建用户自定义 bridge 网络
docker network create beginner-net
# 查看网络列表,确认 beginner-net 已创建
docker network ls --filter name=beginner-net
# 启动 Web 容器并加入该网络;不需要发布宿主机端口
docker run --detach \
--name internal-web \
--network beginner-net \
nginx:1.27-alpine
# 临时启动 curl 客户端并加入同一网络,通过容器名访问
docker run --rm \
--network beginner-net \
curlimages/curl:8.10.1 \
--head http://internal-web
最后一条命令应返回 HTTP/1.1 200 OK。这里的 internal-web 由 Docker 内置 DNS 解析,不需要知道容器 IP,也没有使用 -p。
检查网络详情:
# 查看子网、网关和已连接容器;Containers 字段应包含 internal-web
docker network inspect beginner-net
# 提取 internal-web 在 beginner-net 中的 IP,仅用于观察
docker inspect internal-web \
--format '{{(index .NetworkSettings.Networks "beginner-net").IPAddress}}'
容器删除重建后 IP 可能改变。应用配置应使用容器名或 Compose 服务名,例如 http://internal-web、postgres://db:5432。
5. 动态接入和断开网络
一个容器可以连接多个网络,以分隔不同通信范围:
# 创建第二个实验网络
docker network create extra-net
# 把已运行的 internal-web 接入第二个网络
docker network connect extra-net internal-web
# 确认容器现在同时出现在两个自定义网络中
docker inspect internal-web \
--format '{{json .NetworkSettings.Networks}}'
# 从第二个网络断开;不会停止容器
docker network disconnect extra-net internal-web
# 删除不再使用的第二个网络
docker network rm extra-net
实际项目中,前端代理可以同时加入 frontend 和 backend,数据库只加入 backend,以减少不必要的连通范围。
6. localhost 为什么经常用错
容器内的 127.0.0.1 或 localhost 指向当前容器自身,不是宿主机,也不是另一个容器。
- API 访问数据库:使用同一网络中的服务名,如
db:5432; - 容器访问宿主机:Docker Desktop 通常可使用
host.docker.internal; - Linux Engine:可在运行时添加
--add-host host.docker.internal:host-gateway; - 宿主机访问容器:发布端口后使用
127.0.0.1:宿主机端口。
Linux Engine 示例:
# 添加一个解析到宿主机网关的特殊名称,再显示解析结果
docker run --rm \
--add-host host.docker.internal:host-gateway \
alpine:3.20 \
getent hosts host.docker.internal
只有确实需要访问宿主机服务时才这样做。容器化依赖更适合放入同一个自定义网络。
7. 常见故障排查
端口已被占用
# macOS/Linux 查看谁正在监听 8080 端口
lsof -nP -iTCP:8080 -sTCP:LISTEN
# 查看是否已有 Docker 容器发布了该端口
docker ps --format 'table {{.Names}}\t{{.Ports}}'
停止冲突服务,或把映射改为 -p 8081:80。不要修改右边的容器端口,除非应用确实监听了另一个端口。
连接被拒绝
# 确认容器是否运行以及端口是否发布
docker ps -a --filter name=network-web
docker port network-web
# 查看应用是否因配置错误退出
docker logs --tail 50 network-web
# 在容器内部请求应用,区分“应用未监听”和“端口转发”问题
docker exec network-web \
wget -q -S -O /dev/null http://127.0.0.1:80
容器名无法解析
# 查看两个容器是否连接到同一个自定义网络
docker inspect internal-web \
--format '{{json .NetworkSettings.Networks}}'
docker network inspect beginner-net
# 从同一网络的临时容器测试 DNS
docker run --rm \
--network beginner-net \
alpine:3.20 \
getent hosts internal-web
默认 bridge 网络不提供与自定义 bridge 完全相同的名称解析体验。多容器应用应使用自定义网络或 Compose 默认网络。
8. 清理实验环境
# 删除两个 Nginx 实验容器
docker rm --force network-web internal-web
# 网络有容器连接时无法删除;清理容器后再删除
docker network rm beginner-net
# 确认实验对象已经不存在
docker ps -a --filter name=network-web
docker network ls --filter name=beginner-net
9. 本篇练习
- 在同一网络中启动两个 Nginx 容器,用临时 curl 容器分别按名称访问;
- 只给其中一个 Nginx 发布宿主机端口,说明另一个为何仍能被同网络容器访问;
- 把映射从
8080:80改为9090:80,验证容器内端口不变; - 从
docker network inspect中找到网络网关和容器地址。
下一篇将学习容器数据为何会丢失,以及如何通过命名卷和 bind mount 持久化数据。