Configurar una malla de varios clústeres en GKE

En esta guía se explica cómo unir dos clústeres en una sola malla de servicios de Cloud mediante Mesh CA o Istio CA y cómo habilitar el balanceo de carga entre clústeres. Puedes ampliar fácilmente este proceso para incorporar cualquier número de clústeres a tu malla.

Una configuración de Cloud Service Mesh de varios clústeres puede resolver varios casos prácticos cruciales para las empresas, como la escalabilidad, la ubicación y el aislamiento. Para obtener más información, consulta Casos prácticos de varios clústeres.

Requisitos previos

En esta guía se da por hecho que tienes dos o más Google Cloud clústeres de GKE que cumplen los siguientes requisitos:

Definir variables de proyecto y de clúster

  1. Crea las siguientes variables de entorno para el ID del proyecto, la zona o la región del clúster, el nombre del clúster y el contexto.

    export PROJECT_1=PROJECT_ID_1
    export LOCATION_1=CLUSTER_LOCATION_1
    export CLUSTER_1=CLUSTER_NAME_1
    export CTX_1="gke_${PROJECT_1}_${LOCATION_1}_${CLUSTER_1}"
    
    export PROJECT_2=PROJECT_ID_2
    export LOCATION_2=CLUSTER_LOCATION_2
    export CLUSTER_2=CLUSTER_NAME_2
    export CTX_2="gke_${PROJECT_2}_${LOCATION_2}_${CLUSTER_2}"
    
  2. Si se trata de clústeres recién creados, asegúrate de obtener las credenciales de cada clúster con los siguientes comandos gcloud. De lo contrario, los context asociados no estarán disponibles para usarse en los siguientes pasos de esta guía.

    Los comandos dependen del tipo de clúster, ya sea regional o zonal:

    Regional

    gcloud container clusters get-credentials ${CLUSTER_1} --region ${LOCATION_1}
    gcloud container clusters get-credentials ${CLUSTER_2} --region ${LOCATION_2}
    

    Por zonas

    gcloud container clusters get-credentials ${CLUSTER_1} --zone ${LOCATION_1}
    gcloud container clusters get-credentials ${CLUSTER_2} --zone ${LOCATION_2}
    

Crear regla de cortafuegos

En algunos casos, debe crear una regla de cortafuegos para permitir el tráfico entre clústeres. Por ejemplo, debes crear una regla de cortafuegos si:

  • Utilizas subredes diferentes para los clústeres de tu malla.
  • Tus pods abren puertos distintos de 443 y 15002.

GKE añade automáticamente reglas de cortafuegos a cada nodo para permitir el tráfico dentro de la misma subred. Si tu malla contiene varias subredes, debes configurar explícitamente las reglas de cortafuegos para permitir el tráfico entre subredes. Debes añadir una nueva regla de cortafuegos para cada subred con el fin de permitir los bloques CIDR de IP de origen y los puertos de destino de todo el tráfico entrante.

Las siguientes instrucciones permiten la comunicación entre todos los clústeres de tu proyecto o solo entre $CLUSTER_1 y $CLUSTER_2.

  1. Recopila información sobre la red de tus clústeres.

    Todos los clústeres del proyecto

    Si los clústeres están en el mismo proyecto, puedes usar el siguiente comando para permitir la comunicación entre todos los clústeres del proyecto. Si hay clústeres en tu proyecto que no quieres exponer, usa el comando de la pestaña Clústeres específicos.

    function join_by { local IFS="$1"; shift; echo "$*"; }
    ALL_CLUSTER_CIDRS=$(gcloud container clusters list --project $PROJECT_1 --format='value(clusterIpv4Cidr)' | sort | uniq)
    ALL_CLUSTER_CIDRS=$(join_by , $(echo "${ALL_CLUSTER_CIDRS}"))
    ALL_CLUSTER_NETTAGS=$(gcloud compute instances list --project $PROJECT_1 --format='value(tags.items.[0])' | sort | uniq)
    ALL_CLUSTER_NETTAGS=$(join_by , $(echo "${ALL_CLUSTER_NETTAGS}"))
    

    Clústeres específicos

    El siguiente comando permite la comunicación entre $CLUSTER_1 y $CLUSTER_2, y no expone otros clústeres de tu proyecto.

    function join_by { local IFS="$1"; shift; echo "$*"; }
    ALL_CLUSTER_CIDRS=$(for P in $PROJECT_1 $PROJECT_2; do gcloud --project $P container clusters list --filter="name:($CLUSTER_1,$CLUSTER_2)" --format='value(clusterIpv4Cidr)'; done | sort | uniq)
    ALL_CLUSTER_CIDRS=$(join_by , $(echo "${ALL_CLUSTER_CIDRS}"))
    ALL_CLUSTER_NETTAGS=$(for P in $PROJECT_1 $PROJECT_2; do gcloud --project $P compute instances list  --filter="name:($CLUSTER_1,$CLUSTER_2)" --format='value(tags.items.[0])' ; done | sort | uniq)
    ALL_CLUSTER_NETTAGS=$(join_by , $(echo "${ALL_CLUSTER_NETTAGS}"))
    
  2. Crea la regla de cortafuegos.

    GKE

    gcloud compute firewall-rules create istio-multicluster-pods \
        --allow=tcp,udp,icmp,esp,ah,sctp \
        --direction=INGRESS \
        --priority=900 \
        --source-ranges="${ALL_CLUSTER_CIDRS}" \
        --target-tags="${ALL_CLUSTER_NETTAGS}" --quiet \
        --network=YOUR_NETWORK
    

    Autopilot

    TAGS=""
    for CLUSTER in ${CLUSTER_1} ${CLUSTER_2}
    do
        TAGS+=$(gcloud compute firewall-rules list --filter="Name:$CLUSTER*" --format="value(targetTags)" | uniq) && TAGS+=","
    done
    TAGS=${TAGS::-1}
    echo "Network tags for pod ranges are $TAGS"
    
    gcloud compute firewall-rules create asm-multicluster-pods \
        --allow=tcp,udp,icmp,esp,ah,sctp \
        --network=gke-cluster-vpc \
        --direction=INGRESS \
        --priority=900 --network=VPC_NAME \
        --source-ranges="${ALL_CLUSTER_CIDRS}" \
        --target-tags=$TAGS
    

Configurar el descubrimiento de endpoints

Los pasos necesarios para configurar el descubrimiento de endpoints dependen de si prefieres usar la API declarativa en los clústeres de una flota o habilitarla manualmente en clústeres públicos o clústeres privados.

Configurar el descubrimiento de endpoints entre clústeres públicos

Para configurar el descubrimiento de endpoints entre clústeres de GKE, ejecuta asmcli create-mesh. Este comando:

  • Registra todos los clústeres en la misma flota.
  • Configura la malla para que confíe en la identidad de carga de trabajo de la flota.
  • Crea secretos remotos.

Puedes especificar el URI de cada clúster o la ruta del archivo kubeconfig.

URI del clúster

En el siguiente comando, sustituye FLEET_PROJECT_ID por el ID del proyecto host de la flota y el URI del clúster por el nombre del clúster, la zona o la región, y el ID del proyecto de cada clúster. En este ejemplo solo se muestran dos clústeres, pero puedes ejecutar el comando para habilitar la detección de endpoints en otros clústeres, siempre que no superes el número máximo de clústeres que puedes añadir a tu flota.

./asmcli create-mesh \
    FLEET_PROJECT_ID \
    ${PROJECT_1}/${LOCATION_1}/${CLUSTER_1} \
    ${PROJECT_2}/${LOCATION_2}/${CLUSTER_2}

Archivo kubeconfig

En el siguiente comando, sustituye FLEET_PROJECT_ID por el ID del proyecto host de la flota y PATH_TO_KUBECONFIG por la ruta de cada archivo kubeconfig. En este ejemplo solo se muestran dos clústeres, pero puedes ejecutar el comando para habilitar la detección de endpoints en otros clústeres, siempre que no superes el número máximo de clústeres que puedes añadir a tu flota.

./asmcli create-mesh \
    FLEET_PROJECT_ID \
    PATH_TO_KUBECONFIG_1 \
    PATH_TO_KUBECONFIG_2

Configurar el descubrimiento de endpoints entre clústeres privados

  1. Configura los secretos remotos para permitir que el servidor de la API acceda al plano de control de Cloud Service Mesh del otro clúster. Los comandos dependen del tipo de Cloud Service Mesh (en el clúster o gestionado):

    A. En el caso de Cloud Service Mesh en el clúster, debes configurar las IPs privadas en lugar de las públicas, ya que no se puede acceder a las IPs públicas:

    PRIV_IP=`gcloud container clusters describe "${CLUSTER_1}" --project "${PROJECT_1}" \
     --zone "${LOCATION_1}" --format "value(privateClusterConfig.privateEndpoint)"`
    
    ./istioctl x create-remote-secret --context=${CTX_1} --name=${CLUSTER_1} --server=https://${PRIV_IP} > ${CTX_1}.secret
    
    PRIV_IP=`gcloud container clusters describe "${CLUSTER_2}" --project "${PROJECT_2}" \
     --zone "${LOCATION_2}" --format "value(privateClusterConfig.privateEndpoint)"`
    
    ./istioctl x create-remote-secret --context=${CTX_2} --name=${CLUSTER_2} --server=https://${PRIV_IP} > ${CTX_2}.secret
    

    B. En el caso de Cloud Service Mesh gestionado:

    PUBLIC_IP=`gcloud container clusters describe "${CLUSTER_1}" --project "${PROJECT_1}" \
     --zone "${LOCATION_1}" --format "value(privateClusterConfig.publicEndpoint)"`
    
    ./istioctl x create-remote-secret --context=${CTX_1} --name=${CLUSTER_1} --server=https://${PUBLIC_IP} > ${CTX_1}.secret
    
    PUBLIC_IP=`gcloud container clusters describe "${CLUSTER_2}" --project "${PROJECT_2}" \
     --zone "${LOCATION_2}" --format "value(privateClusterConfig.publicEndpoint)"`
    
    ./istioctl x create-remote-secret --context=${CTX_2} --name=${CLUSTER_2} --server=https://${PUBLIC_IP} > ${CTX_2}.secret
    
  2. Aplica los nuevos secretos a los clústeres:

    kubectl apply -f ${CTX_1}.secret --context=${CTX_2}
    
    kubectl apply -f ${CTX_2}.secret --context=${CTX_1}
    

Configurar redes autorizadas en clústeres privados

Sigue las instrucciones de esta sección solo si se cumplen todas las condiciones siguientes en tu malla:

Cuando se despliegan varios clústeres privados, el plano de control de Cloud Service Mesh de cada clúster debe llamar al plano de control de GKE de los clústeres remotos. Para permitir el tráfico, debe añadir el intervalo de direcciones de los pods del clúster de llamada a las redes autorizadas de los clústeres remotos.

  1. Obtén el bloque CIDR de IP de los pods de cada clúster:

    POD_IP_CIDR_1=`gcloud container clusters describe ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1} \
      --format "value(ipAllocationPolicy.clusterIpv4CidrBlock)"`
    
    POD_IP_CIDR_2=`gcloud container clusters describe ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2} \
      --format "value(ipAllocationPolicy.clusterIpv4CidrBlock)"`
    
  2. Añade los bloques CIDR de IP de los pods del clúster de Kubernetes a los clústeres remotos:

    EXISTING_CIDR_1=`gcloud container clusters describe ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1} \
     --format "value(masterAuthorizedNetworksConfig.cidrBlocks.cidrBlock)"`
    gcloud container clusters update ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1} \
    --enable-master-authorized-networks \
    --master-authorized-networks ${POD_IP_CIDR_2},${EXISTING_CIDR_1//;/,}
    
    EXISTING_CIDR_2=`gcloud container clusters describe ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2} \
     --format "value(masterAuthorizedNetworksConfig.cidrBlocks.cidrBlock)"`
    gcloud container clusters update ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2} \
    --enable-master-authorized-networks \
    --master-authorized-networks ${POD_IP_CIDR_1},${EXISTING_CIDR_2//;/,}
    

    Para obtener más información, consulta Crear un clúster con redes autorizadas.

  3. Verifica que las redes autorizadas se hayan actualizado:

    gcloud container clusters describe ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1} \
     --format "value(masterAuthorizedNetworksConfig.cidrBlocks.cidrBlock)"
    
    gcloud container clusters describe ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2} \
     --format "value(masterAuthorizedNetworksConfig.cidrBlocks.cidrBlock)"
    

Habilitar el acceso global al plano de control

Sigue las instrucciones de esta sección solo si se cumplen todas las condiciones siguientes en tu malla:

  • Estás usando clústeres privados.
  • Utilizas diferentes regiones para los clústeres de tu malla.

Debes habilitar el acceso global al plano de control para permitir que el plano de control de Cloud Service Mesh de cada clúster llame al plano de control de GKE de los clústeres remotos.

  1. Habilita el acceso global al plano de control:

    gcloud container clusters update ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1} \
     --enable-master-global-access
    
    gcloud container clusters update ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2} \
     --enable-master-global-access
    
  2. Comprueba que el acceso global al plano de control esté habilitado:

    gcloud container clusters describe ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1}
    
    gcloud container clusters describe ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2}
    

    En la sección privateClusterConfig de la salida se muestra el estado de masterGlobalAccessConfig.

Verificar la conectividad entre clústeres

En esta sección se explica cómo desplegar los servicios de ejemplo HelloWorld y Sleep en tu entorno multiclúster para verificar que el balanceo de carga entre clústeres funciona.

Definir la variable del directorio de muestras

  1. Ve a la ubicación donde se descargó asmcli y ejecuta el siguiente comando para definir ASM_VERSION:

    export ASM_VERSION="$(./asmcli --version)"
    
  2. Define una carpeta de trabajo para las muestras que usas para verificar que el balanceo de carga entre clústeres funciona. Las muestras se encuentran en un subdirectorio del directorio --output_dir que especificaste en el comando asmcli install. En el siguiente comando, cambia OUTPUT_DIR por el directorio que hayas especificado en --output_dir.

    export SAMPLES_DIR=OUTPUT_DIR/istio-${ASM_VERSION%+*}
    

Habilitar la inyección de sidecar

  1. Crea el espacio de nombres de ejemplo en cada clúster.

    for CTX in ${CTX_1} ${CTX_2}
    do
        kubectl create --context=${CTX} namespace sample
    done
    
  2. Habilita la inserción de sidecars en los espacios de nombres creados.

    Recomendación: Ejecuta el siguiente comando para aplicar la etiqueta de inyección predeterminada al espacio de nombres:

    for CTX in ${CTX_1} ${CTX_2}
    do
        kubectl label --context=${CTX} namespace sample \
            istio.io/rev- istio-injection=enabled --overwrite
    done
    

    Te recomendamos que uses la inyección predeterminada, pero también se admite la inyección basada en revisiones: Sigue estas instrucciones:

    1. Usa el siguiente comando para localizar la etiqueta de revisión en istiod:

      kubectl get deploy -n istio-system -l app=istiod -o \
          jsonpath={.items[*].metadata.labels.'istio\.io\/rev'}'{"\n"}'
      
    2. Aplica la etiqueta de revisión al espacio de nombres. En el siguiente comando, REVISION_LABEL es el valor de la etiqueta de revisión istiod que has anotado en el paso anterior.

      for CTX in ${CTX_1} ${CTX_2}
      do
          kubectl label --context=${CTX} namespace sample \
              istio-injection- istio.io/rev=REVISION_LABEL --overwrite
      done
      

Instalar el servicio HelloWorld

  • Crea el servicio HelloWorld en ambos clústeres:

    kubectl create --context=${CTX_1} \
        -f ${SAMPLES_DIR}/samples/helloworld/helloworld.yaml \
        -l service=helloworld -n sample
    
    kubectl create --context=${CTX_2} \
        -f ${SAMPLES_DIR}/samples/helloworld/helloworld.yaml \
        -l service=helloworld -n sample
    

Desplegar HelloWorld v1 y v2 en cada clúster

  1. Implementa HelloWorld v1 en CLUSTER_1 y v2 en CLUSTER_2, lo que te ayudará más adelante a verificar el balanceo de carga entre clústeres:

    kubectl create --context=${CTX_1} \
      -f ${SAMPLES_DIR}/samples/helloworld/helloworld.yaml \
      -l version=v1 -n sample
    kubectl create --context=${CTX_2} \
      -f ${SAMPLES_DIR}/samples/helloworld/helloworld.yaml \
      -l version=v2 -n sample
  2. Confirma que HelloWorld v1 y v2 se están ejecutando con los siguientes comandos. Comprueba que el resultado sea similar al que se muestra:

    kubectl get pod --context=${CTX_1} -n sample
    NAME                            READY     STATUS    RESTARTS   AGE
    helloworld-v1-86f77cd7bd-cpxhv  2/2       Running   0          40s
    kubectl get pod --context=${CTX_2} -n sample
    NAME                            READY     STATUS    RESTARTS   AGE
    helloworld-v2-758dd55874-6x4t8  2/2       Running   0          40s

Desplegar el servicio de sueño

  1. Despliega el servicio Sleep en ambos clústeres. Este pod genera tráfico de red artificial con fines de demostración:

    for CTX in ${CTX_1} ${CTX_2}
    do
        kubectl apply --context=${CTX} \
            -f ${SAMPLES_DIR}/samples/sleep/sleep.yaml -n sample
    done
    
  2. Espera a que se inicie el servicio Sleep en cada clúster. Comprueba que el resultado sea similar al que se muestra:

    kubectl get pod --context=${CTX_1} -n sample -l app=sleep
    NAME                             READY   STATUS    RESTARTS   AGE
    sleep-754684654f-n6bzf           2/2     Running   0          5s
    kubectl get pod --context=${CTX_2} -n sample -l app=sleep
    NAME                             READY   STATUS    RESTARTS   AGE
    sleep-754684654f-dzl9j           2/2     Running   0          5s

Verificar el balanceo de carga entre clústeres

Llama al servicio HelloWorld varias veces y comprueba el resultado para verificar respuestas alternas de las versiones 1 y 2:

  1. Llama al servicio de HelloWorld:

    kubectl exec --context="${CTX_1}" -n sample -c sleep \
        "$(kubectl get pod --context="${CTX_1}" -n sample -l \
        app=sleep -o jsonpath='{.items[0].metadata.name}')" \
        -- /bin/sh -c 'for i in $(seq 1 20); do curl -sS helloworld.sample:5000/hello; done'
    

    La salida es similar a la que se muestra a continuación:

    Hello version: v2, instance: helloworld-v2-758dd55874-6x4t8
    Hello version: v1, instance: helloworld-v1-86f77cd7bd-cpxhv
    ...
  2. Vuelve a llamar al servicio HelloWorld:

    kubectl exec --context="${CTX_2}" -n sample -c sleep \
        "$(kubectl get pod --context="${CTX_2}" -n sample -l \
        app=sleep -o jsonpath='{.items[0].metadata.name}')" \
        -- /bin/sh -c 'for i in $(seq 1 20); do curl -sS helloworld.sample:5000/hello; done'
    

    La salida es similar a la que se muestra a continuación:

    Hello version: v2, instance: helloworld-v2-758dd55874-6x4t8
    Hello version: v1, instance: helloworld-v1-86f77cd7bd-cpxhv
    ...

Enhorabuena, has verificado tu malla de servicios de Cloud con balanceo de carga y varios clústeres.

Limpiar el servicio HelloWorld

Cuando termines de verificar el balanceo de carga, elimina el servicio HelloWorld y Sleep de tu clúster.

kubectl delete ns sample --context ${CTX_1}
kubectl delete ns sample --context ${CTX_2}