玩转Docker 06 — 容器网络

玩转Docker 06 --- 容器网络

一、为什么学网络

容器不是孤立的------它们要互相通信 (典型场景:后端容器 ↔ MySQL / Redis / 数据库容器),也要对外提供服务-p 端口映射,第 1 课已学)。

本课还是 第 7 课 Docker Compose(多容器编排)的基础------Compose 的服务互连用的就是本课这套机制。

一个悬念(本课揭晓)

第 1 课 nginx 容器的访问日志里,客户端 IP 是 172.17.0.1,不是 127.0.0.1------为什么?谜底在本课。


二、Docker 网络类型

类型 是什么 何时用
bridge(默认) 虚拟桥接网络;容器分到独立 IP(172.17.x.x 段) 不指定网络时,容器默认进它
host 容器直接共用宿主机的网络(无网络隔离) 要极致网络性能、不在乎隔离
none 容器完全没有网络 离线计算、安全隔离
自定义 bridge 你自己创建的 bridge 网络 多容器互访(关键!)

三、核心区分:默认 bridge vs 自定义 bridge(本课最重要)

默认 bridge 自定义 bridge
容器间互访 只能靠 IP(IP 会变、记不住,难用) 可以用名字(内置 DNS 自动解析)
DNS 服务 ❌ 没有 ✅ Docker 内置 DNS
实战地位 历史遗留默认 多容器应用的标准做法(Compose 自动建的就是这种)

实例:本机运行的 Milvus 套件(standalone + minio + etcd + attu)就是靠一个叫 milvus 的自定义网络互连------milvus 连 etcd、minio 用的就是容器名。


四、实验全过程(含目的、命令详解、结果解读)

实验 1:查看现有网络(摸清地形)

【目的】 动手前先看看机器上已有哪些网络,认识"默认三件套 + 自定义网络"各自长什么样,并找到自定义网络的活例子。

【命令】

bash 复制代码
docker network ls

【命令详解】

  • docker network --- Docker 的网络管理命令组
  • ls --- list,列出所有网络

【过程与结果】

sql 复制代码
NETWORK ID     NAME      DRIVER    SCOPE
297a9a73c2c2   bridge    bridge    local
e22f8f5d5f3e   host      host      local
8dc1ab22bc5e   milvus    bridge    local
18fe25d46125   none      null      local

逐行解读:

  • bridge(DRIVER=bridge)--- 默认桥接网络。容器不指定网络时都进它,网段 172.17.x.x。
  • host(DRIVER=host)--- 宿主机网络,容器直接共用宿主机网卡,无隔离。
  • milvus(DRIVER=bridge)--- 👀 一个自定义 bridge 网络!Milvus 那套容器就挂在它上面,靠名字互连(活例子)。
  • none(DRIVER=null)--- "无网络",挂上它的容器完全断网。
  • DRIVER 列说明网络的实现方式;SCOPE=local 表示作用范围是本机。

【结论】 机器上 = 三个内置网络 + 若干自定义网络共存;多容器互连的标准做法就是自定义网络


实验 2:创建自定义网络

【目的】 亲手建一个自定义 bridge 网络,作为后面容器互连的"内部电话网"。

【命令】

bash 复制代码
docker network create mynet

【命令详解】

  • docker network create <名字> --- 创建网络;不指定类型时默认创建 bridge 类型(即"自定义 bridge")
  • mynet --- 给网络起的名字(自定义网络才有可读名字,默认 bridge 没有)

【过程与结果】

复制代码
80e8068374e7f1494be8ac52ec3c31403c6f8a57ea3593f0ffe807652510513f

回显新网络的 64 位 ID → 创建成功。Docker 已自动给它分配了一个独立网段(实验 5 会看到是 172.19.0.0/16)。

【结论】 一条命令就拥有了一个专属网络,后续容器挂上来即可互访。


实验 3:在 mynet 上启动一个"服务容器" web

【目的】 在自定义网络上跑一个 nginx 容器并起名 web,作为被访问的目标(模拟真实项目里"数据库 / 后端服务"的角色)。

【命令】

bash 复制代码
docker run -d --name web --network mynet nginx

【命令详解】docker run 结构 = 选项 + 镜像 + 命令

  • -d --- 后台运行(detach),不占终端
  • --name web --- 容器命名为 web这个名字就是它在网络里的"域名"(自定义网络的 DNS 按名字解析,实验 4 验证)
  • --network mynet --- 把容器挂到 mynet 网络。⚠️ 不写这个参数容器就进默认 bridge(那可没有名字互访!)
  • nginx --- 用哪个镜像(本地已有,秒起)
  • ⚠️ 故意没加 -p :因为接下来要从另一个容器 访问它(走 mynet 内部网络),不是从宿主机访问。-p 只用于把端口发布到宿主机。

【过程与结果】

复制代码
2d21e611326975f0254baa2aa824a0a787d962e7a5e1ba3b8cd1ca00f0fe9dfe

回显容器 ID → web 容器已在 mynet 上后台运行(nginx 监听容器内 80 端口)。

【结论】 容器已入网。此时宿主机访问不到它(没 -p),但同网络的容器可以------这正是下个实验要验证的。


实验 4:从另一个容器用"名字"访问 web(本课高潮)

【目的】 验证本课核心命题:同一自定义网络里的容器,能不能只用"名字"就找到并访问另一个容器(不查 IP、不配置任何东西)。

【命令】

bash 复制代码
docker run --rm --network mynet nginx sh -c 'echo "[1] 名字 web 解析到 IP:"; getent hosts web; echo "[2] 用名字抓 web 的网页:"; (wget -qO- http://web 2>/dev/null || curl -s http://web 2>/dev/null || echo "(无 curl/wget,但 [1] 已证明名字能解析)") | head -3'

【命令详解】 外层(docker run 部分):

  • --rm --- 容器退出后自动删除(一次性测试工具容器,跑完不留垃圾;回忆第 5 课)
  • --network mynet --- 关键 :临时容器也挂进 mynet,和 web 同网才可能互访
  • nginx --- 借用 nginx 镜像的 Linux 环境当"测试机"(本地已有,秒起)
  • sh -c '...' --- 覆盖镜像默认 CMD(第 4 课学的:run 时给命令会替换镜像的 CMD),让它执行我们的测试脚本而不是启动 nginx

内层(脚本部分,逐段):

  • getent hosts web --- 用系统名字解析机制查 web 对应的 IP。getent 是 glibc 自带工具(Debian 系镜像都有),测 DNS 最可靠
  • wget -qO- http://web --- 向 http://web 发 HTTP 请求(注意:名字当域名用! ),-q 静默、-O- 输出到屏幕;2>/dev/null 丢弃报错
  • || curl -s http://web --- wget 不可用就换 curl 试(双保险)
  • || echo "..." --- 两个都没有就打印兜底说明(但 1 的解析已足够证明)
  • | head -3 --- 只显示前 3 行,避免刷屏

【过程与结果】

xml 复制代码
[1] 名字 web 解析到 IP:
172.19.0.2      web
[2] 用名字抓 web 的网页:
<!DOCTYPE html>
<html>
<head>

逐行解读:

  • 1 DNS 解析成功 :名字 web → IP 172.19.0.2。这个临时容器里没配置任何 DNS ------是 Docker 内置 DNS(自定义网络自带)自动把容器名解析成了它的 IP。
  • 2 HTTP 连通成功 :直接访问 http://web(名字当域名),真的抓到了 nginx 欢迎页的 HTML。从"名字解析"到"真实通信",全链路打通。
  • 注意 IP 段是 172.19.x.x------mynet 的专属网段(每个网络独立一段;默认 bridge 用 172.17.x.x)。

【结论】 自定义网络 = 容器间的"内部电话网",容器名即域名。跨容器连接一律用名字,不用记 IP------这就是 Compose 里服务互连的机制。


实验 5:查看 mynet 网络拓扑(把前 4 步串成一张图)

【目的】 从 Docker 视角看 mynet 的完整配置------网段、网关、挂了哪些容器、各自 IP------验证实验 4 的结果并解开第 1 课的悬念。

【命令】

bash 复制代码
docker network inspect mynet

【命令详解】

  • docker network inspect <名字> --- 输出该网络的详细配置(JSON 格式)

【过程与结果】(节选关键字段并注释)

json 复制代码
"Driver": "bridge",                     // 桥接型网络
"IPAM": { "Config": [{
    "Subnet":  "172.19.0.0/16",         // mynet 的专属网段
    "Gateway": "172.19.0.1"             // mynet 的网关
}]},
"Containers": {
    "2d21e611...": {
        "Name": "web",
        "IPv4Address": "172.19.0.2/16", // ← 和实验4 getent 解析出的 IP 完全一致!
        "MacAddress": "6e:3a:93:be:68:1a"
    }
}

逐点解读:

  • Subnet: 172.19.0.0/16 --- mynet 的独立网段(默认 bridge 用 172.17.0.0/16,互不干扰)
  • Gateway: 172.19.0.1 --- 网关 。💡 第 1 课悬念在此闭环 :当时 nginx 日志里的客户端 IP 172.17.0.1 = 默认 bridge 的网关。宿主机访问容器,请求经网桥的网关转发,容器看到的"客户端"就是网关 IP。
  • Containers.web @ 172.19.0.2 --- 挂在网络上的容器及其 IP,与实验 4 的 DNS 解析结果完全一致
  • MacAddress --- 每个容器有自己独立的虚拟网卡和 MAC 地址(Network namespace 的体现:网络栈也是隔离的)

【结论】 inspect = 网络的全景地图:网段、网关、成员、IP 一目了然;也验证了 DNS 解析结果的真实来源。


五、关键认知(五条)

  1. 名字 = 容器在自定义网络里的"域名"。同网络容器直接用名字互访,不用记 IP。
  2. -p 只服务"宿主机/外部访问";容器间互访走内部网络,天然畅通,不需要 -p。
  3. 每个网络有独立网段:默认 bridge = 172.17.x.x;mynet = 172.19.x.x。
  4. 网关 = 172.x.0.1 ------解开第 1 课日志 172.17.0.1 之谜(宿主机经网关访问容器)。
  5. 容器有独立虚拟网卡 + MAC 地址(Network namespace 的体现)。

六、命令清单

命令 作用
docker network ls 列出所有网络
docker network create <名字> 创建自定义 bridge 网络
docker network inspect <名字> 看网络详情(网段/网关/挂了哪些容器及其 IP)
docker network rm <名字> 删网络(前提:没有容器挂在上面)
docker network prune 删所有未使用的网络
docker run --network <名字> ... 把容器挂到指定网络

七、易错点

  1. 用默认 bridge 期望名字互访 → 不行!默认 bridge 没有内置 DNS,必须用自定义网络。
  2. 以为容器间访问也要 -p → 不用!-p 只管宿主机/外部访问。
  3. 删网络报错 "has active endpoints" → 还有容器挂在网上,先 docker rm 那些容器再删网络。

八、一句话总结

自定义网络 = 容器间的"内部电话网",容器名即域名。多容器应用(也是 Compose)全靠它互连------记住:跨容器连接,一律用名字。


九、本课产物(可清理)

  • 容器 web(nginx,挂 mynet)→ docker rm -f web
  • 网络 mynetdocker network rm mynet(注意顺序:先删 web 才能删网络)

十、下一课预告

第 7 课:Docker Compose ------ 多容器编排 🎼

前 6 课的所有拼图(镜像/容器/Dockerfile/volume/网络)在这课合体:

  • 一个 docker-compose.yml 文件描述整套应用(后端 + 数据库 + Redis + ...)
  • docker compose up 一条命令拉起全部容器、自动建自定义网络、互相用名字连接(本课手动做的一切被它自动化)
  • down / restart / logs 批量管理
  • 剖析一个真实在用的 docker-compose.yml(活教材)
  • 为第 8 课「实战:容器化一个真实后端应用」做准备
相关推荐
windliang40 分钟前
Claude Code 源码分析(十二):错误处理与自动恢复:让 Agent 稳定运行
前端·javascript·面试
打呵欠的猫41 分钟前
我用 AI 重写了项目的请求层,从 800 行"面条代码"变成 3 层洋葱模型
前端·ai编程
feng尘1 小时前
深入浅出 Java:ThreadLocal 为什么会产生内存泄漏?
后端
默_笙1 小时前
🛬 前端路由的"高级玩法":懒加载、404、鉴权路由,一个都不能少(下篇)
前端·javascript
用户921080262861 小时前
1. Cesium 在 Vue 项目中的简单初始化配置
前端
YIAN1 小时前
从 Hash 底层原理到 React Router v6 实战:我学会了什么?
前端·react.js·vue-router
lv__pf2 小时前
Spring配置类解析 【TL spring 11】
java·前端·spring
AI分享猿2 小时前
UI设计Prompt系列(十三):响应式布局需求怎么写——让设计Prompt更接近前端实现
前端
ClouGence2 小时前
Selenium 写不动了?这个工具录一次就能跑 Web 自动化
前端·selenium·测试