Language:Chinese VersionEnglish Version

容器网络比容器运行更复杂

运行容器化应用程序很简单。将它连接到其他服务、执行网络策略以及在 Kubernetes 集群中保护东西向流量——这才是大多数团队发现他们所承诺的复杂性的地方。容器网络位于 Linux 内核网络、分布式系统设计和云提供商特定要求的交叉点,而文档很少解释这些层如何交互。这篇深入探讨文章涵盖了 CNI 插件、服务网格架构和 Kubernetes 网络策略,提供了在出现问题时真正有用的技术细节级别。

容器网络实际如何工作

在评估 CNI 插件之前,你需要清楚地了解它们要解决的问题。当容器启动时,它会获得一个网络命名空间——一个具有自己网络接口、路由表和 iptables 规则的内核级隔离边界。默认情况下,这个命名空间没有外部连接。容器网络接口(CNI)规范定义了插件如何将这个隔离的命名空间连接到外部世界。

每次在 Kubernetes 中创建一个 Pod,kubelet 会按顺序调用配置的 CNI 插件。插件负责:

  • 从预分配的 CIDR 中为 Pod 分配 IP 地址
  • 创建一个虚拟以太网对(veth pair),将 Pod 的命名空间连接到主机命名空间
  • 确保发往任何其他 Pod IP 的流量能够到达它,无论它运行在哪个节点上

第三个要求——跨节点的 Pod 间可达性——是 CNI 插件在方法上显著不同的地方。

CNI 插件比较

Flannel:简单覆盖,有限功能

Flannel 是最简单的生产级 CNI 插件。它使用 VXLAN(或在可信网络上使用 host-gw 模式以获得更好性能)创建覆盖网络。每个节点获得一个子网,Flannel 将跨节点流量封装在通过主机网络路由的 UDP 数据包中。

# Flannel ConfigMap — VXLAN 后端(典型生产配置)
apiVersion: v1
kind: ConfigMap
metadata:
  name: kube-flannel-cfg
  namespace: kube-flannel
data:
  net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "vxlan",
        "VNI": 1,
        "Port": 8472
      }
    }

Flannel 的简单性也是它的局限性:它不提供网络策略执行。要在 Flannel 中使用 Kubernetes NetworkPolicy 对象,你需要一个单独的策略守护进程,如仅策略模式的 Calico。对于不需要网络策略的集群,Flannel 仍然是一个可靠、低开销的选择。

Calico:BGP 路由和丰富策略

Calico 使用 BGP(边界网关协议)在节点之间分发路由信息,而不是使用覆盖网络。每个 Pod 的 IP 是一个真实可路由的地址,节点通过 BGP 交换路由表。这避免了覆盖封装的开销,在高数据包速率情况下这一点尤为重要。

# Calico IPPool — 定义地址范围和路由模式
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
  name: default-ipv4-ippool
spec:
  cidr: 10.244.0.0/16
  ipipMode: Never        # 无覆盖封装
  vxlanMode: Never       # 使用 BGP 路由替代
  natOutgoing: true
  nodeSelector: all()

Calico 的网络策略实现也比标准的 Kubernetes NetworkPolicy API 丰富得多。Calico GlobalNetworkPolicy 对象可以跨命名空间强制执行策略,而 Calico 的 HostEndpoint 资源将策略执行扩展到主机网络接口 — 这一功能对于裸机或基于 VM 的集群特别相关,在这些集群中,您需要保护那些永远不会接触 Pod 的流量。

Cilium: 基于 eBPF 的网络

Cilium 用直接加载到 Linux 内核中的 eBPF 程序替代了基于 iptables 的数据包处理。这种架构选择具有显著的性能影响:eBPF 程序在内核中运行,没有 iptables 链的上下文切换开销,并且它们可以原子地更新,而无需重新加载整个规则集。

# Cilium NetworkPolicy 带 L7 HTTP 规则
# 标准的 Kubernetes NetworkPolicy 无法实现此功能
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: api-service-l7-policy
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: api-service
  ingress:
  - fromEndpoints:
    - matchLabels:
        app: frontend
    toPorts:
    - ports:
      - port: "8080"
        protocol: TCP
      rules:
        http:
        - method: "GET"
          path: "/api/v1/products"
        - method: "POST"
          path: "/api/v1/orders"

此策略只允许前端服务向 /api/v1/products 发送 GET 请求,向 /api/v1/orders 发送 POST 请求。任何其他 HTTP 方法或路径都会在内核级别被拒绝 — 而不是到达应用程序进程之后。这是基于 iptables 的 CNI 无法表达的 L7 感知网络策略。

Cilium 还包括 Hubble,一个可观测性层,可以在不采样的情况下提供集群范围内的实时流量可见性:

# 使用 Hubble CLI 观察流量
$ hubble observe --namespace production --follow

# 过滤被丢弃的流量(策略违规)
$ hubble observe --namespace production 
  --verdict DROPPED 
  --follow

# 示例输出:
Mar 29 08:23:11.492: production/frontend-6d8f9b-xyz:52341 
  -> production/payments-7c4d2a-abc:8080 
  http-request DROPPED (policy-deny)
  GET /api/v1/admin/refund HTTP/1.1

这种可见性对于调试服务为何无法相互连接时非常宝贵——您可以实时查看流量是否被策略丢弃以及是哪个策略负责。

服务网格:它增加了什么以及它带来了什么成本

CNI 插件处理 IP 连接和基本网络策略。服务网格在此基础上增加了一层,专注于服务间通信的流量管理、可观测性和安全性。这两个关注点是互补的——您通常会同时运行两者。

Sidecar 模型与 Ambient Mesh

传统的服务网格如 Istio(1.21 版本之前)和 Linkerd 会在每个 Pod 中注入一个 sidecar 代理容器。这个代理拦截 Pod 的所有入站和出站流量,并应用网格功能:mTLS、流量整形、重试逻辑和遥测收集。

Sidecar 模型有明确的成本:每个 Pod 都携带一个额外的容器(Istio 是 Envoy,Linkerd 是 linkerd-proxy),它会消耗 CPU 和内存。在一个有 500 个 Pod 的集群中,这意味着有 500 个额外的代理进程。Istio 的 ambient 模式在 Istio 1.22(2024年)中作为稳定版发布,它通过将网格功能移动到每个节点的代理(ztunnels)和用于 L7 处理的共享 waypoint 代理来解决这一问题。

# 为命名空间启用 Istio ambient 模式
kubectl label namespace production istio.io/dataplane-mode=ambient

# 验证 ambient 注册
kubectl get pods -n production -o wide
# Pod 不会显示注入的 sidecar 容器
# 但流量仍然被节点级别的 ztunnel 拦截

# 为此命名空间部署用于 L7 策略的 waypoint 代理
istioctl waypoint apply --namespace production

# 现在 L7 AuthorizationPolicy 对象对 ambient-mode Pod 生效
kubectl apply -f - <

流量管理:实用模式

团队在生产环境中实际使用的服务网格功能往往是文档所涵盖功能的一小部分。最有价值的模式是:

使用流量转移的灰度发布:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: api-service
  namespace: production
spec:
  hosts:
  - api-service
  http:
  - route:
    - destination:
        host: api-service
        subset: stable
      weight: 90
    - destination:
        host: api-service
        subset: canary
      weight: 10
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: api-service
  namespace: production
spec:
  host: api-service
  subsets:
  - name: stable
    labels:
      version: v2.1.0
  - name: canary
    labels:
      version: v2.2.0-rc1

重试和超时策略:

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: payment-service
  namespace: production
spec:
  hosts:
  - payment-service
  http:
  - timeout: 5s
    retries:
      attempts: 3
      perTryTimeout: 2s
      retryOn: 5xx,reset,connect-failure
    route:
    - destination:
        host: payment-service

Kubernetes NetworkPolicy:能做什么和不能做什么

标准的 Kubernetes NetworkPolicy API 由 CNI 插件(而非 Kubernetes 本身)强制执行,这意味着只有你的 CNI 支持它才能工作。Flannel 不支持,而 Calico、Cilium 和 Weave 都支持。

# 生产命名空间的合理默认拒绝策略
# 在部署服务之前应用此策略,然后根据需要添加允许规则
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}  # 选择命名空间中的所有 pod
  policyTypes:
  - Ingress
  - Egress

---
# 允许 DNS 解析(几乎所有服务都需要)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-egress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: kube-system
    ports:
    - port: 53
      protocol: UDP
    - port: 53
      protocol: TCP

标准的 NetworkPolicy API 有重要限制:它无法表达 L7 规则(没有 HTTP 方法/路径过滤),它没有排序语义(所有匹配的策略都是累加的),它无法通过域名引用外部 IP 范围。对于这些用例,你需要使用 Cilium 的 CiliumNetworkPolicy 或 Calico 的 GlobalNetworkPolicy。

选择合适的堆栈

容器网络决策矩阵归结为三个问题:你的性能预算是多少?是否需要 L7 网络策略?是否需要服务网格?

  • 简单集群,无需网络策略: Flannel VXLAN。运营开销低,文档丰富。
  • 需要网络策略,支持BGP的基础设施: Calico配合BGP模式。原生路由性能,丰富的策略表达。
  • L7网络策略,高可观测性需求: Cilium。eBPF性能,Hubble可观测性,L7策略支持。
  • 用于mTLS和流量管理的服务网格,更低开销: Istio ambient模式或Linkerd。Ambient模式消除了每个pod的sidecar成本。

除非你有明确的用例能够证明运营复杂性是合理的,否则不要运行服务网格。对于大多数团队而言,Cilium的网络策略功能能够满足安全需求,只有当你需要复杂的部署流量管理或每对服务之间的mTLS是严格的合规要求时,服务网格才值得考虑。

关键要点

  • CNI插件处理IP分配和跨节点路由。覆盖路由与BGP路由的选择会影响性能和网络拓扑需求。
  • Cilium的eBPF方法实现了基于iptables的CNI无法表达的L7感知网络策略,并通过Hubble提供实时流量可观测性。
  • 服务网格在CNI层之上添加了mTLS、流量管理和遥测功能。Istio的ambient模式显著降低了sidecar模型的运营成本。
  • 在部署服务之前,始终为生产命名空间应用默认拒绝的NetworkPolicy,然后添加明确的允许策略。
  • 标准的Kubernetes NetworkPolicy无法表达L7规则——对于HTTP感知的策略,请使用Cilium或服务网格的AuthorizationPolicy。

By Michael Sun

Founder and Editor-in-Chief of NovVista. Software engineer with hands-on experience in cloud infrastructure, full-stack development, and DevOps. Writes about AI tools, developer workflows, server architecture, and the practical side of technology. Based in China.

Leave a Reply

Your email address will not be published. Required fields are marked *

You missed