Del perímetro a la malla de varios clústeres: Implementa aplicaciones distribuidas globalmente a través de la puerta de enlace de GKE y Cloud Service Mesh

Last reviewed 2024-06-30 UTC

En este documento, se muestra cómo completar las siguientes tareas:

Esta guía de implementación está dirigida a los administradores de la plataforma. 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 cargas de TCP/UDP externo que expone los proxies de puerta de enlace de entrada en un solo clúster:

Un balanceador de cargas externo enruta los clientes externos a la malla a través de proxies de puerta de enlace de entrada.

En esta guía de implementación, se usan recursos de puerta de enlace de Google Kubernetes Engine (GKE). Específicamente, usa una puerta de enlace de varios clústeres para configurar el balanceo de cargas multirregional frente a varios clústeres de Autopilot que se distribuyen en dos regiones.

Encriptación TLS del cliente, un balanceador de cargas y la malla

En el diagrama anterior, se muestra cómo fluyen los datos a través de situaciones de entrada de la nube y entrada de 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

  • Implementa un par de clústeres de GKE Autopilot en Google Cloud en la misma flota.
  • Implementar una Cloud Service Mesh basada en Istio en la misma flota
  • Configurar un balanceador de cargas a través de la puerta de enlace de GKE para finalizar el tráfico HTTPS público.
  • Dirigir el tráfico de HTTPS público a las aplicaciones alojadas en Cloud Service Mesh que se implementan en varios clústeres y regiones.
  • Implementar la aplicación de muestra whereami en ambos clústeres de Autopilot.

Optimización de costos

En este documento, usarás los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costos en función del uso previsto, usa la calculadora de precios.

Es posible que los usuarios de Google Cloud nuevos cumplan con los requisitos para acceder a una prueba gratuita.

Cuando completes las tareas que se describen en este documento, podrás borrar los recursos que creaste para evitar que se te siga facturando. Para obtener más información, consulta Realiza una limpieza.

Antes de comenzar

  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

    Ejecuta todos los comandos de terminal de esta implementación desde Cloud Shell.

  4. Configura tu proyecto Google Cloud predeterminado:

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

    Reemplaza PROJECT_ID por el ID del proyecto que deseas usar para esta implementación.

  5. Cree un directorio de trabajo:

    mkdir -p ${HOME}/edge-to-mesh-multi-region
    cd ${HOME}/edge-to-mesh-multi-region
    export WORKDIR=`pwd`
    
  6. Crea 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 nuevo. Con este paso te aseguras de no causar un conflicto con el archivo kubeconfig (predeterminado) existente.

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

      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 GKE Autopilot con nodos privados en CLUSTER_1_REGION. Usa la marca --async para evitar esperar a que el primer clúster se aprovisione 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 estén en ejecución. Puede tomar hasta 20 minutos hasta que todos los clústeres estén en ejecución:

      gcloud container clusters list
      

      El resultado es similar a este:

      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. Recopila las credenciales para CLUSTER_1_NAME. Creaste CLUSTER_1_NAME de forma asíncrona para poder ejecutar comandos adicionales mientras se aprovisiona 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ámbialos por los nombres 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}
      

    Instala una malla de servicios

    En esta sección, debes configurar Cloud Service Mesh administrado con la API de Fleet. El uso de la API de la flota para habilitar Cloud Service Mesh proporciona un enfoque declarativo a fin de aprovisionar una malla de servicios.

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

      gcloud container fleet mesh enable
      
    2. Habilita la administració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 alrededor de 20 minutos. Luego, verifica que el estado del plano de control sea ACTIVE:

      gcloud container fleet mesh describe
      

      El resultado es similar a este:

      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'
      

    Implementa un balanceador de cargas de aplicaciones externo y crea puertas de enlace de entrada

    En esta sección, implementarás un balanceador de cargas de aplicaciones externo a través de GKE Gateway Controller y crearás puertas de enlace de entrada para ambos clústeres. Los recursos gateway y gatewayClass automatizan el aprovisionamiento del balanceador de cargas y la verificación de estado del backend. Para proporcionar la finalización de TLS en el balanceador de cargas, crea recursos del Administrador de certificados y adjúntalos al balanceador de cargas. Además, debes usar Endpoints para aprovisionar de forma automática un nombre de DNS público para la aplicación.

    Instala una puerta de enlace de entrada en ambos clústeres

    Como práctica recomendada de seguridad, te recomendamos que implementes la puerta de enlace de entrada en un espacio de nombres diferente en el plano de control de 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. Agrega una etiqueta de espacio de nombres al espacio de nombres 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 es similar a este:

      namespace/asm-ingress labeled
      

      Si etiquetas los espacios de nombres asm-ingress con istio-injection=enabled, se indica a Cloud Service Mesh que inserte de manera automática proxies de sidecar de Envoy cuando se implementa un Pod.

    3. Genera un certificado autofirmado para usarlo en el futuro:

      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 encriptación entre el balanceador de cargas y las puertas de enlace de entrada de la malla de servicios. También habilita la compatibilidad con protocolos basados en HTTP/2, como gRPC. Las instrucciones para adjuntar el certificado autofirmado a las puertas de enlace de entrada se proporcionan más adelante en Crea recursos de dirección IP externa, registro de DNS y certificado TLS.

      Para obtener más información sobre los requisitos del certificado de puerta de enlace de entrada, consulta Encriptación del balanceador de cargas a los backends.

    4. Crea un Secret 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 integrarla al balanceador de cargas de aplicaciones externo, crea una variante de kustomize para configurar los recursos de la puerta de enlace 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 puerta de enlace 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
      

    Expón los Pods de la puerta de enlace de entrada al balanceador de cargas 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 recurso personalizado ServiceExport. Debes exportar los Pods de la puerta de enlace de entrada a través de un recurso personalizado ServiceExport por los siguientes motivos:

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

      gcloud container fleet multi-cluster-services enable
      
    2. Otorga al MCS los permisos de IAM necesarios para 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 YAML ServiceExport:

      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 YAML de ServiceExport 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 momentos para que se instalen las definiciones de recursos personalizados (CRD) de MCS. Luego, vuelve a ejecutar los comandos para aplicar el archivo YAML ServiceExport 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
      

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

    En esta sección, crearás recursos de redes que admitan los recursos de balanceo de cargas que crearás más adelante en esta implementación.

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

      gcloud compute addresses create mcg-ip --global
      

      El recurso de GKE Gateway usa una dirección IP estática. Permite que la dirección IP siga siendo la misma, incluso si se vuelve a crear el balanceador de cargas externo.

    2. Obtén la dirección IP estática y almacénala como una 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 usar para la dirección IP de Ingress, debes tener un registro DNS público.

      Puedes usar el proveedor de DNS y el esquema de automatización que desees. En esta implementación, se usa Endpoints en lugar de crear una zona del DNS administrada. Endpoints proporciona un registro DNS administrado 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 en el formato frontend.endpoints.PROJECT_ID.cloud.goog, en el que PROJECT_ID es tu identificador de proyecto único.

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

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

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

      Se usa un certificado TLS administrado por Google para finalizar las solicitudes entrantes de los clientes en el balanceador de cargas.

    6. Crea un mapa de certificados:

      gcloud certificate-manager maps create mcg-cert-map
      

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

    7. Crea una entrada de mapa de certificados para el certificado que creaste 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"
      

    Crea políticas de servicio de backend y recursos de balanceador de cargas

    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 que el balanceador de cargas verifique la capacidad de respuesta de los Pods de la puerta de enlace de entrada a través del archivo YAML ServiceExport que creaste antes.
    • Usa la API de Gateway de GKE para crear un recurso de balanceador de cargas.
    • Usa el recurso personalizado GatewayClass para establecer el tipo específico de balanceador de cargas.
    • Habilita el balanceo de cargas de varios clústeres para la flota y designa uno de los clústeres como el clúster de configuración para 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 a través de 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 que el balanceador de cargas realice verificaciones de estado en el extremo de estado de Envoy (puerto 15021 en la ruta de acceso /healthz/ready) de los Pods de la puerta de enlace de entrada en 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 creaste 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 cargas de varios clústeres para la flota y designa CLUSTER_1_NAME como el clúster de configuración:

      gcloud container fleet ingress enable \
        --config-membership=${CLUSTER_1_NAME} \
        --location=${CLUSTER_1_REGION}
      
    8. Otorga permisos de IAM para el controlador de puerta de enlace 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 cargas a través de un recurso personalizado de puerta de enlace que haga referencia a gke-l7-global-external-managed-mc gatewayClass y a la dirección IP estática que creaste antes:

      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 YAML de frontend-gateway a ambos clústeres. Solo CLUSTER_1_NAME es autoritativo, a menos que designes otro clúster de configuración como autoritativo:

      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 YAML HTTPRoute llamado default-httproute.yaml que indique al recurso de Gateway que envíe solicitudes a las puertas de enlace 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 creaste 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 redireccionamientos de HTTP a HTTP(S), crea 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 de redireccionamiento 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 verificar el progreso de la implementación del balanceador de cargas:

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

      En el resultado, se muestra la información que ingresaste en esta sección.

    Implementa la aplicación de ejemplo de Whereami

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

    La implementación de frontend es la primera carga de trabajo en recibir la solicitud. Luego, llama a la implementación de backend.

    Este modelo se usa para demostrar una arquitectura de aplicación de varios servicios. Los servicios frontend y backend se implementan en ambos clústeres.

    1. En Cloud Shell, crea los espacios de nombres para un frontend y un backend de Donami en ambos clústeres y habilita la inserció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 whereami backend:

      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 backend de whereami 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 whereami frontend:

      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 frontend de whereami 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 enrutar las solicitudes al frontend de whereami:

      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 YAML de frontend-vs 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 implementaste frontend-vs.yaml en ambos clústeres, intenta llamar al extremo público de tus clústeres:

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

      El resultado es similar a este:

      {
        "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) provienen de diferentes regiones. En su respuesta, el balanceador de cargas proporciona el enrutamiento geográfico. Esto significa que el balanceador de cargas está enrutando las solicitudes del cliente al clúster activo más cercano, pero las solicitudes siguen llegando de forma aleatoria. Cuando las solicitudes ocasionalmente van de una región a otra, aumentan la latencia y el costo.

    En la siguiente sección, implementarás el balanceo de cargas de localidad en la malla de servicios para mantener las solicitudes locales.

    Habilita y prueba el balanceo de cargas de localidad para whereami

    En esta sección, implementarás el balanceo de cargas por localidad en la malla de servicios para mantener las solicitudes locales. También debes realizar algunas pruebas para ver cómo whereami maneja varias situaciones de falla.

    Cuando realizas una solicitud al servicio frontend de whereami, el balanceador de cargas 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 dentro de la malla balancean las solicitudes de carga a los Pods de whereami frontend en ambos clústeres. En esta sección, se abordará ese problema habilitando el balanceo de cargas por localidad dentro de la malla.

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

      La muestra de código anterior solo habilita el enrutamiento local para el servicio frontend. También necesitas una configuración adicional que controle el backend.

    2. Aplica el archivo YAML de frontend-dr 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 del balanceo de cargas de localidad 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 YAML de backend-dr 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
      

      Con ambos conjuntos de archivos YAML de DestinationRule aplicados a ambos clústeres, las solicitudes permanecen locales al clúster al que se enrutan.

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

      Desde la perspectiva del balanceador de cargas multirregional, esta acción simula una falla del clúster. Esto hace que el clúster falle en las verificaciones de estado del balanceador de cargas. En este ejemplo, se usa el clúster en CLUSTER_1_REGION. Solo deberías ver respuestas del clúster en CLUSTER_2_REGION.

    5. Reduce a cero la cantidad de réplicas de la puerta de enlace de entrada en el clúster principal y llama al extremo público para verificar que las solicitudes hayan conmutado por error al otro clúster:

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

      El resultado debe parecerse al 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 típico, restablece 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 una falla para el servicio backend reduciendo la cantidad de réplicas en la región principal a 0:

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

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

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

    8. Restablece las réplicas del servicio de backend al valor original para reanudar el enrutamiento de tráfico típico:

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

    Ahora tienes un balanceador de cargas de HTTP(S) global que funciona como frontend en tu aplicación multirregional alojada en la malla de servicios.

    Limpia

    Para evitar que se apliquen cargos a tu Google Cloud cuenta por los recursos usados en esta implementación, borra el proyecto que contiene los recursos o conserva el proyecto y borra los recursos individuales.

    Borra 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.

    Borra los recursos individuales

    Si deseas conservar el proyecto Google Cloud que usaste en esta implementación, borra los recursos individuales:

    1. En Cloud Shell, borra los recursos de 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. Borra los recursos de la puerta de enlace 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. Borra 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. Borra las exportaciones de servicio:

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

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

      gcloud --project=PROJECT_ID compute addresses delete mcg-ip --global --quiet
      
    9. Borra los clústeres de GKE Autopilot. 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
      

    ¿Qué sigue?

    Colaboradores

    Autores:

    Otros colaboradores: