容器网络
网络设备
网桥Bridge:
网桥Bridge:一种网络设备,用于连接多个网络接口。网桥工作在数据链路层,基于MAC地址进行转发,实现二层交换功能,类似于物理交换机。
网桥设备在操作系统中表现为一个虚拟网络接口(如docker0),但它的主要功能是进行二层转发,而不是直接发送和接收数据包。
docker0 :安装了Docker并启动Docker服务后,宿主机上会默认创建名为docker0的虚拟网桥,连接宿主机的网络接口和容器的虚拟网络接口,可以为每个连接到桥接网络的容器分配一个唯一的IP地址。
cni0: K8s(基于containerd/CRI-O运行时)使用Flannel网络插件,并配置Flannel的网络接口模式为bridge模式,则所有节点都会默认创建cni0网桥,Pod通过cni0连接。
网卡NIC(Network Interface Card):
网卡(又称网络接口):计算机与网络之间的接口设备,负责数据的发送和接收,可以是硬件(例如物理网卡)或 软件(例如虚拟网卡)。
- 物理网卡: eth0、enp1s0等等
- 虚拟网卡:
- docker0: Docker默认的虚拟网桥
- cni0: Flannel默认的虚拟网桥
- vethXXXX: 虚拟以太网接口对,用于连接容器和宿主机。
- caliXXXX: Calico 创建的虚拟接口,用于连接 Pod。
- tunl0:IPIP 隧道接口,用于跨节点通信。
物理网卡
1. 传统网卡命名规则
在早期的 Linux 系统中,网卡通常按照加载顺序命名:
- eth0:第一块以太网卡。
- eth1:第二块以太网卡。
- wlan0:第一块无线网卡。
这种命名方式简单直观,但存在以下问题:
- 网卡名称可能因硬件变化(如插拔网卡)而改变。
- 无法通过名称直接识别网卡的物理位置或类型。
2. 一致性网络设备命名规则
为了解决传统命名方式的问题,Linux 引入了 一致性网络设备命名(Consistent Network Device Naming) 规则。新的命名规则基于网卡的硬件信息,确保名称在系统重启或硬件变化时保持一致。
命名格式:
en<类型><位置><编号>
- en: 表示以太网(Ethernet)
- <类型>:表示网卡的类型或位置来源,常见的有:
- p:PCI 总线
- s:热插拔插槽
- o:主板集成设备
- <位置>:表示网卡在总线或插槽中的位置(如 1、2)
- <编号>: 表示网卡的编号(如 0、1)
eg:
enp1s0: 以太网PCI总线1插槽0
ens0: 以太网热插拔插槽0
eno1: 以太网主板集成的第一个网卡
查看网卡信息
ip link show
确定哪个接口是对外网络接口(连接到外部网络(如互联网)的物理网卡)
# 查看默认网关,默认网关通常配置在对外网络接口上
ip route show default
虚拟网卡
veth对(Virtual Ethernet Pair):
成对出现的虚拟以太网接口,用于连接不同网络命名空间(如容器和宿主机)
- vethXXXX:
- caliXXXX: Calico的veth接口对
隧道接口(Tunnel Interface):
隧道接口通过封装协议将原始数据包嵌套在另一个协议中,实现在现有网络(底层网络)基础设施上构建虚拟网络,常用于构建Overlay网络或VPN,其核心是封装和解封装
封装协议
VXLAN
VXLAN(Virtual Extensible LAN),虚拟可扩展的局域网,将原始的二层以太网帧封装在IP/UDP报文中,通过三层的路由能力,跨越物理网络的地理限制,实现虚拟的二层互通。
- VTEP(XLAN Tunnel Endpoints): VXLAN中的关键组件,负责封装、解封装、转发
- Flannel的VXLAN接口(VTEP):flannel.1
- Calico的VXLAN接口(VTEP):vxlan.calico
- VNI(VXLAN Network Identifier): 每个 vxlan 的标识,是个 24 位整数,一共有 2^24 = 16,777,216(一千多万),一般每个 VNI 对应一个租户,也就是说使用 vxlan 搭建的公有云可以理论上可以支撑千万级别的租户。
- 封装:
- 得到一个原始数据包
- VXLAN封装:VXLAN接口将原始帧封装为 VXLAN 报文
- VXLAN头:vxlan 协议相关的部分,一共 8 个字节
- VXLAN flags:标志位
- Reserved:保留位
- VNID:24 位的 VNI 字段,这也是 vxlan 能支持千万租户的地方
- Reserved:保留字段
- UDP头:8 个字节,UDP 应用通信双方是 vtep 应用,其中目的端口就是接收方 vtep 使用的端口,IANA 分配的端口是 4789
- IP 头:20 字节,主机之间通信的地址,可能是主机的网卡 IP 地址,也可能是多播 IP 地址
- MAC头:14字节,主机之间通信的 MAC 地址,源 MAC 地址为主机 MAC 地址,目的 MAC 地址为下一跳设备的 MAC 地址
- VXLAN头:vxlan 协议相关的部分,一共 8 个字节
- 得到一个原始数据包
- 转发:
- 多播:第一次会进行多播来记录目的主机的MAC地址,后续都会是单播。但还是比较浪费
- 分布式控制中心:提前将目的主机的MAC地址和vtep IP地址保存在分布式控制中心。在每个vtep所在的节点上会有一个agent与控制中心通信去获取信息。
- 解封装:目的主机上的vtep收到vxlan报文后拆除外部头部,得到原始数据包和VNI,根据VNI将原始数据包转发至对应的网桥,由该网桥进行二层转发到达目标容器。
2. IP-in-IP
- 协议:IP-in-IP,工作在网络层
- 接口示例:
- Calico的IPIP接口:tunl0
- 封装原理:
- 将原始 IP 包嵌套在另一个 IP 头中,外层 IP 头指向目标节点
- 无额外传输层头(如 UDP),仅依赖 IP 协议号 4
- 特点:
- 封装开销小(仅增加 20 字节 IP 头)
- 不支持跨 NAT 网络(需确保外层 IP 可达)
网络模式
Docker
1. 桥接模式Bridge(默认)
- 模式类型:单主机网络
- 实现原理:
- 同节点容器通信:当两个容器连接同一个桥接网络,他们可以通过IP地址直接通信
- 同节点容器通信:当两个容器连接同一个桥接网络,他们可以通过IP地址直接通信
- 容器访问外部网络:容器内的应用程序发起请求,请求通过容器的eth0接口发送到veth pair,veth pair将请求转发到网桥(默认docker0),网桥通过NAT技术将请求转发到宿主机的网络接口,最后请求被发送到外部网络。外部网络的响应会通过反NAT技术找到容器ip返回。
NAT(Network Address Translation) 网络地址转换,主要用于私有网络和公共网络之间进行地址转换。这里容器访问外部网络,我们使用NAT技术将容器的私有IP转换为宿主机的公网IP,使容器能够通过宿主机的唯一IP访问外部网络。
- SNAT(Source NAT): 修改数据包的源IP,用于容器主动访问外部网络
- DNAT(Destination NAT): 修改数据包的目的IP,用于将外部请求转发到容器
- 外部网络访问容器:启动容器时指定端口映射,利用-p参数将宿主机的端口与容器的端口绑定。外部网络通过宿主机的ip地址和端口访问服务,宿主机将请求转发到容器对应的端口。
2. 主机模式Host
- 模式类型:共享宿主机网络栈
- 实现原理:容器不会虚拟出自己的网卡,配置主机的IP等,而是使用宿主机的IP和端口
3. 无网络模式None
- 模式类型:无网络
- 实现原理:容器仅有 lo 回环接口,无外部网络连接。
4. Container模式
- 模式类型:共享其他容器的网络栈
- 实现原理:新容器共享指定容器的网络命名空间,两者共享 IP、端口等配置。
5. 覆盖模式Overlay
- 模式类型:跨主机网络(Docker Swarm集群中)
- 实现原理:Docker自身原生支持VXLAN封装
6. Macvlan/IPvlan模式
- 模式类型:直接映射到物理网络
- 实现原理:
- Macvlan: 为容器分配独立MAC地址,直接绑定物理接口
- IPvlan: 多个容器共享物理接口MAC,但分配不同IP
Kubernetes(基于containerd运行时)
Kubernetes本身不直接提供Overlay网络,而是通过CNI(Container Network Interface)插件实现,网络模式取决于所选插件(Flannel,Calico等)。
Flannel
- VXLAN模式:在内核层面实现VXLAN协议的处理,内核态封装容器流量
- 路由模式(host-gw模式): 直接修改宿主机的路由表实现跨主机通信,无需封装流量
- UDP模式:
核心思想:用户态封装容器流量
flanneld启动后会通过打开/dev/net/tun的方式创建tun设备,名称为flannel0,该设备是用户空间与内核空间的数据包交互的通道。然后再将从tun设备获取的IP数据包封装到UDP数据包中通过物理网卡发送到其他节点中,内核通过UDP端口转发给flanneld然后解包,得到其中的IP数据包,然后再通过tun设备进入内核空间中,通过路由到达网桥,然后再到目的容器中。-
- 数据发送端:
- Pod发送的原始IP包到达宿主机网络栈。
- Flannel守护进程(flanneld)在用户态捕获该包。
- flanneld将原始IP包封装在UDP报文中:
- 外层IP头:源IP为宿主机IP,目的IP为目标宿主机IP。
- UDP头:源端口随机,目的端口固定为8285(Flannel默认端口)。
- Payload:原始容器IP包。
- 封装后的UDP报文通过物理网络发送到目标宿主机。
-
- 数据接收端:
- 目标宿主机的flanneld监听UDP端口8285,接收到报文后解封装。
- 提取原始容器IP包,转发到本机目标Pod的网络接口(如cni0网桥)。
-
Calico
- BGP模式:
Calico 的 BGP 模式 是一种非封装网络方案,通过 BGP 协议在集群节点间同步路由信息,实现 Pod 流量的直接路由。- Calico 在每个节点部署 calico-node 作为 BGP Speaker
- 将本节点 Pod 的 IP 子网通过 BGP 协议宣告给其他节点。
- 学习其他节点宣告的路由
- 路由表更新:节点间通过 BGP 交换路由信息后,物理网络直接根据路由表转发流量(无需封装)
- 无封装通信:Pod A(节点1)→ 节点1物理网卡 → 物理网络 → 节点2物理网卡 → Pod B(节点2)
- Calico BGP 模式的配置方式
- 全互联模式(Full Mesh):所有节点之间两两建立 BGP 连接,适合小规模集群(节点数 <100)。
- 路由反射器模式(Route Reflector, RR):大规模集群(节点数 >100),减少 BGP 连接数(从 O(N²) 降为 O(N))。
- Calico 在每个节点部署 calico-node 作为 BGP Speaker
- IPIP模式
Cilium
- 基于eBPF的高性能网络
Weave
常见网络命令
# 查看网络
docker network ls
# 查看网络(带完整的网络id)
docker network ls --no-trunc
# 筛选查看
docker network ls --filter driver=bridge
docker network ls --filter id=<网络id>
# 创建自定义网络,默认模式为bridge
docker network create <network-name>
# 指定网络模式 并自定义子网和默认网关
docker network create --driver <bridge/host/..> --subnet <eg:192.168.0.0/24> --gateway <eg:192.168.0.1> <network-name>
# 创建overlay网络
docker network create -d overlay --subnet=192.168.100.0/24 --gateway=192.168.100.1 --attachable my-overlay
-–attachable:允许集群服务间的容器交互连接或者独立的容器之间能够连接。swarm在设计之初是为了service(一组container)而服务的,因此通过swarm创建的overlay网络在一开始并不支持单独的container加入其中。但是在docker1.13, 我们可以通过“–attach” 参数声明当前创建的overlay网络可以被container直接加入。
# 删除网络
docker network rm <network-name>
# 移除所有未使用的网络
docker network prune
# 查看网络详情
docker network inspect <network-name>
# 将正在运行的容器连接到网络
docker network connect <network-name> <container-name>
# 将容器的网络断开
docker network disconnect <network-name> <container-name>
# 创建时指定容器网络
docker run -d -P --name=<container-name> --network=<network-name> <image-name>
-P --publish-all简写,让Docker把容器内所有暴露的端口随机映射到宿主机的端口上
# 指定容器的ip地址
docker network connect --ip <ip> <network-name> <container-name>
# 创建一对veth pair(veth0,veth1)
ip link add name veth0 type veth peer name veth1
# 创建网络命名空间
ip netns add <name>
# 删除命名空间
ip netns delete <name>
# 查看ip接口
ip link show
ip link show | grep veth
# 查看所有网络命名空间
ip netns list
# 给网络接口配置IP
ip addr add 192.168.1.1/24 dev veth0
# 添加路由记录,让访问172.16.3.96的网络请求直接通过veth0转发
ip route add 172.16.3.96 dev veth0 scope link
# 启用接口
ip link set veth0 up
# 将接口绑定网络命名空间
ip link set veth0 netns ns1
# 在特定的网络命名空间中执行命令
ip netns exec ns1 bash
# 离开网络命名空间
exit