坑边闲话:在上文中,笔者介绍了 Docker 的一些常识和基本命令,并对新手朋友做了友好的介绍。容器作为云计算的底层技术,在存储、网络、计算方面都必须足够完备。上文仅介绍了容器镜像的组成以及如何使用容器进行服务、计算。本文将以 Docker 作为案例,对容器的网络模型进行细致分析。

1. Docker 简介·

如果我们把容器想象成一个虚拟机,那么它本身将有一套完整的网络协议栈和网络硬件。换言之,容器有自己的网卡、IP 地址、传输端口。然而真实的容器并不是虚拟机的替代品。

容器常识

目前容器技术有两个方向:

  1. 进程级容器,比如 Docker. Docker 于 2013 年 3 月首次发布,推动了容器技术的标准化发展,并最终促成了 OCI(Open Container Initiative)标准的建立。目前,Docker 及其兼容实现已成为进程级容器的主流。
  2. 系统级容器,比如 LXC. LXC(Linux Containers)诞生于 2008 年 6 月,早于 Docker. 该技术由 IBM 最初开发,旨在作为虚拟机的一种轻量级替代方案。它更强调运行一个完整的 Linux 系统环境,因此也被称为 “Linux 沙盒”。

需要注意的是,LXC 与 Docker 属于两种完全不同的容器技术路线,它们所使用的镜像格式并不兼容。

2. Docker 网络基础·

介绍内容:

Docker 容器网络和宿主机网络的关系

网络命名空间(network namespace)

虚拟网桥(bridge)、veth pair 等基本组件。

3. Bridge 网络模式与通信机制·

本节仅介绍 Linux 的网络模型。

图 .

bridge 是一种特殊的 Linux 网络设备,它可以不关联任何物理网卡。上图展示了笔者专门用来跑容器服务的设备上的 bridge device, 可以看到此类设备有如下特征:

  1. 可以不关联到物理设备而独立工作。比如,KVM 虚拟机和容器实例可以把网络连接到宿主机的某个 bridge 设备上,此时 bridge 设备充当交换机的角色。
  2. 然而,不同于现实中的交换机,bridge 设备也可以有 IP 地址。现实生活中的交换机是二层设备,IP 地址是三层概念。

综上所述,笔者建议把 bridge 设备理解为一个虚拟交换机加一个虚拟网卡的组合。bridge 设备的 IP 地址即这个组合里的虚拟网卡的 IP 地址。

图 .

4. Docker 默认的五种网络模式·

通过 --network 参数可以指定容器的网络模型。

为什么需要网络模型

传统网络以网卡、交换机、路由器为核心器件,因此后来的虚拟机网络模型、容器网络模型均参照传统网络模型进行构建。笔者认为,这样做的原因主要有以下两个:

  1. 兼容性:容器技术的初衷是为了简化应用部署和管理,因此需要与现有的网络架构兼容。通过模拟传统网络模型,容器可以更容易地集成到现有的 IT 基础设施中。
  2. 灵活性:容器网络模型提供了多种网络模式(如 bridgehostoverlay 等),使得用户可以根据不同的应用场景选择最合适的网络配置。这种灵活性使得容器能够适应各种复杂的网络需求。

4.1 bridge 网络模式·

bridge 网络模式是 Docker 的默认网络模式。它通过创建一个虚拟网桥(bridge)来连接容器和宿主机网络。

5. 自定义网络与 DNS 服务·

6. 实用工具与调试技巧·

6.1 常用命令·

1
2
3
docker network ls
docker inspect
docker exec CONTAINER_ID ip a

6.2 抓包工具·

tcpdump、wireshark 调试容器网络

6.3 nsenter 命令·

如前文所述,在 Linux 系统中,命名空间 Namespace 是一种内核级的隔离机制,用于将特定的系统资源分组隔离到不同的环境中。

  • 命名空间是进程级的隔离单位。
  • 每个进程必然属于一个命名空间,多个进程可以共享同一个命名空间。
  • 子进程默认继承父进程的命名空间,除非父进程在创建子进程时显式为其分配新的命名空间。Docker 在创建容器时会为其分配独立的命名空间。
  • 若两个进程属于不同的命名空间,则它们之间的资源,如进程树网络设备、挂载点等,是互相隔离的,彼此无法直接访问。

Linux 命名空间小贴士

目前 Linux 支持 7 种命名空间。

  1. PID 命名空间 pid
  • 隔离进程 ID(PID)和进程树。
  • 每个 PID 命名空间维护独立的进程 ID 空间,其中的进程 PID 从 1 开始计数。
  • 不同 PID 命名空间中的进程无法通过 PID 直接访问对方,实现进程树的隔离。
  1. UTS 命名空间 uts
  • 隔离主机名(Hostname)和域名(NIS 域名)
  • 不同 UTS 命名空间中的进程可以设置独立的主机名和域名,互不影响。
  • 典型应用:docker run --hostname <HOSTNAME>
  1. IPC 命名空间 ipc
  • 隔离进程间通信(IPC)资源,包括消息队列共享内存信号量
  • 每个 IPC 命名空间拥有独立的 IPC 资源,不同命名空间中的进程无法通过 IPC 机制直接通信,避免资源冲突。
  • 容器通过 IPC 命名空间隔离自身的 IPC 资源,确保与宿主机及其他容器的 IPC 资源互不干扰。
  1. MOUNT 命名空间 mnt or mount
  • 隔离文件系统挂载点。
  • 每个 MOUNT 命名空间维护独立的挂载点视图,进程只能看到当前命名空间内的挂载配置。在一个命名空间中挂载新的文件系统,不会影响其他命名空间。
  • 容器通过 MOUNT 命名空间挂载独立的根文件系统,与宿主机的文件系统隔离。可以使用 chroot 命令构建独立的文件系统环境。
  1. NET 命名空间 net
  • 隔离网络栈(Network Stack),包括网络设备、IP 地址、端口、路由表、防火墙规则等。
  • 每个 NET 命名空间拥有独立的网络环境,进程只能访问当前命名空间内的网络资源。
  • 不同命名空间中的进程无法直接通过网络通信,需通过宿主机或其他方式转发。
  • 容器通过 NET 命名空间拥有独立的 IP 地址和网络接口,如 Docker 的 bridge 网络模式。Linux 系统中创建虚拟网络设备(如 veth pair)时,常将其分配到不同的 NET 命名空间以实现网络隔离。
  1. USER 命名空间 user
  • 隔离用户和组 ID(UID/GID)、权限(Capabilities)、sudo 规则等。
  • 每个 USER 命名空间可以映射宿主机的 UID/GID 到内部的虚拟 UID/GID,实现 “用户权限隔离”。
  1. CGROUP 命名空间 cgroup
  • 隔离控制组(cgroup)子系统的层次结构。
  • 传统 cgroup 资源(如 CPU、内存限制)对所有进程可见,而 CGROUP 命名空间允许不同命名空间中的进程看到独立的 cgroup 层次结构。
  • 目前主要用于容器运行时(如 containerd)的高级资源隔离场景。

很多容器镜像如 Alpine, Scratch 或精简 BusyBox 镜像)默认没有安装 iproute2, ss, netstat 等网络调试工具。此时使用 nsenter 从宿主机进入容器的 网络命名空间,是最干净高效的调试方式之一。

nsenter 进入容器网络的 namespace

1
docker inspect --format '{{.State.Pid}}' <container_name_or_id>

to be continued.