网络模型深度解析:CNI、Pod通信与NetworkPolicy

目录

专栏介绍

作者与平台

您将学到什么?

学习特色

网络模型深度解析:CNI、Pod通信与NetworkPolicy

第一部分:CNI 插件原理 - 网络基础设施的构建者

1.1 CNI 规范:标准化网络接入的基石

1.2 Flannel:简单高效的覆盖网络方案

1.3 Calico:高性能、安全、可扩展的网络与策略方案

1.4 Flannel vs Calico:深度对比

第二部分:Pod 通信与跨节点路由 - 数据包的旅程

2.1 同节点 Pod 通信 (Intra-Node Communication)

2.2 跨节点 Pod 通信 (Inter-Node Communication) - Flannel (VXLAN)

2.3 跨节点 Pod 通信 (Inter-Node Communication) - Calico (纯 BGP)

2.4 跨节点路由总结与对比

第三部分:网络策略 (NetworkPolicy) - 微服务安全的守护者

3.1 NetworkPolicy 核心概念与模型

3.2 NetworkPolicy 实现原理 - 以 Calico 为例

3.3 NetworkPolicy 最佳实践与常见场景

总结


专栏介绍

作者与平台

作者:庸子

用户ID:2401_86478612

第一发表平台:CSDN

欢迎来到《Kubernetes架构师之路:系统化学习与实践》专栏!在这个容器化技术主导的时代,Kubernetes已成为云原生应用编排的事实标准,掌握Kubernetes已成为每位开发者和运维工程师的必备技能。

本专栏采用系统化学习方法,从基础概念到高级实践,循序渐进地带您全面掌握Kubernetes技术栈。无论您是刚接触容器技术的初学者,还是希望深入理解Kubernetes架构的资深工程师,这里都将为您提供清晰的学习路径和实用的实战指导。

您将学到什么?

  • 基础理论:深入理解容器、容器编排、Kubernetes核心概念和架构设计
  • 核心组件:掌握etcd、API Server、Controller Manager、Scheduler等核心组件的工作原理
  • 资源管理:学会Pod、Deployment、Service、Ingress等核心资源的创建与管理
  • 网络与存储:理解Kubernetes网络模型和存储方案,解决实际部署中的网络与存储问题
  • 高可用与扩展:构建高可用的Kubernetes集群,实现应用的自动扩展与故障恢复
  • 监控与日志:掌握集群监控、日志收集与应用性能优化方法
  • CI/CD集成:学习如何将Kubernetes与CI/CD流程结合,实现自动化部署
  • 安全实践:了解Kubernetes安全模型,掌握RBAC、网络策略等安全配置

学习特色

  1. 系统化知识体系:从零开始,构建完整的Kubernetes知识框架
  2. 实战导向:每个知识点都配有实际操作案例,让您"学以致用"
  3. 问题驱动:针对实际部署中常见的问题提供解决方案
  4. 最新版本覆盖:基于最新稳定版Kubernetes,紧跟技术发展趋势
  5. 架构师视角:不仅教您"如何做",更解释"为什么这样设计"

无论您是想提升个人竞争力,还是为企业构建高效的云原生基础设施,本专栏都将助您成为真正的Kubernetes专家。让我们一起开启这段激动人心的Kubernetes学习之旅!

立即订阅,开启您的Kubernetes架构师成长之路!

Kubernetes 的网络模型是其核心优势之一,它提供了一个扁平、无 NAT、Pod 间可直接通信的环境。然而,实现这一看似简单的模型背后,却隐藏着复杂的架构和精心设计的组件。本文将深入剖析 Kubernetes 网络的三大支柱:CNI 插件原理(聚焦 Flannel 与 Calico 的对比)、Pod 通信机制(特别是跨节点路由)以及 NetworkPolicy 的实现,力求在 15000 字的篇幅内,揭示其深度技术细节、设计哲学与工程实践。

第一部分:CNI 插件原理 - 网络基础设施的构建者

1.1 CNI 规范:标准化网络接入的基石

CNI (Container Network Interface) 并非 Kubernetes 独创,而是一个由 CNCF (Cloud Native Computing Foundation) 托管的开放标准。其核心目标是解耦容器运行时与网络实现,定义了一套通用的接口规范,让容器运行时(如 Docker, containerd, CRI-O)能够通过调用符合 CNI 标准的可执行文件(插件),为容器配置网络。

核心工作流程:

  1. 触发时机: 当 Kubelet 需要为 Pod 创建网络命名空间(netns)时(即 Pod ADDED 事件),或删除 Pod 网络命名空间时(Pod DELETED 事件)。
  2. 调用插件: Kubelet 根据配置(通常位于 /etc/cni/net.d/ 目录下的 .conf.conflist 文件)找到对应的 CNI 插件可执行文件(如 flannel, calico-ipam, calico)。
  3. 传递参数: Kubelet 通过环境变量和标准输入(stdin)向插件传递关键信息:
      • 环境变量:CNI_COMMAND: ADD (创建网络) 或 DEL (删除网络)。
      • CNI_CONTAINERID: 容器 ID。
      • CNI_NETNS: 容器网络命名空间的路径(如 /var/run/netns/<nsname>)。
      • CNI_IFNAME: 容器内要创建的虚拟网卡名称(默认 eth0)。
      • CNI_ARGS: 额外的参数(如 K8S_POD_NAMESPACE, K8S_POD_NAME, K8S_POD_UID)。
    • 标准输入 (stdin): 一个 JSON 格式的网络配置对象,包含:
      • cniVersion: CNI 规范版本。
      • name: 网络名称。
      • type: 插件类型(如 flannel, calico, bridge)。
      • ipam: IP 地址管理配置(类型、子网、路由等)。
      • dns: DNS 配置(nameservers, domain, search)。
      • 插件特定的配置(如 Flannel 的 subnetFile, Calico 的 etcd_endpoints)。
      • 插件执行:ADD 操作:插件进入容器的 netns
      • 创建虚拟网卡对(veth pair):一端在容器内(eth0),另一端在宿主机上(如 veth<random>)。
      • 为容器内的 eth0 分配 IP 地址、设置路由(根据 IPAM 配置)。
      • 将宿主机端的 veth 设备连接到指定的网络接口(如网桥、OVS 交换机)或进行路由配置。
      • 可选:设置 ARP、iptables 规则等。
      • 通过标准输出(stdout)返回一个 JSON 结果,包含分配的 IP 地址、网关、DNS、路由等信息。
      • DEL 操作:插件根据传入的参数,找到并删除为该容器创建的网络资源:删除 veth pair、释放 IP 地址、清理相关的路由/iptables 规则等。
      • 无需返回结果。
  4. 结果处理: Kubelet 接收插件返回的结果(ADD 时),将其记录到 Pod 的状态中(status.podIP),或执行清理(DEL 时)。

CNI 插件生态:

  • 主插件 (Main Plugin): 负责创建网络接口、连接到网络(如 bridge, vlan, macvlan, ipvlan, ptp, host-local, flannel, calico, weave-net, cilium)。
  • IPAM 插件 (IP Address Management): 负责分配和管理 IP 地址(如 host-local(基于本地文件/目录)、dhcpstaticcalico-ipam(集成到 Calico 数据库))。
  • 元插件 (Meta Plugin): 调用其他 CNI 插件组合实现更复杂功能(如 flannel 实际上是一个元插件,它调用 bridgehost-local IPAM;multus 用于多网络)。
1.2 Flannel:简单高效的覆盖网络方案

设计哲学: Flannel 的核心目标是简单、易用、快速部署。它通过创建一个**覆盖网络(Overlay Network)**来解决跨节点 Pod 通信问题,将不同节点上的 Pod 置于一个逻辑上的大二层网络中。

核心组件与工作原理:

  1. etcd/Consul (后端存储): Flannel 使用 etcd 或 Consul 存储关键的网络配置信息,主要是:
    • 网络配置:整个集群的 Overlay 网络 CIDR(如 10.244.0.0/16)。
    • 子网分配:每个节点被分配一个唯一的子网(如 10.244.1.0/24, 10.244.2.0/24)。这个信息由 flanneld 守护进程启动时向 etcd 注册并获取。
  2. flanneld 守护进程: 在每个 Kubernetes 节点上运行,是 Flannel 的核心。
    • 启动: 读取配置(/etc/sysconfig/flanneld 或命令行参数),连接 etcd。
    • 子网租约: 向 etcd 申请并租用一个子网(Lease)。租约信息包含子网 CIDR 和该节点的公网 IP(或用于 Overlay 通信的 IP)。
    • 后端实现选择: Flannel 支持多种后端(Backend)数据封装/路由方式,通过 -backend 参数指定:
      • vxlan (默认推荐): 使用 VXLAN (Virtual eXtensible LAN) 隧道技术。
        • 原理: 在宿主机内核创建一个 VXLAN 隧道设备(flannel.1)。当需要发送跨节点 Pod 流量时:
          1. 源 Pod (10.244.1.2) 发送数据包给目标 Pod (10.244.2.3)。
          2. 数据包到达源节点宿主机网络栈。
          3. 源节点根据目标 IP (10.244.2.3) 查询 Flannel 维护的路由表(由 flanneld 根据 etcd 中的子网信息生成)。路由表指示目标子网 10.244.2.0/24 通过 flannel.1 设备可达。
          4. 内核 VXLAN 模块接管:
            • 将原始 Pod 数据包(源 IP: 10.244.1.2, 目标 IP: 10.244.2.3)作为 Payload。
            • 封装一个 UDP 包:
              • 源 IP:源节点宿主机 IP(如 192.168.1.101)。
              • 目标 IP:目标节点宿主机 IP(如 192.168.1.102)。
              • 源端口:随机端口。
              • 目标端口:8472 (VXLAN 默认端口)。
              • VXLAN Header:包含 VNI (VXLAN Network Identifier),用于标识不同的 VXLAN 网络(Flannel 通常使用 1)。
            • 封装一个 Ethernet II 帧(源/目标 MAC 为宿主机网卡 MAC)。
          5. 封装后的 UDP 包通过宿主机物理网卡发送到目标节点。
          6. 目标节点收到 UDP 包,内核 VXLAN 模块解封装,提取出原始 Pod 数据包。
          7. 目标节点根据目标 IP (10.244.2.3) 查询本地路由,将数据包转发到目标 Pod 所在的 veth pair,最终到达目标 Pod。
        • 优点: 对底层网络要求低(只需三层可达),支持大规模集群,性能相对较好(内核级封装)。
        • 缺点: 有封装开销(增加 50-54 字节头部),MTU 需调整(通常设置为 14001450),网络路径变长。
      • host-gw (主机网关): 利用节点作为网关。
        • 原理: flanneld 在每个节点上创建指向其他节点子网的路由条目。例如,在节点 A 上添加路由:ip route add 10.244.2.0/24 via 192.168.1.102 dev eth0
          1. 通信流程:源 Pod (10.244.1.2) 发送数据包给目标 Pod (10.244.2.3)。
          2. 数据包到达源节点宿主机。
          3. 源节点查询路由表,发现目标子网 10.244.2.0/24 的下一跳是目标节点 IP (192.168.1.102)。
          4. 源节点直接将原始 Pod 数据包(源 IP: 10.244.1.2, 目标 IP: 10.244.2.3)通过物理网卡发送给目标节点(目标 MAC 是目标节点网卡的 MAC)。
          5. 目标节点收到数据包,查询本地路由,将数据包转发到目标 Pod。
        • 优点: 性能最佳(无封装开销,直接路由),MTU 无需调整。
        • 缺点: 要求所有节点在同一个二层网络(L2)中,或者底层网络支持直接路由到 Pod IP(通常需要配置静态路由或动态路由协议如 BGP)。扩展性受限于二层网络规模或路由表大小。
      • udp (早期/兼容): 使用用户态的 TAP 设备和 UDP 封装。
        • 原理: flanneld 创建一个 TAP 设备(flannel0),所有 Pod 流量都经过这个设备。flanneld 在用户态读取数据包,封装成 UDP 包(源/目标端口 8285),再通过物理网卡发送。目标节点的 flanneld 接收 UDP 包,解封装后写入本地的 TAP 设备。
        • 优点: 兼容性好,对内核无特殊要求。
        • 缺点: 性能最差(用户态切换开销大),已基本被 vxlan 取代。
      • alloc (仅测试): 只分配子网,不创建网络设备。
      • ipip (较少用): 使用 IPIP 隧道(IP in IP),类似 VXLAN 但更简单(无 UDP 端口和 VNI),性能略好于 UDP 但差于 VXLAN,同样有 MTU 问题。
  3. CNI 集成: Flannel 提供了一个 CNI 插件(通常是 /opt/cni/bin/flannel)。这个插件本身不创建网络设备,它的主要作用是:
    • 读取 flanneld 写入的配置文件(通常是 /run/flannel/subnet.env),获取当前节点的子网信息(FLANNEL_SUBNET)、MTU(FLANNEL_MTU)和后端类型(FLANNEL_NETWORK)。
    • 根据后端类型,调用其他 CNI 插件来完成实际的网络配置:
      • 对于 vxlan/host-gw/udp/ipip:调用 bridge 插件(创建 cni0 网桥)和 host-local IPAM 插件(从 FLANNEL_SUBNET 中分配 Pod IP)。
      • veth 设备的一端插入 cni0 网桥。
      • 设置 Pod 内部的 IP 地址、路由(默认路由指向网桥 IP)。
      • 设置 FLANNEL_MTU 到 Pod 的 eth0 接口。
    • 返回配置结果给 Kubelet。

Flannel 架构总结:

  • 中心化存储 (etcd): 管理全局网络配置和子网分配。
  • 节点守护进程 (flanneld): 负责子网租约、后端网络配置(创建隧道设备/路由)、生成 CNI 所需的配置文件。
  • CNI 插件 (flannel): 轻量级适配器,调用 bridgehost-local 完成容器网络接入,利用 flanneld 提供的信息。
  • 后端 (vxlan/host-gw): 实现跨节点数据包的封装或路由。
1.3 Calico:高性能、安全、可扩展的网络与策略方案

设计哲学: Calico 的目标是提供高性能、原生三层路由、细粒度网络策略的解决方案。它摒弃了传统的覆盖网络,致力于利用标准的 BGP (Border Gateway Protocol) 和 Linux 内核网络功能,构建一个纯三层、可扩展、安全的 Pod 网络。

核心组件与工作原理:

  1. etcd/Kubernetes API (数据存储): Calico 可以使用独立的 etcd 集群,或者更常见地,直接使用 Kubernetes API 作为其数据存储后端(Datastore 模式)。存储的信息包括:
    • IP 池(IPPool):定义可分配给 Pod 的 IP 地址范围(如 192.168.0.0/16)。
    • 节点(Node):记录每个节点的信息,包括其 BGP AS 号、IP 地址、隧道 IP(如果启用 IPIP)。
    • 工作负载端点(WorkloadEndpoint):每个 Pod 的网络端点信息,包含其 IP、MAC、所属节点、命名空间、标签等。
    • 网络策略(NetworkPolicy):定义的访问控制规则。
    • BGP 对等体(BGP Peer):配置的 BGP 邻居信息。
  2. Felix (节点代理): 在每个 Kubernetes 节点上运行,是 Calico 的核心执行引擎。
      • 职责:接口管理: 创建和管理 Calico 使用的网络接口(如 tunl0 用于 IPIP)。
      • 路由编程: 根据从 Datastore 获取的信息(主要是其他节点的 Pod CIDR 和 BGP 路由信息),在 Linux 内核的路由表中动态添加/删除路由条目。这是 Calico 实现跨节点通信的关键。
      • ACL 编程: 将 NetworkPolicy 规则翻译成 Linux 内核的 iptables 或 nftables 规则,实现 Pod 间的访问控制。
      • IP 地址管理: 配置 Pod 网络接口的 IP 地址(通过调用 CNI 或直接操作)。
      • 状态报告: 向 Datastore 报告节点状态和健康信息。
  3. BIRD (BGP 守护进程): 在每个节点上运行(或作为 Felix 的子进程),是一个标准的 BGP 路由器。
      • 职责:建立 BGP 会话: 与集群内其他节点的 BIRD 建立 iBGP(内部 BGP)会话(通常使用同一个 AS 号)。
      • 路由分发: 将本节点上运行的 Pod 的 IP 地址(或整个 Pod CIDR,取决于配置)作为路由信息,通过 BGP 协议宣告(Advertise) 给集群内的其他节点。
      • 路由学习: 接收并学习其他节点宣告的 Pod IP/CIDR 路由。
      • 最佳路径选择: 根据 BGP 路径属性(如 AS_PATH)选择到达目标 Pod IP 的最佳下一跳。
      • 外部连接: 可配置与物理网络中的 BGP 路由器(如 ToR 交换机)建立 eBGP(外部 BGP)会话,将 Pod IP 路由宣告到物理网络,实现 Pod IP 的外部可达性(可选)。
  4. confd (配置生成器 - 可选/旧版): 监听 Datastore 中的变化(如 BGP Peer 配置),动态生成 BIRD 的配置文件(bird.cfg)并重载 BIRD。在新版本中,Felix 可能直接管理 BIRD 配置。
    • CNI 插件 (calico, calico-ipam):calico-ipam: Calico 的 IPAM 插件。它直接从 Datastore 中管理的 IPPool 里为 Pod 分配 IP 地址。支持多种分配模式(如顺序、随机)。
    • calico: Calico 的主 CNI 插件。当 Kubelet 调用它时:
      • 调用 calico-ipam 为 Pod 分配 IP。
      • 在 Pod 的 netns 中创建 veth pair。
      • 将宿主机端的 veth 设备连接到 Calico 的网络接口(通常是直接路由,不依赖网桥)。
      • 为 Pod 内的 eth0 设置 IP 地址、路由(默认路由指向 Felix 配置的网关,通常是节点 IP)。
      • 关键点: Calico 默认不创建网桥。Pod 的 veth 设备直接连接到节点的根网络命名空间。Felix 负责在节点上配置到所有其他 Pod IP 的直接路由。
      • 返回配置结果给 Kubelet。
  5. IPIP 模式: Calico 默认使用 纯三层路由(BGP)。但在某些场景下(如节点不在同一二层网络且无法配置物理路由),可以启用 IPIP (IP in IP) 隧道模式。
    • 原理: 类似 Flannel 的 ipip 后端。Felix 会在每个节点创建一个 tunl0 隧道设备。当需要发送跨节点 Pod 流量时:
      1. 源节点根据 Felix 配置的路由表,发现目标 Pod IP 需要通过 tunl0 发送。
      2. 内核 IPIP 模块将原始 Pod 数据包(源 IP: PodA_IP, 目标 IP: PodB_IP)封装在一个新的 IP 包中:
        • 新 IP 包源 IP:源节点 IP。
        • 新 IP 包目标 IP:目标节点 IP。
        • 协议号:4 (IPIP)。
      3. 封装后的 IP 包通过物理网络发送到目标节点。
      4. 目标节点内核 IPIP 模块解封装,提取原始 Pod 数据包。
      5. 目标节点根据目标 IP (PodB_IP) 查询本地路由(由 Felix 配置),将数据包发送到目标 Pod。
    • 启用方式: 在 IPPool 配置中设置 ipipMode: AlwaysCrossSubnet(仅在跨子网时使用 IPIP)。
    • 影响: 启用 IPIP 会引入封装开销(增加 20 字节),影响 MTU(通常设置为 1480),但解决了跨子网路由问题,性能仍优于 VXLAN(无 UDP 头部)。
  6. BGP 路由模式: 这是 Calico 的核心优势和默认模式。
    • 全互联 (Full Mesh): 在集群内部,Calico 默认配置所有节点之间建立 iBGP 会话,形成一个全互联的 BGP 拓扑。每个节点都将自己负责的 Pod CIDR(或具体 Pod IP)宣告给所有其他节点。
    • 路由反射器 (Route Reflector - RR): 当节点数量很大时(如 > 50),全互联的 BGP 会话数量会呈 O(N²) 增长,导致管理复杂和资源消耗。Calico 支持部署 BGP Route Reflector。节点只需与 RR 建立 iBGP 会话,RR 负责在节点之间反射路由信息,大大减少会话数量(O(N))。RR 可以是专用节点,也可以复用 Kubernetes Master 节点(需注意高可用)。
    • eBGP 对接物理网络: Calico 节点可以与物理网络设备(如 ToR 交换机)建立 eBGP 会话。节点将 Pod IP 路由宣告给物理设备,物理设备再将这些路由宣告到更广的网络中。这使得:
      • Pod IP 可以被外部网络直接路由访问(无需 NAT)。
      • 外部流量可以直接路由到 Pod IP(无需经过 NodePort 或 LoadBalancer 的额外跳转)。
      • 简化了网络架构,减少了 SNAT/DNAT 的性能损耗。
    • 无覆盖网络: 在纯 BGP 模式下(无 IPIP),Pod 之间的通信完全依赖物理网络的三层路由能力。数据包从源 Pod 发出,经过源节点路由,直接通过物理网络路由到目标节点,再由目标节点路由到目标 Pod。没有隧道封装,性能接近物理网络。

Calico 架构总结:

  • 数据存储 (etcd/K8s API): 集中管理所有网络状态、策略和配置。
  • 节点代理 (Felix): 核心大脑,负责路由编程(Linux 路由表)、策略编程(iptables/nftables)、接口管理、状态报告。
  • BGP 路由器 (BIRD): 路由引擎,负责通过 BGP 协议在节点间(iBGP)或与物理网络(eBGP)分发和学习 Pod IP 路由。
  • CNI 插件 (calico/calico-ipam): 负责 Pod 网络接入和 IP 地址分配,利用 Felix 和 BGP 提供的路由基础设施。
  • 路由模式: 纯 BGP(高性能,要求底层路由) 或 BGP + IPIP(兼容性更好,有封装开销)。
  • 策略引擎: Felix 将 NetworkPolicy 转化为高效的内核级 ACL。
1.4 Flannel vs Calico:深度对比

特性维度

Flannel

Calico

深度解析与权衡

核心原理

覆盖网络 (Overlay)

纯三层路由 (BGP) / IPIP 隧道

Flannel 通过隧道(VXLAN/UDP/IPIP)或主机网关(host-gw)在现有网络上构建一个逻辑二层网络,隔离了 Pod 网络和物理网络。Calico 则致力于将 Pod 网络融入物理网络的三层路由体系,利用 BGP 实现高效路由。

性能

中等 (vxlan/udp/ipip) / 优秀 (host-gw)

优秀 (纯 BGP) / 良好 (IPIP)

Flannel host-gw 和 Calico 纯 BGP 性能最佳,无封装开销,接近物理网络性能。Flannel vxlan/udp/ipip 和 Calico IPIP 因隧道封装(增加 20-54 字节头部)和内核/用户态处理开销,性能有损耗(通常 10%-30%)。Calico 纯 BGP 在大规模下性能优势更明显。

网络模型

二层 Overlay (vxlan/udp/ipip) / 三层直连 (host-gw)

纯三层 (BGP/IPIP)

Flannel 在 Overlay 模式下,Pod 通信逻辑上是二层广播域,物理上是三层封装。host-gw 模式下是纯三层,但依赖底层 L2 或静态路由。Calico 始终是三层模型,Pod IP 就是路由表中的目标地址。

复杂度

中高

Flannel 部署配置极其简单(kubectl apply -f kube-flannel.yml),概念少(etcd, 子网, 后端),故障排查相对直观(看路由、抓包)。Calico 涉及 BGP 概念(AS, Peer, RR)、Felix 状态、iptables 规则、IPPool 管理等,部署(尤其 RR)、配置和故障排查(calicoctl, BGP 状态)复杂度显著更高。

扩展性

良好 (vxlan) / 受限 (host-gw)**

优秀 (BGP + RR)

Flannel vxlan 基于 L3 Overlay,扩展性很好,支持大规模集群。host-gw 受限于 L2 域大小或静态路由维护能力。Calico 基于 BGP,天然具备互联网级的扩展能力,通过 Route Reflector 可轻松支持数千节点规模。

对底层网络要求

低 (vxlan/udp/ipip - 只需 L3 可达) / 高 (host-gw - 需 L2 或静态路由)

高 (纯 BGP - 需 L3 路由支持或 eBGP 对接) / 中 (IPIP - 只需 L3 可达)

Flannel Overlay 模式对底层网络要求最低,只要节点间 IP 可通即可。host-gw 要求苛刻。Calico 纯 BGP 要求底层网络能够路由 Pod IP 流量(要么节点在同一 L2,要么物理设备支持动态路由或配置静态路由到节点)。IPIP 模式降低了对底层的要求,只需 L3 可达。

网络策略 (NetworkPolicy)

不支持原生 (需集成 Canal/Cilium 等)

原生支持,功能强大且高效

Flannel 本身不实现 NetworkPolicy。若需策略,需部署其他 CNI(如 Canal = Flannel + Calico Felix,或 Cilium)。Calico 原生、深度集成 NetworkPolicy,由 Felix 直接翻译为内核 iptables/nftables 规则,性能高,支持丰富的规则(基于标签、命名空间、端口、协议、IP 段等)。

IP 地址管理 (IPAM)

简单 (host-local)

强大 (calico-ipam)

Flannel 通常使用 host-local IPAM,基于节点本地文件管理子网分配,简单但功能有限(如无 IP 池复用、固定 IP 分配复杂)。Calico 的 calico-ipam 集成到 Datastore,支持复杂的 IPPool 管理(多池、选择器、固定 IP、IP 池复用、地址回收策略等),功能远超 Flannel。

外部网络集成

困难 (通常需要 SNAT/DNAT)

优秀 (通过 eBGP 宣告 Pod IP 路由)

Flannel 的 Pod IP 是私有 Overlay 地址,外部无法直接访问。暴露服务需依赖 Kubernetes Service (NodePort/LoadBalancer) 或 Ingress,涉及 SNAT/DNAT。Calico 通过 eBGP 将 Pod IP 路由宣告到物理网络,Pod IP 可被外部网络直接路由访问,无需 NAT,简化架构,提升性能(尤其对东西向流量)。

MTU 处理

需手动调整 (通常 1400/1450 for vxlan, 1480 for ipip)

需手动调整 (纯 BGP 无需, IPIP 需 1480)

所有隧道方案(Flannel vxlan/udp/ipip, Calico IPIP)都因封装头导致有效 MTU 减小,必须在 CNI 配置或网络插件中设置正确的 MTU(通常比物理网络 MTU 小 20-50 字节),否则可能导致分片或连接问题。Flannel host-gw 和 Calico 纯 BGP 无此问题。

适用场景

中小规模集群、开发测试环境、对网络策略要求不高、追求简单快速部署

大规模生产环境、对性能要求高、需要细粒度网络策略、需要外部直接路由 Pod IP、网络架构复杂

Flannel 是入门和中小型集群的理想选择,开箱即用,满足基本需求。Calico 是企业级生产环境的首选方案之一,尤其适合性能敏感、安全要求高、网络规模大或需要深度网络集成的场景。其复杂度带来的管理成本在大型环境中是值得的。

选择建议:

  • 选 Flannel: 集群规模小(< 50 节点)、团队网络经验有限、追求极致部署简便性、当前或可预见的未来不需要 NetworkPolicy、对性能要求不是极端苛刻、底层网络环境简单(如公有云 VPC 内同一子网)。
  • 选 Calico: 集群规模大(> 50 节点或有扩展计划)、对网络性能有高要求(尤其东西向流量)、必须使用 NetworkPolicy 实现安全隔离、需要 Pod IP 被外部网络直接路由访问(避免 NAT 性能损耗和复杂性)、底层网络支持 BGP 或愿意配置 IPIP、团队具备一定的 BGP 和网络排错能力。
  • 折中方案 (Canal): 如果既想要 Flannel 的简单部署(Overlay),又需要 Calico 的 NetworkPolicy,可以选择 Canal 项目。它将 Flannel 作为网络数据平面,同时部署 Calico 的 Felix 来提供 NetworkPolicy 功能。但性能和扩展性受限于 Flannel 的 Overlay 后端。

第二部分:Pod 通信与跨节点路由 - 数据包的旅程

理解了 CNI 如何构建网络基础设施后,我们深入探究 Pod 之间是如何通信的,特别是跨越不同物理节点时,数据包是如何被正确路由的。我们将结合 Flannel (VXLAN) 和 Calico (纯 BGP) 两种典型模式进行剖析。

2.1 同节点 Pod 通信 (Intra-Node Communication)

无论使用哪种 CNI 插件,同节点上的 Pod 通信机制是相似的,且相对简单直接。

核心组件:

    • cni0 网桥 (Flannel) / 直接路由 (Calico):Flannel: Flannel CNI 调用 bridge 插件在节点上创建一个 Linux 网桥(通常叫 cni0)。每个 Pod 的 veth pair 的宿主机端(vethxxx)都会被插入到这个网桥中。网桥拥有一个 IP 地址(如 10.244.0.1),作为该节点子网的网关。
    • Calico: Calico 默认不创建网桥。Pod 的 veth pair 的宿主机端(calixxx)直接存在于节点的根网络命名空间。Felix 会在节点上配置一条到该 Pod IP 的直连路由(ip route add <Pod_IP>/32 dev <veth_device>)。
  1. veth pair (Virtual Ethernet Pair): 连接 Pod 网络命名空间和节点根命名空间的虚拟以太网对。一端在 Pod 内(eth0),另一端在节点上(vethxxxcalixxx)。它们像一根虚拟网线,数据包从一端进入,必从另一端出去。
  2. Pod 路由表: Pod 内部通常只有一条默认路由,指向其网关:
    • Flannel: default via 10.244.0.1 dev eth0 (网桥 IP)。
    • Calico: default via 169.254.1.1 dev eth0 (Calico 使用一个特殊的 link-local 地址 169.254.1.1 作为网关,Felix 会确保这个地址在节点上可达,通常指向节点自身的 IP 或一个虚拟接口)。

通信流程 (以 Pod A -> Pod B 为例,同节点):

  1. Pod A 发送数据包: Pod A (10.244.0.2) 的应用想发送数据给 Pod B (10.244.0.3)。
  2. Pod A 路由决策: Pod A 查询自己的路由表。目标 IP 10.244.0.310.244.0.2 在同一子网(10.244.0.0/24),因此无需经过网关。Pod A 直接通过 ARP 解析目标 IP 10.244.0.3 对应的 MAC 地址。
    • ARP 解析:Flannel: Pod A 发送 ARP 请求(Who has 10.244.0.3? Tell 10.244.0.2)。这个请求通过 veth pair 到达 cni0 网桥。网桥会泛洪这个 ARP 请求到所有连接的端口(包括所有其他 Pod 的 veth 端)。Pod B 收到 ARP 请求后,回复自己的 MAC 地址。网桥学习到 10.244.0.3 对应的 MAC 地址在哪个端口(Pod B 的 veth 端)。
    • Calico: Pod A 发送 ARP 请求。请求通过 veth pair 到达节点根命名空间。节点内核(或由 Felix 代理的 ARP 响应)会直接回复 10.244.0.3 的 MAC 地址(即 Pod B 的 eth0 MAC)。Calico 通常会配置 proxy_arp 或使用 Felix 主动响应 ARP,避免泛洪。
  3. 数据包发送: Pod A 构造二层帧:源 MAC = Pod A eth0 MAC, 目标 MAC = Pod B eth0 MAC, 源 IP = 10.244.0.2, 目标 IP = 10.244.0.3。数据包通过 veth pair 发送到节点。
    • 节点内转发:Flannel: 数据包到达 cni0 网桥。网桥查询其 MAC 地址表(通过之前的 ARP 学习),知道目标 MAC 地址对应的端口是 Pod B 的 veth 端。网桥直接将数据帧转发到该端口。
    • Calico: 数据包到达节点根命名空间。节点内核查询路由表,发现有一条到 10.244.0.3/32 的直连路由,下一跳是 caliyyy 设备(Pod B 的 veth 端)。内核直接将数据包发送到该设备。
  4. 到达 Pod B: 数据包通过 Pod B 的 veth pair 进入 Pod B 的网络命名空间,最终被 Pod B 的应用接收。

关键点: 同节点通信主要依赖 Linux 网桥(Flannel)或直连路由(Calico)和 veth pair,数据包始终在节点内核空间完成转发,不涉及物理网络,延迟极低,带宽接近内存带宽。

2.2 跨节点 Pod 通信 (Inter-Node Communication) - Flannel (VXLAN)

这是 Flannel 解决的核心问题。假设 Pod A (10.244.1.2) 在 Node 1 上,Pod B (10.244.2.3) 在 Node 2 上。Flannel 使用 VXLAN 后端。

核心组件 (Node 1 & Node 2):

  1. flanneld: 运行在每个节点,负责:
    • 向 etcd 注册/获取子网(Node 1: 10.244.1.0/24, Node 2: 10.244.2.0/24)。
    • 创建并管理 VXLAN 隧道设备 flannel.1
    • 根据 etcd 中的信息,在节点上维护路由表。
  2. VXLAN 隧道设备 (flannel.1): 一个虚拟网络接口,由内核 VXLAN 模块管理。它有自己的 MAC 地址和 IP 地址(通常是一个 169.254.x.x10.244.x.x 地址,但主要用于封装/解封装)。
节点路由表 (由 flanneld 维护):Node 1 路由表关键条目:
10.244.0.0/16 dev flannel.1 scope link
10.244.1.0/24 dev cni0 scope link  # 本节点子网
10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink  # Node 2 子网,下一跳是 Node 2 的隧道 IP
... (其他节点子网类似)
Node 2 路由表关键条目:
10.244.0.0/16 dev flannel.1 scope link
10.244.2.0/24 dev cni0 scope link  # 本节点子网
10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink  # Node 1 子网,下一跳是 Node 1 的隧道 IP
... (其他节点子网类似)
    • 注意: via <IP> dev flannel.1 表示目标子网 10.244.2.0/24 的流量需要通过 flannel.1 设备发送,并且下一跳(VXLAN VTEP IP)是 10.244.2.0(这个地址是 flanneld 为 Node 2 分配的隧道端点 IP,通常存储在 etcd 中,Node 1 的 flanneld 会读取它)。
  1. ARP 表 (由 flanneld/FDB 维护): 内核需要知道目标 VTEP IP (10.244.2.0) 对应的物理 MAC 地址(即 Node 2 物理网卡 eth0 的 MAC)。flanneld 会通过 etcd 获取 Node 2 的物理 IP (192.168.1.102),然后 Node 1 内核会通过 ARP 解析 192.168.1.102 得到其 MAC。内核会将这个映射(VTEP IP -> 物理 MAC)存储在 Forwarding Database (FDB) 中,用于 VXLAN 封装。

通信流程 (Pod A -> Pod B):

  1. Pod A 发送数据包: Pod A (10.244.1.2) 发送数据包给 Pod B (10.244.2.3)。数据包:源 IP=10.244.1.2, 目标 IP=10.244.2.3
  2. Pod A 路由决策: Pod A 查询路由表,目标 IP 10.244.2.3 不在本地子网 (10.244.1.0/24),因此走默认路由,发送到网关 (10.244.1.1,即 cni0 网桥 IP)。
  3. 到达 Node 1 网桥: 数据包通过 veth pair 到达 Node 1 的 cni0 网桥。
  4. Node 1 路由决策 (关键): Node 1 内核收到数据包(目标 IP=10.244.2.3),查询节点路由表。找到最长匹配的路由:10.244.2.0/24 via 10.244.2.0 dev flannel.1。这表示:
    • 该数据包需要从 flannel.1 设备发出。
    • 下一跳(VTEP IP)是 10.244.2.0
  5. VXLAN 封装 (Node 1 内核): 内核 VXLAN 模块接管数据包:
    • 原始数据包 (Inner Packet): 源 IP=10.244.1.2, 目标 IP=10.244.2.3 (以及 L4 头部)。
      • VXLAN 封装:VXLAN Header: 添加 VXLAN 头部,包含 VNI (通常为 1)。
      • UDP 头部: 源端口(内核随机选择),目标端口=8472 (VXLAN 标准端口)。
      • Outer IP Header: 源 IP = Node 1 物理 IP (192.168.1.101),目标 IP = Node 2 物理 IP (192.168.1.102)。注意: 目标 IP 192.168.1.102 是如何得到的?内核根据下一跳 VTEP IP (10.244.2.0) 查询 FDB 表,找到对应的物理 MAC 地址(Node 2 eth0 MAC),然后根据 Node 1 的路由表(目标 192.168.1.102 的路由,通常是直连或默认路由)确定 Outer IP 的目标 IP。
      • Outer Ethernet Header: 源 MAC = Node 1 eth0 MAC, 目标 MAC = Node 2 eth0 MAC (通过 ARP 解析 192.168.1.102 得到)。
    • 封装后的数据包: [Eth(Node1MAC->Node2MAC)] [IP(192.168.1.101->192.168.1.102)] [UDP(SrcPort->8472)] [VXLAN(VNI=1)] [IP(10.244.1.2->10.244.2.3)] [L4...]
  6. 物理网络传输: 封装后的数据包通过 Node 1 的物理网卡 eth0 发送到物理网络(交换机、路由器)。物理网络设备只看到 Outer IP Header (192.168.1.101 -> 192.168.1.102),像处理普通 IP 包一样将其路由到 Node 2。
  7. 到达 Node 2: 数据包到达 Node 2 的物理网卡 eth0
  8. VXLAN 解封装 (Node 2 内核): Node 2 内核收到数据包:
    • 识别目标 UDP 端口是 8472,交给 VXLAN 模块处理。
    • VXLAN 模块检查 VNI (1),确认是自己的隧道。
    • 剥离 Outer Ethernet, Outer IP, UDP, VXLAN Header。
    • 提取 出原始数据包:源 IP=10.244.1.2, 目标 IP=10.244.2.3
  9. Node 2 路由决策: Node 2 内核得到原始数据包(目标 IP=10.244.2.3),查询节点路由表。找到最长匹配的路由:10.244.2.0/24 dev cni0 scope link。这表示目标 IP 在本节点子网内,需要通过 cni0 网桥发送。
  10. Node 2 内转发: 数据包被发送到 cni0 网桥。网桥查询 MAC 地址表(或通过 ARP),找到目标 IP 10.244.2.3 对应的 MAC 地址(Pod B eth0 MAC)所在的端口(Pod B 的 veth 端),将数据帧转发到该端口。
  11. 到达 Pod B: 数据包通过 Pod B 的 veth pair 进入 Pod B 的网络命名空间,被 Pod B 的应用接收。

关键点: Flannel VXLAN 通过隧道封装,将 Pod IP 包裹在宿主机 IP 包中传输。核心在于:

  • 路由表: flanneld 维护的到其他节点子网的路由,指向 flannel.1 设备和下一跳 VTEP IP。
  • FDB 表: 内核维护的 VTEP IP 到物理 MAC 的映射,用于确定 Outer IP 的目标 IP。
  • 封装/解封装: 由内核 VXLAN 模块高效完成,用户态 flanneld 不参与数据平面转发。
  • 性能影响: 封装开销(50+ 字节)、MTU 问题、内核处理开销(相对较小)。
2.3 跨节点 Pod 通信 (Inter-Node Communication) - Calico (纯 BGP)

Calico 纯 BGP 模式展现了其设计的高效性。假设 Pod A (192.168.1.2) 在 Node 1 上,Pod B (192.168.2.3) 在 Node 2 上。Calico 使用 BGP 全互联模式。

核心组件 (Node 1 & Node 2):

  1. Felix: 运行在每个节点,负责:
    • 配置 Pod 网络接口(veth)。
    • 编程路由表: 根据从 Datastore (K8s API) 获取的信息(其他节点的 Pod CIDR 和 BGP 学习到的路由),在 Linux 内核路由表中动态添加/删除路由条目。
    • 编程 iptables/nftables 规则(用于 NetworkPolicy)。
  2. BIRD: 运行在每个节点,作为 BGP 路由器:
    • 与集群内所有其他节点建立 iBGP 会话(AS 号相同)。
    • 宣告路由: 将本节点上运行的 Pod 的 IP 地址(或整个 Pod CIDR,取决于配置 ipInIP: DisabledadvertiseClusterIPs: true/false)作为路由信息,通过 BGP 协议宣告给其他节点。例如,Node 1 宣告 192.168.1.0/24
    • 学习路由: 接收并学习其他节点宣告的 Pod IP/CIDR 路由。例如,Node 1 学习到 Node 2 宣告的 192.168.2.0/24
    • 安装路由: BIRD 将学习到的最佳 BGP 路由安装到 Linux 内核的路由表中。
节点路由表 (由 Felix + BIRD 维护):Node 1 路由表关键条目:
192.168.1.0/24 dev cali123 scope link  # 本节点 Pod CIDR,直连路由 (Felix)
192.168.2.0/24 via 192.168.1.102 dev eth0 proto bird  # Node 2 Pod CIDR,BIRD 学习并安装,下一跳是 Node 2 IP
... (其他节点 Pod CIDR 类似)
Node 2 路由表关键条目:
192.168.2.0/24 dev cali456 scope link  # 本节点 Pod CIDR,直连路由 (Felix)
192.168.1.0/24 via 192.168.1.101 dev eth0 proto bird  # Node 1 Pod CIDR,BIRD 学习并安装,下一跳是 Node 1 IP
... (其他节点 Pod CIDR 类似)
    • 关键: 路由条目 via <Node_IP> dev eth0 proto bird 表明目标 Pod CIDR 的流量需要通过物理网卡 eth0 发送,下一跳直接是目标节点的物理 IP 地址。没有隧道设备!
  1. 物理网络: 必须能够支持路由 Pod IP 流量。这意味着:
    • 所有节点在同一个 L2 广播域(如同一交换机下),或者
    • 物理网络设备(路由器、三层交换机)配置了到达各个节点 Pod CIDR 的路由(静态路由或通过 BGP/OSPF 等动态路由协议学习)。在纯 BGP 模式下,Calico 依赖物理网络的三层可达性。

通信流程 (Pod A -> Pod B):

  1. Pod A 发送数据包: Pod A (192.168.1.2) 发送数据包给 Pod B (192.168.2.3)。数据包:源 IP=192.168.1.2, 目标 IP=192.168.2.3
  2. Pod A 路由决策: Pod A 查询路由表,目标 IP 192.168.2.3 不在本地网络(通常 Pod 网关是 169.254.1.1),走默认路由,发送到网关。
  3. 到达 Node 1 根命名空间: 数据包通过 veth pair (cali123) 到达 Node 1 的根网络命名空间。
  4. Node 1 路由决策 (关键): Node 1 内核收到数据包(目标 IP=192.168.2.3),查询节点路由表。找到最长匹配的路由:192.168.2.0/24 via 192.168.1.102 dev eth0 proto bird。这表示:
    • 该数据包需要从物理网卡 eth0 发出。
    • 下一跳直接是 Node 2 的物理 IP 地址 (192.168.1.102)。
  5. Node 1 发送数据包: Node 1 内核构造二层帧:
    • 源 MAC = Node 1 eth0 MAC。
    • 目标 MAC = 下一跳 (192.168.1.102) 的 MAC 地址。Node 1 内核通过 ARP 解析 192.168.1.102 得到其 MAC 地址(Node 2 eth0 MAC)。
    • 源 IP = 192.168.1.2 (Pod A IP)。
    • 目标 IP = 192.168.2.3 (Pod B IP)。
    • 注意: 数据包没有被封装!它就是一个标准的 IP 包,源和目标 IP 都是 Pod IP。
  6. 物理网络传输: Node 1 将这个原始 IP 包通过 eth0 发送到物理网络。
    • 场景一 (L2 网络): 如果节点在同一个 L2 域,交换机根据目标 MAC (Node 2 eth0 MAC) 直接将帧转发到 Node 2。
    • 场景二 (L3 网络): 如果节点在不同 L3 子网,物理路由器收到包:
      • 查看目标 IP (192.168.2.3)。
      • 查询自己的路由表。假设路由器通过 BGP 从 Node 2 学习到了 192.168.2.0/24 的路由,下一跳是 Node 2 (192.168.1.102)。
      • 路由器将包转发给下一跳(Node 2)。在转发过程中,路由器会重写二层帧头:源 MAC 改为路由器出口 MAC,目标 MAC 改为 Node 2 eth0 MAC。但IP 包头保持不变(源 IP=192.168.1.2, 目标 IP=192.168.2.3)。
  7. 到达 Node 2: 数据包(原始 IP 包)到达 Node 2 的物理网卡 eth0
  8. Node 2 路由决策: Node 2 内核收到数据包(目标 IP=192.168.2.3),查询节点路由表。找到最长匹配的路由:192.168.2.0/24 dev cali456 scope link。这表示目标 IP 在本节点 Pod CIDR 内,需要通过 cali456 设备(Pod B 的 veth 端)发送。
  9. Node 2 内转发: Node 2 内核直接将数据包发送到 cali456 设备。
  10. 到达 Pod B: 数据包通过 Pod B 的 veth pair 进入 Pod B 的网络命名空间,被 Pod B 的应用接收。

关键点: Calico 纯 BGP 模式实现了真正的三层路由:

  • 无封装: Pod IP 包在物理网络中裸奔,没有额外的隧道头部,性能最优,MTU 无需调整。
  • 路由驱动: BGP 协议负责在节点间分发 Pod CIDR 路由信息。BIRD 将这些路由安装到内核路由表。
  • 直接路由: 节点路由表中的条目直接指向目标节点的物理 IP 作为下一跳。数据包的 IP 头在整个传输过程中(除了最后一跳的 ARP 解析)保持不变。
  • 依赖物理网络: 这是该模式最大的前提和潜在限制。物理网络必须能够正确路由 Pod IP 流量。在公有云环境中,通常需要配合云平台的路由表(如 AWS Route Table, GCP Custom Route)或使用 IPIP 模式。在私有数据中心,需要网络设备支持 BGP 或配置静态路由。
  • eBGP 集成: 通过与物理网络设备建立 eBGP 会话,Calico 可以将 Pod IP 路由宣告到整个物理网络,实现 Pod IP 的全局可达性,这是其区别于 Overlay 方案的重大优势。
2.4 跨节点路由总结与对比

特性

Flannel (VXLAN)

Calico (纯 BGP)

深度解析

数据包路径

Pod -> Node1 (veth/bridge) -> VXLAN Encap -> Physical Network -> Node2 -> VXLAN Decap -> Node2 (bridge/veth) -> Pod

Pod -> Node1 (veth) -> Physical Network (L3 Routing) -> Node2 (veth) -> Pod

Flannel 数据包在 Node1 和 Node2 经历了封装/解封装过程,路径更长。Calico 数据包在物理网络中以原始 Pod IP 形式被路由,路径最短,效率最高。

封装开销

有 (50+ 字节:VXLAN+UDP+Outer IP+Outer Eth)

Flannel 的封装开销增加了网络负载,降低了有效带宽,并可能导致分片(需调整 MTU)。Calico 无此开销,带宽利用率最高。

MTU

需调整 (通常 1400/1450)

无需调整 (使用物理网络 MTU)

Flannel 必须在 CNI 配置或网络插件中设置小于物理 MTU 的值,否则可能导致 TCP 性能下降或连接问题。Calico 直接使用物理网络 MTU,无此烦恼。

路由表维护

集中式 (etcd) + 节点 (flanneld 写路由)

分布式 (BGP 协议) + 节点 (Felix/BIRD)

Flannel 路由信息由 flanneld 从 etcd 读取后写入节点路由表,相对静态。Calico 路由信息通过 BGP 协议在节点间动态分发和学习,更适应网络变化,扩展性更好。

下一跳

VTEP IP (如 10.244.2.0) -> 隧道设备 (flannel.1)

目标节点物理 IP (如 192.168.1.102) -> 物理网卡 (eth0)

Flannel 的下一跳是逻辑隧道端点,需要额外的 FDB 表解析到物理地址。Calico 的下一跳直接是物理地址,路由决策更直接高效。

对物理网络依赖

低 (只需 L3 可达)

高 (需 L3 路由支持 Pod IP)

Flannel VXLAN 对底层网络要求最低,只要节点 IP 能通即可工作。Calico 纯 BGP 要求物理网络能路由 Pod IP 流量,部署限制更多,但网络架构更扁平、高效。

性能

中等 (封装/解封装开销)

优秀 (接近物理网络极限)

在同等硬件条件下,Calico 纯 BGP 的吞吐量和延迟通常显著优于 Flannel VXLAN,尤其在大包传输和高并发场景下。

外部访问

困难 (需 NAT)

容易 (通过 eBGP 宣告路由)

Flannel 的 Pod IP 是私有地址,外部无法直接访问。Calico 通过 eBGP 将 Pod IP 路由宣告到物理网络,Pod IP 可被外部直接路由访问,无需 NAT,简化架构。

结论: 跨节点路由是 Kubernetes 网络的核心挑战。Flannel 通过 VXLAN 等隧道技术提供了一种通用、易部署的解决方案,牺牲了一定的性能换取了对底层网络的低依赖。Calico 则通过 BGP 和纯三层路由,追求极致性能和网络集成度,但要求更高的网络规划和管理能力。选择哪种方案,取决于集群规模、性能要求、网络环境复杂度和团队运维能力。理解数据包在两种模式下的完整旅程,是进行网络优化和故障排查的基础。

第三部分:网络策略 (NetworkPolicy) - 微服务安全的守护者

在微服务架构中,服务间通信是常态,但并非所有服务都应该能够相互访问。NetworkPolicy 是 Kubernetes 提供的原生网络策略 API,用于定义 Pod 组之间如何通信以及哪些通信是被允许的。它实现了命名空间级别的微分段(Micro-segmentation),是构建零信任网络(Zero Trust Network)的关键组件。

3.1 NetworkPolicy 核心概念与模型

设计目标: 提供一种声明式、基于标签选择器的方式,定义 Pod 之间的入站(Ingress)和出站(Egress)网络访问规则。

核心资源: NetworkPolicy (属于 networking.k8s.io/v1 API Group)。

关键字段解析:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:name: <policy-name>  # 策略名称namespace: <namespace> # 策略所属命名空间
spec:podSelector: {} # **核心:选择此策略应用到的目标 Pod 组**# matchLabels: {key: value}  # 基于标签选择# matchExpressions:          # 基于标签表达式选择policyTypes: # 指定策略类型(Ingress, Egress 或两者)- Ingress- Egressingress: # **入站规则列表**:允许哪些流量访问目标 Pod- from: # 流量来源列表(OR 关系)- podSelector: {} # 来源 Pod 选择器(当前命名空间)# matchLabels: {key: value}- namespaceSelector: {} # 来源命名空间选择器# matchLabels: {key: value}- namespaceSelector: {} # 可组合:来源是特定命名空间中符合标签的 PodmatchLabels: {project: myproject}podSelector:matchLabels: {role: frontend}- ipBlock: # 来源 IP 段(CIDR)cidr: 192.168.1.0/24except: # 排除的 IP 段- 192.168.1.1/32ports: # 允许的目标端口列表(AND 关系,需与 from 匹配)- protocol: TCPport: 80- protocol: UDPport: 53egress: # **出站规则列表**:允许目标 Pod 访问哪些外部资源- to: # 流量目标列表(OR 关系)- podSelector: {} # 目标 Pod 选择器(当前命名空间)- namespaceSelector: {} # 目标命名空间选择器- namespaceSelector: {} # 可组合:目标是特定命名空间中符合标签的 Pod- ipBlock: # 目标 IP 段(CIDR)cidr: 10.0.0.0/8ports: # 允许的目标端口列表(AND 关系,需与 to 匹配)- protocol: TCPport: 443

核心语义与行为:

  1. 默认行为 (Deny All): 这是 NetworkPolicy 最重要也最容易被误解的规则!
    • 如果一个 Pod 没有被任何 NetworkPolicy 的 podSelector 选中,那么它不受任何 NetworkPolicy 限制,可以自由访问任何地方,也可以被任何地方访问(相当于网络完全开放)。
      • 一旦一个 Pod 被至少一个 NetworkPolicy 的 podSelector 选中,那么:入站 (Ingress): 只有 明确匹配该 Pod 关联的 NetworkPolicy 中 ingress 规则的流量才被允许。所有其他入站流量都被拒绝! (Deny All Ingress)
      • 出站 (Egress): 只有 明确匹配该 Pod 关联的 NetworkPolicy 中 egress 规则的流量才被允许。所有其他出站流量都被拒绝! (Deny All Egress)
    • 关键点: NetworkPolicy 是叠加性的。一个 Pod 可以被多个 NetworkPolicy 选中。最终生效的规则是所有选中该 Pod 的 NetworkPolicy 规则的并集。但默认拒绝的语义始终存在:未被任何策略明确允许的流量,一律拒绝。
  2. podSelector (目标选择): 定义策略保护谁。它选择当前命名空间中的一组 Pod 作为策略的应用对象。只有被选中的 Pod 才会遵循此策略的规则(并受默认拒绝约束)。
  3. policyTypes (策略类型): 明确指定策略是用于控制入站 (Ingress)、出站 (Egress) 还是两者。如果省略,Kubernetes 会根据 spec.ingressspec.egress 字段是否存在来推断,但显式指定是最佳实践。
  4. ingress (入站规则):
    • from (来源): 定义允许访问目标 Pod 的流量来源。from 下的多个子项(podSelector, namespaceSelector, ipBlock)是 OR 关系。例如,from: [podSelector: {app: db}, namespaceSelector: {env: dev}] 表示允许来自 app=db 的 Pod 或者 来自 env=dev 命名空间中任何 Pod 的访问。
    • ports (端口): 定义允许访问的目标端口。ports 下的多个端口规则是 OR 关系。例如,ports: [{protocol: TCP, port: 80}, {protocol: TCP, port: 443}] 表示允许访问 TCP 80 或者 TCP 443。
    • from + ports: 一条完整的 ingress 规则(- from: ... ports: ...)表示:允许来自 from 定义的来源,访问 ports 定义的端口。多个 ingress 规则是 OR 关系。
  5. egress (出站规则):
    • to (目标): 定义允许目标 Pod 访问的外部资源。to 下的多个子项(podSelector, namespaceSelector, ipBlock)是 OR 关系。
    • ports (端口): 定义允许访问的目标端口。ports 下的多个端口规则是 OR 关系。
    • to + ports: 一条完整的 egress 规则(- to: ... ports: ...)表示:允许目标 Pod 访问 to 定义的资源的 ports 定义的端口。多个 egress 规则是 OR 关系。
  6. namespaceSelectorpodSelector 组合: 这是实现跨命名空间策略的关键。例如:
ingress:
- from:- namespaceSelector:matchLabels:project: myprojectpodSelector:matchLabels:role: frontend
  1. 表示:允许来自标签为 project=myproject 的命名空间中,标签为 role=frontend 的 Pod 的访问。注意:podSelector 在这里选择的是源命名空间中的 Pod,而不是目标命名空间中的 Pod。
  2. ipBlock: 用于基于 IP 段(CIDR)控制流量,常用于:
    • 允许/拒绝来自特定外部 IP 段的访问(如办公网 IP)。
    • 允许 Pod 访问特定的外部服务 IP(如数据库 IP)。
    • 注意: ipBlock 规则只适用于 IP 层,它不感知 Pod 的标签或命名空间。当 ipBlockpodSelector/namespaceSelector 混用时,需要小心理解其语义(通常是 OR 关系)。except 字段用于排除 CIDR 中的特定 IP。
3.2 NetworkPolicy 实现原理 - 以 Calico 为例

NetworkPolicy 是一个 API 资源,其本身不执行任何网络控制。它的实现完全依赖于底层的 CNI 网络插件。不同的 CNI 插件(Calico, Cilium, Kube-router, Canal 等)有不同的实现机制,但核心思想都是将声明式的 NetworkPolicy 规则翻译成底层的网络访问控制列表(ACL)。我们以功能强大且广泛使用的 Calico 为例,深入剖析其实现原理。

Calico 实现架构:

  1. Kubernetes API Server: 用户创建 NetworkPolicy 资源。
  2. Calico Controller (Kube-controllers): 一个独立的控制器(通常以 Deployment 运行),监听 Kubernetes API Server 中的 NetworkPolicy, Pod, Namespace, Service 等资源变化。
  3. Calico Datastore (etcd/K8s API): Controller 将监听到的 Kubernetes 资源状态转换并同步到 Calico 的 Datastore 中。例如,一个 Kubernetes NetworkPolicy 会被转换为一个或多个 Calico 原生的 NetworkPolicyProfile 资源。
  4. Felix (节点代理): 运行在每个节点上,监听 Calico Datastore 的变化。当它检测到 NetworkPolicy 相关的资源(如 NetworkPolicy, WorkloadEndpoint, Profile)发生变化时:
    • 计算策略: Felix 结合当前节点上运行的 Pod 信息(WorkloadEndpoint)和 Datastore 中的策略规则,计算出需要应用到每个 Pod(更准确地说是每个 veth 接口)上的具体访问控制规则。
    • 编程 ACL: Felix 将计算出的规则翻译成 Linux 内核的 iptables (或可选的 nftables) 规则,并动态地加载到节点的内核网络栈中。这些规则直接作用于 Pod 的 veth 接口或节点上的 chain
  5. Linux Kernel (iptables/nftables): 最终执行者。当网络数据包流经 Pod 的 veth 接口或节点网络栈时,内核会按照 Felix 编程的 iptables/nftables 规则进行匹配和决策(允许 ACCEPT 或拒绝 DROP)。

iptables 实现深度解析:

Calico 主要利用 Linux iptables 的 filter 表来实现 NetworkPolicy。它精心设计了一系列的 chain 和 规则。

核心 Chain 结构 (简化版):

  1. FORWARD 链 (节点根命名空间): 所有跨节点或从节点发往 Pod 的流量(即非本机进程发起且非发往本机进程的流量)都会经过此链。Calico 在此链的开头插入规则,将流量跳转到 Calico 专门处理 Pod 流量的链。
    • 规则示例:-A FORWARD -m comment --comment "cali:..." -j cali-FORWARD
  2. cali-FORWARD 链: Calico 的主入口链。它根据数据包的方向(入站到 Pod 还是出站自 Pod)进行分流:
    • 入站流量 (到 Pod): 数据包目标 IP 是某个 Pod IP。规则:-A cali-FORWARD -m set --match-set cali40:some-pod-ip-hash dst -j cali-from-wl-dispatch (跳转到 cali-from-wl-dispatch)。
    • 出站流量 (从 Pod): 数据包源 IP 是某个 Pod IP。规则:-A cali-FORWARD -m set --match-set cali40:some-pod-ip-hash src -j cali-to-wl-dispatch (跳转到 cali-to-wl-dispatch)。
    • 其他流量: 通常直接 ACCEPT 或返回 FORWARD 链继续处理。
  3. cali-from-wl-dispatch 链 (入站分发): 根据目标 Pod IP,将流量精确分发到处理该特定 Pod 入站策略的链。
    • 规则示例:-A cali-from-wl-dispatch -m set --match-set cali40:some-pod-ip-hash dst -j cali-fw-some-pod-name (跳转到 cali-fw-some-pod-name)。
  4. cali-fw-<pod-name> 链 (特定 Pod 入站策略): 这是真正执行该 Pod 入站 NetworkPolicy 规则的地方。Felix 根据应用于该 Pod 的所有 NetworkPolicy 的 ingress 规则,在此链中生成对应的 iptables 规则。
    • 默认拒绝 (Deny All): 链的最后一条规则通常是 -j DROP。这实现了“未被明确允许即拒绝”的核心语义。
    • 允许规则: 在 DROP 规则之前,Felix 会插入匹配 NetworkPolicy ingress 规则的 ACCEPT 规则。例如:
      • 允许来自 app=frontend Pod 的 TCP 80 流量:
-A cali-fw-pod-x -m set --match-set cali40:frontend-pod-ips src -p tcp --dport 80 -j ACCEPT
    • (这里 cali40:frontend-pod-ips 是一个 ipset,包含了所有 app=frontend Pod 的 IP)。
      • 允许来自 namespace=dev 中任何 Pod 的流量:
-A cali-fw-pod-x -m set --match-set cali40:dev-namespace-pod-ips src -j ACCEPT
    • (ipset 包含 dev 命名空间中所有 Pod IP)。
      • 允许来自 IP 段 192.168.1.0/24 的流量:
-A cali-fw-pod-x -s 192.168.1.0/24 -j ACCEPT
  1. cali-to-wl-dispatch 链 (出站分发): 类似入站分发,根据源 Pod IP,将流量分发到处理该特定 Pod 出站策略的链。
    • 规则示例:-A cali-to-wl-dispatch -m set --match-set cali40:some-pod-ip-hash src -j cali-tw-some-pod-name
  2. cali-tw-<pod-name> 链 (特定 Pod 出站策略): 执行该 Pod 出站 NetworkPolicy 规则。
    • 默认拒绝 (Deny All): 链末尾 -j DROP
    • 允许规则: 在 DROP 之前插入匹配 egress 规则的 ACCEPT 规则。例如:
      • 允许访问 app=database Pod 的 TCP 3306 流量:
-A cali-tw-pod-y -m set --match-set cali40:database-pod-ips dst -p tcp --dport 3306 -j ACCEPT
      • 允许访问外部 IP 10.0.0.5 的 TCP 443 流量:
-A cali-tw-pod-y -d 10.0.0.5/32 -p tcp --dport 443 -j ACCEPT

关键技术点:

  • ipset 高效匹配: Calico 大量使用 ipset 来存储和匹配 IP 地址集合(如某个标签选择器选中的所有 Pod IP,某个命名空间的所有 Pod IP)。ipset 是基于哈希的高效数据结构,匹配速度远快于在 iptables 中写多条 -s <ip>/32 规则,尤其当规则涉及大量 Pod 时,性能优势巨大。Felix 会动态维护这些 ipset。
  • 规则顺序与性能: iptables 规则是顺序匹配的。Calico Felix 在生成规则时,会尽量优化规则顺序,将匹配频率高或范围小的规则放在前面,以提高整体匹配效率。默认拒绝的 DROP 规则始终在最后。
  • 状态检测 (Connection Tracking): 对于 TCP 等有状态的协议,Calico 依赖 Linux 内核的 连接跟踪(conntrack) 机制。一旦一个连接被 ACCEPT 规则允许,其后续的返回包通常会被 conntrack 自动识别为 ESTABLISHED/RELATED 状态,并被允许通过,无需再次匹配完整的策略规则。这大大提高了性能。Calico 会在策略链中插入类似 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 的规则(通常在靠前位置)。
  • 多策略叠加: 当一个 Pod 被多个 NetworkPolicy 选中时,Felix 会将所有策略的规则合并到该 Pod 对应的 cali-fw-<pod-name>cali-tw-<pod-name> 链中。由于规则是 OR 关系,只要匹配任意一条 ACCEPT 规则,流量就会被允许。最终的 DROP 规则保证了未被任何策略允许的流量被拒绝。
  • Service 访问: NetworkPolicy 默认不感知 Kubernetes Service 的 ClusterIP。Pod 访问 Service 的 ClusterIP 流量,会被 kube-proxy 的 iptables/IPVS 规则DNAT 成具体的 Pod IP。因此,在 NetworkPolicy 看来,流量是直接发往目标 Pod IP 的。策略需要允许访问目标 Pod IP(或标签) 才能生效。策略中不能直接写 Service 的 ClusterIP 或名称。这是一个常见的陷阱。解决方法是在策略中通过 podSelector 选择后端 Pod,或者如果后端 Pod 标签一致,直接使用该标签。

其他 CNI 插件实现简述:

  • Cilium: 基于 eBPF 技术。它在内核层面(XDP 或 TC hook 点)实现高性能的网络策略。eBPF 程序直接在数据包进入内核网络栈的早期阶段执行,无需经过 iptables,性能极高,尤其适合高吞吐、低延迟场景。Cilium 还支持基于 HTTP/7 层的策略(如 HTTP 方法、路径、头),这是传统 iptables 难以高效实现的。
  • Kube-router: 主要使用 iptables 实现 NetworkPolicy,同时提供 IPVS 服务代理和 BGP 路由功能。其实现原理与 Calico 的 iptables 模式类似,但架构更轻量(将 Felix、BIRD、IPVS 等功能集成在一个 DaemonSet 中)。
  • Canal: 结合 Flannel(网络数据平面)和 Calico Felix(策略引擎)。其 NetworkPolicy 实现原理与 Calico 的 iptables 模式完全相同,因为策略部分就是用的 Calico Felix。
3.3 NetworkPolicy 最佳实践与常见场景

1. 遵循最小权限原则:

  • 默认拒绝: 在生产环境中,强烈建议为关键命名空间(如 production, payment)或所有命名空间配置默认拒绝所有的 NetworkPolicy。这是零信任的基础。
 ```yamlapiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:name: default-deny-allnamespace: productionspec:podSelector: {} # 选择该命名空间所有 PodpolicyTypes:- Ingress- Egress```
  • 按需开放: 只为必要的通信添加明确的允许规则。避免使用过于宽泛的规则(如允许所有 TCP 流量)。

2. 合理使用标签和命名空间:

  • 标签 (Labels): 是 NetworkPolicy 选择 Pod 的核心。为 Pod 打上清晰、有意义、稳定的标签(如 app, component, tier, version)。避免使用易变或无意义的标签。
  • 命名空间 (Namespaces): 是逻辑隔离和环境划分的天然边界。利用 namespaceSelector 实现跨命名空间的访问控制。例如,只允许 testing 命名空间的 Pod 访问 staging 命名空间的特定服务。
  • 组合选择器: 灵活运用 podSelectornamespaceSelector 的组合,实现精细的跨命名空间策略。

3. 常见场景示例:

  • 场景一:允许前端 Pod 访问后端 Pod 的特定端口。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:name: frontend-to-backendnamespace: default
spec:podSelector:matchLabels:app: backend # 保护后端 PodpolicyTypes:- Ingressingress:- from:- podSelector:matchLabels:app: frontend # 允许来自前端 Podports:- protocol: TCPport: 8080 # 允许访问后端 8080 端口
  • 场景二:允许命名空间 A 中的 Pod 访问命名空间 B 中的 Pod。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:name: ns-a-to-ns-bnamespace: ns-b # 保护 ns-b 中的 Pod
spec:podSelector: {} # ns-b 中所有 PodpolicyTypes:- Ingressingress:- from:- namespaceSelector:matchLabels:name: ns-a # 允许来自 ns-a 命名空间中任何 Pod
  • 场景三:允许 Pod 访问外部数据库 IP (Egress)。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:name: pod-to-dbnamespace: default
spec:podSelector:matchLabels:app: myapp # 限制 myapp Pod 的出站policyTypes:- Egressegress:- to:- ipBlock:cidr: 10.10.20.30/32 # 数据库 IPports:- protocol: TCPport: 3306 # 数据库端口# 通常还需要允许 DNS 查询- to: []ports:- protocol: UDPport: 53
  • 场景四:允许 Pod 访问 Kubernetes API Server (Egress)。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:name: allow-api-servernamespace: default
spec:podSelector:matchLabels:app: needs-api # 需要访问 API 的 PodpolicyTypes:- Egressegress:- to:- ipBlock:cidr: <KUBERNETES_SERVICE_IP>/32 # API Server ClusterIP (如 10.96.0.1)ports:- protocol: TCPport: 443 # API Server 端口# 或者直接允许访问 API Server 的 Pod IP(如果知道且稳定)# - to:#   - ipBlock:#       cidr: <API_SERVER_POD_IP>/32#   ports:#   - protocol: TCP#     port: 6443
  • 场景五:隔离命名空间,仅允许特定入站。
# 1. 默认拒绝所有入站和出站
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:name: default-deny-allnamespace: secure-ns
spec:podSelector: {}policyTypes:- Ingress- Egress# 2. 允许来自 Ingress Controller 的流量 (假设 Ingress Controller 在 ingress-ns 命名空间)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:name: allow-ingressnamespace: secure-ns
spec:podSelector: {} # 所有 PodpolicyTypes:- Ingressingress:- from:- namespaceSelector:matchLabels:name: ingress-nspodSelector:matchLabels:app: ingress-controller # 假设 Ingress Controller 有此标签ports:- protocol: TCPport: 80 # HTTP- protocol: TCPport: 443 # HTTPS# 3. 允许 DNS 和 API Server 访问 (Egress)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:name: allow-dns-apinamespace: secure-ns
spec:podSelector: {}policyTypes:- Egressegress:- to: [] # 允许访问任何 IP 的 DNS (UDP 53)ports:- protocol: UDPport: 53- to:- ipBlock:cidr: <KUBERNETES_SERVICE_IP>/32ports:- protocol: TCPport: 443

4. 调试与排错:

    • 确认策略生效:检查 Pod 是否被策略选中:kubectl describe pod <pod-name> -n <ns>,查看是否有 NetworkPolicy 相关的 Annotation(如 k8s.v1.cni.cncf.io/networks-status)或使用 kubectl get networkpolicy 查看策略的 podSelector
    • 检查 CNI 插件日志:查看 Calico Felix (kubectl logs -n calico-system <felix-pod>)、Cilium Agent (kubectl logs -n kube-system <cilium-agent>) 等日志,看策略是否被正确加载和翻译。
    • 检查节点上的 ACL:对于 Calico iptables 模式,登录节点执行 sudo iptables -L -v -n | grep cali 查看规则和计数器。计数器 (pkts, bytes) 增长表示规则被匹配。sudo ipset list 查看 ipset 内容。
    • 测试连通性:使用 kubectl exec 进入源 Pod,ping/curl/telnet 目标 Pod IP/端口。
    • 使用 kubectl port-forward 暴露服务进行测试。
    • 使用 tcpdump 在源 Pod、目标 Pod、节点 veth 接口上抓包,观察数据包是否被发送、是否到达、是否被丢弃(无响应或 ICMP Port Unreachable)。
    • 常见问题:策略未生效: Pod 未被任何策略选中(检查 podSelector 和 Pod 标签);CNI 插件未正确安装或配置;策略语法错误。
    • 允许的流量被拒绝: 规则写错(端口、协议、IP、标签);Service 访问未转换为后端 Pod IP 访问(策略需允许目标 Pod IP);conntrack 表满或状态异常;MTU 问题导致分片被丢弃;底层网络问题。
    • 默认拒绝过于严格: 忘记允许 DNS 查询(UDP 53)、访问 Kubernetes API Server、节点本地服务(如 kubelet metrics)等必要出站流量。

5. 性能考虑:

  • 规则数量: 过于复杂或数量庞大的 NetworkPolicy 规则会增加 iptables/nftables/eBPF 规则集的规模,可能影响数据包处理性能。尽量保持策略简洁。
  • ipset 效率: Calico/Cilium 等使用 ipset 的插件在处理大量 Pod IP 时性能较好。避免在策略中直接写大量单个 IP。
  • eBPF 优势: 对于对网络性能要求极高的场景,Cilium 的 eBPF 实现通常比 iptables 有显著性能优势。
  • 监控: 监控节点上的 CPU 使用率(特别是软中断 si)、网络吞吐量、延迟。监控 CNI 插件自身的指标(如 Calico Felix 的 Prometheus 指标)。

总结

Kubernetes 的网络模型是其强大生命力的关键支撑。通过本文的深度解析,我们揭示了其核心组件的精妙设计与复杂交互:

  1. CNI 插件: 作为网络基础设施的构建者,Flannel 以其简单易用、覆盖网络的特性成为入门和中小规模集群的理想选择,而 Calico 则凭借纯三层 BGP 路由、原生高性能策略、灵活的外部集成能力,成为大规模、高性能、高安全要求生产环境的标杆。理解它们的架构差异(Overlay vs Routing)、性能权衡(封装开销 vs 路由依赖)和适用场景,是进行技术选型和网络优化的基础。
  2. Pod 通信与跨节点路由: 从同节点的高效网桥/直连路由,到跨节点的复杂路径,我们追踪了数据包的完整旅程。Flannel VXLAN 通过隧道封装跨越了底层网络的限制,而 Calico 纯 BGP 则利用标准路由协议将 Pod 网络无缝融入物理网络,实现了接近物理极限的性能。掌握不同模式下的路由表维护、封装/解封装过程、MTU 处理和对物理网络的依赖,是进行网络规划和故障排查的核心技能。
  3. NetworkPolicy: 作为微服务安全的守护者,NetworkPolicy 提供了声明式、基于标签的网络微分段能力。其“默认拒绝”的核心语义是实现零信任网络的基石。通过 Calico 等插件的实现,我们看到策略如何被翻译成高效的内核级 ACL(如 iptables/nftables 或 eBPF)。遵循最小权限原则、合理运用标签与命名空间、掌握常见场景的配置方法以及有效的调试手段,是构建安全、合规、可运维的 Kubernetes 网络环境的关键。

Kubernetes 网络的深度和广度远超本文所及,涉及 SDN、内核网络、服务网格、云原生网络等更广阔的领域。然而,深入理解 CNI、Pod 通信和 NetworkPolicy 这三大支柱,就如同掌握了 Kubernetes 网络世界的“牛顿定律”,为探索更高级的主题、解决复杂的网络问题、构建高性能高可用的云原生应用奠定了坚实的基础。随着云原生技术的持续演进,Kubernetes 网络模型也将在性能、安全性、可观测性和易用性上不断突破,持续为分布式系统的繁荣发展提供强大的网络动力。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/94373.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/94373.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构青铜到王者第二话---数据结构基本常识(2)

续接上一话 一、包装类 在Java中&#xff0c;由于基本类型不是继承自Object&#xff0c;为了在泛型代码中可以支持基本类型&#xff0c;Java给每个基本类型都对应了一个包装类型。 1、基本数据类型和对应的包装类 除了 Integer 和 Character&#xff0c; 其余基本类型的包装类…

fastdds qos:DeadlineQosPolicy

1含义DeadlineQosPolicy这种qos使用在DataWriter、DataReader、Topic。该qos用来监督数据是不是按照预期的频率进行收发。假如数据是周期性发送和接收&#xff0c;周期是固定的100ms&#xff0c;我们如果想要监督数据收发是不是按照预期的周期进行的&#xff0c;那么就可以配置…

QT-窗口类部件

Qt窗口类部件 一、窗口类部件 窗口就是没有父部件的部件&#xff0c;所以又称顶级部件。窗口类主要包括基本窗口类QWidget、对话框类QDialog和主窗口类QMainWindow三种。QObject是Qt框架中的一个核心基类&#xff0c;它提供了对象模型和信号槽机制。而QPaintDevice及其子类则提…

【CSP初赛】程序阅读3

文章目录前置知识阅读程序判断选择答案解析判断选择总结前置知识 埃氏筛素数、C 基础。 阅读程序 #include <bits/stdc.h> using namespace std; int main(){int a1[51] {0};int i,j,t,t2,n 50;for(i 2;i<sqrt(n);i){if(a1[i] 0){t2 n/i;for(j 2;j<t2;j) …

【ESP32-IDF】高级外设开发4:SPI

系列文章目录 持续更新中… 文章目录系列文章目录前言一、SPI概述1.主要功能2.SPI控制器架构3.SPI通信模式4.SPI数据帧与事务5.DMA与传输性能6.中断与驱动事件二、SPI类型定义及相关API三、SPI示例程序总结前言 在嵌入式开发中&#xff0c;SPI&#xff08;串行外设接口&#…

遥感机器学习入门实战教程|Sklearn案例⑧:评估指标(metrics)全解析

很多同学问&#xff1a;“模型好不好&#xff0c;怎么量化&#xff1f;” 本篇系统梳理 sklearn.metrics 中常用且“够用”的多分类指标&#xff0c;并给出一段可直接运行的示例代码&#xff0c;覆盖&#xff1a;准确率、宏/微/加权 F1、Kappa、MCC、混淆矩阵&#xff08;计数/…

【Bluedroid】深入解析A2DP SBC编码器初始化(a2dp_sbc_encoder_init)

SBC(Subband Coding)作为蓝牙 A2DP 协议的标准编解码器,其编码器的初始化与参数配置直接影响音频传输的音质、效率与兼容性。本文基于Andoird A2DP 协议栈源码,系统剖析 SBC 编码器的初始化流程,包括核心参数(比特池、采样率、声道模式等)的解析、计算与动态调整逻辑,以…

linux shell测试函数

在 C 语言中&#xff0c;int main(int argc, char *argv[])是程序的入口函数&#xff0c;而​​在 main函数中调用专门的测试逻辑&#xff08;如测试函数&#xff09;​​的程序结构&#xff0c;通常被称为​​测试程序&#xff08;Test Program&#xff09;​​或​​测试驱动…

【Java SE】抽象类、接口与Object类

文章目录一、 抽象类&#xff08;Abstract Class&#xff09;1.1 什么是抽象类&#xff1f;1.2 抽象类的语法1.2.1 定义抽象类1.2.2 继承抽象类1.3 抽象类的特性1.3.1 不能直接实例化1.3.2 抽象方法的限制1.3.3 抽象类可以包含构造方法1.3.4 抽象类不一定包含抽象方法1.3.5 抽象…

Autodl 创建新虚拟环境 python3.9

问题&#xff1a;本人在autodl上保存的环境因为很长时间没有开机&#xff0c;autodl竟然给我删除了。后来看了官网的介绍我才发现&#xff0c;原来15天不开机&#xff0c;autodl就会自动释放实例。 因此&#xff0c;我就自己重新选了一个虚拟环境&#xff0c;从头开始配置。 GP…

应急响应靶机-WindowsServer2022挖矿事件

依旧手痒开局&#xff0c;知攻善防实验室的原创靶机 https://mp.weixin.qq.com/s/URrNHvQSnFKOyefHKXKjQQ 相关账户密码&#xff1a; Administrator/zgsf123 注意&#xff1a;做个原始快照&#xff08;方便日后复习&#xff09;&#xff0c;安装VMware tool&#xff08;安装后图…

PCB电路设计学习3 电路原理图设计 元件PCB封装设计与添加

目录PCB电路设计学习3五、电路原理图设计5.1 32个发光二极管电路5.2 单片机外围电路5.3 供电与程序下载电路5.4 连接各部分网络&#xff0c;绘制边框和说明六、元件PCB封装设计与添加6.1 名词解释6.2 绘制PCB附学习参考网址欢迎大家有问题评论交流 (* ^ ω ^)PCB电路设计学习3 …

redis---常用数据类型及内部编码

Redis 中每种常用数据类型都对应多种内部编码&#xff0c;这些编码会根据数据特征&#xff08;如大小、数量&#xff09;自动切换&#xff0c;以平衡存储效率和操作性能。1.字符串&#xff08;String&#xff09;用途&#xff1a;存储文本、数字或二进制数据&#xff0c;是最基…

crypto.randomUUID is not a function

在本地运行时 crypto.randomUUID 好使&#xff0c;build 后放到服务器上用域名访问就不好使。原因&#xff1a;浏览器策略&#xff0c;浏览器在非https、localhost的环境中访问时&#xff0c;crypto.randomUUID 是不可用的开发时使用的是localhost正常访问 生产临时使用的是htt…

【思考】什么是服务器?什么是服务?什么是部署?

文章目录1 什么是服务器&#xff1f;什么是服务&#xff1f;端口是什么意思&#xff1f;2 什么是部署&#xff1f;1 什么是服务器&#xff1f;什么是服务&#xff1f;端口是什么意思&#xff1f; 服务器本质是一台运行着程序的电脑&#xff0c;它可以运行着很多程序&#xff0c…

自动驾驶导航信号使用方式调研

1 总结 本文调研在给定导航信号后&#xff0c;如何在端到端架构下&#xff0c;利用导航信息引导轨迹生成。 目前主流的方案可以分为2种。一种是将导航作为“前置引导”深度融入轨迹生成过程&#xff08;导航前置型&#xff09;&#xff1b;另一种则是将导航作为“后置评价”标准…

玳瑁的嵌入式日记D21-08020(数据结构)

双向链表double link listtypedef struct dou_node { DATATYPE data; struct dou_node *prev; struct dou_node *next; }DouLinkNode;双向链表&#xff1a;节点 数据 NEXT PREV . 手撕代码(增加删除) 增加&#xff0c;删除的操作&#xff0c; 需要 tmp 停止待操作节点的前一…

Uipath查找元素 查找子元素 获取属性活动组合使用示例

Uipath 查找元素 查找子元素 获取属性组合使用示例使用场景案例介绍项目流程图附加浏览器查找元素查找子元素遍历循环获取属性点击元素使用场景 在实际场景中&#xff0c;有时需RPA自动点击某组范围元素或获取某组范围元素的值&#xff0c;如需获取指定的父元素&#xff0c;再…

【MongoDB与MySQL对比】

MongoDB 与 MySQL 全方位对比分析在现代软件开发中&#xff0c;数据库的选择直接影响系统性能、扩展性和开发效率。MongoDB 和 MySQL 作为两种主流数据库&#xff0c;分别代表了 NoSQL 和关系型数据库的典型&#xff0c;各自在不同场景中发挥着重要作用。本文将抛开代码示例&am…

Spring AI开发指导-对话模型

对话模型接口描述Spring AI基于Spring Cloud的架构体系&#xff0c;定义了一系列可扩展的API接口&#xff0c;支持对接不同类型的AI大模型的核心功能&#xff0c;这些API接口支持同步编程模式或者异步编程模式&#xff1a;接口ModelModel是同步编程模式接口&#xff0c;其参数支…