Implementação de referência do balanceamento de carga da camada 7 do NGINX

O Google Distributed Cloud (GDC) com isolamento físico oferece um balanceador de carga gerenciado integrado de camada 4 (L4), mas muitos aplicativos corporativos exigem recursos avançados de camada 7 (L7), como roteamento com base em host, gerenciamento centralizado de TLS e divisão de tráfego complexa. Historicamente, isso é feito usando a API Entrada, que agora é considerada um recurso congelado na comunidade do Kubernetes.

Esta arquitetura de referência fornece uma solução autogerenciada de balanceamento de carga da camada 7. Ao implantar o conhecido controlador de código aberto NGINX Gateway Fabric em um cluster padrão do GDC, os clientes podem rotear o tráfego L7 para ambientes híbridos de maneira integrada. Essa arquitetura usa o encerramento de TLS (HTTPRoute) para rotear o tráfego com base na indicação de nome do servidor (SNI, na sigla em inglês) para pods em contêineres integrados e aplicativos hospedados em máquinas virtuais externas.

Arquitetura

Diagrama de arquitetura para balanceamento de carga da camada 7 do NGINX no GDC com isolamento físico.

Os principais componentes da solução incluem:

  • Cliente: uma entidade que inicia solicitações HTTPS para interagir com os aplicativos.
  • Cluster padrão do GDC: o GDC oferece uma maneira integrada de criar clusters Vanilla do Kubernetes. Nesta solução, o cluster vai hospedar o LB L7 e os controladores, além das cargas de trabalho e do serviço sem cabeçalho para VMs externas.
  • Balanceador de carga GDC L4: o balanceador de carga L4 integrado que serve como ponto de entrada, distribuindo o tráfego TCP/443 diretamente para os pods do Kubernetes que executam os controladores de gateway.
  • Controladores de gateway: operadores do NGINX em execução no cluster padrão. Eles monitoram os recursos da API Gateway (como Gateway e HTTPRoute) e atualizam dinamicamente os proxies subjacentes. O Nginx Gateway Fabric será usado para a solução.
  • Gateway (com HTTPRoute): recursos padronizados do Kubernetes que definem a porta de detecção física (443) e as regras de roteamento de host baseadas em SNI com encerramento de TLS.
  • Carga de trabalho conteinerizada (pods): uma implantação padrão do Kubernetes exposta internamente com um Service regular do Kubernetes.
  • Carga de trabalho baseada em VM (externa): uma carga de trabalho hospedada em uma VM externa na rede do projeto, exposta ao proxy com um Service do Kubernetes headless e um endpoint personalizado que contém o IP direto da VM.
  • Registro do Harbor: um Container Registry particular usado para armazenar e disponibilizar as imagens de proxy e de aplicativo no ambiente com isolamento físico.

No cluster padrão, você cria quatro namespaces:

  • O namespace nginx-gateway que hospeda os recursos do Nginx Gateway Fabric:

    Os recursos do namespace nginx-gateway.

  • O namespace load-balancer que hospeda os recursos Gateway:

    Os recursos de namespace do balanceador de carga.

  • O namespace vm-app hospeda o IP externo da VM vm-app, um serviço headless, um EndpointSlice que aponta para o IP externo e um HTTPRoute para o Gateway:

    Os recursos do namespace vm-app.

  • O namespace hello-app hospeda o Deployment, um Service, um HTTPRoute e um Gateway para a carga de trabalho do contêiner de demonstração:

    Os recursos de namespace do hello-app.

Antes de começar

Antes de implantar essa solução, verifique se você tem os seguintes pré-requisitos:

  • Software necessário: helm, docker, kubectl
  • Login da CLI e configuração local: faça o download da CLI gdcloud no console do GDC e configure seu ambiente localmente.

    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
    
  • Configuração do projeto: crie um projeto no ambiente do GDC com isolamento físico para armazenar os recursos.

    gdcloud projects create $PROJECT_ID
    
  • Papéis do IAM: conceda ao usuário os papéis de Administrador de cluster e Administrador de cluster padrão para gerenciar recursos do Kubernetes e o papel de Administrador de instância do Harbor para enviar imagens.

    # 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
    

criar um cluster padrão

Nesta seção, você vai aprender a configurar um cluster padrão do Kubernetes no ambiente do GDC com isolamento físico. Um cluster padrão oferece uma base flexível e robusta para implantar várias cargas de trabalho, incluindo o controlador de gateway do Nginx e seus aplicativos personalizados. As etapas a seguir garantem que o cluster esteja configurado corretamente e acessível para implantações subsequentes.

  1. Identifique os tipos de imagem de máquina virtual disponíveis executando:

    gdcloud compute machine-types list
    
  2. Selecione um tipo de máquina apropriado para os nós de trabalho do cluster. Para este tutorial, recomendamos um tipo de máquina com pelo menos 4 vCPUs.

    export MACHINE_TYPE="MACHINE_TYPE"
    
  3. Acesse o kubeconfig do servidor da API de gerenciamento e defina um 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. Crie um cluster padrão com dois nós de trabalho:

    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 mais detalhes sobre as opções disponíveis, consulte a documentação.

    A criação de um cluster padrão pode levar até 60 minutos. Para verificar o status, use o seguinte comando:

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

    Quando o cluster estiver pronto, a saída vai mostrar o estado Running, assim:

    NAME         STATE     K8S VERSION
    my-cluster   Running   1.30.12-gke.300
    
  5. Quando o cluster estiver pronto, recupere as credenciais dele:

    KUBECONFIG=kubeconfig-${CLUSTER_NAME}.yaml gdcloud clusters \
      get-credentials ${CLUSTER_NAME} \
      --standard \
      --project ${PROJECT_ID} \
      --zone ${ZONE}
    
  6. Crie um alias para manter os comandos kubectl mais concisos no restante deste guia. Esse alias será usado para interagir com o cluster padrão:

    alias kk="kubectl --kubeconfig kubeconfig-${CLUSTER_NAME}.yaml"
    
  7. Crie namespaces para o controlador, o app de demonstração conteinerizado "hello-app" e o app de demonstração baseado em VM:

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

Criar e integrar o Harbor Registry

O Harbor é um registro de imagens de contêiner com suporte integrado no GDC com isolamento físico. Nesta seção, mostramos as etapas para integrar um registro do Harbor ao cluster padrão, incluindo a configuração de credenciais e secrets para permitir a extração e o envio seguros de imagens.

  1. Crie uma instância do Harbor no seu projeto.
  2. Crie um projeto do Harbor na sua instância do Harbor.
  3. Defina as variáveis de ambiente:

    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. Faça login na instância do Harbor usando uma conta de robô:

    docker --config=./docker login ${HARBOR_INSTANCE_URL}
    
  5. Crie os secrets no cluster padrão:

    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
    

Implantar app de demonstração em contêineres

Nesta seção, detalhamos a implantação de um aplicativo conteinerizado de demonstração (hello-app) no cluster do Kubernetes com isolamento físico do GDC. Você vai criar os recursos necessários de implantação e serviço do Kubernetes para executar o hello-app e expô-lo internamente no cluster, preparando-o para acesso usando o balanceador de carga L7.

  1. Faça upload de uma imagem de amostra para o app de demonstração em contêiner no 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. Implante o seguinte manifesto no cluster padrão:

    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
    

Em seguida, verifique se a implantação e o serviço estão lá:

kk get svc,deploy -n hello-app

Implantar o app de demonstração em uma VM

Nesta seção, detalhamos a implantação de um aplicativo de demonstração em uma máquina virtual (VM) fora do cluster do Kubernetes. Ao configurar um servidor HTTP em uma VM, você vai simular um aplicativo externo que o balanceador de carga pode expor, demonstrando a capacidade de gerenciar o tráfego para recursos dentro e fora do cluster.

Primeiro, crie uma VM para o app de demonstração

  1. Abra o console do GDC no navegador da Web.
  2. Selecione o mesmo projeto em que você criou o cluster padrão do Kubernetes.
  3. Abra o menu e clique em Máquinas virtuais.
  4. Clique em Criar instância.
  5. Dê à VM o nome vm-workload. Uma imagem de 2 vCPUs é suficiente para o exemplo.
  6. Para a imagem do disco de inicialização, selecione uma distribuição do Ubuntu 22.04, que vem com o Python pré-instalado.
  7. Clique em Criar.
  8. Aguarde alguns minutos até que a VM esteja pronta.
  9. Estabeleça uma conexão SSH com a VM:
    1. No console do GDC, clique na VM.
    2. Clique em Conectar com SSH.

Depois de se conectar ao console SSH, execute o seguinte:

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

Para rotear o tráfego para uma VM, crie um serviço headless (sem seletores). Ele será mapeado manualmente para o endereço IP interno da VM usando um 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

Para saber o endereço IP da VM vm-workload, execute o seguinte comando:

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

A saída será o endereço IP da VM que será necessário para configurar o recurso EndpointSlice.

Crie o recurso EndpointSlice que vai se conectar ao serviço sem seletor da VM-app e use o endereço IP da VM para onde o tráfego precisa ser roteado.

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

Criar certificados autoassinados

Nesta seção, você vai aprender a gerar certificados TLS autoassinados para seus aplicativos. Este guia usa certificados autoassinados por conveniência, mas em ambientes de produção, é necessário usar certificados de nível de produção, conforme descrito em Opcional: usar certificados prontos para produção. Esses certificados são essenciais para permitir o encerramento de HTTPS no gateway do Nginx, garantindo a comunicação criptografada entre os clientes e o balanceador de carga para cargas de trabalho em contêineres e baseadas em VM.

  1. Crie um certificado para o app conteinerizado:

    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. Crie um certificado para o app da 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
    

Implantar o NGINX

Nesta seção, descrevemos as etapas para implantar o controlador de gateway do Nginx, incluindo a instalação das definições de recursos personalizados (CRDs, na sigla em inglês) da API Gateway, a configuração do Nginx Gateway Fabric usando o Helm e a verificação da instalação no cluster padrão. Isso prepara a infraestrutura para o roteamento avançado da camada 7.

Instale os CRDs da API Gateway

A API Gateway exige que as definições de recursos personalizados (CRDs, na sigla em inglês) sejam instaladas no cluster antes de implantar o controlador. Vamos usar as definições de recursos personalizados (CRD, na sigla em inglês) experimentais oficiais do projeto da API Gateway (a versão 1.2.0 é usada neste guia).

  1. Instale os CRDs experimentais:

    kk apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/experimental-install.yaml
    
  2. Execute kk get crd gateways.gateway.networking.k8s.io para verificar se os CRDs foram instalados corretamente.

Instalar o NGINX Gateway Fabric

Implante o controlador do NGINX Gateway Fabric usando o Helm. Esse controlador vai observar os recursos da API Gateway e configurar o NGINX para processar o tráfego.

  1. Defina a tag de imagem do 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. Extraia as imagens do NGINX Gateway Fabric e do NGINX e envie-as para o registro do 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. Crie o namespace nginx-gateway e adicione o secret de extração de imagem do 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. Implante o NGINX Gateway Fabric com o Helm, apontando para as imagens no registro do 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}"
    

    A implantação do Kubernetes extrai imagens do Harbor com segurança usando o secret fornecido (${IMAGE_PULL_SECRET_NAME}), que contém as credenciais da conta de robô do Harbor.

  5. Verifique se os recursos GatewayClass foram aceitos:

    kk get gatewayclass
    

    Você vai encontrar nginx listado com ACCEPTED = True.

Criar a instância do gateway

Defina a instância lógica do balanceador de carga que detecta a porta 443. Vamos configurá-lo para o modo Terminate, o que significa que o gateway vai realizar o término de TLS e descriptografar o tráfego antes de transmiti-lo.

Crie e aplique 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

Verifique se o gateway foi implantado com sucesso conferindo se PROGRAMMED é True:

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

Verifique se um balanceador de carga L4 gerenciado do GDC foi implantado como um serviço ao lado do gateway.

kk get services -n load-balancer

Verifique se o serviço do Nginx foi criado e configurado. A saída será semelhante a esta:

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

Definir a lógica de roteamento L7 (HTTPRoute)

Vincule HTTPRoutes ao gateway para definir como o tráfego deve ser distribuído com base no nome do host solicitado (SNI).

Crie e aplique 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   

Recuperar o IP do balanceador de carga

Execute o comando para receber o IP do balanceador de carga do Nginx.

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

Isso será necessário ao verificar o acesso aos apps. Ele será indicado como LOAD_BALANCER_IP.

Criar uma VM cliente

Siga as etapas para criar uma VM de cliente

  1. Abra o console do GDC no navegador da Web.
  2. Abra o menu e clique em Máquinas virtuais.
  3. Clique em Criar instância.
  4. Crie uma VM chamada client, selecione um tipo de máquina pequeno e escolha Rocky Linux ou Ubuntu, que vêm com o curl pré-instalado.
  5. Clique em Criar.
  6. Aguarde alguns minutos até que a VM esteja pronta.
  7. Depois que a VM estiver pronta, estabeleça uma conexão SSH com ela:
    1. No console do GDC, clique na VM.
    2. Clique em Conectar com SSH.

Verificar o acesso verificado e o roteamento

Para testar o roteamento, execute comandos curl na VM do cliente. É possível se conectar aos dois aplicativos usando os nomes de host definidos com o endereço IP do balanceador de carga.

Ao transmitir a flag --resolve no curl, é possível forçar os nomes de domínio a serem resolvidos para o endereço IP do balanceador de carga L4 do GDC com isolamento físico. Observe que transmitimos a flag -k para confiar nos certificados autoassinados.

Teste o app conteinerizado do Kubernetes:

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

Teste o app da VM externa:

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

Se configurado corretamente, o controlador de gateway vai atuar como um terminador de TLS e transmitir o tráfego para o destino.

Opcional: usar certificados prontos para produção

Nesta seção, mostramos como usar o CA Service do GDC com isolamento físico para criar uma autoridade certificadora (CA) raiz particular, emitir certificados assinados para suas cargas de trabalho e atualizar com segurança o cluster padrão e as VMs de cliente do GDC com isolamento físico.

Nesta seção, descrevemos como usar o CA Service do GDC com isolamento físico para criar uma autoridade certificadora raiz particular e emitir certificados válidos para seus aplicativos. Ao instalar essa CA raiz na VM do cliente, é possível verificar se o término de TLS funciona perfeitamente com certificados confiáveis, sem precisar ignorar avisos de SSL (por exemplo, usando curl -k).

Conceder as permissões necessárias e receber credenciais

Para gerenciar o CA Service e emitir certificados, o usuário precisa ter os papéis do IAM apropriados no projeto.

  1. Conceda os papéis certificate-authority-service-admin e 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. Receba as credenciais do servidor da API Management:

    gdcloud clusters get-credentials ${ORG_NAME}-admin
    

Criar a CA raiz

Você vai criar uma autoridade certificadora no servidor da API de gerenciamento no namespace do projeto.

  1. Aplique o 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'
    

Emitir e implantar certificados

Depois que a CA estiver pronta, você vai solicitar certificados para o app conteinerizado e o app baseado em VM. Essas solicitações acontecem no servidor de API de gerenciamento, e as chaves resultantes precisam ser movidas para o cluster padrão.

Crie solicitações para os dois domínios:

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

Aguarde alguns instantes até que os certificados sejam emitidos. É possível verificar se eles estão prontos quando a condição "Ready" é "True":

km get certificaterequests -n ${PROJECT_ID}

Atualizar o cluster padrão

Se você seguiu as seções anteriores deste guia, tem secrets autoassinados no cluster padrão. Exclua-as antes de criar as novas versões assinadas:

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

Agora, extraia os certificados assinados do servidor da API Management e crie os novos secrets no cluster padrão.

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

Os novos secrets serão obtidos automaticamente e atualizados pelos balanceadores de carga.

Configurar a confiança do cliente

Para verificar a configuração, informe à VM cliente que ela deve confiar na nova CA raiz.

Extraia o certificado de CA raiz para um arquivo:

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

Transfira o certificado para a VM do cliente. É possível copiar o conteúdo de my-root-ca.crt e colá-lo em um arquivo na VM do cliente.

Na VM do cliente, atualize o repositório de confiança.

Se a VM client for 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

Se a VM client for Rocky Linux:

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

Verifique o acesso

Agora é possível acessar os aplicativos usando curl sem a flag -k. A conexão será totalmente confiável.

Teste o app conteinerizado do Kubernetes:

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

Teste o app da VM:

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

Se for bem-sucedido, você verá a saída do aplicativo imediatamente, sem avisos de certificado SSL.