Implementación de referencia del balanceo de cargas de la capa 7 de NGINX

Google Distributed Cloud (GDC) aislado proporciona un balanceador de cargas de capa 4 (L4) administrado integrado, pero muchas aplicaciones empresariales requieren capacidades avanzadas de capa 7 (L7), como el enrutamiento basado en el host, la administración centralizada de TLS y la división de tráfico compleja. Históricamente, esto se logra con la API de Ingress, que ahora se considera con funciones congeladas en la comunidad de Kubernetes.

Esta arquitectura de referencia proporciona una solución de balanceo de cargas de capa 7 autoadministrada. Implementando el popular controlador de código abierto NGINX Gateway Fabric en un clúster estándar de GDC, los clientes pueden enrutar sin problemas el tráfico de L7 a entornos híbridos. Esta arquitectura usa la finalización de TLS (HTTPRoute) para enrutar el tráfico según la indicación del nombre del servidor (SNI) a los Pods alojados en contenedores integrados y a las aplicaciones alojadas en máquinas virtuales externas.

Arquitectura

Diagrama de arquitectura para el balanceo de cargas de capa 7 de NGINX en GDC aislado.

Entre los componentes clave de la solución, se incluyen los siguientes:

  • Cliente: Es una entidad que inicia solicitudes HTTPS para interactuar con las aplicaciones.
  • Clúster estándar de GDC: GDC proporciona una forma integrada de crear clústeres Vanilla de Kubernetes. En esta solución, el clúster alojará el LB de L7 y sus controladores, junto con las cargas de trabajo y el servicio sin interfaz gráfica para las VMs externas
  • Balanceador de cargas de L4 de GDC: Es el balanceador de cargas de L4 integrado que funciona como punto de entrada y distribuye el tráfico de TCP/443 directamente a los Pods de Kubernetes que ejecutan los controladores de Gateway.
  • Controladores de puerta de enlace: Son operadores de NGINX que se ejecutan en el clúster estándar. Supervisan los recursos de la API de Gateway (como Gateway y HTTPRoute) y actualizan de forma dinámica los proxies subyacentes. Para la solución, se usará Nginx Gateway Fabric.
  • Puerta de enlace (con HTTPRoute): Son recursos de Kubernetes estandarizados que definen el puerto de escucha físico (443) y las reglas de enrutamiento de host basadas en SNI con finalización de TLS.
  • Carga de trabajo alojada en contenedores (Pods): Es un Deployment de Kubernetes estándar expuesto de forma interna con un Service de Kubernetes normal.
  • Carga de trabajo basada en VM (externa): Es una carga de trabajo alojada en una VM externa en la red del proyecto, expuesta al proxy con un Service de Kubernetes sin interfaz gráfica y un extremo personalizado que contiene la IP directa de la VM.
  • Registro de Harbor: Es un registro de contenedores privado que se usa para almacenar y entregar las imágenes de proxy y de aplicación en el entorno aislado.

En el clúster Standard, creas cuatro espacios de nombres:

  • El espacio de nombres nginx-gateway que aloja los recursos de Nginx Gateway Fabric:

    Los recursos del espacio de nombres nginx-gateway.

  • El espacio de nombres load-balancer que aloja los recursos de Gateway:

    Los recursos del espacio de nombres del balanceador de cargas.

  • El espacio de nombres vm-app aloja la IP de VM externa vm-app, un servicio sin interfaz gráfica, un EndpointSlice que apunta a la IP externa y un HTTPRoute para el Gateway:

    Los recursos del espacio de nombres vm-app.

  • El espacio de nombres hello-app aloja el Deployment, un Service, un HTTPRoute y un Gateway para la carga de trabajo del contenedor de demostración:

    Los recursos del espacio de nombres de hello-app.

Antes de comenzar

Antes de implementar esta solución, asegúrate de cumplir con los siguientes requisitos:

  • Software necesario: helm, docker, kubectl
  • Acceso a la CLI y configuración local: Descarga la CLI de gdcloud desde la consola de GDC y configura tu entorno de forma local.

    export USER_NAME="USER_NAME"
    export PROJECT_ID="PROJECT_ID"
    export ZONE="ZONE"
    export ORG_NAME="ORG_NAME"
    export GDC_URL="GDC_URL"
    
    gdcloud components install gdcloud-k8s-auth-plugin
    
    gdcloud config set core/organization_console_url \
      https://console.$ORG_NAME.$ZONE.$GDC_URL
    gdcloud config set core/zone $ZONE
    gdcloud config set core/project ${PROJECT_ID}
    
    gdcloud auth login # use --login-config-cert option in case of TLS error
    
  • Configuración del proyecto: Crea un proyecto en tu entorno de GDC aislado para almacenar los recursos.

    gdcloud projects create $PROJECT_ID
    
  • Roles de IAM: Otorga a tu usuario los roles de Administrador de clústeres y Administrador de clústeres estándar para administrar los recursos de Kubernetes, y el rol de Administrador de instancias de Harbor para enviar imágenes.

    # Grant standard cluster and cluster admin roles
    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member="user:${USER_NAME}" \
      --role=cluster-admin
    
    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member="user:${USER_NAME}" \
      --role=standard-cluster-admin
    
    # Grant Harbor instance admin role
    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member="user:${USER_NAME}" \
      --role=harbor-instance-admin
    

Crea un clúster estándar

En esta sección, se te guiará a través del proceso de configuración de un clúster de Kubernetes estándar en tu entorno de GDC aislado. Un clúster estándar proporciona una base flexible y sólida para implementar varias cargas de trabajo, incluido el controlador de puerta de enlace de Nginx y tus aplicaciones personalizadas. Con los siguientes pasos, te asegurarás de que tu clúster esté configurado de forma correcta y sea accesible para las implementaciones posteriores.

  1. Ejecuta el siguiente comando para identificar los tipos de imágenes de máquina virtual disponibles:

    gdcloud compute machine-types list
    
  2. Selecciona un tipo de máquina adecuado para los nodos trabajadores de tu clúster. Para este instructivo, se recomienda un tipo de máquina con al menos 4 CPU virtuales.

    export MACHINE_TYPE="MACHINE_TYPE"
    
  3. Obtén el kubeconfig del servidor de la API de administración y establece un alias:

    export CLUSTER_NAME="CLUSTER_NAME"
    
    KUBECONFIG=kubeconfig-admin.yaml gdcloud clusters \
      get-credentials ${ORG_NAME}-admin
    
    alias km="kubectl --kubeconfig kubeconfig-admin.yaml"
    
  4. Crea un clúster estándar con 2 nodos trabajadores:

    km create -f - <<EOF
    apiVersion: cluster.gdc.goog/v1
    kind: Cluster
    metadata:
      name: ${CLUSTER_NAME}
      namespace: ${PROJECT_ID}
    spec:
      nodePools:
      - machineTypeName: ${MACHINE_TYPE}
        nodeCount: 2
        name: ${CLUSTER_NAME}-node-pool
    EOF
    

    Para obtener más detalles sobre las opciones disponibles, consulta la documentación.

    La creación de un clúster estándar puede tardar hasta 60 minutos en completarse. Para verificar el estado, usa el siguiente comando:

    km get clusters/${CLUSTER_NAME} \
      -n ${PROJECT_ID} \
      --watch
    

    Una vez que el clúster esté listo, el resultado debería mostrar un estado de Running, como este:

    NAME         STATE     K8S VERSION
    my-cluster   Running   1.30.12-gke.300
    
  5. Una vez que el clúster esté listo, recupera sus credenciales:

    KUBECONFIG=kubeconfig-${CLUSTER_NAME}.yaml gdcloud clusters \
      get-credentials ${CLUSTER_NAME} \
      --standard \
      --project ${PROJECT_ID} \
      --zone ${ZONE}
    
  6. Crea un alias para que los comandos de kubectl sean más concisos en el resto de esta guía. Este alias se usará para interactuar con el clúster estándar:

    alias kk="kubectl --kubeconfig kubeconfig-${CLUSTER_NAME}.yaml"
    
  7. Crea espacios de nombres para el controlador, la app alojada en contenedores de demostración "hello-app" y la app de demo basada en VM:

    kk create namespace load-balancer
    kk create namespace hello-app
    kk create namespace vm-app
    

Crea e integra Harbor Registry

Harbor es un registro de imágenes de contenedor con compatibilidad integrada en GDC aislado. En esta sección, se te guiará por los pasos para integrar un registro de Harbor con tu clúster estándar, lo que incluye configurar credenciales y Secrets para habilitar la extracción y el envío seguros de imágenes.

  1. Crea una instancia de Harbor en tu proyecto.
  2. Crea un proyecto de Harbor en tu instancia de Harbor.
  3. Establece las variables de entorno:

    export HARBOR_INSTANCE_NAME="HARBOR_INSTANCE_NAME"
    export HARBOR_INSTANCE_URL="HARBOR_INSTANCE_URL"
    export HARBOR_PROJECT="HARBOR_INSTANCE_PROJECT"
    export IMAGE_PULL_SECRET_NAME="harbor-secret"
    
  4. Accede a la instancia de Harbor con una cuenta de robot:

    docker --config=./docker login ${HARBOR_INSTANCE_URL}
    
  5. Crea los Secrets en el clúster estándar:

    kk create secret docker-registry ${IMAGE_PULL_SECRET_NAME} \
      --from-file=.dockerconfigjson=./docker/config.json \
      -n load-balancer
    
    kk create secret docker-registry ${IMAGE_PULL_SECRET_NAME} \
      --from-file=.dockerconfigjson=./docker/config.json \
      -n hello-app
    

Implementa una app alojada en contenedores de demostración

En esta sección, se detalla la implementación de una aplicación alojada en contenedores de demostración (hello-app) dentro de tu clúster de Kubernetes de GDC aislado. Crearás los recursos de Deployment y Service de Kubernetes necesarios para ejecutar hello-app y exponerlo de forma interna dentro del clúster, lo que lo preparará para el acceso con el balanceador de cargas de L7.

  1. Sube una imagen de muestra para la app alojada en contenedores de demostración a Harbor:

    docker pull gcr.io/google-samples/hello-app:1.0 \
      --platform linux/amd64
    docker tag gcr.io/google-samples/hello-app:1.0 \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/hello-app:1.0
    docker --config=./docker push \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/hello-app:1.0
    
  2. Implementa el siguiente manifiesto en el clúster estándar:

    cat << EOF > hello-app.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: hello-app
      namespace: hello-app
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: hello-app
      template:
        metadata:
          labels:
            app: hello-app
        spec:
          containers:
          - name: hello-server
            image: ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/hello-app:1.0
            ports:
            - containerPort: 8080
          imagePullSecrets:
          - name: ${IMAGE_PULL_SECRET_NAME}
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: hello-app
      namespace: hello-app
    spec:
      type: ClusterIP
      selector:
        app: hello-app
      ports:
        - protocol: TCP
          port: 80
          targetPort: 8080
    EOF
    
    kk apply -f hello-app.yaml
    

Luego, verifica que la implementación y el servicio estén allí:

kk get svc,deploy -n hello-app

Implementa app de demo en una VM

En esta sección, se detalla la implementación de una aplicación de demostración en una máquina virtual (VM) fuera de tu clúster de Kubernetes. Si configuras un servidor HTTP en una VM, simularás una aplicación externa que el balanceador de cargas puede exponer, lo que demuestra su capacidad para administrar el tráfico hacia los recursos dentro y fuera del clúster.

Primero, crea una VM para la app de demo

  1. Abre la consola de GDC en tu navegador web.
  2. Selecciona el mismo proyecto en el que creaste tu clúster de Kubernetes estándar.
  3. Abre el menú y, luego, haz clic en Máquinas virtuales.
  4. Haz clic en Crear instancia.
  5. Asigna el nombre vm-workload a la VM. Una imagen de 2 CPU virtuales es suficiente para el ejemplo.
  6. Para la imagen de disco de arranque, selecciona una distribución de Ubuntu 22.04, que incluye Python preinstalado.
  7. Haz clic en Crear.
  8. Espera unos minutos hasta que la VM esté lista.
  9. Establece una conexión SSH a la VM:
    1. En la consola de GDC, haz clic en la VM.
    2. Haz clic en Conectarse con SSH.

Después de conectarte a la consola de SSH, ejecuta lo siguiente:

mkdir ~/simple-server
cd ~/simple-server
echo 'Welcome to my VM!' > index.html
python3 -m http.server --bind 0.0.0.0 8080 &

Para enrutar el tráfico a una VM, crea un Service sin interfaz gráfica (sin selectores). Esta se asignará de forma manual a la dirección IP interna de la VM con un recurso EndpointSlice.

kk apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: vm-app-svc
  namespace: vm-app
spec:
  ports:
  - protocol: TCP
    port: 443
    targetPort: 443
EOF

Ejecuta el siguiente comando para obtener la dirección IP de la VM vm-workload:

gdcloud compute instances list --project ${PROJECT_ID} \
  | grep workload-vm | awk '{print $3}'

El resultado será la dirección IP de la VM que se necesitará para configurar el recurso EndpointSlice.

Crea el recurso EndpointSlice que se conectará al Service sin selector de VM-app y direccionará la IP de la VM a la que se debe enrutar el tráfico.

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: vm-app-endpoints
  namespace: vm-app
  labels:
    kubernetes.io/service-name: vm-app-svc
addressType: IPv4
ports:
  - port: 8080
endpoints:
  - addresses:
    - "VM_IP"
    conditions:
      ready: true

Crea certificados autofirmados

En esta sección, se te guiará a través del proceso de generación de certificados TLS autofirmados para tus aplicaciones. En esta guía, se usan certificados autofirmados por conveniencia, pero en los entornos de producción, debes usar certificados de nivel de producción, como se describe en Opcional: Usa certificados listos para producción. Estos certificados son fundamentales para habilitar la finalización de HTTPS en la puerta de enlace de Nginx, lo que garantiza la comunicación encriptada entre los clientes y el balanceador de cargas para las cargas de trabajo basadas en contenedores y en VM.

  1. Crea un certificado para la app alojada en contenedores:

    openssl req -x509 -newkey rsa:2048 -nodes \
      -keyout tls-containerized.key \
      -out tls-containerized.crt \
      -subj "/CN=k8s-app.example.com" \
      -days 365
    
    kk create secret tls tls-containerized \
      --namespace load-balancer \
      --key tls-containerized.key \
      --cert tls-containerized.crt
    
    kk create secret tls tls-containerized \
      --namespace hello-app \
      --key tls-containerized.key \
      --cert tls-containerized.crt
    
  2. Crea un certificado para la app de la VM:

    openssl req -x509 -newkey rsa:2048 -nodes \
      -keyout tls-vm.key \
      -out tls-vm.crt \
      -subj "/CN=vm-app.example.com" \
      -days 365
    
    kk create secret tls tls-vm \
      --namespace load-balancer \
      --key tls-vm.key \
      --cert tls-vm.crt
    
    kk create secret tls tls-vm \
      --namespace vm-app \
      --key tls-vm.key \
      --cert tls-vm.crt
    

Implementa NGINX

En esta sección, se describen los pasos para implementar el controlador de puerta de enlace de Nginx, lo que incluye instalar las definiciones de recursos personalizados (CRD) de la API de Gateway, configurar Nginx Gateway Fabric con Helm y verificar la instalación en tu clúster estándar. Esto prepara la infraestructura para el enrutamiento avanzado de capa 7.

Instala las CRD de la API de Gateway

La API de Gateway requiere que se instalen definiciones de recursos personalizados (CRD) en el clúster antes de implementar el controlador. Usaremos las definiciones de recursos personalizados (CRD) experimentales oficiales del proyecto de la API de Gateway (para esta guía, se usa la versión 1.2.0).

  1. Instala las CRD experimentales:

    kk apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/experimental-install.yaml
    
  2. Ejecuta kk get crd gateways.gateway.networking.k8s.io para verificar que los CRD se hayan instalado correctamente.

Instala NGINX Gateway Fabric

Implementa el controlador de NGINX Gateway Fabric con Helm. Este controlador observará los recursos de la API de Gateway y configurará NGINX para que controle el tráfico.

  1. Establece la etiqueta de la imagen de NGINX:

    helm template nfg oci://ghcr.io/nginx/charts/nginx-gateway-fabric | grep "image:" # get the tag of the nginx-gateway-fabric (in following case 2.4.2)
    
    export NGINX_TAG="2.4.2"
    
  2. Extrae las imágenes de NGINX Gateway Fabric y NGINX y, luego, envíalas a tu registro de Harbor:

    docker pull ghcr.io/nginx/nginx-gateway-fabric:${NGINX_TAG}
    docker tag ghcr.io/nginx/nginx-gateway-fabric:${NGINX_TAG} \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/nginx-gateway-fabric:${NGINX_TAG}
    docker --config=./docker push \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/nginx-gateway-fabric:${NGINX_TAG}
    
    docker pull nginx:1.27.3
    docker tag nginx:1.27.3 \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/nginx:1.27.3
    docker --config=./docker push \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/nginx:1.27.3
    
  3. Crea el espacio de nombres nginx-gateway y agrega el Secret de extracción de imágenes de Harbor:

    kk create namespace nginx-gateway
    
    kk create secret docker-registry ${IMAGE_PULL_SECRET_NAME} \
      --from-file=.dockerconfigjson=./docker/config.json \
      -n nginx-gateway
    
  4. Implementa NGINX Gateway Fabric con Helm, apuntando a las imágenes en tu registro de Harbor:

    KUBECONFIG=kubeconfig-${CLUSTER_NAME}.yaml helm upgrade --install ngf \
      oci://ghcr.io/nginx/charts/nginx-gateway-fabric \
      --create-namespace -n nginx-gateway \
      --set nginxGateway.image.tag="${NGINX_TAG}" \
      --set nginxGateway.image.repository="${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/nginx-gateway-fabric" \
      --set nginx.image.repository="${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/nginx" \
      --set nginxGateway.serviceAccount.imagePullSecret="${IMAGE_PULL_SECRET_NAME}" \
      --set nginx.imagePullSecret="${IMAGE_PULL_SECRET_NAME}"
    

    El Deployment de Kubernetes extrae imágenes de Harbor de forma segura con el Secret proporcionado (${IMAGE_PULL_SECRET_NAME}), que contiene las credenciales de la cuenta de robot de Harbor.

  5. Verifica que se acepten los recursos GatewayClass:

    kk get gatewayclass
    

    Deberías ver nginx en la lista con ACCEPTED = True.

Crea la instancia de Gateway

Define la instancia de balanceador de cargas lógica que escucha en el puerto 443. La configuraremos para el modo Terminate, lo que significa que la puerta de enlace realizará la finalización de TLS y desencriptará el tráfico antes de pasarlo.

Crea y aplica gateway.yaml:

cat << EOF > gateway.yaml 
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: my-gateway
  namespace: load-balancer
spec:
  gatewayClassName: nginx
  listeners:
  - name: https-k8s-workload
    hostname: "k8s-app.example.com"
    port: 443
    protocol: HTTPS
    allowedRoutes: 
      namespaces: 
        from: All
    tls:
      mode: Terminate
      certificateRefs:
      - kind: Secret
        name: tls-containerized 
  - name: https-vm-workload
    hostname: "vm-app.example.com"
    port: 443
    protocol: HTTPS
    allowedRoutes: 
      namespaces: 
        from: All
    tls:
      mode: Terminate
      certificateRefs:
      - kind: Secret
        name: tls-vm 
EOF

kk apply -f gateway.yaml

Verifica que la puerta de enlace se haya implementado de forma correcta. Para ello, comprueba si PROGRAMMED es True:

kk get gateway my-gateway --n load-balancer

Verifica que se haya implementado un balanceador de cargas de L4 de GDC administrado como un Service junto con la Gateway

kk get services -n load-balancer

Verifica que el servicio de Nginx se haya creado y configurado. El resultado debería ser similar al siguiente:

NAME               TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)         AGE
my-gateway-nginx   LoadBalancer   10.252.19.148   10.200.32.43   443:30649/TCP   45h

Define la lógica de enrutamiento de L7 (HTTPRoute)

Vincula HTTPRoute a tu puerta de enlace para definir cómo se debe distribuir el tráfico en función del nombre de host solicitado (SNI).

Crea y aplica routing.yaml:

cat << EOF > routing.yaml 
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: k8s-http-route
  namespace: hello-app
spec:
  parentRefs:
  - name: my-gateway 
    namespace: load-balancer
  hostnames:
  - "k8s-app.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: hello-app 
      namespace: hello-app
      port: 80          
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: vm-http-route
  namespace: vm-app
spec:
  parentRefs:
  - name: my-gateway
    namespace: load-balancer
  hostnames:
  - "vm-app.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: vm-app-svc  
      namespace: vm-app
      port: 443       
EOF

kk apply -f routing.yaml   

Recupera la IP del balanceador de cargas

Ejecuta el comando para obtener la IP del balanceador de cargas de Nginx

kk get services/my-gateway-nginx \
  -n load-balancer \
  -o jsonpath='{.status.loadBalancer.ingress[0].ip}'

Esto será necesario cuando verifiques el acceso a las apps. Se denominará LOAD_BALANCER_IP.

Crear una VM de cliente

Sigue los pasos para crear una VM de cliente

  1. Abre la consola de GDC en tu navegador web.
  2. Abre el menú y, luego, haz clic en Máquinas virtuales.
  3. Haz clic en Crear instancia.
  4. Crea una VM llamada client, selecciona un tipo de máquina pequeño y elige Rocky Linux o Ubuntu, que vienen con curl preinstalado.
  5. Haz clic en Crear.
  6. Espera unos minutos hasta que la VM esté lista.
  7. Cuando la VM esté lista, establece una conexión SSH a ella:
    1. En la consola de GDC, haz clic en la VM.
    2. Haz clic en Conectarse con SSH.

Verifica el acceso y el enrutamiento

Para probar el enrutamiento, ejecuta comandos curl desde tu VM de cliente. Puedes conectarte a ambas aplicaciones con sus nombres de host definidos con la dirección IP del balanceador de cargas.

Si pasas la marca --resolve en curl, puedes forzar que los nombres de dominio se resuelvan en la dirección IP del balanceador de cargas de L4 de GDC aislado. Ten en cuenta que pasamos la marca -k para confiar en los certificados autofirmados.

Prueba la app alojada en contenedores de Kubernetes:

curl -k --resolve k8s-app.example.com:443:$LOAD_BALANCER_IP https://k8s-app.example.com -v

Prueba la app de la VM externa:

curl -k --resolve vm-app.example.com:443:$LOAD_BALANCER_IP https://vm-app.example.com -v

Si se configura de forma correcta, el controlador de puerta de enlace actuará sin problemas como finalizador de TLS y pasará el tráfico al destino.

Opcional: Usa certificados listos para producción

En esta sección, se explica cómo aprovechar el servicio de CA de GDC aislado para crear una autoridad certificadora (CA) raíz privada, emitir certificados firmados para tus cargas de trabajo y actualizar de forma segura el clúster estándar de GDC aislado y las VMs de cliente.

En esta sección, se describe cómo usar el Servicio de CA de GDC aislado para crear una autoridad de certificación raíz privada y emitir certificados válidos para tus aplicaciones. Si instalas esta CA raíz en tu VM de cliente, puedes verificar que la finalización de TLS funcione sin problemas con certificados de confianza, sin necesidad de omitir las advertencias de SSL (por ejemplo, con curl -k).

Otorga los permisos necesarios y obtén credenciales

Para administrar el Servicio de CA y emitir certificados, tu usuario necesita los roles de IAM adecuados en el proyecto.

  1. Otorga los roles certificate-authority-service-admin y certificate-requester:

    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member=user:${USER_NAME} \
      --role=certificate-authority-service-admin
    
    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member=user:${USER_NAME} \
      --role=certificate-requester
    
  2. Obtén las credenciales del servidor de la API de administración:

    gdcloud clusters get-credentials ${ORG_NAME}-admin
    

Crea la CA raíz

Crearás una autoridad certificadora en el servidor de la API de Management dentro del espacio de nombres de tu proyecto.

  1. Aplica el recurso CertificateAuthority:

    km apply -f - <<EOF
    apiVersion: pki.security.gdc.goog/v1
    kind: CertificateAuthority
    metadata:
      name: my-root-ca
      namespace: ${PROJECT_ID}
    spec:
      caProfile:
        commonName: "My Root CA"
        duration: 87600h # 10 years
        keyAlgorithm: RSA_2048
        maxChainLength: 1
      caType: ROOT
      keyLocation: HSM
      rotationPolicy:
        cronTime: 0 0 1 1 *
    EOF
    
    km -n ${PROJECT_ID} get \
    certificateauthority.pki.security.gdc.goog/my-root-ca -ojson \
    | jq -r '
    .status.conditions[] | select( .type as $id | "Ready" | index($id)) .status'
    

Emite e implementa certificados

Después de que la AC esté lista, solicitarás certificados para la app alojada en contenedores y la app basada en VM. Estas solicitudes se realizan en el servidor de la API de Management, y las claves resultantes se deben mover a tu clúster estándar.

Crea solicitudes para ambos dominios:

km apply -f - <<EOF
apiVersion: pki.security.gdc.goog/v1
kind: CertificateRequest
metadata:
  name: tls-containerized-req
  namespace: ${PROJECT_ID}
spec:
  certificateAuthorityRef:
    name: my-root-ca
    namespace: ${PROJECT_ID}
  certificateConfig:
    subjectConfig:
      commonName: "k8s-app.example.com"
      dnsNames:
      - "k8s-app.example.com"
  signedCertificateSecret: tls-containerized-signed
---
apiVersion: pki.security.gdc.goog/v1
kind: CertificateRequest
metadata:
  name: tls-vm-req
  namespace: ${PROJECT_ID}
spec:
  certificateAuthorityRef:
    name: my-root-ca
    namespace: ${PROJECT_ID}
  certificateConfig:
    subjectConfig:
      commonName: "vm-app.example.com"
      dnsNames:
      - "vm-app.example.com"
  signedCertificateSecret: tls-vm-signed
EOF

Espera unos instantes a que se emitan los certificados. Puedes verificar que estén listos cuando la condición Ready sea True:

km get certificaterequests -n ${PROJECT_ID}

Actualiza el clúster estándar

Si seguiste las secciones anteriores de esta guía, tienes secrets autofirmados en tu clúster estándar. Debes borrarlos antes de crear las versiones nuevas firmadas:

kk delete secret tls-containerized -n load-balancer
kk delete secret tls-vm -n load-balancer

kk delete secret tls-containerized -n hello-app
kk delete secret tls-vm -n vm-app

Ahora, extrae los certificados firmados del servidor de la API de Management y crea los Secrets nuevos en el clúster estándar.

km get secret -n ${PROJECT_ID} tls-containerized-signed \
  -o jsonpath='{.data.tls\.crt}' \
  | base64 -d > tls-containerized.crt

km get secret -n ${PROJECT_ID} tls-containerized-signed \
  -o jsonpath='{.data.tls\.key}' \
  | base64 -d > tls-containerized.key

kk create secret tls tls-containerized \
  --namespace load-balancer \
  --key tls-containerized.key \
  --cert tls-containerized.crt

kk create secret tls tls-containerized \
  --namespace hello-app \
  --key tls-containerized.key \
  --cert tls-containerized.crt

km get secret -n ${PROJECT_ID} tls-vm-signed \
  -o jsonpath='{.data.tls\.crt}' \
  | base64 -d > tls-vm.crt

km get secret -n ${PROJECT_ID} tls-vm-signed \
  -o jsonpath='{.data.tls\.key}' \
  | base64 -d > tls-vm.key

kk create secret tls tls-vm \
  --namespace load-balancer \
  --key tls-vm.key \
  --cert tls-vm.crt

kk create secret tls tls-vm \
  --namespace vm-app \
  --key tls-vm.key \
  --cert tls-vm.crt

Los balanceadores de cargas obtendrán y actualizarán automáticamente los secretos nuevos.

Configura la confianza del cliente

Para verificar la configuración, debes indicarle a tu VM de cliente que confíe en tu nueva AC raíz.

Extrae el certificado de la AC raíz en un archivo:

km get secret -n ${PROJECT_ID} my-root-ca-secret \
  -o jsonpath='{.data.tls\.crt}' \
  | base64 -d > my-root-ca.crt

Transfiere el certificado a tu VM cliente. (Puedes copiar el contenido de my-root-ca.crt y pegarlo en un archivo de la VM cliente).

En la VM de cliente, actualiza el almacén de confianza.

Si la VM de client es Ubuntu:

sudo cp my-root-ca.crt /usr/local/share/ca-certificates/
sudo chmod 644 /usr/local/share/ca-certificates/my-root-ca.crt
sudo update-ca-certificates

Si la VM de client es Rocky Linux:

sudo cp my-root-ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust

Verifica el acceso

Ahora puedes acceder a tus aplicaciones con curl sin la marca -k. La conexión será de total confianza.

Prueba la app alojada en contenedores de Kubernetes:

curl -v --resolve k8s-app.example.com:443:LOAD_BALANDER_IP https://k8s-app.example.com

Prueba la app de VM:

curl -v --resolve vm.example.com:443:LOAD_BALANDER_IP https://vm-app.example.com

Si se ejecuta correctamente, verás el resultado de la aplicación de inmediato sin ninguna advertencia de certificado SSL.