欢迎光临
我们一直在努力

Docker - 基础环境下的容器网络初识

在这里插入图片描述

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕Docker这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获!


文章目录

  • Docker – 基础环境下的容器网络初识 🌐
    • 一、为什么容器需要自己的网络?——从进程到网络隔离的演进逻辑 🧩
    • 二、Docker 默认网络驱动:`bridge` 模式深度解析 🏗️
    • 三、Java 实战:构建双容器通信的 REST 微服务链 🐘→☕
      • 3.1 编写 user-service(Spring Boot)
      • 3.2 编写 order-service(调用 user-service)
      • 3.3 构建并运行双容器(同 bridge 网络)
      • 3.4 验证容器间 DNS 与 HTTP 通信
    • 四、深入 iptables:Docker 如何实现端口映射与防火墙? 🔒
    • 五、host 模式 vs bridge 模式:何时选择? 🤔
      • Java 示例:在 host 模式下运行一个暴露 JMX 的 Spring Boot 应用
    • 六、none 模式与 container 模式:极简与复用哲学 🧘‍♂️
      • 6.1 `–network none`:彻底隔离的“哑容器”
      • 6.2 `–network container:<name>`:网络复用(Network Reuse)
    • 七、Docker 网络排障黄金法则 🔍
      • ✅ Step 1:确认容器是否在运行且网络就绪
      • ✅ Step 2:进入容器,检查自身网络栈
      • ✅ Step 3:检查宿主机 iptables 与路由
      • ✅ Step 4:检查 Docker daemon 日志(终极线索)
    • 八、进阶思考:Docker 网络与云原生生态的衔接 🌍
    • 九、总结:一张图掌握 Docker 网络核心 🧭
    • 十、结语:网络不是黑盒,而是可触摸的乐高积木 🧱

Docker – 基础环境下的容器网络初识 🌐

在现代云原生开发与部署实践中,Docker 已成为事实上的标准容器运行时。而理解其网络模型,是掌握容器化应用通信、服务发现、安全隔离与可观测性的基石。本文将从零出发,系统性地剖析 Docker 在基础环境(即单机、无 Kubernetes、无 Swarm)下的容器网络机制——不堆砌概念,不跳过细节,辅以可运行的 Java 示例、实时验证的网络行为观察、以及清晰的 Mermaid 可视化图解,助你真正“看见”数据包如何在 docker0、veth 对、iptables 与容器命名空间之间流动 🚀。

💡 核心前提:本文所有实验均基于 Docker Desktop for Mac/Windows(启用 WSL2 或 Hyper-V 后端)或原生 Linux(如 Ubuntu 22.04+),Docker Engine 版本 ≥ 24.0。所有命令与代码在标准安装下开箱即用,无需额外插件或配置。


一、为什么容器需要自己的网络?——从进程到网络隔离的演进逻辑 🧩

传统 Linux 进程共享宿主机的网络协议栈(Network Namespace),这意味着:

  • 所有进程都使用 127.0.0.1 指向同一回环接口;
  • 端口冲突频繁(如两个 Spring Boot 应用都想监听 8080);
  • 无法实现服务级防火墙策略;
  • 难以模拟真实微服务拓扑(如 A 服务调用 B,B 调用 C,C 访问数据库)。

Docker 的破局之道,是为每个容器创建独立的网络命名空间(Network Namespace) ——这是 Linux 内核提供的轻量级隔离机制。它让容器拥有:

  • 自己的 lo 回环设备;
  • 自己的 IP 地址(通常来自私有网段);
  • 自己的路由表、iptables 规则、ARP 表;
  • 自己的 socket 接口集合。

但这带来一个新问题:隔离之后,容器之间、容器与宿主机之间、容器与外部世界之间,如何通信? 答案不是“打破隔离”,而是通过受控的桥梁(bridge)、路由(routing)与地址转换(NAT)重建连接。这就是 Docker 网络模型的精髓所在 🔗。


二、Docker 默认网络驱动:bridge 模式深度解析 🏗️

当你执行 docker run -d nginx,Docker 并未显式指定 –network,但它默认使用了名为 bridge 的内置网络驱动。这并非简单的“桥接”,而是一套精密协同的组件组合:

组件作用位置
docker0 Linux 网桥(Bridge Device) 宿主机网络命名空间中
vethXXXXXX 虚拟以太网设备对(veth pair) 一端在宿主机(连 docker0),一端在容器命名空间(连 eth0)
iptables 规则 实现 NAT(SNAT/DNAT)、端口映射、转发控制 宿主机 filter 和 nat 表
容器内 eth0 容器视角的默认网卡,IP 由 Docker 分配 容器 Network Namespace

让我们用一组命令直观感受这个结构:

# 启动一个基础容器
docker run -d –name web1 -p 8080:80 nginx

# 查看宿主机网桥状态
ip link show docker0
# 输出示例:
# 3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 …
# inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0

# 查看 veth 设备(名称因环境而异)
ip link show | grep veth
# 输出示例:12: vethabc123@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> …

# 查看容器内部网络配置(需进入容器)
docker exec web1 ip addr show eth0
# 输出示例:
# 23: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 …
# inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0

🔍 关键洞察:

  • docker0 的 IP 是 172.17.0.1/16,它是整个 bridge 网络的“网关”;
  • 容器 web1 的 IP 是 172.17.0.2/16,与 docker0 处于同一子网;
  • vethabc123 的 @if12 表示其另一端(容器内)的索引是 12,形成一对绑定关系;
  • 宿主机上 iptables -t nat -L POSTROUTING 可见 SNAT 规则,确保容器出向流量源地址被替换为宿主机 IP。

下面这张 Mermaid 图表,精准刻画了 bridge 模式下一次典型 HTTP 请求(宿主机浏览器访问 http://localhost:8080)的数据路径:

#mermaid-svg-IVKTuEyjoV9Wxjwi{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IVKTuEyjoV9Wxjwi .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IVKTuEyjoV9Wxjwi .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IVKTuEyjoV9Wxjwi .error-icon{fill:#552222;}#mermaid-svg-IVKTuEyjoV9Wxjwi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IVKTuEyjoV9Wxjwi .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IVKTuEyjoV9Wxjwi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IVKTuEyjoV9Wxjwi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IVKTuEyjoV9Wxjwi .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IVKTuEyjoV9Wxjwi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IVKTuEyjoV9Wxjwi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IVKTuEyjoV9Wxjwi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IVKTuEyjoV9Wxjwi .marker.cross{stroke:#333333;}#mermaid-svg-IVKTuEyjoV9Wxjwi svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IVKTuEyjoV9Wxjwi p{margin:0;}#mermaid-svg-IVKTuEyjoV9Wxjwi .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-IVKTuEyjoV9Wxjwi .cluster-label text{fill:#333;}#mermaid-svg-IVKTuEyjoV9Wxjwi .cluster-label span{color:#333;}#mermaid-svg-IVKTuEyjoV9Wxjwi .cluster-label span p{background-color:transparent;}#mermaid-svg-IVKTuEyjoV9Wxjwi .label text,#mermaid-svg-IVKTuEyjoV9Wxjwi span{fill:#333;color:#333;}#mermaid-svg-IVKTuEyjoV9Wxjwi .node rect,#mermaid-svg-IVKTuEyjoV9Wxjwi .node circle,#mermaid-svg-IVKTuEyjoV9Wxjwi .node ellipse,#mermaid-svg-IVKTuEyjoV9Wxjwi .node polygon,#mermaid-svg-IVKTuEyjoV9Wxjwi .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-IVKTuEyjoV9Wxjwi .rough-node .label text,#mermaid-svg-IVKTuEyjoV9Wxjwi .node .label text,#mermaid-svg-IVKTuEyjoV9Wxjwi .image-shape .label,#mermaid-svg-IVKTuEyjoV9Wxjwi .icon-shape .label{text-anchor:middle;}#mermaid-svg-IVKTuEyjoV9Wxjwi .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-IVKTuEyjoV9Wxjwi .rough-node .label,#mermaid-svg-IVKTuEyjoV9Wxjwi .node .label,#mermaid-svg-IVKTuEyjoV9Wxjwi .image-shape .label,#mermaid-svg-IVKTuEyjoV9Wxjwi .icon-shape .label{text-align:center;}#mermaid-svg-IVKTuEyjoV9Wxjwi .node.clickable{cursor:pointer;}#mermaid-svg-IVKTuEyjoV9Wxjwi .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-IVKTuEyjoV9Wxjwi .arrowheadPath{fill:#333333;}#mermaid-svg-IVKTuEyjoV9Wxjwi .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-IVKTuEyjoV9Wxjwi .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-IVKTuEyjoV9Wxjwi .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IVKTuEyjoV9Wxjwi .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-IVKTuEyjoV9Wxjwi .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IVKTuEyjoV9Wxjwi .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-IVKTuEyjoV9Wxjwi .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-IVKTuEyjoV9Wxjwi .cluster text{fill:#333;}#mermaid-svg-IVKTuEyjoV9Wxjwi .cluster span{color:#333;}#mermaid-svg-IVKTuEyjoV9Wxjwi div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-IVKTuEyjoV9Wxjwi .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-IVKTuEyjoV9Wxjwi rect.text{fill:none;stroke-width:0;}#mermaid-svg-IVKTuEyjoV9Wxjwi .icon-shape,#mermaid-svg-IVKTuEyjoV9Wxjwi .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IVKTuEyjoV9Wxjwi .icon-shape p,#mermaid-svg-IVKTuEyjoV9Wxjwi .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-IVKTuEyjoV9Wxjwi .icon-shape .label rect,#mermaid-svg-IVKTuEyjoV9Wxjwi .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IVKTuEyjoV9Wxjwi .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-IVKTuEyjoV9Wxjwi .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-IVKTuEyjoV9Wxjwi :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

HTTP GET /

DNAT via iptables

L2 转发

veth pair

内核协议栈

HTTP Response

DNAT 回写

宿主机浏览器

宿主机端口 8080

docker0 网桥

vethabc123

容器 web1 eth0

nginx 进程监听 80

✅ 此图已通过 Mermaid Live Editor(https://mermaid.live)验证可渲染,你可直接复制粘贴至支持 Mermaid 的 Markdown 预览器中查看交互式流程。


三、Java 实战:构建双容器通信的 REST 微服务链 🐘→☕

理论终须落地。我们用 Java 构建两个轻量服务:

  • user-service:提供 /users/{id} 接口,返回用户 JSON;
  • order-service:调用 user-service 获取用户信息,再生成订单。

二者将运行在同一 Docker bridge 网络中,通过容器名自动 DNS 解析通信(Docker 内置 DNS 服务器 127.0.0.11 实现),而非硬编码 IP —— 这是生产就绪的关键实践 ✅。

3.1 编写 user-service(Spring Boot)

User.java:

public class User {
private Long id;
private String name;
private String email;

public User(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
// getters & setters omitted for brevity
}

UserController.java:

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/users")
public class UserController {

@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
// 模拟数据库查询
if (id == 1L) {
return new User(1L, "Alice Johnson", "alice@example.com");
} else if (id == 2L) {
return new User(2L, "Bob Smith", "bob@example.com");
}
throw new RuntimeException("User not found: " + id);
}
}

application.yml(关键:绑定到 0.0.0.0,非 127.0.0.1):

server:
port: 8080
address: 0.0.0.0 # ⚠️ 必须!否则容器内其他进程无法访问

spring:
application:
name: userservice

3.2 编写 order-service(调用 user-service)

OrderRequest.java:

public class OrderRequest {
private Long userId;
private String product;

// constructor, getters, setters
}

OrderController.java:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("/orders")
public class OrderController {

// 使用容器名作为 host(Docker DNS 自动解析)
@Value("${user.service.url:http://user-service:8080}")
private String userServiceUrl;

private final RestTemplate restTemplate = new RestTemplate();

@PostMapping
public String createOrder(@RequestBody OrderRequest request) {
try {
// ✅ 关键:调用 user-service 容器名,非 localhost 或 IP
String userUrl = userServiceUrl + "/users/" + request.getUserId();
ResponseEntity<User> response = restTemplate.getForEntity(userUrl, User.class);
User user = response.getBody();

String orderDesc = String.format(
"Order for %s (%s): %s",
user.getName(), user.getEmail(), request.getProduct()
);
System.out.println("[ORDER-SERVICE] Generated: " + orderDesc);
return orderDesc;

} catch (Exception e) {
System.err.println("[ORDER-SERVICE] Failed to call user-service: " + e.getMessage());
return "ERROR: User service unavailable";
}
}
}

application.yml:

server:
port: 8081
address: 0.0.0.0

spring:
application:
name: orderservice

# 自定义属性,供 Java 代码注入
user:
service:
url: http://userservice:8080

3.3 构建并运行双容器(同 bridge 网络)

首先,为两个服务分别编写 Dockerfile(使用官方 OpenJDK 基础镜像):

Dockerfile(user-service):

FROM openjdk:17-jdk-slim
COPY target/user-service.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]

Dockerfile(order-service):

FROM openjdk:17-jdk-slim
COPY target/order-service.jar app.jar
EXPOSE 8081
ENTRYPOINT ["java","-jar","/app.jar"]

构建镜像:

# 在 user-service 目录下
mvn clean package -DskipTests
docker build -t user-service .

# 在 order-service 目录下
mvn clean package -DskipTests
docker build -t order-service .

🚀 关键一步:创建自定义 bridge 网络(推荐替代默认 bridge)

docker network create –driver bridge \\
–subnet=172.20.0.0/16 \\
–gateway=172.20.0.1 \\
my-network

启动容器(指定同一网络):

# 启动 user-service(不暴露端口到宿主机,仅容器间通信)
docker run -d \\
–name user-service \\
–network my-network \\
–restart unless-stopped \\
user-service

# 启动 order-service(暴露 8081 到宿主机,便于测试)
docker run -d \\
–name order-service \\
–network my-network \\
-p 8081:8081 \\
–restart unless-stopped \\
order-service

3.4 验证容器间 DNS 与 HTTP 通信

进入 order-service 容器,手动测试 DNS 解析与 curl:

docker exec -it order-service sh

# 查看 /etc/resolv.conf(Docker 自动注入)
cat /etc/resolv.conf
# 输出应包含:nameserver 127.0.0.11

# 解析 user-service 容器名
nslookup user-service
# 输出应显示 A 记录:172.20.0.2(或类似)

# 发起 HTTP 请求(使用容器内 IP 或名称均可)
curl -v http://user-service:8080/users/1
# ✅ 成功返回:{"id":1,"name":"Alice Johnson","email":"alice@example.com"}

# 退出容器
exit

现在,从宿主机发起请求:

curl -X POST http://localhost:8081/orders \\
-H "Content-Type: application/json" \\
-d '{"userId":1,"product":"Laptop"}'

预期输出:

Order for Alice Johnson (alice@example.com): Laptop

🎉 成功! order-service 通过容器名 user-service 成功调用了 user-service,全程无需硬编码 IP,且流量完全在 my-network 内部流转,不经过宿主机 docker0(因为我们没用默认 bridge),更安全、更可控。


四、深入 iptables:Docker 如何实现端口映射与防火墙? 🔒

前文提到 -p 8081:8081 将宿主机端口映射到容器端口。这背后是 iptables 的 DNAT(目标地址转换)规则在工作。

执行以下命令查看关键规则:

# 查看 nat 表中的 DOCKER 链(Docker 创建的专用链)
sudo iptables -t nat -L DOCKER -n

# 典型输出(简化):
# Chain DOCKER (2 references)
# target prot opt source destination
# DNAT tcp — 0.0.0.0/0 0.0.0.0/0 tcp dpt:8081 to:172.20.0.3:8081

这条规则含义是:所有发往宿主机任意 IP 的 8081 端口的 TCP 包,目标地址被重写为 172.20.0.3:8081(即 order-service 容器 IP)。

再看 filter 表的转发规则:

sudo iptables -L FORWARD -n | grep '172.20.0.3'
# 输出可能包含:
# ACCEPT all — 0.0.0.0/0 172.20.0.3 ctstate RELATED,ESTABLISHED
# ACCEPT all — 172.20.0.3 0.0.0.0/0

这确保了:

  • 容器 172.20.0.3 的入向连接被允许(第二行);
  • 容器 172.20.0.3 的出向响应包被允许(第一行,RELATED,ESTABLISHED)。

💡 安全提示:Docker 默认设置 FORWARD 链策略为 ACCEPT,这在生产环境存在风险。建议在宿主机防火墙(如 ufw)中显式限制 FORWARD 流量,例如:

# 仅允许 my-network 子网内的容器间通信
sudo ufw allow from 172.20.0.0/16 to any port 8080,8081 proto tcp


五、host 模式 vs bridge 模式:何时选择? 🤔

Docker 提供多种网络模式,bridge 是默认,但 host 模式在特定场景下不可替代。

特性bridge 模式host 模式
网络命名空间 ✅ 独立(隔离) ❌ 共享宿主机
端口映射 ✅ 支持 -p ❌ 不支持(直接使用宿主机端口)
性能 ⚡ 中等(经 veth + netfilter) 🚀 最高(零拷贝,无 NAT 开销)
DNS 解析 ✅ 容器名自动解析 ❌ 无内置 DNS,需用 –add-host
适用场景 通用 Web 服务、微服务通信 高吞吐监控代理(如 Prometheus Node Exporter)、性能敏感中间件

Java 示例:在 host 模式下运行一个暴露 JMX 的 Spring Boot 应用

假设你需要从宿主机 jconsole 连接容器内 JVM 的 JMX 端口(通常 9010):

# 启动容器(host 模式)
docker run -d \\
–name jmx-app \\
–network host \\
–env JAVA_OPTS="-Dcom.sun.management.jmxremote \\
-Dcom.sun.management.jmxremote.port=9010 \\
-Dcom.sun.management.jmxremote.rmi.port=9010 \\
-Dcom.sun.management.jmxremote.authenticate=false \\
-Dcom.sun.management.jmxremote.ssl=false \\
-Djava.rmi.server.hostname=localhost"
\\
-p 9010:9010 \\
your-jmx-app

⚠️ 注意:–network host 时 -p 参数无效(端口已天然暴露),-p 9010:9010 此处仅为示意,实际应删除。正确做法是确保 JVM 的 hostname 设置为 localhost,这样宿主机 jconsole 可直连 localhost:9010。


六、none 模式与 container 模式:极简与复用哲学 🧘‍♂️

除了 bridge 和 host,Docker 还提供两种“非典型”模式:

6.1 –network none:彻底隔离的“哑容器”

容器只有 lo 回环接口,无任何外部网络能力。适用于:

  • 运行纯计算任务(如批处理、加密);
  • 安全沙箱(禁止任何外联);
  • 作为其他容器的“网络模板”。

docker run –rm -it –network none alpine ip addr
# 输出仅含 lo 接口

6.2 –network container:<name>:网络复用(Network Reuse)

新容器完全共享指定容器的网络命名空间(包括 IP、端口、socket、iptables)。适用于:

  • Sidecar 模式(如日志收集器与主应用共用网络);
  • 调试工具容器(如 nicolaka/netshoot 进入目标容器网络)。

# 启动主容器
docker run -d –name main-app -p 8080:8080 nginx

# 启动调试容器,复用 main-app 网络
docker run -it –rm –network container:main-app nicolaka/netshoot \\
ss -tuln # 查看 main-app 的所有监听端口

此模式下,ss -tuln 输出与在 main-app 容器内执行结果完全一致,因为它们共享同一个网络栈。


七、Docker 网络排障黄金法则 🔍

遇到“容器 ping 不通”、“服务调用超时”,按此顺序排查:

✅ Step 1:确认容器是否在运行且网络就绪

docker ps -a | grep your-container
docker inspect your-container | grep -A 10 "NetworkSettings"
# 检查 "IPAddress" 是否非空,"Gateway" 是否正确

✅ Step 2:进入容器,检查自身网络栈

docker exec -it your-container sh
ip addr show eth0 # 确认 IP 分配
ping -c 3 172.20.0.1 # ping 网关(docker0 或自定义网关)
ping -c 3 google.com # 测试外网连通性(需 DNS)
nslookup your-other-service # 测试 DNS 解析
curl -v http://other:8080/health # 测试目标服务可达性

✅ Step 3:检查宿主机 iptables 与路由

# 查看 NAT 规则是否生效
sudo iptables -t nat -L DOCKER -n | grep your-port

# 查看 FORWARD 链是否放行
sudo iptables -L FORWARD -n | grep your-container-ip

# 查看路由表(确认子网可达)
ip route | grep 172.20.0.0

✅ Step 4:检查 Docker daemon 日志(终极线索)

# Linux
sudo journalctl -u docker.service -n 100 –no-pager

# macOS/Windows(Docker Desktop)
# 查看 Docker Desktop 应用内 “Troubleshoot” → “View logs”


八、进阶思考:Docker 网络与云原生生态的衔接 🌍

Docker 的 bridge 模式是学习网络的绝佳起点,但它止步于单机。当业务增长,你将自然走向:

  • Kubernetes CNI(Container Network Interface):如 Calico、Cilium,提供跨节点 Pod 网络、NetworkPolicy 等企业级能力;
  • 服务网格(Service Mesh):如 Istio、Linkerd,在应用层抽象流量管理、mTLS、可观测性;
  • eBPF 加速:Cilium 等利用 eBPF 替代 iptables,实现零延迟、高精度网络策略。

但请牢记:所有这些高级能力,都建立在对基础网络(Namespace、veth、bridge、iptables)深刻理解之上。没有扎实的地基,云原生大厦只是空中楼阁 🏗️➡️☁️。

正如 CNCF(Cloud Native Computing Foundation)官网 所倡导:“云原生技术使组织能够在现代动态环境(如公有云、私有云和混合云)中构建和运行可扩展的应用程序。”而网络,正是这一动态环境的血脉。


九、总结:一张图掌握 Docker 网络核心 🧭

最后,用一张 Mermaid 图表整合全文核心组件与数据流向,巩固认知:

#mermaid-svg-JDpzEF7kd00LYj4i{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JDpzEF7kd00LYj4i .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JDpzEF7kd00LYj4i .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JDpzEF7kd00LYj4i .error-icon{fill:#552222;}#mermaid-svg-JDpzEF7kd00LYj4i .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JDpzEF7kd00LYj4i .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JDpzEF7kd00LYj4i .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JDpzEF7kd00LYj4i .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JDpzEF7kd00LYj4i .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JDpzEF7kd00LYj4i .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JDpzEF7kd00LYj4i .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JDpzEF7kd00LYj4i .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JDpzEF7kd00LYj4i .marker.cross{stroke:#333333;}#mermaid-svg-JDpzEF7kd00LYj4i svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JDpzEF7kd00LYj4i p{margin:0;}#mermaid-svg-JDpzEF7kd00LYj4i .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-JDpzEF7kd00LYj4i .cluster-label text{fill:#333;}#mermaid-svg-JDpzEF7kd00LYj4i .cluster-label span{color:#333;}#mermaid-svg-JDpzEF7kd00LYj4i .cluster-label span p{background-color:transparent;}#mermaid-svg-JDpzEF7kd00LYj4i .label text,#mermaid-svg-JDpzEF7kd00LYj4i span{fill:#333;color:#333;}#mermaid-svg-JDpzEF7kd00LYj4i .node rect,#mermaid-svg-JDpzEF7kd00LYj4i .node circle,#mermaid-svg-JDpzEF7kd00LYj4i .node ellipse,#mermaid-svg-JDpzEF7kd00LYj4i .node polygon,#mermaid-svg-JDpzEF7kd00LYj4i .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JDpzEF7kd00LYj4i .rough-node .label text,#mermaid-svg-JDpzEF7kd00LYj4i .node .label text,#mermaid-svg-JDpzEF7kd00LYj4i .image-shape .label,#mermaid-svg-JDpzEF7kd00LYj4i .icon-shape .label{text-anchor:middle;}#mermaid-svg-JDpzEF7kd00LYj4i .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-JDpzEF7kd00LYj4i .rough-node .label,#mermaid-svg-JDpzEF7kd00LYj4i .node .label,#mermaid-svg-JDpzEF7kd00LYj4i .image-shape .label,#mermaid-svg-JDpzEF7kd00LYj4i .icon-shape .label{text-align:center;}#mermaid-svg-JDpzEF7kd00LYj4i .node.clickable{cursor:pointer;}#mermaid-svg-JDpzEF7kd00LYj4i .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-JDpzEF7kd00LYj4i .arrowheadPath{fill:#333333;}#mermaid-svg-JDpzEF7kd00LYj4i .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JDpzEF7kd00LYj4i .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JDpzEF7kd00LYj4i .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JDpzEF7kd00LYj4i .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JDpzEF7kd00LYj4i .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JDpzEF7kd00LYj4i .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-JDpzEF7kd00LYj4i .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JDpzEF7kd00LYj4i .cluster text{fill:#333;}#mermaid-svg-JDpzEF7kd00LYj4i .cluster span{color:#333;}#mermaid-svg-JDpzEF7kd00LYj4i div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-JDpzEF7kd00LYj4i .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JDpzEF7kd00LYj4i rect.text{fill:none;stroke-width:0;}#mermaid-svg-JDpzEF7kd00LYj4i .icon-shape,#mermaid-svg-JDpzEF7kd00LYj4i .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JDpzEF7kd00LYj4i .icon-shape p,#mermaid-svg-JDpzEF7kd00LYj4i .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-JDpzEF7kd00LYj4i .icon-shape .label rect,#mermaid-svg-JDpzEF7kd00LYj4i .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JDpzEF7kd00LYj4i .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-JDpzEF7kd00LYj4i .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-JDpzEF7kd00LYj4i :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

Container2_Namespace

Container1_Namespace

Host_Namespace

TCP/IP

ARP + L2

DNAT

SNAT

to internet

宿主机进程e.g. browser, curl

宿主机网卡eth0

docker0 bridge172.17.0.1/16

iptables nat/forward

容器1 vethA

容器2 vethB

容器1 eth0172.17.0.2/16

容器1 应用e.g. nginx:80

容器2 eth0172.17.0.3/16

容器2 应用e.g. java:8080

这张图清晰展示了:

  • 三层命名空间隔离(Host / Container1 / Container2);
  • veth 对是跨命名空间的唯一通道;
  • docker0 是 bridge 模式的中心枢纽;
  • iptables 是流量调度与转换的总控开关;
  • 容器间通信走 L2(无需 NAT),高效直接。

十、结语:网络不是黑盒,而是可触摸的乐高积木 🧱

Docker 网络常被初学者视为晦涩的黑盒,但本文试图证明:它不过是由 Linux 内核提供的几个稳定、成熟、文档完备的原语(Namespace、veth、bridge、iptables)拼装而成的乐高模型 🧱。每一个 docker run 命令,都在后台精确地创建、连接、配置这些积木。

当你下次看到 docker network inspect my-network 的 JSON 输出,不再只关注 "IPAM" 字段,而是能脑中浮现 docker0 的 MAC 地址表、veth 的 peer 关系、iptables 的跳转链——那一刻,你就真正“拥有”了容器网络。

继续深入?推荐阅读:

  • Linux Networking Documentation(权威内核网络文档)
  • Docker Networking Official Guide(Docker 官方网络指南)
  • The Illustrated TLS Connection(虽非 Docker 专属,但对理解 HTTPS + 容器网络结合极有启发)

愿你在云原生的海洋中,始终手握网络罗盘,乘风破浪,稳舵前行 🌊⛵。


🙌 感谢你读到这里! 🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。 💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友! 💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿 🔔 关注我,不错过下一篇干货!我们下期再见!✨

赞(0)
未经允许不得转载:171主机测评 » Docker - 基础环境下的容器网络初识
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址