用于现代化改造的配置更新

本文档介绍了在将网格从 ISTIOD 控制平面现代化改造为 TRAFFIC_DIRECTOR 控制平面之前,您可能需要对托管式 Cloud Service Mesh 进行的配置更新。

下面列出了为使集群准备好现代化改造而需要进行的可能的配置更新。如需查看更新说明,请参阅各个部分:

如需详细了解现代化改造工作流,请参阅托管式控制平面现代化改造页面。

从 Istio Secret 迁移到 multicluster_mode

当集群使用 TRAFFIC_DIRECTOR 控制平面时,不支持多集群 Secret。本文档介绍了如何从使用 Istio 多集群 Secret 现代化改造到使用 multicluster_mode

Istio Secret 与声明式 API 概览

开源 Istio 多集群端点发现的工作原理是:使用 istioctl 或其他工具在集群中创建 Kubernetes Secret。借助此 Secret,集群可以将流量负载均衡到网格中的其他集群。然后,ISTIOD 控制平面会读取此 Secret,并开始将流量路由到这一其他集群。

Cloud Service Mesh 具有声明式 API 来控制多集群流量,而不是直接创建 Istio Secret。此 API 会将 Istio Secret 视为实现细节,且比手动创建 Istio Secret 更可靠。将来的 Cloud Service Mesh 功能将依赖于声明式 API,您无法直接将这些新功能与 Istio Secret 搭配使用。声明式 API 是未来唯一受支持的路径。

如果您使用的是 Istio Secret,请尽快迁移到使用声明式 API。请注意,multicluster_mode 设置会指示每个集群将流量定向到网格中的每个其他集群。使用 Secret 可实现更灵活的配置,让您为每个集群配置它应将流量定向到网格中的哪个其他集群。如需查看声明式 API 和 Istio Secret 支持的功能之间的差异的完整列表,请参阅使用 Istio API 支持的功能

从 Istio Secret 迁移到声明式 API

如果您使用舰队功能 API 通过自动管理配置 Cloud Service Mesh,则无需按照这些说明操作。仅当您使用 asmcli --managed 进行初始配置时,才需要执行这些步骤。

请注意,此过程会更改指向集群的 Secret。在此过程中,系统会移除端点,然后重新添加端点。在移除和添加端点之间,流量会暂时还原到本地路由,而不是负载均衡到其他集群。如需了解详情,请参阅 GitHub 问题

如需从使用 Istio Secret 迁移到声明式 API,请按照以下步骤操作。 同时或连续执行以下步骤:

  1. 通过设置 multicluster_mode=connected,为舰队中要启用多集群端点发现的每个集群启用声明式 API。请注意,如果您不希望集群可发现,则需要明确设置 multicluster_mode=disconnected

    使用以下命令将集群加入多集群端点发现:

     kubectl patch configmap/asm-options -n istio-system --type merge -p '{"data":{"multicluster_mode":"connected"}}'
    

    使用以下命令将集群从端点发现中退出:

     kubectl patch configmap/asm-options -n istio-system --type merge -p '{"data":{"multicluster_mode":"disconnected"}}'
    
  2. 删除旧 Secret。

    在集群上设置 multicluster_mode=connected 后,每个集群都会为已设置 multicluster_mode=connected 的每个其他集群生成一个新 Secret。Secret 位于 istio-system 命名空间中,格式如下:

    istio-remote-secret-projects-PROJECT_NAME-locations-LOCATION-memberships-MEMBERSHIPS
    

    每个 Secret 还会应用标签 istio.io/owned-by: mesh.googleapis.com

    创建新 Secret 后,您可以删除使用 istioctl create-remote-secret 手动创建的任何 Secret:

    kubectl delete secret SECRET_NAME -n istio-system
    

迁移完成后,检查请求指标,以确保请求按预期路由。

启用 Workload Identity Federation for GKE

工作负载身份联合是建议用于 Google Kubernetes Engine 的安全方法。这样就可以访问 Compute Engine、BigQuery 和 Machine Learning API 等 Google Cloud 服务。由于工作负载身份联合使用的是 IAM 政策,因此无需手动配置或安全性较低的方法(例如服务账号密钥文件)。如需详细了解工作负载身份联合,请参阅 Workload Identity Federation for GKE 的工作原理

以下部分介绍了如何启用工作负载身份联合。

在集群上启用工作负载身份联合

  1. 检查是否已为您的集群启用工作负载身份联合。为此,请确保 GKE 集群已配置工作负载身份联合池,这对于 IAM 凭证验证至关重要。

    使用以下命令检查为集群设置的工作负载身份池:

    gcloud container clusters describe CLUSTER_NAME \
      --format="value(workloadIdentityConfig.workloadPool)"
    

    CLUSTER_NAME 替换为 GKE 集群的名称。如果您尚未指定 gcloud 的默认可用区或区域,则您可能还需要在运行此命令时指定 --region--zone 标志。

  2. 如果输出为空,请按照更新现有集群中的说明在现有 GKE 集群上启用工作负载身份。

在节点池上启用工作负载身份联合

在集群上启用工作负载身份联合后,必须将节点池配置为使用 GKE 元数据服务器

  1. 列出 Standard 集群的所有节点池。运行 gcloud container node-pools list 命令:

    gcloud container node-pools list --cluster CLUSTER_NAME
    

    CLUSTER_NAME 替换为 GKE 集群的名称。如果您尚未指定 gcloud 的默认可用区或区域,则您可能还需要在运行此命令时指定 --region--zone 标志。

  2. 验证每个节点池是否使用 GKE 元数据服务器:

    gcloud container node-pools describe NODEPOOL_NAME \
        --cluster=CLUSTER_NAME \
        --format="value(config.workloadMetadataConfig.mode)"
    

    替换以下内容:

    • NODEPOOL_NAME 替换为节点池的名称。
    • CLUSTER_NAME 替换为 GKE 集群的名称。
  3. 如果输出不包含 GKE_METADATA,请使用更新现有节点池指南更新节点池。

启用托管式容器网络接口 (CNI)

本部分将指导您在 Google Kubernetes Engine 上为 Cloud Service Mesh 启用托管式 CNI。

托管式 CNI 概览

托管式容器网络接口 (CNI) 是 Google 管理的 Istio CNI 实现。CNI 插件通过配置 iptables 规则来简化 Pod 网络。这样,应用和 Envoy 代理之间就可以进行流量重定向,而无需管理 iptables 所需的 init-container 特权权限。

Istio CNI 插件会替换 istio-init 容器。以前,istio-init 容器负责设置 Pod 的网络环境,以便为 Istio 边车实现流量拦截。CNI 插件执行相同的网络重定向功能,但具有减少对提升特权的需求的额外优势,从而增强安全性。

因此,为了增强安全性和可靠性,并简化管理和问题排查,所有托管式 Cloud Service Mesh 部署都需要使用托管式 CNI。

对 init 容器的影响

init 容器是一种专用容器,用于在应用容器之前运行以完成设置任务。设置任务可能包括下载配置文件、与外部服务通信或执行应用前初始化等任务。在集群中启用托管式 CNI 后,依赖于网络访问权限的 Init 容器可能会遇到问题。

使用托管式 CNI 的 Pod 设置流程如下所示:

  1. CNI 插件会设置 Pod 网络接口、分配 Pod IP 并将流量重定向到尚未启动的 Istio 边车代理。
  2. 所有 init 容器都执行并完成。
  3. Istio 边车代理与应用容器一起启动。

因此,如果 init 容器尝试建立出站网络连接或连接到网格中的服务,则来自 init 容器的网络请求可能会被丢弃或错误路由。这是因为在发出请求时,用于管理 Pod 网络流量的 Istio 边车代理未运行。如需了解详情,请参阅 Istio CNI 文档

为集群启用托管式 CNI

请按照本部分中的步骤在集群上启用托管式 CNI。

  1. 从 init 容器中移除网络依赖项。请考虑以下替代方案:

    • 修改应用逻辑或容器:您可以修改服务,以在边车代理启动后移除对需要网络请求或在应用容器内执行网络操作的 init 容器的依赖关系。
    • 使用 Kubernetes ConfigMap 或 Secret:将网络请求提取的配置数据存储在 Kubernetes ConfigMap 或 Secret 中,并将其挂载到应用容器中。如需了解替代解决方案,请参阅 Istio 文档
  2. 在集群上启用托管式 CNI:

    1. 进行以下配置更改:

      1. 运行以下命令以找到 controlPlaneRevision

        kubectl get controlplanerevision -n istio-system
        
      2. ControlPlaneRevision (CPR) 自定义资源 (CR) 中,将标签 mesh.cloud.google.com/managed-cni-enabled 设置为 true

        kubectl label controlplanerevision CPR_NAME \
            -n istio-system mesh.cloud.google.com/managed-cni-enabled=true \
            --overwrite
        

        CPR_NAME 替换为上一步输出中的 NAME 列下的值。

      3. 在 asm-options ConfigMap 中,将 ASM_OPTS 值设置为 CNI=on

        kubectl patch configmap asm-options -n istio-system \
            -p '{"data":{"ASM_OPTS":"CNI=on"}}'
        
      4. ControlPlaneRevision (CPR) 自定义资源 (CR) 中,将标签 mesh.cloud.google.com/force-reprovision 设置为 true。此操作会触发控制平面重启。

        kubectl label controlplanerevision CPR_NAME \
            -n istio-system mesh.cloud.google.com/force-reprovision=true \
            --overwrite
        
    2. 检查功能状态。使用以下命令检索功能状态:

      gcloud container fleet mesh describe --project FLEET_PROJECT_ID
      

      FLEET_PROJECT_ID 替换为您的舰队宿主项目的 ID。通常,FLEET_PROJECT_ID 的名称与项目相同。

      • 验证 MANAGED_CNI_NOT_ENABLED 条件是否已从 servicemesh.conditions 中移除。
      • 请注意,状态更新最多可能需要 15-20 分钟。尝试等待几分钟,然后重新运行该命令。
    3. 当集群功能中的 controlPlaneManagement.stateActive 时,请重启 Pod

停止在边车中使用非标准二进制文件

本部分提出了使部署与 Distroless Envoy 代理映像兼容的方法。

Distroless Envoy 代理边车映像

Cloud Service Mesh 会根据您的控制平面配置使用两种类型的 Envoy 代理边车映像:基于 Ubuntu 的映像(包含各种二进制文件)和 Distroless 映像。Distroless 基础映像是最小的容器映像,仅包含必要组件,可优先考虑安全性和资源优化。攻击面会缩小,有助于防止出现漏洞。如需了解详情,请参阅有关 Distroless 代理映像的文档。

二进制文件兼容性

最佳做法是将容器运行时的内容限制为仅包含必要的软件包。此方法可提高常见漏洞和披露 (CVE) 扫描程序的安全性和信噪比。Distroless 边车映像具有一组最少的依赖项,并且已剥离所有非必要的可执行文件、库和调试工具。因此,无法在容器中执行 shell 命令或使用 curl、ping 或其他调试实用程序(例如 kubectl exec)。

使集群与 Distroless 映像兼容

  • 从配置中移除对任何不受支持的二进制文件(如 bash 或 curl)的引用。特别是在就绪性、启动和活跃性探测以及 istio-proxy、istio-init 或 istio-validation 容器内的生命周期 PostStart 和 PreStop 钩子中。
  • 对于某些应用场景,请考虑使用 holdApplicationUntilProxyStarts 等替代方案。
  • 如需进行调试,您可以使用临时容器附加到正在运行的工作负载 Pod。然后,您可以检查该 Pod 并运行自定义命令。如需查看示例,请参阅收集 Cloud Service Mesh 日志

如果您找不到适用于特定应用场景的解决方案,请参阅获取支持,与 Google Cloud支持团队联系。

迁移到 Istio Ingress 网关

本部分介绍了如何迁移到 Istio Ingress 网关。您可以通过以下两种方法迁移到 Istio Ingress 网关:

  1. 通过流量拆分进行分阶段迁移

    此方法优先考虑尽量减少中断,您将逐步向新的 Istio 网关发送流量,以便监控其在一小部分请求中的性能,并在必要时快速还原。请注意,对于某些应用,配置第 7 层流量拆分可能具有挑战性,因此您需要在过渡期间同时管理这两个网关系统。如需了解相关步骤,请参阅通过流量拆分进行分阶段迁移

  2. 直接迁移

    此方法涉及在您彻底完成测试后,同时将所有流量重新路由到新的 Istio 网关。此方法的优势在于完全与旧网关的基础设施分离,从而可以灵活配置新网关,而不受现有设置的限制。不过,如果在过渡期间新网关出现意外问题,停机风险会增加。如需了解相关步骤,请参阅直接迁移

以下迁移示例假设您有一个 HTTP 服务 (httpbin) 在应用命名空间(默认)中运行,并使用 Kubernetes Gateway API 对外公开。相关配置如下:

  • 网关:k8-api-gateway(位于 istio-ingress 命名空间中)- 配置为监听端口 80 上以 .example.com 结尾的任何主机名的 HTTP 流量。
  • HTTPRoute:httpbin-route(位于 default 命名空间中)- 将主机名为 httpbin.example.com 且路径以 /get 开头的任何 HTTP 请求定向到 default 命名空间中的 httpbin 服务。
  • httpbin 应用可使用外部 IP 34.57.246.68 进行访问。

基本网关示意图

通过流量拆分进行分阶段迁移

配置新的 Istio Ingress 网关

  1. 按照部署示例网关部分中的步骤部署新的 Ingress 网关,并根据您的需求自定义示例配置。anthos-service-mesh 仓库中的示例旨在部署 istio-ingressgateway loadBalancer 服务和相应的 ingress-gateway Pod。

    网关资源示例 (istio-ingressgateway.yaml)

     apiVersion: networking.istio.io/v1beta1
     kind: Gateway
     metadata:
       name: istio-api-gateway
       namespace: GATEWAY_NAMESPACE
     spec:
       selector:
         istio: ingressgateway  # The selector should match the ingress-gateway pod labels.
       servers:
       - port:
           number: 80
           name: http
           protocol: HTTP
         hosts:   # or specific hostnames if needed
         - "httpbin.example.com"
    
  2. 应用网关配置来管理流量:

    kubectl apply -f istio-ingressgateway.yaml -n GATEWAY_NAMESPACE
    

    确保网关资源中的“spec.selector”与 ingress-gateway Pod 的标签匹配。例如,如果 ingress-gateway Pod 具有 istio=ingressgateway 标签,则您的网关配置也必须选择此 istio=ingressgateway 标签。

为新网关配置初始路由

  1. 使用 Istio VirtualService 为应用定义初始路由规则。

    VirtualService 示例 (my-app-vs-new.yaml):

    apiVersion: networking.istio.io/v1beta1
    kind: VirtualService
    metadata:
      name: httpbin-vs
      namespace: APPLICATION_NAMESPACE
    spec:
        gateways:
        - istio-ingress/istio-api-gateway  # Replace with <gateway-namespace/gateway-name>
        hosts:
        - httpbin.example.com
        http:
        - match:
          - uri:
              prefix: /get
          route:
          - destination:
              host: httpbin
              port:
                number: 8000
    
  2. 应用 VirtualService:

    kubectl apply -f my-app-vs-new.yaml -n MY_APP_NAMESPACE
    

通过新部署的 Istio Ingress 网关访问后端 (httpbin) 服务

  1. 将 Ingress 主机环境变量设置为与最近部署的 istio-ingressgateway 负载均衡器关联的外部 IP 地址:

    export INGRESS_HOST=$(kubectl -n GATEWAY_NAMESPACE get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    
  2. 验证应用 (httpbin) 是否可使用新网关进行访问:

    curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST/get"
    

    输出类似于以下内容:

    HTTP/1.1 200 OK
    

使用新的 Istio Ingress 网关的请求流

修改现有 Ingress 以进行流量拆分

确认新网关(例如 istio-api-gateway)已成功设置后,您可以开始通过该网关路由部分流量。为此,请更新当前的 HTTPRoute,以将一小部分流量定向到新网关,而大部分流量继续使用现有网关 (k8-api-gateway)。

  1. 打开 httproute 进行修改:

    kubectl edit httproute httpbin-route -n MY_APP_NAMESPACE
    
  2. 添加指向新 Ingress 网关的 loadbalancer 服务的新后端引用,初始权重为 10%,并更新旧网关后端的权重。

    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: httpbin-route
      namespace: MY_APP_NAMESPACE  # your application's namespace
    spec:
      parentRefs:
      - name: k8-api-gateway
        namespace: istio-ingress
      hostnames: ["httpbin.example.com"]
      rules:
      - matches:
        - path:
            type: PathPrefix
            value: /get
        backendRefs:
        - name: httpbin
          port: 8000
          weight: 90
        - name: istio-ingressgateway # Newly deployed load balancer service
          namespace: GATEWAY_NAMESPACE
          port: 80
          weight: 10
    
  3. 使用引用授权授予跨命名空间引用的权限。

    如需允许应用命名空间(默认)中的 HTTPRoute 访问网关命名空间 (istio-ingress) 中的 loadbalancer 服务,您可能需要创建引用授权。此资源充当安全控制,用于明确定义允许哪些跨命名空间引用。

    以下 istio-ingress-grant.yaml 描述了一个引用授权示例:

    apiVersion: gateway.networking.k8s.io/v1beta1
    kind: ReferenceGrant
    metadata:
      name: istio-ingressgateway-grant
      namespace: istio-ingress # Namespace of the referenced resource
    spec:
      from:
      - group: gateway.networking.k8s.io
        kind: HTTPRoute 
        namespace: MY_APP_NAMESPACE # Namespace of the referencing resource
      to:
      - group: ""               # Core Kubernetes API group for Services
        kind: Service
        name: istio-ingressgateway # Loadbalancer Service of the new ingress gateway
    
  4. 应用引用授权:

    kubectl apply -f istio-ingress-grant.yaml -n GATEWAY_NAMESPACE
    
  5. 验证对现有外部 IP 地址(例如 34.57.246.68)的请求是否未失败。以下 check-traffic-flow.sh 描述了一个用于检查请求失败情况的脚本:

    # Update the following values based on your application setup
    external_ip="34.57.246.68" # Replace with existing external IP
    url="http://$external_ip/get"
    host_name="httpbin.example.com"
    
    # Counter for successful requests
    success_count=0
    
    # Loop 50 times
    for i in {1..50}; do
      # Perform the curl request and capture the status code
      status_code=$(curl -s -HHost:"$host_name" -o /dev/null -w "%{http_code}" "$url")
      # Check if the request was successful (status code 200)
      if [ "$status_code" -eq 200 ]; then
        ((success_count++))  # Increment the success counter
      else
        echo "Request $i: Failed with status code $status_code"
      fi
    done
    
    # After the loop, check if all requests were successful
    if [ "$success_count" -eq 50 ]; then
      echo "All 50 requests were successful!"
    else
      echo "Some requests failed.  Successful requests: $success_count"
    fi
    
  6. 执行该脚本以确认无论流量路由如何,都不会有请求失败:

    chmod +x check-traffic-flow.sh
    ./check-traffic-flow.sh
    

在现有网关和新的 Istio Ingress 网关之间进行流量拆分的请求流

逐步提高流量百分比

如果现有外部 IP 地址(例如 34.57.246.68)未出现任何请求失败情况,请通过调整 HTTPRoute 中的后端权重,逐步将更多流量转移到新的 Istio Ingress 网关。以小增量(例如 10%、20% 等)增加 istio-ingressgateway 的权重,并减小旧网关的权重。

使用以下命令更新现有 HTTPRoute

kubectl edit httproute httpbin-route -n MY_APP_NAMESPACE

完全迁移流量并移除旧网关

  1. 当新的 Istio Ingress 网关表现出稳定的性能并成功处理请求时,将所有流量转移到该网关。更新 HTTPRoute,将旧网关的后端权重设置为 0,并将新网关的后端权重设置为 100

  2. 流量完全路由到新网关后,请更新应用主机名(例如 httpbin.example.com)的外部 DNS 记录,以指向在预配新的 Istio Ingress 网关中创建的负载均衡器服务的外部 IP 地址。

  3. 最后,删除旧网关及其关联资源:

    kubectl delete gateway OLD_GATEWAY -n GATEWAY_NAMESPACE
    kubectl delete service OLD_GATEWAY_SERVICE -n GATEWAY_NAMESPACE
    

直接迁移

配置新的 Istio Ingress 网关

  1. 按照部署示例网关部分中的步骤部署新的 Ingress 网关,并根据您的需求自定义示例配置。anthos-service-mesh 仓库中的示例旨在部署 istio-ingressgateway loadBalancer 服务和相应的 ingress-gateway Pod。

    网关资源示例 (istio-ingressgateway.yaml)

     apiVersion: networking.istio.io/v1beta1
     kind: Gateway
     metadata:
       name: istio-api-gateway
       namespace: GATEWAY_NAMESPACE
     spec:
       selector:
         istio: ingressgateway  # The selector should match the ingress-gateway pod labels.
       servers:
       - port:
           number: 80
           name: http
           protocol: HTTP
         hosts:   # or specific hostnames if needed
         - "httpbin.example.com"
    
  2. 应用网关配置来管理流量:

    kubectl apply -f istio-ingressgateway.yaml -n GATEWAY_NAMESPACE
    

    确保网关资源中的“spec.selector”与 ingress-gateway Pod 的标签匹配。例如,如果 ingress-gateway Pod 具有 istio=ingressgateway 标签,则您的网关配置也必须选择此 istio=ingressgateway 标签。

为新网关配置初始路由

  1. 使用 Istio VirtualService 为应用定义初始路由规则。

    VirtualService 示例 (my-app-vs-new.yaml):

    apiVersion: networking.istio.io/v1beta1
    kind: VirtualService
    metadata:
      name: httpbin-vs
      namespace: APPLICATION_NAMESPACE
    spec:
        gateways:
        - istio-ingress/istio-api-gateway  # Replace with <gateway-namespace/gateway-name>
        hosts:
        - httpbin.example.com
        http:
        - match:
          - uri:
              prefix: /get
          route:
          - destination:
              host: httpbin
              port:
                number: 8000
    
  2. 应用 VirtualService:

    kubectl apply -f my-app-vs-new.yaml -n MY_APP_NAMESPACE
    

通过新部署的 Istio Ingress 网关访问后端 (httpbin) 服务

  1. 将 Ingress 主机环境变量设置为与最近部署的 istio-ingressgateway 负载均衡器关联的外部 IP 地址:

    export INGRESS_HOST=$(kubectl -n GATEWAY_NAMESPACE get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    
  2. 验证应用 (httpbin) 是否可使用新网关进行访问:

    curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST/get"
    

    输出类似于以下内容:

    HTTP/1.1 200 OK
    

使用新的 Istio Ingress 网关的请求流

测试和监控新网关

  1. 测试所有路由规则,验证 TLS 配置、安全政策和其他功能。执行负载测试,以验证新网关是否可以处理预期流量。

  2. 对新网关进行全面测试后,请更新应用主机名(例如 httpbin.example.com)的外部 DNS 记录,以指向在预配新的 Istio Ingress 网关中创建的负载均衡器服务的外部 IP 地址。

  3. 监控请求成功率、延迟时间、错误率和应用 Pod 的资源利用率等关键指标,以验证新 Istio Ingress 网关的稳定性。稳定后,您可以删除旧网关及其关联的资源

    kubectl delete gateway OLD_GATEWAY -n GATEWAY_NAMESPACE
    kubectl delete service OLD_GATEWAY_SERVICE -n GATEWAY_NAMESPACE
    

重要注意事项:如果您的应用需要 HTTPS,请确保在新的 Istio Ingress 网关上正确设置 TLS 证书和配置。如需了解详情,请参阅在入站流量网关中设置 TLS 终结