IPv4/IPv6 双栈网络

适用于 Bare Metal 的 GDCV 支持 IPv4/IPv6 双栈网络。这意味着集群可以接受来自使用 互联网协议版本 4 (IPv4) 或互联网协议版本 6 (IPv6) 的外部设备的流量。

双栈网络会向 Pod 和节点同时分配 IPv4 和 IPv6 地址。Kubernetes Service 可以具有 IPv4 地址和/或 IPv6 地址。

所有双栈集群都对 IPv6 使用平面模式。默认情况下,双栈集群对 IPv4 使用孤岛模式,但您可以将其配置为对 IPv4 使用平面模式。

如需创建双栈集群,您的底层网络必须启用双栈。如果您的底层网络是单栈 IPv4 或 IPv6 网络,则您无法启动双栈集群。

准备工作

如果您的集群节点运行的是 CentOS 或 RedHat Enterprise Linux,并且启用了 SELinux,则在每个节点上:

  • /etc/firewalld/firewalld.conf 中,设置 IPv6_rpfilter=no

  • 运行 systemctl restart firewalld

双栈集群创建概览

您可以在创建新集群时启用双栈网络,但无法为现有集群启用双栈网络。

按照集群创建文档之一中的说明执行操作。

在配置文件中,添加以下内容的清单:

  • Namespace 资源
  • Cluster 资源
  • 一个或多个 NodePool 资源
  • 一个或多个 ClusterCIDRConfig 资源

填写 Namespace 清单和 NodePool 清单,这与单栈集群一样。

在 Cluster 清单中的 clusterNetwork.services.cidrBlocks 下,同时指定 IPv4 CIDR 范围和 IPv6 CIDR 范围。这是双栈集群的启用条件。也就是说,如果您同时为 IPv4 和 IPv6 提供 Service CIDR 范围,则您的集群将具有双栈网络。

在 Cluster 清单中的 clusterNetwork.pods.cidrBlocks 下,指定 IPv4 CIDR 范围,但不要指定 IPv6 CIDR 范围。Pod 的 IPv6 CIDR 范围需在 ClusterCIDRConfig 清单中指定。

如果您使用的是捆绑式负载均衡,请在 Cluster 清单的 loadBalancer.addressPools 部分中同时提供 IPv4 和 IPv6 地址。

ClusterCIDRConfig 资源用于为 Pod 指定 IPv4 和 IPv6 CIDR 范围。您可以使用单个 ClusterCIDRConfig 资源来指定集群范围的 CIDR 范围。也就是说,所有节点的 IPv4 Pod 地址均来自单个 CIDR 范围,所有节点的 IPv6 Pod 地址也全部来自单个 CIDR 范围。或者,您也可以使用多个 ClusterCIDRConfig 资源来指定适用于特定节点池或特定节点的 CIDR 范围。

Pod IP 地址的可达性

双栈集群对 IPv6 网络使用平面模式。本文档中提供的示例适用于使用 IPv6 静态平面模式网络的集群。也就是说,集群未配置为使用边界网关协议 (BGP)。

对于使用静态平面模式网络的集群,您必须指定全部属于同一子网的节点和 Pod IP 地址。这样,在集群外部但与集群节点位于同一第 2 层 (L2) 网域的客户端就可以将数据包直接发送到 Pod IP 地址。

例如,假设您的集群节点和一些其他机器都在同一个 L2 网域中。您可以通过以下方法指定地址范围:

目的范围地址数量
整个 L2 网域fd12::/1082^20
Podfd12::1:0/1122^16
节点数fd12::2:0/1122^16
其他机器fd12::3:0/1122^16
VIPfd12::4:0/1122^16

在上面的示例中,您需要了解以下几个要点:

  • 所有节点、Pod 和机器地址均在一个大范围 fd12::/108 内。

  • Pod IP 地址是大范围的一个子集。

  • 节点 IP 地址是大范围的另一个子集。

  • 其他机器的 IP 地址是大范围的另一个子集。

  • 所有子集范围都各不相同。

在上面的示例中,L2 网域中的每个机器(包括集群节点)都必须具有大范围的转发规则。例如:

inet fd12::/108 scope global eth0

示例:创建双栈集群

创建双栈集群时,有多种选项可供您选择。例如,您可以配置集群范围的 CIDR 范围,也可以配置适用于特定节点池的 CIDR 范围。您可以将 IPv6 平面网络与 IPv4 孤岛模式网络结合使用。您也可以将 IPv4 和 IPv6 网络都设为平面网络。您可以使用捆绑式负载均衡,也可以使用手动负载均衡。

本部分通过一个示例介绍如何创建双栈集群。此示例中的集群具有以下特征:

  • 孤岛模式的 IPv4 网络
  • 平面模式的 IPv6 网络
  • Pod 的集群级 IPv4 CIDR 范围
  • Pod 的集群级 IPv6 CIDR 范围
  • Service 的集群级 IPv4 CIDR 范围
  • Service 的集群级 IPv6 CIDR 范围
  • LoadBalancer 类型的 Service 的 IPv4 地址池
  • LoadBalancer 类型的 Service 的 IPv6 地址池
  • 捆绑式负载均衡

如需查看其他配置示例,请参阅 ClusterCIDRConfig 的不同用法

填写配置文件

按照集群创建文档之一中的说明执行操作。

在配置文件的 Cluster 清单中:

  • 对于 clusterNetwork.pods.cidrBlocks,提供单个 IPv4 CIDR 范围。

  • 对于 clusterNetwork.services.cidrBlocks,提供两个 CIDR 范围:一个用于 IPv4,另一个用于 IPv6。

  • 对于 loadBalancer.addressPools,提供两个地址范围:一个用于 IPv4,另一个用于 IPv6。创建 LoadBalancer 类型的 Service 时,系统会从这些范围中选择该 Service 的外部 IP 地址。

以下示例显示了集群清单的相关部分:

apiVersion: baremetal.cluster.gke.io/v1
kind: Cluster
metadata:
  name: "dual-stack"
  namespace: "cluster-dual-stack"

spec:
  clusterNetwork:
    pods:
      cidrBlocks:
      - "192.168.0.0/16"
    services
      cidrBlocks:
       - "172.16.0.0/20"
       - "fd12::5:0/116"
...
  loadBalancer:
    mode: "bundled"
    ...
    addressPools:
    - name: "pool-1"
      addresses:
       - "10.2.0.212-10.2.0.221"
       - "fd12::4:101-fd12::4:110"

在同一配置文件中,添加 ClusterCIDRConfig 的清单。

  • ipv4.cidr 设置为您在 Cluster 清单中提供的 CIDR 范围。如果 IPv4 处于孤岛模式,则此操作是必需的。

  • namespace 设置为您在 Cluster 清单中提供的值。

  • ipv6.cidr 设置为 Pod 的 IPv6 CIDR 范围。

  • 对于每个 CIDR 范围,提供 perNodeMaskSize 的值以指定将分配给每个节点的 Pod 地址数量。分配给每个节点的 IPv4 地址数量必须与分配给每个节点的 IPv6 地址数量相同。您必须相应地设置 perNodeMaskSize 的值。例如,如果每个节点需要 2^8 个地址,请按如下方式设置 perNodeMaskSize 值:

    • ipv4.perNodeMaskSize: 24 # (32 - 8 = 24)
    • ipv6.perNodeMaskSize: 120 # (128 - 8 = 120)

以下是 ClusterCIDRConfig 清单的示例:

apiVersion: baremetal.cluster.gke.io/v1alpha1
kind: ClusterCIDRConfig
metadata:
  name: "cluster-wide-ranges"
  namespace: "cluster-dual-stack"  # Must be the same as the Cluster namespace.
spec:
  ipv4:
    cidr: "192.168.0.0/16"  #  For island mode, must be the same as the Cluster CIDR.
    perNodeMaskSize: 24
  ipv6:
    cidr: "fd12::1:0/112"
    perNodeMaskSize: 120

在前面的示例中:

  • IPv4 Pod CIDR 范围有 2^(32-16) = 2^16 个地址。每个节点的掩码大小为 24,因此分配给每个节点的地址数量为 2^(32-24) = 2^8。

  • IPv6 Pod CIDR 范围有 2^(128-112) = 2^16 个地址。每个节点的掩码大小为 120,因此分配给每个节点的地址数量为 2^(128-120) = 2^8。

示例配置文件

完成集群创建

按照集群创建文档中所述完成集群创建。

查看集群节点和 Pod

列出集群节点:

kubectl --kubeconfig CLUSTER_KUBECONFIG get nodes --output yaml

CLUSTER_KUBECONFIG 替换为集群 kubeconfig 文件的路径。

在输出中,您可以看到每个节点的 IPv4 和 IPv6 地址。您还可以看到节点上 Pod 的 IPv4 和 IPv6 地址范围。例如:

- apiVersion: v1
  kind: Node
  ...
  spec:
    podCIDR: 192.168.1.0/24
    podCIDRs:
    - 192.168.1.0/24
    - fd12::1:100/120
    providerID: baremetal://10.2.0.5
  status:
    addresses:
    - address: 10.2.0.5
      type: InternalIP
    - address: fd12::2:5
      type: InternalIP

列出集群中的 Pod:

kubectl --kubeconfig CLUSTER_KUBECONFIG get pods --all-namespaces

选择一个 Pod,然后列出详细信息。例如:

kubectl --kubeconfig CLUSTER_KUBECONFIG get pod gke-metrics-agent-b9qrv \
  --namespace kube-system \
  -- output yaml

在输出中,您可以看到 Pod 的 IPv4 和 IPv6 地址。例如:

apiVersion: v1
kind: Pod
metadata:
  ...
  name: gke-metrics-agent-b9qrv
  namespace: kube-system
...
status:
  ...
  podIPs:
  - ip: 192.168.1.146
  - ip: fd12::1:11a

ClusterCIDRConfig 的不同用法

上述示例使用了一个 ClusterCIDRConfig 对象来指定集群级 Pod CIDR 范围。也就是说,单个 IPv4 CIDR 范围用于集群中的所有 Pod。同样,单个 IPv6 CIDR 范围用于集群中的所有 Pod。

在某些情况下,您可能不希望将单个 CIDR 范围用于集群中的所有 Pod。例如,您可能需要为每个节点池指定单独的 CIDR 范围,或者可能需要为每个节点指定单独的 CIDR 范围。 如需详细了解 ClusterCIDRConfig 及其用法示例,请参阅了解 ClusterCIDRConfig 自定义资源

例如,以下 ClusterCIDRConfig 为名为 "workers" 的节点池指定 CIDR 范围。

apiVersion: baremetal.cluster.gke.io/v1alpha1
kind: ClusterCIDRConfig
metadata:
  name: "worker-pool-ccc"
  namespace: "cluster-dual-stack"
spec:
  ipv4:
    cidr: "192.168.0.0/16"
    perNodeMaskSize: 24
  ipv6:
    cidr: "fd12::1:0/112"
    perNodeMaskSize: 120
  nodeSelector:
    matchLabels:
      baremetal.cluster.gke.io/node-pool: "workers"

以下 ClusterCIDRConfig 为 IP 地址为 10.2.0.5 的单个节点指定 CIDR 范围:

apiVersion: baremetal.cluster.gke.io/v1alpha1
kind: ClusterCIDRConfig
metadata:
  name: "range-node1"
  namespace: "cluster-dual-stack"
spec:
  ipv4:
    cidr: "192.168.1.0/24"
    perNodeMaskSize: 24
  ipv6:
    cidr: "fd12::1:0/120"
    perNodeMaskSize: 120
  nodeSelector:
    matchLabels:
      baremetal.cluster.gke.io/k8s-ip: "10.2.0.5"

创建 ClusterIP 类型的双栈 Service

以下是 Deployment 的清单:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: "my-deployment"
spec:
  selector:
    matchLabels:
      app: "try-dual-stack"
  replicas: 3
  template:
    metadata:
      labels:
        app: "try-dual-stack"
    spec:
      containers:
      - name: "hello"
        image: "us-docker.pkg.dev/google-samples/containers/gke/hello-app:2.0"

将此清单保存到名为 my-deployment.yaml 的文件中,然后创建 Deployment:

kubectl --kubeconfig CLUSTER_KUBECONFIG apply -f my-deployment.yaml

CLUSTER_KUBECONFIG 替换为集群 kubeconfig 文件的路径。

以下是 ClusterIP 类型的 Service 的清单:

apiVersion: v1
kind: Service
metadata:
  name: "my-service"
spec:
  selector:
    app: "try-dual-stack"
  type: "ClusterIP"
  ipFamilyPolicy: "RequireDualStack"
  ipFamilies:
  - "IPv6"
  - "IPv4"
  ports:
  - port: 80
    targetPort: 8080

在本练习中,您需要了解上述 Service 清单的以下要点:

  • ipFamilyPolicy 字段设置为 RequireDualStack。这表示为该 Service 同时分配了 IPv6 和 IPv4 ClusterIP 地址。

  • ipFamilies 字段先指定了 IPv6 系列,然后指定了 IPv4 系列。这意味着此 Service 的 spec.ClusterIP 是从 Cluster 清单内的 clusterNetwork.services.cidrBlocks 中选择的 IPv6 地址。

将此清单保存到名为 my-cip-service.yaml 的文件中,然后创建该 Service:

kubectl --kubeconfig CLUSTER_KUBECONFIG apply -f my-cip-service.yaml

列出 Service 的详细信息:

kubectl --kubeconfig CLUSTER_KUBECONFIG get service my-service --output yaml

在输出中,您可以看到 Service 的集群 IP 地址。例如:

apiVersion: v1
kind: Service
metadata:
  name: my-service
  …
spec:
  clusterIP: fd12::5:9af
  clusterIPs:
  - fd12::5:9af
  - 172.16.12.197

在集群节点上,调用 Service:

curl IPV4_CLUSTER_IP
curl [IPV6_CLUSTER_IP]

输出显示“Hello world”消息:

Hello, world!
Version: 2.0.0
Hostname: my-deployment-xxx

创建 LoadBalancer 类型的双栈 Service

以下是 LoadBalancer 类型的 Service 的清单:

apiVersion: v1
kind: Service
metadata:
  name: "my-lb-service"
spec:
  selector:
    app: "try-dual-stack"
  type: "LoadBalancer"
  ipFamilyPolicy: "RequireDualStack"
  ipFamilies:
  - "IPv6"
  - "IPv4"
  ports:
  - port: 80
    targetPort: 8080

将此清单保存到名为 my-lb-service.yaml 的文件中,然后创建该 Service:

kubectl --kubeconfig CLUSTER_KUBECONFIG apply -f my-lb-service.yaml

回想一下,在 Cluster 清单中,您指定了用于 LoadBalancer 类型的 Service 的 IPv6 地址范围和 IPv4 地址范围:

  loadBalancer:
    mode: "bundled"
    ...
    addressPools:
    - name: "pool-1"
      addresses:
      - "10.2.0.112-10.2.0.221"
      - "fd12::4:101-fd12::4:110"

系统将为您的 Service 分配一个从 IPv4 范围中选择的外部 IPv4 地址以及一个从 IPv6 范围中选择的外部 IPv6 地址。

列出 Service 的详细信息:

kubectl --kubeconfig CLUSTER_KUBECONFIG get service my-lb-service --output yaml

在输出中,您可以看到 Service 的外部地址。例如:

apiVersion: v1
kind: Service
metadata:
  name: my-lb-service
...
status:
  loadBalancer:
    ingress:
    - ip: 10.2.0.213
    - ip: fd12::4:101

ipFamilyPolicy 的可能值

创建双栈 Service 时,您可以将 ipFamilyPolicy 设置为以下值之一:

  • SingleStack:控制器为该 Service 分配集群 IP 地址(从 clusterNetwork.services.cidrBlocks 下的 Cluster 清单内指定的第一个范围中选择)。

  • PreferDualStack:控制器为该 Service 分配 IPv4 和 IPv6 集群 IP 地址(从 clusterNetwork.services.cidrBlocks 下的 Cluster 清单内指定的范围中选择)。如果集群不是双栈集群,则其行为与 SingleStack 相同。

  • RequireDualStack:控制器为 Service 分配 IPv4 和 IPv6 集群 IP 地址(从 clusterNetwork.services.cidrBlocks 下的 Cluster 清单内指定的范围中选择)。它会根据 ipFamilies 下的 Service 清单中指定的第一个地址系列设置 spec.clusterIP 的值。

更多信息

如需详细了解如何创建双栈 Service,请参阅新 Service 的双栈选项