网络概览

本页面简要介绍 Google Kubernetes Engine 网络的几个主要方面。这些信息适合刚刚开始使用 Kubernetes 的人员,以及经验丰富的集群运营商或应用开发者;他们需要具备更多的 Kubernetes 网络知识,以便更好地设计应用或配置 Kubernetes 工作负载。

利用 Kubernetes,您可以采用声明方式定义应用的部署方式、应用与应用之间以及应用与 Kubernetes 控制平面之间的通信方式,还可以定义客户端访问应用的方式。本页面还介绍了 GKE 如何配置与网络相关的 Google Cloud 服务。

在使用 Kubernetes 编排应用时,您必须改变对应用及其主机的网络设计的构思方式。使用 Kubernetes 时,您需要考虑的是 Pod、Service 和外部客户端的通信方式,而不是主机或虚拟机的连接方式。

Kubernetes 的高级软件定义网络 (SDN) 支持在同一区域级集群中的不同地区之间路由和转发 Pod、Service、节点的数据包。Kubernetes 和 Google Cloud 还会在每个节点上动态配置 IP 过滤规则、路由表和防火墙规则,具体取决于 Kubernetes 部署的声明模型和 Google Cloud 上的集群配置。

前提条件

本页面使用了与互联网协议套件(包括 HTTPDNS)的传输层、互联网层和应用层相关的术语,但您无需深入了解这些主题。

此外,如果您对 Linux 网络管理概念和实用程序(如 iptables 规则和路由)有基本了解,则可能更容易理解这些内容。

Kubernetes 网络模型在很大程度上依赖于 IP 地址。服务、Pod、容器和节点使用 IP 地址和端口进行通信。Kubernetes 提供不同类型的负载平衡,用于将流量定向到正确的 Pod。本主题的后面部分将更详细地介绍所有这些机制。在阅读本页中的内容时,请牢记以下术语:

  • ClusterIP 地址:分配给 Service 的 IP 地址。在其他文档中,这可能被称为“集群 IP”。此地址会在 Service 的生命周期内保持不变,具体请参阅本主题的 Service 部分。
  • Pod IP 地址:分配给给定 Pod 的 IP 地址。这是临时地址,具体请参阅本主题的 Pod 部分。
  • 节点 IP:分配给给定节点的 IP 地址。

集群内部网络

本部分介绍了 Kubernetes 集群中的网络设置,它与 IP 地址分配PodService 的关系。

IP 分配

Kubernetes 使用各种 IP 范围为节点、Pod 和服务分配 IP 地址。

  • 系统会从集群的 Virtual Private Cloud (VPC) 网络为每个节点分配一个 IP 地址。此节点 IP 地址用于提供从控制组件(如 kube-proxykubelet)到 Kubernetes API 服务器的连接。此 IP 地址用于提供节点与集群其余组件的连接。
  • 每个节点都有一个 IP 地址池(默认为 /24 CIDR 块),供 GKE 分配在该节点上运行的 Pod。您可以选择在创建集群时指定 IP 地址范围。借助灵活的 Pod CIDR 范围功能,您可以缩小给定节点池中节点的 Pod IP 地址范围。

  • 每个 Pod 都有从其节点的 Pod CIDR 范围分配的单个 IP 地址。此 IP 地址由 Pod 内运行的所有容器共享,并将它们连接到集群中运行的其他 Pod。

  • 系统会从集群的 VPC 网络为每项 Service 分配一个 IP 地址(称为 ClusterIP 地址)。您可以选择在创建集群时自定义 VPC 网络。

如需了解详情,请参阅使用别名 IP 地址创建 VPC 原生集群

Pod

在 Kubernetes 中,Pod 是可在 Kubernetes 集群中部署的最基本单元。Pod 运行一个或多个容器。 节点运行零个或更多 Pod。集群中的每个节点都是节点池的一部分。在 GKE 中,这些节点是虚拟机,每个节点都作为 Compute Engine 中的一个实例运行。

Pod 还可以连接到外部存储卷和其他自定义资源。 下图显示了一个运行两个 Pod 的节点,每个 Pod 都连接到两个卷。

显示了运行两个 Pod 的单个节点(如上一段所述)的图片

当 Kubernetes 安排 Pod 在某节点上运行时,它会在该节点的 Linux 内核中为 Pod 创建网络命名空间。此网络命名空间使用虚拟网络接口将节点的物理网络接口(如 eth0)与 Pod 连接起来,从而使数据包可以进出 Pod。节点的根网络命名空间中的关联虚拟网络接口连接到 Linux 网桥,可让同一节点上的各 Pod 之间进行通信。Pod 还可以使用同一虚拟接口在节点外部发送数据包。

Kubernetes 会从为节点上的 Pod 预留的一系列地址中,为 Pod 的网络命名空间中的虚拟网络接口分配一个 IP 地址(即 Pod IP)。该地址范围是分配给 Pod 所在集群的 IP 地址范围的子集,可在创建集群时进行配置。

Pod 中运行的容器使用 Pod 的网络命名空间。从容器的角度来看,Pod 是具有一个网络接口的物理机器。Pod 中的所有容器都会看到此同一网络接口。 每个容器的 localhost 通过 Pod 连接到节点的物理网络接口(例如 eth0)。

请注意,根据您具体是使用 GKE 的原生 CNI 还是通过在创建群集时启用网络政策来选择使用 Calico 的实现,该连接会有很大差别。

  • 如果使用 GKE 的 CNI,则 veth pair 一端连接其命名空间中的 Pod,另一端连接 Linux 网桥设备 cbr0。在这种情况下,以下命令会显示连接到 cbr0 的各个 Pod 的 MAC 地址:

    arp -n
    

    此外,在工具箱容器中调用以下命令会显示连接到 cbr0 的各 veth pair 的根命名空间端:

    brctl show cbr0
    
  • 如果启用了网络政策,则 veth pair 的一端连接到 Pod,另一端连接到 eth0。在这种情况下,以下命令会显示连接到不同 veth 设备的各个 Pod 的 MAC 地址。

    arp -n
    

    此外,在工具箱容器中调用以下命令会显示系统中没有名为 cbr0 的 Linux 网桥设备:

    brctl show
    

集群内促进转发的 iptables 规则因场景而异。详细排查连接问题时,必须清楚地意识到这一差别。

默认情况下,每个 Pod 对所有集群节点上运行的其他所有 Pod 都具有未经过滤的访问权限,但您可以限制各 Pod 之间的访问。Kubernetes 会定期删除并重新创建 Pod。如果升级节点池、更改 Pod 的声明性配置、更改容器的映像,或节点不可用,就会发生这种情况。因此,Pod 的 IP 地址属于实现细节,您不应该依赖于这些地址。Kubernetes 使用 Service 提供稳定的 IP 地址。

Service

在 Kubernetes 中,您可以为任何 Kubernetes 资源分配任意键值对(称为标签)。Kubernetes 使用标签来将多个相关的 Pod 组合成一个逻辑单元(称为 Service)。Service 具有稳定的 IP 地址和端口,并会在一组 Pod 之间提供负载平衡(前提是这些 Pod 的标签与您在创建 Service 时于标签选择器中定义的所有标签相匹配)。

下图显示了两项独立的 Service,每项 Service 都由多个 Pod 组成。图中的每个 Pod 都带有 app=demo 标签,但这些 Pod 的其他标签有所不同。“frontend” Service 匹配所有带有 app=democomponent=frontend 的 Pod,而“users” Service 匹配所有带有 app=democomponent=users 的 Pod。客户端 Pod 与任一 Service 选择器都不完全匹配,因此它不是任一 Service 的一部分。但是,由于客户端 Pod 在同一集群内运行,因此它可与任一 Service 进行通信。

该图显示了两项独立服务(如前一段所述)

Kubernetes 会从集群的可用 Service IP 地址池中为每项新创建的 Service 分配一个稳定可靠的 IP 地址(ClusterIP 地址)。Kubernetes 还会通过添加 DNS 条目为 ClusterIP 地址分配主机名。ClusterIP 地址和主机名在集群内是独一无二的,并且在 Service 的整个生命周期内不会更改。只有将 Service 从集群的配置中删除时,Kubernetes 才会释放 ClusterIP 地址和主机名。您可以使用服务的 ClusterIP 或主机名访问正常运行应用的 Pod。

乍一看,服务似乎是应用的单点故障。但是,Kubernetes 会尽可能均匀地将流量分布到在多个节点上运行的一系列 Pod,因此集群可以顺利度过影响一个或多个(但不是所有)节点的服务中断情况。

Kube-Proxy

Kubernetes 使用 kube-proxy 组件管理各个 Pod 和各个 Service 之间的连接。默认情况下,它会在每个节点上部署为静态 Pod。任何运行 1.16 或更高版本的 GKE 集群都将有一个 kube-proxy DaemonSet。这将仅选择运行的 GKE 版本介于 1.16.0 到 1.16.8-gke.13 之间的节点。如果集群没有任何节点运行这些版本,则此 DaemonSet 会显示 0 个 Pod。

kube-proxy 不是内嵌代理,而是一个基于出站流量的负载平衡控制器。该组件通过为节点的 iptables 子系统添加目标 NAT (DNAT) 规则以及从中移除这些规则,监控 Kubernetes API 服务器并持续将 ClusterIP 地址映射到运行状况良好的 Pod。当 Pod 中运行的容器将数据包发送到 Service 的 ClusterIP 地址时,节点会随机选择一个 Pod 并将流量路由到该 Pod。

在配置 Service 时,您可以选择定义 porttargetPort 的值来重新映射其侦听端口。

  • port 是客户端用来访问应用的位置。
  • targetPort 是应用用来实际侦听 Pod 内流量的端口。

kube-proxy 通过在节点上添加 iptables 规则以及从中移除这些规则来管理此端口重新映射过程。

下图演示了从客户端 Pod 到不同节点上的服务器 Pod 的流量流动。客户端通过 172.16.12.100:80 连接到 Service。Kubernetes API 服务器会维护一个运行应用的 Pod 列表。每个节点上的 kube-proxy 进程都使用此列表创建 iptables 规则,将流量定向到适当的 Pod(例如 10.255.255.202:8080)。客户端 Pod 不需要知道集群的拓扑或者个别 Pod 或其中容器的任何详细信息。

演示了客户端如何连接到 Service 并被路由到 Pod(如前一段所述)的图片

集群外部网络

本部分介绍来自集群外部的流量如何到达 Kubernetes 集群内运行的应用。在设计集群的应用和工作负载时,此信息非常重要。

您已经了解了 Kubernetes 如何使用 ServicePod 中运行的应用提供稳定的 IP 地址。默认情况下,Pod 不会公开外部 IP 地址,因为每个节点上的所有流量由 kube-proxy 管理。Pod 及其容器可以自由通信,但集群外部的连接无法访问 Service。例如,在上图中,集群外部的客户端无法通过其 ClusterIP 访问前端服务。

GKE 提供了三种不同类型的负载平衡器,用于控制访问并尽可能均匀地将传入流量分布到整个集群中。您可以将一项 Service 配置为同时使用多种类型的负载平衡器。

  • 外部负载平衡器用于管理来自集群外部和 Google Cloud Virtual Private Cloud (VPC) 网络外部的流量。它们使用与 Google Cloud 网络关联的转发规则来将流量路由到 Kubernetes 节点。
  • 内部负载平衡器用于管理来自同一 VPC 网络的流量。与外部负载平衡器一样,它们使用与 Google Cloud 网络关联的转发规则来将流量路由到 Kubernetes 节点。
  • HTTP(S) 负载平衡器是专用于处理 HTTP(S) 流量的外部负载平衡器。它们使用 Ingress 资源(而不是转发规则)将流量路由到 Kubernetes 节点。

当流量到达 Kubernetes 节点时,无论负载平衡器为何种类型,这些流量都会按照相同方式进行处理。负载平衡器并不清楚集群中的哪些节点正在为其服务运行 Pod。在这种情况下,它会将流量平衡到集群中的所有节点,即使某些节点并没有运行相关 Pod 也是如此。在区域级集群上,负载将分布到集群所在区域内所有地区中的所有节点上。当流量路由到某节点时,该节点会将流量路由到一个 Pod;该 Pod 可能在同一节点上运行,也可能在不同节点上运行。该节点使用 iptables 规则(由 kube-proxy 在节点上管理)将流量转发到随机选择的 Pod。

在下图中,网络负载平衡器将流量定向到中间节点,然后该流量又被重定向到第一个节点上的 Pod。

显示了流量从一个节点路由到另一个节点上的 Pod(如前一段所述)的图片

当负载平衡器将流量发送到节点时,流量可能会被转发到其他节点上的 Pod。这需要额外的网络跃点。如果要避免额外的跃点,您可以指定流量必须转到最初接收流量的节点上的 Pod。

如需指定流量必须转到同一节点上的 Pod,请在 Service 清单中将 externalTrafficPolicy 设置为 Local

apiVersion: v1
kind: Service
metadata:
  name: my-lb-service
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  selector:
    app: demo
    component: users
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080

externalTrafficPolicy 设置为 Local 时,负载平衡器仅将流量发送到具有属于 Service 的正常 Pod 的节点。负载平衡器使用运行状况检查确定哪些节点具有适当的 Pod。

外部负载平衡器

如果您的 Service 需要能够从集群外部或 VPC 网络外部进行访问,您可以将 Service 配置为 LoadBalancer,方法是在定义 Service 时将其 type 字段设置为 LoadBalancer。然后,GKE 会在该 Service 前面预配一个网络负载平衡器。网络负载平衡器可识别集群中的所有节点,并使用 Service 的外部 IP 地址配置 VPC 网络的防火墙规则,以允许从 VPC 网络外部连接到 Service。您可以为 Service 分配静态外部 IP 地址。如需了解详情,请参阅使用静态 IP 地址配置域名

技术详情

使用外部负载平衡器时,系统最初会使用与 Google Cloud 网络关联的转发规则将到达的流量路由到节点。流量到达节点后,节点会使用其 iptables NAT 表选择 Pod。节点的 iptables 规则由 kube-proxy 管理。

内部负载平衡器

对于需要从同一 VPC 网络内到达集群的流量,您可以将 Service 配置为预配一个内部负载平衡器。内部负载平衡器会从集群的 VPC 子网中选择一个 IP 地址,而非使用外部 IP 地址。VPC 网络内的应用或服务可以使用此 IP 地址与集群内的 Service 进行通信。

技术详情

内部负载平衡功能由 Google Cloud 提供。当流量到达给定节点时,该节点会使用其 iptables NAT 表选择 Pod,即使 Pod 位于其他节点上也是如此。节点的 iptables 规则由 kube-proxy 管理。

如需详细了解内部负载平衡器,请参阅内部负载平衡器文档

HTTP(S) 负载平衡器

许多应用(如 RESTful Web 服务 API)使用 HTTP(S) 进行通信。您可以使用 Kubernetes Ingress 资源允许 VPC 网络的外部客户端访问此类应用。 借助 Ingress 资源,您可以将主机名和网址路径映射到集群中的 Service。使用 HTTP(S) 负载平衡器时,您必须将 Service 配置为使用 NodePort 以及 ClusterIP 地址。当流量通过节点的 IP 地址和 NodePort 到达 Service 时,GKE 会将流量路由到 Service 的运行状况良好的 Pod。您可以指定 NodePort,也可以允许 GKE 随机分配一个未使用的端口。

在您创建 Ingress 资源时,GKE 会在 Google Cloud 项目中预配 HTTP(S) 负载平衡器。此负载平衡器会通过 NodePort 向节点的 IP 地址发送请求。请求到达节点后,节点会使用其 iptables NAT 表选择 Pod。节点的 iptables 规则由 kube-proxy 管理。

以下 Ingress 定义通过端口 80 将 demo.example.com 流量路由到名为 frontend 的 Service,并通过端口 8080 将 demo-backend.example.com 流量路由到名为 users 的 Service。

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: demo
spec:
  rules:
  - host: demo.example.com
    http:
      paths:
      - backend:
          serviceName: frontend
          servicePort: 80
  - host: demo-backend.example.com
    http:
      paths:
      - backend:
          serviceName: users
          servicePort: 8080

如需了解详情,请参阅 Google Cloud 上的 Ingress

技术详情

当您创建 Ingress 对象时,GKE Ingress 控制器会根据 Ingress 清单及其关联的 Service 清单中的规则配置 Google Cloud HTTP(S) 负载平衡器。客户端会向 HTTP(S) 负载平衡器发送请求。负载平衡器是一个实际代理;它用于选择节点并将请求转发到该节点的 NodeIP:NodePort 组合。节点使用其 iptables NAT 表选择 Pod。节点的 iptables 规则由 kube-proxy 管理。

限制节点之间的连接

在集群中创建针对节点的入站或出站防火墙规则可能会产生不利影响。例如,将出站拒绝规则应用于集群中的节点可能会破坏 NodePortkubectl exec 等功能。

限制与 Pod 和服务的连接

默认情况下,在同一集群内运行的所有 Pod 都可以自由通信。 但是,您可以根据需要以不同方式限制集群内的连接。

限制 Pod 之间的访问

您可以使用网络政策限制 Pod 之间的访问。通过网络政策定义,您可以根据标签、IP 地址范围和端口号的任意组合来限制 Pod 的入站流量出站流量。默认情况下,系统不配置网络政策,因此允许集群中各个 Pod 之间的所有流量。当您在命名空间中创建第一项网络政策后,系统会拒绝其他所有流量。

如需详细了解如何指定政策本身,请参阅网络政策

创建网络政策后,您必须为集群明确启用该政策。 如需了解详情,请参阅为应用配置网络政策

限制对外部负载平衡器的访问

如果您的 Service 使用外部负载平衡器,则默认情况下,来自任何外部 IP 地址的流量都可以访问您的 Service。通过在配置 Service 时配置 loadBalancerSourceRanges 选项,您可以限制哪些 IP 地址范围可以访问集群内的端点。您可以指定多个范围,并且可以随时更新正在运行的 Service 的配置。在每个节点上运行的 kube-proxy 实例会配置该节点的 iptables 规则,以拒绝与指定的 loadBalancerSourceRanges 不匹配的所有流量。系统不会创建 VPC 防火墙规则。

限制对 HTTP(S) 负载平衡器的访问

如果您的服务使用 HTTP(S) 负载平衡器,则您可以使用 Google Cloud Armor 安全政策限制哪些外部 IP 地址可以访问您的 Service,以及系统因为安全政策而拒绝相关访问时要返回哪些响应。您可以配置 Cloud Logging 以记录这些交互的相关信息。

如果 Google Cloud Armor 安全政策不够细致,您可以在端点上启用 Identity-Aware Proxy,以针对您的应用实现基于用户的身份验证和授权。如需了解详情,请参阅有关配置 IAP 的详细教程

后续步骤