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 realizar 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). En particular, usa una puerta de enlace de varios clústeres para configurar el balanceo de cargas multirregión frente a varios clústeres de Autopilot que son distribuidos 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 la entrada de la nube y las situaciones de 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

  • Implementar 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 nuevos de Google Cloud califiquen para obtener una prueba gratuita.

Cuando finalices las tareas que se describen en este documento, puedes borrar los recursos que creaste para evitar que continúe la facturación. Para obtener más información, consulta Cómo realizar una limpieza.

Antes de comenzar

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

    Go to project selector

  2. Make sure 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 un proyecto predeterminado de Google Cloud:

    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`
    

Crea clústeres de GKE

En esta sección, crearás clústeres de GKE para alojar las aplicaciones y la infraestructura de respaldo, 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 dentro de ellos. Modifica las opciones de región predeterminadas para adaptarlas a tus fines.

    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ámbiales el nombre 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 el plano de control y la administración automáticos 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 uso 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 una dirección IP externa, un registro DNS y recursos de 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 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 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, debes exportar 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 a MCS los permisos de IAM necesarios al 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 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 minutos 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 direcciones IP externas, registros DNS y recursos de certificados TLS

En esta sección, crearás recursos de herramientas de redes que admiten 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 puerta de enlace de GKE usa una dirección IP estática. Permite que la dirección IP permanezca igual, 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 de clientes entrantes 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 de mapa de certificados que creas en el siguiente paso.

  7. Crea una entrada de mapa de certificado para el certificado que creaste antes 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 balanceadores de cargas

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

  • Crea una política de seguridad de Google 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 puerta de enlace de GKE para crear un recurso de balanceador de cargas.
  • Usa el recurso personalizado GatewayClass para establecer el tipo de balanceador de cargas específico.
  • 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 de la flota.
  1. En Cloud Shell, crea una política de seguridad de Google 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 Google 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 /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 la 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/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
    
  10. Aplica el archivo YAML frontend-gateway a ambos clústeres. Solo CLUSTER_1_NAME es autorizado, a menos que designes un clúster de configuración diferente como autorizado:

    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 le indique al recurso de puerta de enlace que envíe solicitudes a las puertas de enlace de entrada:

    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
    
  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 HTTP a HTTP(S), crea un archivo YAML HTTPRoute adicional llamado default-httproute-redirect.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
    
  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
    

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

Implementa la aplicación de muestra 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 independientes de ambas en ambos clústeres: una implementación frontend y una implementación backend.

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

Este modelo se usa para demostrar una arquitectura de aplicaciones 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 lugar en el que 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 YAML 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 para 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 backend) provienen de diferentes regiones. En su respuesta, el balanceador de cargas proporciona enrutamiento geográfico. Esto significa que el balanceador de cargas enruta las solicitudes del cliente al clúster activo más cercano, pero las solicitudes siguen llegando de forma aleatoria. Cuando las solicitudes van de una región a otra en ocasiones, 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 de 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, 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 las solicitudes de balanceo de cargas de la malla a los Pods frontend en ambos clústeres. En esta sección, se abordará ese problema mediante la habilitación del balanceo de cargas de 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
    

    En la muestra de código anterior, solo se habilita el enrutamiento local para el servicio frontend. También necesitas una configuración adicional que controla el backend.

  2. Aplica el archivo YAML 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:
    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
    
  4. Aplica el archivo YAML 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 DestinationRule aplicados a ambos clústeres, las solicitudes permanecen locales en el clúster al que se enruta la solicitud.

    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. Hace que el clúster falle en las verificaciones de estado del balanceador de cargas. 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 la cantidad de réplicas para la puerta de enlace de entrada en tu 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 del servicio backend mediante la reducción de 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 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 cuenta de Google Cloud 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 de 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 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 servicios:

    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 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
    
  6. Borra los recursos del Administrador de certificados:

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

¿Qué sigue?

Colaboradores

Autores:

Otros colaboradores: