CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
Docker 容器入门/课时/网络
RawBlame
课时目录12 节

Docker 容器入门

  1. 01容器是什么
  2. 02安装与环境
  3. 03Docker CLI 基础
  4. 04镜像与分层
  5. 05编写 Dockerfile
  6. 06构建与优化
  7. 07镜像仓库
  8. 08数据卷与挂载
  9. 09网络
  10. 10Docker Compose
  11. 11容器安全基础
  12. 12综合练习与 K8s 方向

网络

端口映射、自定义 network、容器互访。

30 分钟

网络

这一课你将能做到

  1. 解释清楚「容器里的 localhost 不是宿主机也不是别的容器」,并让 Spring Boot 容器用容器名直连 MySQL/Redis
  2. 创建自定义网络,验证其内置 DNS 能力,说清它与默认 bridge 的核心区别
  3. 熟练使用 -p 做端口映射(含只绑定 127.0.0.1 的安全写法),排查端口不通问题

为什么要学

容器化 Java 应用最常卡住人的地方不是构建,而是连接:docker run 起了 MySQL,Spring Boot 配置里写 localhost:3306,启动报 Communications link failure。原因是每个容器有自己独立的网络命名空间——你的应用容器里的 localhost 是它自己,不是 MySQL 容器。

网络也是生产安全的分界线:哪些端口该映射到宿主机、哪些只该在容器网络内部互通,直接决定数据库是否暴露在公网。本课把「连通」和「隔离」一起讲清楚。

先建立图像

容器网络的三层视角:

外部用户 ──→ 宿主机:8080 ──(端口映射 -p)──→ 容器A:8080  (Spring Boot)
                                                │
宿主机开发 ──→ 宿主机:6379 ──(-p 6379)──→ 容器B:6379 (Redis)
                                                │
              容器A ──(自定义网络 app-net,走内部 DNS)──→ 容器C:3306 (MySQL)
                          注意:容器A 连的是 mysql:3306,不是 localhost:3306

三种网络模式速览:

模式说明场景
bridge(默认)容器接入虚拟网桥,经 NAT 出网绝大多数场景
host容器直接用宿主机网络栈,无隔离性能极敏感、少用
none无网络离线任务、安全隔离
container:xxx共享另一个容器的网络栈sidecar 模式(K8s Pod 的原理)

跟着做

步骤 1:先踩一次「localhost 连不上」的坑

docker run -d --name lonely-mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:8.0
docker run -d --name lonely-app demo-app:1.0    # 假设配置是 jdbc:mysql://localhost:3306/app
docker logs lonely-app --tail 20
# Communications link failure —— app 容器里的 localhost 是它自己,不是 MySQL

结论固化到笔记:跨容器连接永远不用 localhost。宿主机上直连容器,走的是端口映射;容器连容器,走的是容器网络。

步骤 2:认识默认网络与网络命令

docker network ls                # bridge / host / none 三个内置网络
docker network inspect bridge    # 看网段(通常 172.17.0.0/16)与已连接容器

进入容器看自己的网络身份:

docker exec lonely-mysql hostname -i    # 容器自己的 IP
docker exec lonely-mysql cat /etc/hosts

步骤 3:创建自定义网络(本课主角)

docker network create app-net

步骤 4:MySQL、Redis 接入自定义网络

docker run -d --name app-mysql \
  --network app-net \
  -v mysql-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=123456 \
  -e MYSQL_DATABASE=appdb \
  mysql:8.0

docker run -d --name app-redis --network app-net redis:7 --requirepass "dev123"

注意:两个容器都没有 -p。它们不需要对外暴露,只需网络内部可达。

步骤 5:验证容器名互访(自定义网络的内置 DNS)

# 用任何同网络容器 ping / 探测其他容器的【容器名】
docker exec app-mysql ping -c 2 app-redis       # 能 ping 通(部分镜像无 ping,用下面方式)
docker exec app-mysql getent hosts app-redis    # DNS 解析:返回容器 IP

容器名(app-mysql)可直接当域名用,DNS 由 Docker 内置解析器提供。这是与默认 bridge 的核心区别——默认 bridge 里容器名不通,只能互查 IP,而 IP 重启后会变。

步骤 6:Spring Boot 容器接入并连通

docker run -d --name app-backend \
  --network app-net \
  -p 8080:8080 \
  -e SPRING_DATASOURCE_URL=jdbc:mysql://app-mysql:3306/appdb \
  -e SPRING_DATASOURCE_USERNAME=root \
  -e SPRING_DATASOURCE_PASSWORD=123456 \
  -e SPRING_DATA_REDIS_HOST=app-redis \
  -e SPRING_DATA_REDIS_PASSWORD=dev123 \
  demo-app:1.0

docker logs -f app-backend        # HikariPool 启动成功
curl http://localhost:8080/actuator/health

数据源 URL 里的主机名是 app-mysql(容器名),不是 localhost 也不是 IP。

步骤 7:端口映射的细节

docker run -d -p 8080:8080 ...        # 宿主机所有网卡的 8080 → 容器 8080
docker run -d -p 127.0.0.1:8080:8080 ...  # 只绑定本机回环:外网访问不到,调试更安全
docker run -d -p 8081:80              # 宿主机 8081 → 容器 80(两侧端口可不同)
docker run -d -P ...                  # 大写 P:把 EXPOSE 声明的端口随机映射
docker port app-backend               # 查看某容器的映射关系

步骤 8:验证网络隔离

docker network create other-net
docker run -d --name lonely-redis --network other-net redis:7
docker exec app-backend getent hosts lonely-redis   # 无输出——不同网络,DNS 不通
docker network connect app-net lonely-redis          # 热接入:立即互通
docker network disconnect app-net lonely-redis       # 摘除

讲清楚要点

  1. 每个容器 = 独立网络命名空间:有自己的 lo、eth0、IP、端口空间。所以容器间 localhost 互不相通;容器内监听的端口想被宿主机访问,必须 -p。
  2. 自定义 bridge 是多容器部署的标准做法:与默认 bridge 的三大区别——内置 DNS(容器名即域名)、网络隔离(不同业务可分网)、支持动态连接断开(connect/disconnect)。默认 bridge 存在只为兼容旧版。
  3. -p 与 EXPOSE 各司其职:EXPOSE 是元数据声明;-p 是实际的 DNAT 端口转发规则。数据库类容器(MySQL/Redis)在生产上刻意不加 -p,只允许同网络容器访问——纵深防御的第一层。
  4. 容器 IP 不可靠,容器名才可靠:容器重建后 IP 会变(Docker 按顺序分配),容器名跟着配置走。Spring Boot 配置里永远写容器名/服务名,禁止写死容器 IP。
  5. 连接地址速查:宿主机 → 容器:localhost:映射端口;容器 → 容器(同网络):容器名:容器端口;容器 → 宿主机上的服务:host.docker.internal(Docker Desktop;Linux 需 --add-host=host.docker.internal:host-gateway)。

你可能会懵的地方

  • 两个容器都在默认 bridge 里,能互相访问吗? 能,通过对方 IP(网桥内互通)。但没有内置 DNS,只能拿 IP 或 /etc/hosts 硬编码——IP 不稳定,所以默认 bridge 里跑多容器架构是反模式,务必用自定义网络。

  • -p 3306:3306 暴露 MySQL 有多危险? 云服务器安全组若放行了 3306,等于数据库裸奔公网(root 弱密码场景必被扫)。规范:MySQL/Redis 不做 -p;确需本地调试时用 -p 127.0.0.1:3306:3306 只绑回环。

  • Spring Boot 在本地 IDE 跑(不在容器里),怎么连容器里的 MySQL? 走宿主机端口映射:容器加 -p 3306:3306,配置写 localhost:3306。注意区分:应用在容器里时写容器名,应用在宿主机时写 localhost。

  • 容器如何访问宿主机上的其他服务(比如宿主机的 Kafka)? Docker Desktop 下用域名 host.docker.internal;Linux 上启动时加 --add-host=host.docker.internal:host-gateway。

  • 为什么 compose 文件里我不用自己建网络? Compose 默认为每个项目创建一个自定义网络,服务名即 DNS 名。这也是第 10 课里服务间直接写 jdbc:mysql://mysql:3306 的原因——你已经在享受自定义网络的便利。

错法对照

错法问题正解
容器 A 连 localhost:3306 找容器 B 的 MySQLlocalhost 是容器自己同网络容器名直连 mysql:3306
多容器挤在默认 bridge 里无 DNS,只能靠会变的 IPdocker network create + --network
数据库容器 -p 3306:3306 暴露公网被扫描爆破只是时间问题不映射端口;调试只绑 127.0.0.1
Spring Boot 配置写死容器 IP容器重建 IP 即变写容器名/服务名
-p 8080:8080 忘了容器内也监听 8080映射了也连不上对齐:server.port、EXPOSE、-p 三处一致
排查网络只看配置不看现状盲猜浪费时间network inspect + 容器内 getent hosts 实测

卡点急救

  1. Spring Boot 报 Communications link failure:按序检查——两容器是否同一网络(docker network inspect app-net);URL 主机名是否为容器名;MySQL 是否初始化完成(首次启动要几十秒,docker logs app-mysql 看 ready for connections)。
  2. -p 报 port is already allocated:宿主机端口被别的容器或本地进程占用。docker ps 查现有映射;Windows 用 netstat -ano | findstr 8080 找进程。
  3. 外部机器访问不了映射端口:确认 -p 没绑死 127.0.0.1;云服务器检查安全组/防火墙规则;确认应用监听的是 0.0.0.0 而非仅 127.0.0.1(Spring Boot 默认是 0.0.0.0)。
  4. 容器名解析失败(getent 无输出):容器不在同一网络,或用的是默认 bridge。network connect 网络名 容器名 热修。
  5. 网段冲突:公司内网与 Docker 默认网段重叠导致路由异常。在 daemon.json 配 default-address-pools 自定义网段(如 172.30.0.0/16)。

面试常问

  • 容器之间是如何通信的? 同一 bridge 网络内:容器通过虚拟网桥(veth pair + bridge)二层互通,自定义网络由 Docker 内置 DNS 把容器名解析为容器 IP;跨网络默认隔离;出公网走宿主机 NAT。

  • 默认 bridge 和自定义 bridge 的区别?(高频) 自定义 bridge 提供内置 DNS(容器名互访)、更好的隔离性(不同网络互不可达)、支持运行时动态 connect/disconnect;默认 bridge 只有 IP 互通,主要为了向后兼容保留。

  • EXPOSE 和 -p 的区别? EXPOSE 是镜像元数据声明(文档与 -P 的依据),不做任何转发;-p 建立宿主机到容器的 DNAT 映射,真正决定外部可达性。

  • 容器化后 Spring Boot 的数据源 URL 应该怎么写? 与应用运行位置有关:应用在容器内且与 MySQL 同网络 → jdbc:mysql://容器名或服务名:3306/db;应用在宿主机调试 → 容器 -p 3306:3306 后写 localhost:3306。核心是搞清楚「相对于谁」。

自测三问

  1. 为什么容器 A 无法通过 localhost:3306 访问容器 B 的 MySQL?正确的连接方式有哪些?
  2. 自定义 bridge 相比默认 bridge 多了哪些能力?分别用哪个命令验证?
  3. 生产环境 MySQL 容器要不要 -p 3306:3306?需要本地调试时怎么写更安全?

小结一句话

容器网络的关键认知是「地址相对于谁」——容器内 localhost 只是自己,跨容器靠自定义网络的容器名 DNS,对外靠 -p 端口映射,数据库端口保持对公网沉默。

延伸

  • Docker 网络官方文档
  • 下一课预告:Docker Compose——把本课的 app-mysql、app-redis、app-backend 三个容器写进一个 YAML,一键起停。
加载本节练习…
← 上一课数据卷与挂载下一课 →Docker Compose