Del perímetro a la malla multiclúster: desplegar aplicaciones distribuidas globalmente a través de GKE Gateway y Cloud Service Mesh

Last reviewed 2024-06-30 UTC

En este documento se explica cómo llevar a cabo las siguientes tareas:

Esta guía de implementación está dirigida a los administradores de plataformas. También está dirigido a profesionales avanzados que ejecutan Cloud Service Mesh. Las instrucciones también funcionan para Istio on GKE.

Arquitectura

En el siguiente diagrama se muestra la topología de entrada predeterminada de una malla de servicios: un balanceador de carga TCP/UDP externo que expone los proxies de la puerta de enlace de entrada en un solo clúster:

Un balanceador de carga externo dirige los clientes externos a la malla a través de proxies de la pasarela de entrada.

En esta guía de implementación se usan recursos de Gateway de Google Kubernetes Engine (GKE). En concreto, usa una gateway multiclúster para configurar el balanceo de carga multirregional delante de varios clústeres Autopilot distribuidos en dos regiones.

Cifrado TLS desde el cliente, un balanceador de carga y la malla.

En el diagrama anterior se muestra cómo fluyen los datos en los casos de entrada en la nube y de entrada en la malla. Para obtener más información, consulta la explicación del diagrama de arquitectura en el documento de arquitectura de referencia asociado.

Objetivos

  • Despliega un par de clústeres de Autopilot de GKE en Google Cloud en la misma flota.
  • Despliega un Cloud Service Mesh basado en Istio en la misma flota.
  • Configura un balanceador de carga con GKE Gateway para terminar el tráfico HTTPS público.
  • Dirige el tráfico HTTPS público directamente a las aplicaciones alojadas por Cloud Service Mesh que se han desplegado en varios clústeres y regiones.
  • Despliega la aplicación de ejemplo whereami en ambos clústeres de Autopilot.

Optimización de costes

En este documento, se utilizan los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costes basada en el uso previsto, utiliza la calculadora de precios.

Los usuarios nuevos Google Cloud pueden disfrutar de una prueba gratuita.

Cuando termines las tareas que se describen en este documento, puedes evitar que se te siga facturando eliminando los recursos que has creado. Para obtener más información, consulta la sección Limpiar.

Antes de empezar

  1. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  2. Verify that billing is enabled for your Google Cloud project.

  3. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

    Todos los comandos de terminal de este despliegue se ejecutan desde Cloud Shell.

  4. Configura tu Google Cloud proyecto predeterminado:

    export PROJECT=YOUR_PROJECT
    export PROJECT_NUMBER=$(gcloud projects describe PROJECT_ID --format="value(projectNumber)")
    gcloud config set project PROJECT_ID
    

    Sustituye PROJECT_ID por el ID del proyecto que quieras usar en esta implementación.

  5. Crea un directorio de trabajo:

    mkdir -p ${HOME}/edge-to-mesh-multi-region
    cd ${HOME}/edge-to-mesh-multi-region
    export WORKDIR=`pwd`
    
  6. Crear clústeres de GKE

    En esta sección, crearás clústeres de GKE para alojar las aplicaciones y la infraestructura de asistencia, que crearás más adelante en esta guía de implementación.

    1. En Cloud Shell, crea un archivo kubeconfig. De esta forma, te aseguras de que no se produzca un conflicto con el archivo kubeconfig (predeterminado) que ya tienes.

      touch edge2mesh_mr_kubeconfig
      export KUBECONFIG=${WORKDIR}/edge2mesh_mr_kubeconfig
      
    2. Define las variables de entorno que se usan al crear los clústeres de GKE y los recursos que contienen. Modifica las opciones de región predeterminadas para adaptarlas a tus necesidades.

      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
      
    3. Habilita las APIs de Google Cloud que se usan en esta guía:

      gcloud services enable \
        container.googleapis.com \
        mesh.googleapis.com \
        gkehub.googleapis.com \
        multiclusterservicediscovery.googleapis.com \
        multiclusteringress.googleapis.com \
        trafficdirector.googleapis.com \
        certificatemanager.googleapis.com
      
    4. Crea un clúster de Autopilot de GKE con nodos privados en CLUSTER_1_REGION. Usa la marca --async para no tener que esperar a que se aprovisione el primer clúster y se registre en la flota:

      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
      
    5. Crea y registra un segundo clúster de Autopilot en CLUSTER_2_REGION:

      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
      
    6. Asegúrate de que los clústeres se estén ejecutando. Puede tardar hasta 20 minutos en que todos los clústeres se ejecuten:

      gcloud container clusters list
      

      El resultado debería ser similar al siguiente:

      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
      
    7. Recoge las credenciales de CLUSTER_1_NAME.Has creado CLUSTER_1_NAME de forma asíncrona para poder ejecutar comandos adicionales mientras se aprovisionaba el clúster.

      gcloud container clusters get-credentials ${CLUSTER_1_NAME} \
          --region ${CLUSTER_1_REGION}
      
    8. Para aclarar los nombres de los contextos de Kubernetes, cámbiales el nombre por el de los clústeres:

      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}
      

    Instalar una malla de servicios

    En esta sección, se configura Cloud Service Mesh gestionado con la API Fleet. Usar la API de flota para habilitar Cloud Service Mesh proporciona un enfoque declarativo para aprovisionar una malla de servicios.

    1. En Cloud Shell, habilita Cloud Service Mesh en la flota:

      gcloud container fleet mesh enable
      
    2. Habilita la gestión automática del plano de control y del plano de datos:

      gcloud container fleet mesh update \
        --management automatic \
        --memberships ${CLUSTER_1_NAME},${CLUSTER_2_NAME}
      
    3. Espera unos 20 minutos. A continuación, comprueba que el estado del plano de control sea ACTIVE:

      gcloud container fleet mesh describe
      

      El resultado debería ser similar al siguiente:

      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'
      

    Desplegar un balanceador de carga de aplicación externo y crear pasarelas de entrada

    En esta sección, desplegarás un balanceador de carga de aplicaciones externo a través del controlador de GKE Gateway y crearás gateways de entrada para ambos clústeres. Los recursos gateway y gatewayClass automatizan el aprovisionamiento del balanceador de carga y la comprobación del estado del backend. Para proporcionar la terminación de TLS en el balanceador de carga, crea recursos de Gestor de certificados y adjúntalos al balanceador de carga. Además, puedes usar endpoints para aprovisionar automáticamente un nombre de DNS público para la aplicación.

    Instalar una pasarela de entrada en ambos clústeres

    Como medida de seguridad recomendada, te aconsejamos que implementes la puerta de enlace de entrada en un espacio de nombres diferente del plano de control de la malla.

    1. En Cloud Shell, crea un espacio de nombres asm-ingress dedicado en cada clúster:

      kubectl --context=${CLUSTER_1_NAME} create namespace asm-ingress
      kubectl --context=${CLUSTER_2_NAME} create namespace asm-ingress
      
    2. Añade una etiqueta de espacio de nombres a los espacios de nombres de 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
      

      El resultado debería ser similar al siguiente:

      namespace/asm-ingress labeled
      

      Al etiquetar los espacios de nombres asm-ingress con istio-injection=enabled, se indica a Cloud Service Mesh que inserte automáticamente proxies sidecar de Envoy cuando se despliegue un pod.

    3. Genera un certificado autofirmado para usarlo más adelante:

      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
      

      El certificado proporciona una capa adicional de cifrado entre el balanceador de carga y las pasarelas de entrada de la malla de servicios. También permite la compatibilidad con protocolos basados en HTTP/2, como gRPC. Las instrucciones sobre cómo adjuntar el certificado autofirmado a las pasarelas de entrada se proporcionan más adelante en Crear recursos de dirección IP externa, registro DNS y certificado TLS.

      Para obtener más información sobre los requisitos del certificado de la pasarela de entrada, consulta Cifrado del balanceador de carga a los back-ends.

    4. Crea un secreto de Kubernetes en cada clúster para almacenar el certificado autofirmado:

      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
      
    5. Para integrar un balanceador de carga de aplicación externo, crea una variante de kustomize para configurar los recursos de pasarela de entrada:

      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
      
    6. Aplica la configuración de la pasarela de entrada a ambos clústeres:

      kubectl --context ${CLUSTER_1_NAME} apply -k ${WORKDIR}/asm-ig/variant
      kubectl --context ${CLUSTER_2_NAME} apply -k ${WORKDIR}/asm-ig/variant
      

    Exponer pods de pasarela de entrada al balanceador de carga mediante un servicio de varios clústeres

    En esta sección, exportarás los pods de la puerta de enlace de entrada a través de un ServiceExport recurso personalizado. Debes exportar los pods de la pasarela de entrada mediante un recurso personalizado ServiceExport por los siguientes motivos:

    1. En Cloud Shell, habilita los servicios de varios clústeres (MCS) en la flota:

      gcloud container fleet multi-cluster-services enable
      
    2. Concede a MCS los permisos de gestión de identidades y accesos necesarios en el proyecto o la flota:

      gcloud projects add-iam-policy-binding PROJECT_ID \
       --member "serviceAccount:PROJECT_ID.svc.id.goog[gke-mcs/gke-mcs-importer]" \
       --role "roles/compute.networkViewer"
      
    3. Crea el archivo ServiceExport YAML:

      cat <<EOF > ${WORKDIR}/svc_export.yaml
      kind: ServiceExport
      apiVersion: net.gke.io/v1
      metadata:
        name: asm-ingressgateway
        namespace: asm-ingress
      EOF
      
    4. Aplica el archivo ServiceExport YAML a ambos clústeres:

      kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/svc_export.yaml
      kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/svc_export.yaml
      

      Si recibes el siguiente mensaje de error, espera unos instantes a que se instalen las definiciones de recursos personalizados (CRDs) de MCS. A continuación, vuelve a ejecutar los comandos para aplicar el archivo ServiceExport YAML a ambos clústeres.

      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
      

    Crear recursos de dirección IP externa, registro DNS y certificado TLS

    En esta sección, crearás recursos de red que admitan los recursos de balanceo de carga que crearás más adelante en este despliegue.

    1. En Cloud Shell, reserva una dirección IP externa estática:

      gcloud compute addresses create mcg-ip --global
      

      El recurso de la pasarela de GKE usa una dirección IP estática. Permite que la dirección IP siga siendo la misma, aunque se vuelva a crear el balanceador de carga externo.

    2. Obtén la dirección IP estática y almacénala como variable de entorno:

      export MCG_IP=$(gcloud compute addresses describe mcg-ip --global --format "value(address)")
      echo ${MCG_IP}
      

      Para crear una asignación estable y fácil de recordar a la dirección IP de tu pasarela, debes tener un registro DNS público.

      Puedes usar el proveedor de DNS y el esquema de automatización que quieras. Esta implementación usa Endpoints en lugar de crear una zona DNS gestionada. Endpoints proporciona un registro DNS gestionado por Google gratuito para una dirección IP externa.

    3. Ejecuta el siguiente comando para crear un archivo YAML llamado dns-spec.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
      

      El archivo dns-spec.yaml define el registro DNS público con el formato frontend.endpoints.PROJECT_ID.cloud.goog, donde PROJECT_ID es el identificador único de tu proyecto.

    4. Implementa el archivo dns-spec.yaml para crear la entrada DNS. Este proceso tarda unos minutos.

      gcloud endpoints services deploy ${WORKDIR}/dns-spec.yaml
      
    5. Crea un certificado con Gestor de certificados para el nombre de entrada DNS que has creado en el paso anterior:

      gcloud certificate-manager certificates create mcg-cert \
          --domains="frontend.endpoints.PROJECT_ID.cloud.goog"
      

      Se usa un certificado TLS gestionado por Google para finalizar las solicitudes de clientes entrantes en el balanceador de carga.

    6. Crea un mapa de certificados:

      gcloud certificate-manager maps create mcg-cert-map
      

      El balanceador de carga hace referencia al certificado a través de la entrada del mapa de certificados que crees en el siguiente paso.

    7. Crea una entrada de mapa de certificados para el certificado que has creado anteriormente en esta sección:

      gcloud certificate-manager maps entries create mcg-cert-map-entry \
          --map="mcg-cert-map" \
          --certificates="mcg-cert" \
          --hostname="frontend.endpoints.PROJECT_ID.cloud.goog"
      

    Crear políticas de servicio de backend y recursos de balanceador de carga

    En esta sección, completarás las siguientes tareas:

    • Crea una política de seguridad de Cloud Armor con reglas.
    • Crea una política que permita al balanceador de carga comprobar la capacidad de respuesta de los pods de la puerta de enlace de entrada a través del archivo YAML ServiceExport que has creado anteriormente.
    • Usa la API Gateway de GKE para crear un recurso de balanceador de carga.
    • Usa el recurso personalizado GatewayClass para definir el tipo de balanceador de carga específico.
    • Habilita el balanceo de carga entre varios clústeres en la flota y designa uno de los clústeres como clúster de configuración de la flota.
    1. En Cloud Shell, crea una política de seguridad de Cloud Armor:

      gcloud compute security-policies create edge-fw-policy \
          --description "Block XSS attacks"
      
    2. Crea una regla para la política de seguridad:

      gcloud compute security-policies rules create 1000 \
          --security-policy edge-fw-policy \
          --expression "evaluatePreconfiguredExpr('xss-stable')" \
          --action "deny-403" \
          --description "XSS attack filtering"
      
    3. Crea un archivo YAML para la política de seguridad y haz referencia al archivo YAML ServiceExport mediante un archivo YAML ServiceImport correspondiente:

      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
      
    4. Aplica la política de Cloud Armor a ambos clústeres:

      kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/cloud-armor-backendpolicy.yaml
      kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/cloud-armor-backendpolicy.yaml
      
    5. Crea un archivo YAML personalizado que permita al balanceador de carga realizar comprobaciones del estado en el endpoint de comprobación del estado de Envoy (puerto 15021 en la ruta /healthz/ready) de los pods de la puerta de enlace de entrada de ambos clústeres:

      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
      
    6. Aplica el archivo YAML personalizado que has creado en el paso anterior a ambos clústeres:

      kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/ingress-gateway-healthcheck.yaml
      kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/ingress-gateway-healthcheck.yaml
      
    7. Habilita el balanceo de carga entre clústeres para la flota y designa CLUSTER_1_NAME como clúster de configuración:

      gcloud container fleet ingress enable \
        --config-membership=${CLUSTER_1_NAME} \
        --location=${CLUSTER_1_REGION}
      
    8. Concede permisos de gestión de identidades y accesos al controlador de Gateway en la flota:

      gcloud projects add-iam-policy-binding PROJECT_ID \
          --member "serviceAccount:service-${PROJECT_NUMBER}@gcp-sa-multiclusteringress.iam.gserviceaccount.com" \
          --role "roles/container.admin"
      
    9. Crea el archivo YAML del balanceador de carga mediante un recurso personalizado Gateway que haga referencia a gke-l7-global-external-managed-mc gatewayClass y a la dirección IP estática que has creado anteriormente:

      cat <<EOF > ${WORKDIR}/frontend-gateway.yaml
      kind: Gateway
      apiVersion: gateway.networking.k8s.io/v1
      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
      
    10. Aplica el archivo frontend-gateway YAML a ambos clústeres. Solo CLUSTER_1_NAME tiene autoridad, a menos que designes otro clúster de configuración como autoridad:

      kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/frontend-gateway.yaml
      kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/frontend-gateway.yaml
      
    11. Crea un archivo HTTPRoute YAML llamado default-httproute.yaml que indique al recurso Gateway que envíe solicitudes a las pasarelas de entrada:

      cat << EOF > ${WORKDIR}/default-httproute.yaml
      apiVersion: gateway.networking.k8s.io/v1
      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
      
    12. Aplica el archivo YAML HTTPRoute que has creado en el paso anterior a ambos clústeres:

      kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/default-httproute.yaml
      kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/default-httproute.yaml
      
    13. Para realizar redirecciones de HTTP a HTTP(S), cree un archivo HTTPRoute YAML adicional llamado default-httproute-redirect.yaml:

      cat << EOF > ${WORKDIR}/default-httproute-redirect.yaml
      kind: HTTPRoute
      apiVersion: gateway.networking.k8s.io/v1
      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
      
    14. Aplica el archivo YAML HTTPRoute a ambos clústeres:

      kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/default-httproute-redirect.yaml
      kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/default-httproute-redirect.yaml
      
    15. Inspecciona el recurso Gateway para comprobar el progreso de la implementación del balanceador de carga:

      kubectl --context=${CLUSTER_1_NAME} describe gateway external-http -n asm-ingress
      

      El resultado muestra la información que has introducido en esta sección.

    Desplegar la aplicación de ejemplo whereami

    En esta guía se usa whereami como aplicación de ejemplo para proporcionar comentarios directos sobre los clústeres que responden a las solicitudes. En la siguiente sección se configuran dos implementaciones independientes de whereami en ambos clústeres: una implementación de frontend y otra de backend.

    La implementación de frontend es la primera carga de trabajo que recibe la solicitud. Después, llama a la backendimplementación.

    Este modelo se usa para mostrar una arquitectura de aplicación multiservicio. Los servicios frontend y backend se han desplegado en ambos clústeres.

    1. En Cloud Shell, crea los espacios de nombres de whereami frontend y whereami backend en ambos clústeres y habilita la inyección de espacios de nombres:

      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
      
    2. Crea una variante de Kustomize para el backend de whereami:

      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
      
    3. Aplica la variante whereami backend a ambos clústeres:

      kubectl --context=${CLUSTER_1_NAME} apply -k ${WORKDIR}/whereami-backend/variant
      kubectl --context=${CLUSTER_2_NAME} apply -k ${WORKDIR}/whereami-backend/variant
      
    4. Crea una variante de Kustomize para el frontend de whereami:

      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
      
    5. Aplica la variante whereami frontend a ambos clústeres:

      kubectl --context=${CLUSTER_1_NAME} apply -k ${WORKDIR}/whereami-frontend/variant
      kubectl --context=${CLUSTER_2_NAME} apply -k ${WORKDIR}/whereami-frontend/variant
      
    6. Crea un archivo YAML VirtualService para dirigir las solicitudes a whereami frontend:

      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
      
    7. Aplica el archivo frontend-vs YAML a ambos clústeres:

      kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/frontend-vs.yaml
      kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/frontend-vs.yaml
      
    8. Ahora que has implementado frontend-vs.yaml en ambos clústeres, intenta llamar al endpoint público de tus clústeres:

      curl -s https://frontend.endpoints.PROJECT_ID.cloud.goog | jq
      

      El resultado debería ser similar al siguiente:

      {
        "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"
      }
      

    Si ejecutas el comando curl varias veces, verás que las respuestas (tanto de frontend como de backend) proceden de diferentes regiones. En su respuesta, el balanceador de carga proporciona el enrutamiento geográfico. Esto significa que el balanceador de carga está enrutando las solicitudes del cliente al clúster activo más cercano, pero las solicitudes siguen llegando de forma aleatoria. Cuando las solicitudes pasan ocasionalmente de una región a otra, aumenta la latencia y el coste.

    En la siguiente sección, implementarás el balanceo de carga de la localidad en la malla de servicios para que las solicitudes se mantengan locales.

    Habilitar y probar el balanceo de carga según la ubicación de whereami

    En esta sección, implementarás el balanceo de carga de la localidad en la malla de servicios para que las solicitudes se mantengan locales. También realizas algunas pruebas para ver cómo gestiona whereami varios casos de error.

    Cuando haces una solicitud al servicio whereami frontend, el balanceador de carga envía la solicitud al clúster con la latencia más baja en relación con el cliente. Esto significa que los pods de la puerta de enlace de entrada de la malla equilibran la carga de las solicitudes a los pods de whereami frontend en ambos clústeres. En esta sección se abordará ese problema habilitando el balanceo de carga según la ubicación en la malla.

    1. En Cloud Shell, crea un archivo YAML DestinationRule que habilite la conmutación por error regional del balanceo de carga por localidad al servicio 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
      

      El código de ejemplo anterior solo habilita el enrutamiento local para el servicio frontend. También necesitas una configuración adicional que gestione el backend.

    2. Aplica el archivo frontend-dr YAML a ambos clústeres:

      kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/frontend-dr.yaml
      kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/frontend-dr.yaml
      
    3. Crea un archivo YAML DestinationRule que habilite la conmutación por error regional con balanceo de carga local al servicio backend:

      cat << EOF > ${WORKDIR}/backend-dr.yaml
      apiVersion: networking.istio.io/v1beta1
      kind: DestinationRule
      metadata:
        name: 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
      
    4. Aplica el archivo backend-dr YAML a ambos clústeres:

      kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/backend-dr.yaml
      kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/backend-dr.yaml
      

      Si se aplican ambos conjuntos de archivos YAML a los dos clústeres, las solicitudes seguirán siendo locales del clúster al que se dirijan.DestinationRule

      Para probar la conmutación por error del servicio frontend, reduce el número de réplicas de la puerta de enlace de entrada de tu clúster principal.

      Desde la perspectiva del balanceador de carga multirregional, esta acción simula un fallo del clúster. Esto provoca que el clúster no supere las comprobaciones de estado del balanceador de carga. En este ejemplo se usa el clúster de CLUSTER_1_REGION. Solo deberías ver las respuestas del clúster en CLUSTER_2_REGION.

    5. Reduce a cero el número de réplicas de la puerta de enlace de entrada de tu clúster principal y llama al endpoint público para verificar que las solicitudes se han transferido al otro clúster:

      kubectl --context=${CLUSTER_1_NAME} -n asm-ingress scale --replicas=0 deployment/asm-ingressgateway
      
      .

      La salida debería ser similar a la siguiente:

      $ 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"
      }
      
    6. Para reanudar el enrutamiento de tráfico habitual, restaura las réplicas de la puerta de enlace de entrada al valor original en el clúster:

      kubectl --context=${CLUSTER_1_NAME} -n asm-ingress scale --replicas=3 deployment/asm-ingressgateway
      
    7. Simula un fallo del servicio backend reduciendo a 0 el número de réplicas de la región principal:

      kubectl --context=${CLUSTER_1_NAME} -n backend scale --replicas=0 deployment/whereami-backend
      

      Verifica que las respuestas del servicio frontend procedan de la región principal us-central1 a través del balanceador de carga y que las respuestas del servicio backend procedan de la región secundaria us-east4.

      El resultado también debe incluir una respuesta del servicio frontend de la región principal (us-central1) y una respuesta del servicio backend de la región secundaria (us-east4), tal como se espera.

    8. Restaura las réplicas del servicio de backend al valor original para reanudar el enrutamiento de tráfico habitual:

      kubectl --context=${CLUSTER_1_NAME} -n backend scale --replicas=3 deployment/whereami-backend
      

    Ahora tienes un balanceador de carga HTTP(S) global que actúa como frontend de tu aplicación multirregión alojada en una malla de servicios.

    Limpieza

    Para evitar que se apliquen cargos en tu cuenta de Google Cloud por los recursos utilizados en esta implementación, elimina el proyecto que contiene los recursos o conserva el proyecto y elimina los recursos.

    Eliminar el proyecto

    1. In the Google Cloud console, go to the Manage resources page.

      Go to Manage resources

    2. In the project list, select the project that you want to delete, and then click Delete.
    3. In the dialog, type the project ID, and then click Shut down to delete the project.

    Eliminar los recursos concretos

    Si quieres conservar el Google Cloud proyecto que has usado en esta implementación, elimina los recursos individuales:

    1. En Cloud Shell, elimina los recursos 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
      
    2. Elimina los recursos de la pasarela de GKE:

      kubectl --context=${CLUSTER_1_NAME} delete -f ${WORKDIR}/frontend-gateway.yaml
      kubectl --context=${CLUSTER_2_NAME} delete -f ${WORKDIR}/frontend-gateway.yaml
      
    3. Elimina las políticas:

      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
      
    4. Elimina las exportaciones de servicios:

      kubectl --context=${CLUSTER_1_NAME} delete -f ${WORKDIR}/svc_export.yaml
      kubectl --context=${CLUSTER_2_NAME} delete -f ${WORKDIR}/svc_export.yaml
      
    5. Elimina los recursos de 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
      
    6. Elimina los recursos de 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
      
    7. Elimina la entrada DNS de Endpoints:

      gcloud --project=PROJECT_ID endpoints services delete "frontend.endpoints.PROJECT_ID.cloud.goog" --quiet
      
    8. Elimina la dirección IP estática:

      gcloud --project=PROJECT_ID compute addresses delete mcg-ip --global --quiet
      
    9. Elimina los clústeres de Autopilot de GKE. Este paso tarda varios minutos.

      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
      

    Siguientes pasos

    Colaboradores

    Autores:

    Otros colaboradores: