本文档介绍如何完成以下任务:
- 部署通过 GKE 网关和 Cloud Service Mesh 公开的分布全球的应用。
- 通过将 Cloud Load Balancing 与 Cloud Service Mesh 结合使用,将应用公开给多个客户端。
- 将负载均衡器与跨多个 Google Cloud 区域部署的服务网格相集成。
本部署指南适用于平台管理员,以及运行 Cloud Service Mesh 的高级从业人员。这些说明也适用于 Istio on GKE。
架构
下图展示了服务网格的默认入站流量拓扑,即公开单个集群上的入站流量网关代理的外部 TCP/UDP 负载均衡器:
本部署指南使用 Google Kubernetes Engine (GKE) 网关资源。具体来说,它使用多集群网关配置多区域负载均衡,作为分布在两个区域中的多个 Autopilot 集群的前端。
上图展示了数据如何流经 Cloud Ingress 和网格 Ingress。如需了解详情,请参阅相关参考架构文档中的架构图说明。
目标
- 将 Google Cloud 上的两个 GKE Autopilot 集群部署到同一舰队。
- 将基于 Istio 的 Cloud Service Mesh 部署到同一舰队。
- 使用 GKE 网关配置负载均衡器,以终止公共 HTTPS 流量。
- 将公共 HTTPS 流量定向到跨多个集群和区域部署的 Cloud Service Mesh 托管的应用。
- 将 whereami 示例应用部署到这两个 Autopilot 集群。
费用优化
在本文档中,您将使用 Google Cloud 的以下收费组件:
- Google Kubernetes Engine
- Cloud Load Balancing
- Cloud Service Mesh
- Multi Cluster Ingress
- Google Cloud Armor
- Certificate Manager
- Cloud Endpoints
您可使用价格计算器根据您的预计使用情况来估算费用。
完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理。
准备工作
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Make sure that billing is enabled for your Google Cloud project.
-
In the Google Cloud console, activate Cloud Shell.
您将通过 Cloud Shell 运行此部署中的所有终端命令。
设置默认的 Google Cloud 项目:
export PROJECT=YOUR_PROJECT export PROJECT_NUMBER=$(gcloud projects describe PROJECT_ID --format="value(projectNumber)") gcloud config set project PROJECT_ID
将
PROJECT_ID
替换为您要用于此部署的项目的 ID。创建工作目录:
mkdir -p ${HOME}/edge-to-mesh-multi-region cd ${HOME}/edge-to-mesh-multi-region export WORKDIR=`pwd`
创建 GKE 集群
在本部分中,您将创建 GKE 集群来托管应用和配套基础架构,这些稍后将在本部署指南中创建。
在 Cloud Shell 中,创建一个新的
kubeconfig
文件。此步骤确保不会导致与现有(默认)kubeconfig
文件冲突。touch edge2mesh_mr_kubeconfig export KUBECONFIG=${WORKDIR}/edge2mesh_mr_kubeconfig
定义在创建 GKE 集群及其中的资源时要使用的环境变量。您可以根据需要修改默认区域选择。
export CLUSTER_1_NAME=edge-to-mesh-01 export CLUSTER_2_NAME=edge-to-mesh-02 export CLUSTER_1_REGION=us-central1 export CLUSTER_2_REGION=us-east4 export PUBLIC_ENDPOINT=frontend.endpoints.PROJECT_ID.cloud.goog
启用本指南中需要使用的 Google Cloud API:
gcloud services enable \ container.googleapis.com \ mesh.googleapis.com \ gkehub.googleapis.com \ multiclusterservicediscovery.googleapis.com \ multiclusteringress.googleapis.com \ trafficdirector.googleapis.com \ certificatemanager.googleapis.com
在
CLUSTER_1_REGION
中创建一个具有专用节点的 GKE Autopilot 集群。使用--async
标志,这样便无需等待第一个集群预配完毕和注册到舰队:gcloud container clusters create-auto --async \ ${CLUSTER_1_NAME} --region ${CLUSTER_1_REGION} \ --release-channel rapid --labels mesh_id=proj-${PROJECT_NUMBER} \ --enable-private-nodes --enable-fleet
在
CLUSTER_2_REGION
中创建并注册第二个 Autopilot 集群:gcloud container clusters create-auto \ ${CLUSTER_2_NAME} --region ${CLUSTER_2_REGION} \ --release-channel rapid --labels mesh_id=proj-${PROJECT_NUMBER} \ --enable-private-nodes --enable-fleet
确保这两个集群都在正常运行。最多可能需要 20 分钟,这两个集群才能都处于正常运行状态:
gcloud container clusters list
输出类似于以下内容:
NAME LOCATION MASTER_VERSION MASTER_IP MACHINE_TYPE NODE_VERSION NUM_NODES STATUS edge-to-mesh-01 us-central1 1.27.5-gke.200 34.27.171.241 e2-small 1.27.5-gke.200 RUNNING edge-to-mesh-02 us-east4 1.27.5-gke.200 35.236.204.156 e2-small 1.27.5-gke.200 RUNNING
收集
CLUSTER_1_NAME
的凭据。由于您是异步创建CLUSTER_1_NAME
,因此可以在该集群预配期间运行其他命令。gcloud container clusters get-credentials ${CLUSTER_1_NAME} \ --region ${CLUSTER_1_REGION}
为明确起见,用集群的名称重命名 Kubernetes 上下文:
kubectl config rename-context gke_PROJECT_ID_${CLUSTER_1_REGION}_${CLUSTER_1_NAME} ${CLUSTER_1_NAME} kubectl config rename-context gke_PROJECT_ID_${CLUSTER_2_REGION}_${CLUSTER_2_NAME} ${CLUSTER_2_NAME}
安装服务网格
在本部分中,您将配置使用 Fleet API 的托管式 Cloud Service Mesh。如果使用 Fleet API 启用 Cloud Service Mesh,便可以使用声明式方法来预配服务网格。
在 Cloud Shell 中,为舰队启用 Cloud Service Mesh:
gcloud container fleet mesh enable
启用控制平面和数据平面自动管理功能:
gcloud container fleet mesh update \ --management automatic \ --memberships ${CLUSTER_1_NAME},${CLUSTER_2_NAME}
等待大约 20 分钟。然后,确认控制平面状态是否为
ACTIVE
:gcloud container fleet mesh describe
输出类似于以下内容:
createTime: '2023-11-30T19:23:21.713028916Z' membershipSpecs: projects/603904278888/locations/us-central1/memberships/edge-to-mesh-01: mesh: management: MANAGEMENT_AUTOMATIC projects/603904278888/locations/us-east4/memberships/edge-to-mesh-02: mesh: management: MANAGEMENT_AUTOMATIC membershipStates: projects/603904278888/locations/us-central1/memberships/edge-to-mesh-01: servicemesh: controlPlaneManagement: details: - code: REVISION_READY details: 'Ready: asm-managed-rapid' implementation: ISTIOD state: ACTIVE dataPlaneManagement: details: - code: OK details: Service is running. state: ACTIVE state: code: OK description: |- Revision ready for use: asm-managed-rapid. All Canonical Services have been reconciled successfully. updateTime: '2024-06-27T09:00:21.333579005Z' projects/603904278888/locations/us-east4/memberships/edge-to-mesh-02: servicemesh: controlPlaneManagement: details: - code: REVISION_READY details: 'Ready: asm-managed-rapid' implementation: ISTIOD state: ACTIVE dataPlaneManagement: details: - code: OK details: Service is running. state: ACTIVE state: code: OK description: |- Revision ready for use: asm-managed-rapid. All Canonical Services have been reconciled successfully. updateTime: '2024-06-27T09:00:24.674852751Z' name: projects/e2m-private-test-01/locations/global/features/servicemesh resourceState: state: ACTIVE spec: {} updateTime: '2024-06-04T17:16:28.730429993Z'
部署外部应用负载均衡器并创建入站流量网关
在本部分中,您将通过 GKE Gateway Controller 部署外部应用负载均衡器,并为两个集群创建入站流量网关。gateway
和 gatewayClass
资源会自动预配负载均衡器和后端健康检查。为了在负载均衡器上实现 TLS 终结,您需要创建 Certificate Manager 资源并将其关联到负载均衡器。此外,您还可以使用 Endpoints 自动为应用预配公共 DNS 名称。
在两个集群上安装入站流量网关
为保证安全性,我们建议在与网格控制平面不同的命名空间中部署入站流量网关。
在 Cloud Shell 中,为每个集群创建一个专用的
asm-ingress
命名空间:kubectl --context=${CLUSTER_1_NAME} create namespace asm-ingress kubectl --context=${CLUSTER_2_NAME} create namespace asm-ingress
将命名空间标签添加到
asm-ingress
命名空间:kubectl --context=${CLUSTER_1_NAME} label namespace asm-ingress istio-injection=enabled kubectl --context=${CLUSTER_2_NAME} label namespace asm-ingress istio-injection=enabled
输出类似于以下内容:
namespace/asm-ingress labeled
使用
istio-injection=enabled
为asm-ingress
命名空间添加标签会指示 Cloud Service Mesh 在部署 pod 时自动注入 Envoy 边车代理。生成自签名证书以备将来使用:
openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 \ -subj "/CN=frontend.endpoints.PROJECT_ID.cloud.goog/O=Edge2Mesh Inc" \ -keyout ${WORKDIR}/frontend.endpoints.PROJECT_ID.cloud.goog.key \ -out ${WORKDIR}/frontend.endpoints.PROJECT_ID.cloud.goog.crt
该证书可在负载均衡器和服务网格入站流量网关之间提供额外一层加密。此外,它还支持基于 HTTP/2 的协议(如 gRPC)。如需了解如何将自签名证书关联到入站流量网关,请参阅后文的创建外部 IP 地址、DNS 记录和 TLS 证书资源部分。
如需详细了解入站流量网关证书的要求,请参阅从负载均衡器到后端的加密。
在每个集群上创建一个 Kubernetes Secret 来存储自签名证书:
kubectl --context ${CLUSTER_1_NAME} -n asm-ingress create secret tls \ edge2mesh-credential \ --key=${WORKDIR}/frontend.endpoints.PROJECT_ID.cloud.goog.key \ --cert=${WORKDIR}/frontend.endpoints.PROJECT_ID.cloud.goog.crt kubectl --context ${CLUSTER_2_NAME} -n asm-ingress create secret tls \ edge2mesh-credential \ --key=${WORKDIR}/frontend.endpoints.PROJECT_ID.cloud.goog.key \ --cert=${WORKDIR}/frontend.endpoints.PROJECT_ID.cloud.goog.crt
如需与外部应用负载均衡器相集成,请创建 kustomize 变体来配置入站流量网关资源:
mkdir -p ${WORKDIR}/asm-ig/base cat <<EOF > ${WORKDIR}/asm-ig/base/kustomization.yaml resources: - github.com/GoogleCloudPlatform/anthos-service-mesh-samples/docs/ingress-gateway-asm-manifests/base EOF mkdir ${WORKDIR}/asm-ig/variant cat <<EOF > ${WORKDIR}/asm-ig/variant/role.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: asm-ingressgateway namespace: asm-ingress rules: - apiGroups: [""] resources: ["secrets"] verbs: ["get", "watch", "list"] EOF cat <<EOF > ${WORKDIR}/asm-ig/variant/rolebinding.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: asm-ingressgateway namespace: asm-ingress roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: asm-ingressgateway subjects: - kind: ServiceAccount name: asm-ingressgateway EOF cat <<EOF > ${WORKDIR}/asm-ig/variant/service-proto-type.yaml apiVersion: v1 kind: Service metadata: name: asm-ingressgateway namespace: asm-ingress spec: ports: - name: status-port port: 15021 protocol: TCP targetPort: 15021 - name: http port: 80 targetPort: 8080 appProtocol: HTTP - name: https port: 443 targetPort: 8443 appProtocol: HTTP2 type: ClusterIP EOF cat <<EOF > ${WORKDIR}/asm-ig/variant/gateway.yaml apiVersion: networking.istio.io/v1beta1 kind: Gateway metadata: name: asm-ingressgateway namespace: asm-ingress spec: servers: - port: number: 443 name: https protocol: HTTPS hosts: - "*" # IMPORTANT: Must use wildcard here when using SSL, as SNI isn't passed from GFE tls: mode: SIMPLE credentialName: edge2mesh-credential EOF cat <<EOF > ${WORKDIR}/asm-ig/variant/kustomization.yaml namespace: asm-ingress resources: - ../base - role.yaml - rolebinding.yaml patches: - path: service-proto-type.yaml target: kind: Service - path: gateway.yaml target: kind: Gateway EOF
将入站流量网关配置应用于这两个集群:
kubectl --context ${CLUSTER_1_NAME} apply -k ${WORKDIR}/asm-ig/variant kubectl --context ${CLUSTER_2_NAME} apply -k ${WORKDIR}/asm-ig/variant
使用多集群服务将入站流量网关 pod 公开给负载均衡器
在本部分中,您将通过 ServiceExport
自定义资源导出入站流量网关 pod。出于以下原因,您必须通过 ServiceExport
自定义资源导出入站流量网关 pod:
- 允许负载均衡器跨多个集群对入站流量网关 pod 进行寻址。
- 允许入站流量网关 pod 将请求通过代理传输到在服务网格中运行的服务。
在 Cloud Shell 中,为舰队启用多集群服务 (MCS):
gcloud container fleet multi-cluster-services enable
向 MCS 授予对项目或舰队的必要 IAM 权限:
gcloud projects add-iam-policy-binding PROJECT_ID \ --member "serviceAccount:PROJECT_ID.svc.id.goog[gke-mcs/gke-mcs-importer]" \ --role "roles/compute.networkViewer"
创建
ServiceExport
YAML 文件:cat <<EOF > ${WORKDIR}/svc_export.yaml kind: ServiceExport apiVersion: net.gke.io/v1 metadata: name: asm-ingressgateway namespace: asm-ingress EOF
将
ServiceExport
YAML 文件应用于这两个集群:kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/svc_export.yaml kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/svc_export.yaml
如果您收到以下错误消息,请稍等数分钟,让 MCS 自定义资源定义 (CRD) 完成安装。然后,重新运行命令,将
ServiceExport
YAML 文件应用于这两个集群。error: resource mapping not found for name: "asm-ingressgateway" namespace: "asm-ingress" from "svc_export.yaml": no matches for kind "ServiceExport" in version "net.gke.io/v1" ensure CRDs are installed first
创建外部 IP 地址、DNS 记录和 TLS 证书资源
在本部分中,您将创建相关的网络资源,这些资源将为您稍后在本部署中创建的负载均衡资源提供支持。
在 Cloud Shell 中,预留静态外部 IP 地址:
gcloud compute addresses create mcg-ip --global
GKE 网关资源会使用静态 IP 地址。即使重新创建外部负载均衡器,该静态 IP 地址也会保持不变。
获取该静态 IP 地址并将其存储在环境变量中:
export MCG_IP=$(gcloud compute addresses describe mcg-ip --global --format "value(address)") echo ${MCG_IP}
如需针对您的网关 IP 地址创建易于使用的稳定映射,您必须具有公共 DNS 记录。
您可以使用所需的任何 DNS 提供商和自动化架构。本部署使用 Endpoints 而不是创建托管式 DNS 区域。Endpoints 为外部 IP 地址提供免费的 Google 管理的 DNS 记录。
运行以下命令以创建名为
dns-spec.yaml
的 YAML 文件:cat <<EOF > ${WORKDIR}/dns-spec.yaml swagger: "2.0" info: description: "Cloud Endpoints DNS" title: "Cloud Endpoints DNS" version: "1.0.0" paths: {} host: "frontend.endpoints.PROJECT_ID.cloud.goog" x-google-endpoints: - name: "frontend.endpoints.PROJECT_ID.cloud.goog" target: "${MCG_IP}" EOF
dns-spec.yaml
文件以frontend.endpoints.PROJECT_ID.cloud.goog
的形式定义公共 DNS 记录,其中PROJECT_ID
是您的唯一项目标识符。部署
dns-spec.yaml
文件以创建 DNS 条目。此过程需要几分钟才能完成。gcloud endpoints services deploy ${WORKDIR}/dns-spec.yaml
使用 Certificate Manager 为您在上一步中创建的 DNS 条目名称创建证书:
gcloud certificate-manager certificates create mcg-cert \ --domains="frontend.endpoints.PROJECT_ID.cloud.goog"
Google 管理的 TLS 证书用于终止负载均衡器上的入站客户端请求。
创建证书映射:
gcloud certificate-manager maps create mcg-cert-map
负载均衡器通过您在下一步中创建的证书映射条目来引用证书。
为您在本部分前面创建的证书创建证书映射条目:
gcloud certificate-manager maps entries create mcg-cert-map-entry \ --map="mcg-cert-map" \ --certificates="mcg-cert" \ --hostname="frontend.endpoints.PROJECT_ID.cloud.goog"
创建后端服务政策和负载均衡器资源
在本部分中,您将完成以下任务:
- 创建 Google Cloud Armor 安全政策及相关规则。
- 创建一个政策,让负载均衡器通过您在前面创建的
ServiceExport
YAML 文件检查入站流量网关 pod 的响应能力。 - 使用 GKE Gateway API 创建负载均衡器资源。
- 使用
GatewayClass
自定义资源设置特定的负载均衡器类型。 - 为舰队启用多集群负载均衡,并将其中一个集群指定为舰队的配置集群。
在 Cloud Shell 中,创建 Google Cloud Armor 安全政策:
gcloud compute security-policies create edge-fw-policy \ --description "Block XSS attacks"
为安全政策创建规则:
gcloud compute security-policies rules create 1000 \ --security-policy edge-fw-policy \ --expression "evaluatePreconfiguredExpr('xss-stable')" \ --action "deny-403" \ --description "XSS attack filtering"
为安全政策创建 YAML 文件,并通过相应的
ServiceImport
YAML 文件引用ServiceExport
YAML 文件:cat <<EOF > ${WORKDIR}/cloud-armor-backendpolicy.yaml apiVersion: networking.gke.io/v1 kind: GCPBackendPolicy metadata: name: cloud-armor-backendpolicy namespace: asm-ingress spec: default: securityPolicy: edge-fw-policy targetRef: group: net.gke.io kind: ServiceImport name: asm-ingressgateway EOF
将 Google Cloud Armor 政策应用于这两个集群:
kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/cloud-armor-backendpolicy.yaml kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/cloud-armor-backendpolicy.yaml
创建自定义 YAML 文件,以允许负载均衡器对这两个集群中入站流量网关 pod 的 Envoy 健康端点(路径
/healthz/ready
上的端口15021
)执行健康检查:cat <<EOF > ${WORKDIR}/ingress-gateway-healthcheck.yaml apiVersion: networking.gke.io/v1 kind: HealthCheckPolicy metadata: name: ingress-gateway-healthcheck namespace: asm-ingress spec: default: config: httpHealthCheck: port: 15021 portSpecification: USE_FIXED_PORT requestPath: /healthz/ready type: HTTP targetRef: group: net.gke.io kind: ServiceImport name: asm-ingressgateway EOF
将您在上一步中创建的自定义 YAML 文件应用于这两个集群:
kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/ingress-gateway-healthcheck.yaml kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/ingress-gateway-healthcheck.yaml
为舰队启用多集群负载均衡,并将
CLUSTER_1_NAME
指定为配置集群:gcloud container fleet ingress enable \ --config-membership=${CLUSTER_1_NAME} \ --location=${CLUSTER_1_REGION}
向舰队中的网关控制器授予 IAM 权限:
gcloud projects add-iam-policy-binding PROJECT_ID \ --member "serviceAccount:service-${PROJECT_NUMBER}@gcp-sa-multiclusteringress.iam.gserviceaccount.com" \ --role "roles/container.admin"
通过引用
gke-l7-global-external-managed-mc
gatewayClass
和您在前面创建的静态 IP 地址的网关自定义资源创建负载均衡器 YAML 文件:cat <<EOF > ${WORKDIR}/frontend-gateway.yaml kind: Gateway apiVersion: gateway.networking.k8s.io/v1beta1 metadata: name: external-http namespace: asm-ingress annotations: networking.gke.io/certmap: mcg-cert-map spec: gatewayClassName: gke-l7-global-external-managed-mc listeners: - name: http # list the port only so we can redirect any incoming http requests to https protocol: HTTP port: 80 - name: https protocol: HTTPS port: 443 allowedRoutes: kinds: - kind: HTTPRoute addresses: - type: NamedAddress value: mcg-ip EOF
将
frontend-gateway
YAML 文件应用于这两个集群。只有CLUSTER_1_NAME
具有权威性,除非您将其他配置集群指定为具有权威性:kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/frontend-gateway.yaml kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/frontend-gateway.yaml
创建一个名为
default-httproute.yaml
的HTTPRoute
YAML 文件,以指示网关资源向入站流量网关发送请求:cat << EOF > ${WORKDIR}/default-httproute.yaml apiVersion: gateway.networking.k8s.io/v1beta1 kind: HTTPRoute metadata: name: default-httproute namespace: asm-ingress spec: parentRefs: - name: external-http namespace: asm-ingress sectionName: https rules: - backendRefs: - group: net.gke.io kind: ServiceImport name: asm-ingressgateway port: 443 EOF
将您在上一步中创建的
HTTPRoute
YAML 文件应用于这两个集群:kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/default-httproute.yaml kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/default-httproute.yaml
如需执行从 HTTP 到 HTTP(S) 的重定向,请额外创建一个名为
default-httproute-redirect.yaml
的HTTPRoute
YAML 文件:cat << EOF > ${WORKDIR}/default-httproute-redirect.yaml kind: HTTPRoute apiVersion: gateway.networking.k8s.io/v1beta1 metadata: name: http-to-https-redirect-httproute namespace: asm-ingress spec: parentRefs: - name: external-http namespace: asm-ingress sectionName: http rules: - filters: - type: RequestRedirect requestRedirect: scheme: https statusCode: 301 EOF
将重定向
HTTPRoute
YAML 文件应用于这两个集群:kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/default-httproute-redirect.yaml kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/default-httproute-redirect.yaml
查看网关资源,以检查负载均衡器部署的进度:
kubectl --context=${CLUSTER_1_NAME} describe gateway external-http -n asm-ingress
输出会显示您在此部分输入的信息。
部署 whereami 示例应用
本指南使用 whereami 作为示例应用,直接提供有关哪些集群正在响应请求的反馈。以下部分将在这两个集群中设置两个单独的 whereami 部署:frontend
部署和 backend
部署。
frontend
部署是接收请求的第一个工作负载。然后,它会调用 backend
部署。
此模型用于演示多服务应用架构。frontend
和 backend
服务均会部署到这两个集群。
在 Cloud Shell 中,在这两个集群中为 whereami
frontend
和 whereamibackend
创建命名空间,并启用命名空间注入:kubectl --context=${CLUSTER_1_NAME} create ns frontend kubectl --context=${CLUSTER_1_NAME} label namespace frontend istio-injection=enabled kubectl --context=${CLUSTER_1_NAME} create ns backend kubectl --context=${CLUSTER_1_NAME} label namespace backend istio-injection=enabled kubectl --context=${CLUSTER_2_NAME} create ns frontend kubectl --context=${CLUSTER_2_NAME} label namespace frontend istio-injection=enabled kubectl --context=${CLUSTER_2_NAME} create ns backend kubectl --context=${CLUSTER_2_NAME} label namespace backend istio-injection=enabled
为 whereami
backend
创建 kustomize 变体:mkdir -p ${WORKDIR}/whereami-backend/base cat <<EOF > ${WORKDIR}/whereami-backend/base/kustomization.yaml resources: - github.com/GoogleCloudPlatform/kubernetes-engine-samples/quickstarts/whereami/k8s EOF mkdir ${WORKDIR}/whereami-backend/variant cat <<EOF > ${WORKDIR}/whereami-backend/variant/cm-flag.yaml apiVersion: v1 kind: ConfigMap metadata: name: whereami data: BACKEND_ENABLED: "False" # assuming you don't want a chain of backend calls METADATA: "backend" EOF cat <<EOF > ${WORKDIR}/whereami-backend/variant/service-type.yaml apiVersion: "v1" kind: "Service" metadata: name: "whereami" spec: type: ClusterIP EOF cat <<EOF > ${WORKDIR}/whereami-backend/variant/kustomization.yaml nameSuffix: "-backend" namespace: backend commonLabels: app: whereami-backend resources: - ../base patches: - path: cm-flag.yaml target: kind: ConfigMap - path: service-type.yaml target: kind: Service EOF
将 whereami
backend
变体应用于这两个集群:kubectl --context=${CLUSTER_1_NAME} apply -k ${WORKDIR}/whereami-backend/variant kubectl --context=${CLUSTER_2_NAME} apply -k ${WORKDIR}/whereami-backend/variant
为 whereami
frontend
创建 kustomize 变体:mkdir -p ${WORKDIR}/whereami-frontend/base cat <<EOF > ${WORKDIR}/whereami-frontend/base/kustomization.yaml resources: - github.com/GoogleCloudPlatform/kubernetes-engine-samples/quickstarts/whereami/k8s EOF mkdir whereami-frontend/variant cat <<EOF > ${WORKDIR}/whereami-frontend/variant/cm-flag.yaml apiVersion: v1 kind: ConfigMap metadata: name: whereami data: BACKEND_ENABLED: "True" BACKEND_SERVICE: "http://whereami-backend.backend.svc.cluster.local" EOF cat <<EOF > ${WORKDIR}/whereami-frontend/variant/service-type.yaml apiVersion: "v1" kind: "Service" metadata: name: "whereami" spec: type: ClusterIP EOF cat <<EOF > ${WORKDIR}/whereami-frontend/variant/kustomization.yaml nameSuffix: "-frontend" namespace: frontend commonLabels: app: whereami-frontend resources: - ../base patches: path: cm-flag.yaml target: kind: ConfigMap - path: service-type.yaml target: kind: Service EOF
将 whereami
frontend
变体应用于这两个集群:kubectl --context=${CLUSTER_1_NAME} apply -k ${WORKDIR}/whereami-frontend/variant kubectl --context=${CLUSTER_2_NAME} apply -k ${WORKDIR}/whereami-frontend/variant
创建
VirtualService
YAML 文件以将请求路由到 whereamifrontend
:cat << EOF > ${WORKDIR}/frontend-vs.yaml apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: whereami-vs namespace: frontend spec: gateways: - asm-ingress/asm-ingressgateway hosts: - 'frontend.endpoints.PROJECT_ID.cloud.goog' http: - route: - destination: host: whereami-frontend port: number: 80 EOF
将
frontend-vs
YAML 文件应用于这两个集群:kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/frontend-vs.yaml kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/frontend-vs.yaml
现在,您已将
frontend-vs.yaml
部署到这两个集群,可以尝试调用集群的公共端点:curl -s https://frontend.endpoints.PROJECT_ID.cloud.goog | jq
输出类似于以下内容:
{ "backend_result": { "cluster_name": "edge-to-mesh-02", "gce_instance_id": "8396338201253702608", "gce_service_account": "e2m-mcg-01.svc.id.goog", "host_header": "whereami-backend.backend.svc.cluster.local", "metadata": "backend", "node_name": "gk3-edge-to-mesh-02-pool-2-675f6abf-645h", "pod_ip": "10.124.0.199", "pod_name": "whereami-backend-7cbdfd788-8mmnq", "pod_name_emoji": "📸", "pod_namespace": "backend", "pod_service_account": "whereami-backend", "project_id": "e2m-mcg-01", "timestamp": "2023-12-01T03:46:24", "zone": "us-east4-b" }, "cluster_name": "edge-to-mesh-01", "gce_instance_id": "1047264075324910451", "gce_service_account": "e2m-mcg-01.svc.id.goog", "host_header": "frontend.endpoints.e2m-mcg-01.cloud.goog", "metadata": "frontend", "node_name": "gk3-edge-to-mesh-01-pool-2-d687e3c0-5kf2", "pod_ip": "10.54.1.71", "pod_name": "whereami-frontend-69c4c867cb-dgg8t", "pod_name_emoji": "🪴", "pod_namespace": "frontend", "pod_service_account": "whereami-frontend", "project_id": "e2m-mcg-01", "timestamp": "2023-12-01T03:46:24", "zone": "us-central1-c" }
如果您多次运行 curl
命令,会看到响应(来自 frontend
和 backend
)来自不同的区域。负载均衡器在其响应中提供地理位置路由。这意味着负载均衡器会将请求从客户端路由到最近的活跃集群,但请求仍然随机着陆。如果请求偶尔从一个区域发送到另一个区域,会增加延迟时间和费用。
在下一部分中,您将在服务网格中实现局部负载均衡,以使请求保持在本地。
为 whereami 启用并测试局部负载均衡
在本部分中,您将在服务网格中实现局部负载均衡,以使请求保持在本地。您还将执行一些测试,了解 whereami 如何处理各种故障场景。
当您向 whereami frontend
服务发出请求时,负载均衡器会将请求发送到相对于客户端延迟时间最短的集群。这意味着网格中的入站流量网关 pod 会跨两个集群负载均衡发送到 whereami frontend
pod 的请求。本部分将通过在网格内启用局部负载均衡来解决此问题。
在 Cloud Shell 中,创建
DestinationRule
YAML 文件,以启用局部负载均衡,在区域内故障切换到frontend
服务:cat << EOF > ${WORKDIR}/frontend-dr.yaml apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: frontend namespace: frontend spec: host: whereami-frontend.frontend.svc.cluster.local trafficPolicy: connectionPool: http: maxRequestsPerConnection: 0 loadBalancer: simple: LEAST_REQUEST localityLbSetting: enabled: true outlierDetection: consecutive5xxErrors: 1 interval: 1s baseEjectionTime: 1m EOF
上述代码示例只为
frontend
服务启用了本地路由。您还需要一个用来处理后端的配置。将
frontend-dr
YAML 文件应用于这两个集群:kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/frontend-dr.yaml kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/frontend-dr.yaml
创建
DestinationRule
YAML 文件,以启用局部负载均衡,在区域内故障切换到backend
服务:cat << EOF > ${WORKDIR}/backend-dr.yaml apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: n ame: backend namespace: backend spec: host: whereami-backend.backend.svc.cluster.local trafficPolicy: connectionPool: http: maxRequestsPerConnection: 0 loadBalancer: simple: LEAST_REQUEST localityLbSetting: enabled: true outlierDetection: consecutive5xxErrors: 1 interval: 1s baseEjectionTime: 1m EOF
将
backend-dr
YAML 文件应用于这两个集群:kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/backend-dr.yaml kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/backend-dr.yaml
将两组
DestinationRule
YAML 文件应用于这两个集群后,请求将保持在请求所路由到的集群本地。为了测试
frontend
服务的故障切换,减少主集群中入站流量网关的副本数量。从多区域负载均衡器的角度来看,此操作是为了模拟集群故障。而这会导致该集群的负载均衡器健康检查失败。此示例使用
CLUSTER_1_REGION
中的集群。您应该只会在CLUSTER_2_REGION
中看到来自集群的响应。将主集群中入站流量网关的副本数量减少到零,并调用公共端点以验证请求是否已故障切换到另一个集群:
kubectl --context=${CLUSTER_1_NAME} -n asm-ingress scale --replicas=0 deployment/asm-ingressgateway
输出应类似于以下内容:
$ curl -s https://frontend.endpoints.PROJECT_ID.cloud.goog | jq { "backend_result": { "cluster_name": "edge-to-mesh-02", "gce_instance_id": "2717459599837162415", "gce_service_account": "e2m-mcg-01.svc.id.goog", "host_header": "whereami-backend.backend.svc.cluster.local", "metadata": "backend", "node_name": "gk3-edge-to-mesh-02-pool-2-675f6abf-dxs2", "pod_ip": "10.124.1.7", "pod_name": "whereami-backend-7cbdfd788-mp8zv", "pod_name_emoji": "🏌🏽♀", "pod_namespace": "backend", "pod_service_account": "whereami-backend", "project_id": "e2m-mcg-01", "timestamp": "2023-12-01T05:41:18", "zone": "us-east4-b" }, "cluster_name": "edge-to-mesh-02", "gce_instance_id": "6983018919754001204", "gce_service_account": "e2m-mcg-01.svc.id.goog", "host_header": "frontend.endpoints.e2m-mcg-01.cloud.goog", "metadata": "frontend", "node_name": "gk3-edge-to-mesh-02-pool-3-d42ddfbf-qmkn", "pod_ip": "10.124.1.142", "pod_name": "whereami-frontend-69c4c867cb-xf8db", "pod_name_emoji": "🏴", "pod_namespace": "frontend", "pod_service_account": "whereami-frontend", "project_id": "e2m-mcg-01", "timestamp": "2023-12-01T05:41:18", "zone": "us-east4-b" }
如需恢复典型的流量路由方式,请将入站流量网关副本数量恢复为集群中的原始值:
kubectl --context=${CLUSTER_1_NAME} -n asm-ingress scale --replicas=3 deployment/asm-ingressgateway
通过将主要区域中的副本数量减少到 0,模拟
backend
服务故障:kubectl --context=${CLUSTER_1_NAME} -n backend scale --replicas=0 deployment/whereami-backend
验证
frontend
服务的响应是否来自us-central1
主要区域(通过负载均衡器传输),backend
服务的响应是否来自us-east4
次要区域。输出还应包含来自主要区域 (
us-central1
) 的frontend
服务响应,以及来自次要区域 (us-east4
) 的backend
服务响应。将后端服务副本数量恢复为原始值,以恢复典型的流量路由方式:
kubectl --context=${CLUSTER_1_NAME} -n backend scale --replicas=3 deployment/whereami-backend
现在,您有一个全球 HTTP(S) 负载均衡器,作为服务网格托管的多区域应用的前端。
清理
为避免因本部署中使用的资源导致您的 Google Cloud 账号产生费用,请删除包含这些资源的项目,或者保留项目但删除各个资源。
删除项目
- In the Google Cloud console, go to the Manage resources page.
- In the project list, select the project that you want to delete, and then click Delete.
- In the dialog, type the project ID, and then click Shut down to delete the project.
逐个删除资源
如果您希望保留在此部署中使用的 Google Cloud 项目,请删除单个资源:
在 Cloud Shell 中,删除
HTTPRoute
资源:kubectl --context=${CLUSTER_1_NAME} delete -f ${WORKDIR}/default-httproute-redirect.yaml kubectl --context=${CLUSTER_2_NAME} delete -f ${WORKDIR}/default-httproute-redirect.yaml kubectl --context=${CLUSTER_1_NAME} delete -f ${WORKDIR}/default-httproute.yaml kubectl --context=${CLUSTER_2_NAME} delete -f ${WORKDIR}/default-httproute.yaml
删除 GKE 网关资源:
kubectl --context=${CLUSTER_1_NAME} delete -f ${WORKDIR}/frontend-gateway.yaml kubectl --context=${CLUSTER_2_NAME} delete -f ${WORKDIR}/frontend-gateway.yaml
删除政策:
kubectl --context=${CLUSTER_1_NAME} delete -f ${WORKDIR}/ingress-gateway-healthcheck.yaml kubectl --context=${CLUSTER_2_NAME} delete -f ${WORKDIR}/ingress-gateway-healthcheck.yaml kubectl --context=${CLUSTER_1_NAME} delete -f ${WORKDIR}/cloud-armor-backendpolicy.yaml kubectl --context=${CLUSTER_2_NAME} delete -f ${WORKDIR}/cloud-armor-backendpolicy.yaml
删除服务导出内容:
kubectl --context=${CLUSTER_1_NAME} delete -f ${WORKDIR}/svc_export.yaml kubectl --context=${CLUSTER_2_NAME} delete -f ${WORKDIR}/svc_export.yaml
删除 Google Cloud Armor 资源:
gcloud --project=PROJECT_ID compute security-policies rules delete 1000 --security-policy edge-fw-policy --quiet gcloud --project=PROJECT_ID compute security-policies delete edge-fw-policy --quiet
删除 Certificate Manager 资源:
gcloud --project=PROJECT_ID certificate-manager maps entries delete mcg-cert-map-entry --map="mcg-cert-map" --quiet gcloud --project=PROJECT_ID certificate-manager maps delete mcg-cert-map --quiet gcloud --project=PROJECT_ID certificate-manager certificates delete mcg-cert --quiet
删除 Endpoints DNS 条目:
gcloud --project=PROJECT_ID endpoints services delete "frontend.endpoints.PROJECT_ID.cloud.goog" --quiet
删除静态 IP 地址:
gcloud --project=PROJECT_ID compute addresses delete mcg-ip --global --quiet
删除 GKE Autopilot 集群。此步骤需要几分钟时间来完成。
gcloud --project=PROJECT_ID container clusters delete ${CLUSTER_1_NAME} --region ${CLUSTER_1_REGION} --quiet gcloud --project=PROJECT_ID container clusters delete ${CLUSTER_2_NAME} --region ${CLUSTER_2_REGION} --quiet
后续步骤
- 了解 GKE 网关提供的可与服务网格配合使用的更多功能。
- 了解适用于 GKE 的不同类型的 Cloud Load Balancing。
- 了解 Cloud Service Mesh 提供的功能。
- 如需查看更多参考架构、图表和最佳实践,请浏览云架构中心。
贡献者
作者:
- Alex Mattson | 应用专家工程师
- Mark Chilvers | 应用专家工程师
其他贡献者:
- Abdelfettah Sghiouar | Cloud 开发技术推广工程师
- Greg Bray | 客户工程师
- Paul Revello | Cloud 解决方案架构师
- Valavan Rajakumar | 关键企业架构师