Implantar o Apache Kafka no GKE usando o Strimzi

O guia mostra como usar o operador Strimzi para implantar clusters do Apache Kafka.

O Kafka é um sistema de mensagens distribuído de código aberto projetado para lidar com dados de streaming de alto volume, alta capacidade e em tempo real. Ele permite criar pipelines de dados de streaming para transferência de dados confiável em diferentes sistemas e aplicativos, para dar suporte a tarefas de processamento e análise.

Operadores são extensões de software que usam recursos personalizados para gerenciar aplicativos e seus componentes. Para saber mais sobre a motivação para usar operadores, consulte Padrão de operador (em inglês) na documentação de código aberto do Kubernetes. O operador Strimzi oferece flexibilidade nas opções de implantação e permite usar taints e tolerâncias do Kubernetes para executar o Kafka em nós dedicados.

Este guia é destinado a administradores de plataformas, arquitetos de nuvem e profissionais de operações interessados em implantar clusters do Kafka no GKE.

Esta solução é um bom ponto de partida se você quiser saber como implantar clusters do Kafka usando um operador de terceiros para automatizar o gerenciamento e reduzir erros. Se preferir um controle operacional mais granular, consulte Implantar clusters do Kafka altamente disponíveis no GKE.

Prepare o ambiente

Neste tutorial, você vai usar o Cloud Shell para gerenciar recursos hospedados no Google Cloud. O Cloud Shell vem pré-instalado com o software necessário para este tutorial, incluindo kubectl, a CLI gcloud, o Helm e o Terraform.

Para configurar o ambiente com o Cloud Shell, siga estas etapas:

  1. Inicie uma sessão do Cloud Shell no console do Google Cloud clicando em Ícone de ativação do Cloud Shell Ativar o Cloud Shell no console doGoogle Cloud . Isso inicia uma sessão no painel inferior do console Google Cloud .

  2. Defina as variáveis de ambiente:

    export PROJECT_ID=PROJECT_ID
    export KUBERNETES_CLUSTER_PREFIX=kafka
    export REGION=us-central1
    

    Substitua PROJECT_ID: seu Google Cloud pelo ID do projeto.

  3. Clone o repositório do GitHub:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    
  4. Mude para o diretório de trabalho:

    cd kubernetes-engine-samples/streaming/
    

Criar a infraestrutura do cluster

Nesta seção, você executa um script do Terraform para criar um cluster regional do GKE privado e altamente disponível. As etapas a seguir permitem acesso público ao plano de controle. Para restringir o acesso, crie um cluster particular.

É possível instalar o operador usando um cluster padrão ou Autopilot.

Padrão

O diagrama a seguir mostra um cluster regional padrão particular do GKE implantado em três zonas diferentes:

Para implantar essa infraestrutura, execute os seguintes comandos no Cloud Shell:

export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
terraform -chdir=kafka/terraform/gke-standard init
terraform -chdir=kafka/terraform/gke-standard apply -var project_id=${PROJECT_ID} \
  -var region=${REGION} \
  -var cluster_prefix=${KUBERNETES_CLUSTER_PREFIX}

Quando solicitado, digite yes. Pode levar vários minutos para que esse comando seja concluído e o cluster mostre um status pronto.

O Terraform cria os seguintes recursos:

  • Uma rede VPC e uma sub-rede particular para os nós do Kubernetes.
  • Um roteador para acessar a Internet por meio de NAT.
  • Um cluster particular do GKE na região us-central1.
  • 2 pools de nós com escalonamento automático ativado (de 1 a 2 nós por zona, no mínimo 1 nó por zona)
  • Um ServiceAccount com permissões de geração de registros e monitoramento.
  • Backup do GKE para recuperação de desastres.
  • Google Cloud Managed Service para Prometheus para monitoramento de clusters.

O resultado será assim:

...
Apply complete! Resources: 14 added, 0 changed, 0 destroyed.

Outputs:

kubectl_connection_command = "gcloud container clusters get-credentials strimzi-cluster --region us-central1"

Piloto automático

O diagrama a seguir mostra um cluster particular regional do Autopilot do GKE:

Para implantar a infraestrutura, execute os seguintes comandos do Cloud Shell:

export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
terraform -chdir=kafka/terraform/gke-autopilot init
terraform -chdir=kafka/terraform/gke-autopilot apply -var project_id=${PROJECT_ID} \
  -var region=${REGION} \
  -var cluster_prefix=${KUBERNETES_CLUSTER_PREFIX}

Quando solicitado, digite yes. Pode levar vários minutos para que esse comando seja concluído e o cluster mostre um status pronto.

O Terraform cria os seguintes recursos:

  • Rede VPC e sub-rede privada para os nós do Kubernetes.
  • Um roteador para acessar a Internet por meio de NAT.
  • Um cluster particular do GKE na região us-central1.
  • Um ServiceAccount com permissões de registro e monitoramento
  • Google Cloud Managed Service para Prometheus para monitoramento de clusters.

O resultado será assim:

...
Apply complete! Resources: 12 added, 0 changed, 0 destroyed.

Outputs:

kubectl_connection_command = "gcloud container clusters get-credentials strimzi-cluster --region us-central1"

Como se conectar ao cluster

Configure kubectl para se comunicar com o cluster:

gcloud container clusters get-credentials ${KUBERNETES_CLUSTER_PREFIX}-cluster --region ${REGION}

Implantar o operador do Strimzi no cluster

Nesta seção, você implantará o operador do Strimzi usando um gráfico do Helm. Há também várias outras maneiras de implantar o Strimzi.

  1. Adicione o repositório do Strimzi Helm Chart:

    helm repo add strimzi https://strimzi.io/charts/
    
  2. Adicione um namespace para o operador do Strimzi e o cluster do Kafka:

    kubectl create ns kafka
    
  3. Implante o operador de cluster do Strimzi usando o Helm:

    helm install strimzi-operator strimzi/strimzi-kafka-operator -n kafka
    

    Para implantar os clusters do Strimzi Cluster Operator e do Kafka em diferentes namespaces, adicione o parâmetro --set watchNamespaces="{kafka-namespace,kafka-namespace-2,...}" ao comando helm.

  4. Verifique se o operador de cluster do Strimzi foi implantado usando o Helm:

    helm ls -n kafka
    

    O resultado será assim:

    NAME            NAMESPACE    REVISION    UPDATED                              STATUS    CHART                        APP VERSION
    strimzi-operator    kafka      1       2023-06-27 11:22:15.850545 +0200 CEST    deployed    strimzi-kafka-operator-0.35.0    0.35.0
    

Implantar o Kafka

Depois que o operador for implantado no cluster, você estará pronto para implantar uma instância de cluster do Kafka.

Nesta seção, você implantará o Kafka em uma configuração básica e, em seguida, testará vários cenários de configuração avançada para atender aos requisitos de disponibilidade, segurança e observabilidade.

Configuração básica

A configuração básica da instância do Kafka inclui os seguintes componentes:

  • Três réplicas de agentes do Kafka, com um mínimo de duas réplicas disponíveis necessárias para a consistência do cluster.
  • Três réplicas de nós do ZooKeeper, formando um cluster.
  • Dois listeners Kafka: um sem autenticação e outro usando a autenticação TLS com um certificado gerado pelo Strimzi.
  • Java MaxHeapSize e MinHeapSize definidos como 4 GB para Kafka e 2 GB para ZooKeeper.
  • Alocação de recursos de CPU de uma solicitação de CPU e dois limites de CPU para o Kafka e o ZooKeeper, junto com 5 GB de solicitações de memória e limites para o Kafka (4 GB para o serviço principal e 0,5 GB para o exportador de métricas) e 2,5 GB para o ZooKeeper (2 GB para o serviço principal e 0,5 GB para o exportador de métricas).
  • Operador de entidade com as seguintes solicitações e limites:
    • tlsSidecar: CPU de 100 m/500 m e memória de 128 Mi.
    • topicOperator: CPU de 100 m/500 m e memória de 512 Mi.
    • userOperator: CPU de 500 m e memória de 2 Gi.
  • 100 GB de armazenamento alocado para cada pod usando premium-rwo storageClass.
  • Tolerâncias, nodeAffinities e podAntiAffinities configurados para cada carga de trabalho, garantindo a distribuição apropriada entre nós, utilizando os respectivos pools de nós e zonas diferentes.
  • Comunicação dentro do cluster protegida por certificados autoassinados: CAs (Autoridades de certificação) separadas para cluster e clientes (mTLS). Também é possível configurar para usar uma autoridade de certificação diferente.

Essa configuração representa a configuração mínima necessária para criar um cluster do Kafka pronto para produção. As seções a seguir demonstram configurações personalizadas para abordar aspectos como segurança de cluster, listas de controle de acesso (ACLs), gerenciamento de tópicos, gerenciamento de certificados e muito mais.

Criar um cluster básico do Kafka

  1. Crie um novo cluster do Kafka usando a configuração básica:

    kubectl apply -n kafka -f kafka-strimzi/manifests/01-basic-cluster/my-cluster.yaml
    

    Esse comando cria um recurso personalizado do Kafka do operador Strimzi que inclui solicitações e limites de CPU e memória, solicitações de armazenamento em blocos e uma combinação de taints e afinidades para distribuir os pods provisionados nos nós do Kubernetes.

  2. Aguarde alguns minutos enquanto o Kubernetes inicia as cargas de trabalho necessárias:

    kubectl wait kafka/my-cluster --for=condition=Ready --timeout=600s -n kafka
    
  3. Verifique se as cargas de trabalho do Kafka foram criadas:

    kubectl get pod,service,deploy,pdb -l=strimzi.io/cluster=my-cluster -n kafka
    

    O resultado será assim:

    NAME                                            READY   STATUS  RESTARTS   AGE
    pod/my-cluster-entity-operator-848698874f-j5m7f   3/3   Running   0        44m
    pod/my-cluster-kafka-0                          1/1   Running   0        5m
    pod/my-cluster-kafka-1                          1/1   Running   0        5m
    pod/my-cluster-kafka-2                          1/1   Running   0        5m
    pod/my-cluster-zookeeper-0                      1/1   Running   0        6m
    pod/my-cluster-zookeeper-1                      1/1   Running   0        6m
    pod/my-cluster-zookeeper-2                      1/1   Running   0        6m
    
    NAME                                TYPE      CLUSTER-IP   EXTERNAL-IP   PORT(S)                             AGE
    service/my-cluster-kafka-bootstrap  ClusterIP   10.52.8.80   <none>      9091/TCP,9092/TCP,9093/TCP          5m
    service/my-cluster-kafka-brokers    ClusterIP   None         <none>      9090/TCP,9091/TCP,9092/TCP,9093/TCP   5m
    service/my-cluster-zookeeper-client   ClusterIP   10.52.11.144   <none>      2181/TCP                            6m
    service/my-cluster-zookeeper-nodes  ClusterIP   None         <none>      2181/TCP,2888/TCP,3888/TCP          6m
    
    NAME                                       READY   UP-TO-DATE   AVAILABLE   AGE
    deployment.apps/my-cluster-entity-operator   1/1   1          1         44m
    
    NAME                                            MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
    poddisruptionbudget.policy/my-cluster-kafka     2             N/A             1                   5m
    poddisruptionbudget.policy/my-cluster-zookeeper   2             N/A             1                   6m
    

O operador cria os seguintes recursos:

  • Dois StrimziPodSets para Kafka e ZooKeeper.
  • Três pods para réplicas do agente do Kafka.
  • Três pods para réplicas do ZooKeeper.
  • Dois PodDisruptionBudgets, garantindo a disponibilidade mínima de duas réplicas para consistência de cluster.
  • Um serviço chamado my-cluster-kafka-bootstrap, que serve como o servidor de inicialização para os clientes do Kafka que se conectam de dentro do cluster do Kubernetes. Todos os listeners internos do Kafka estão disponíveis neste serviço.
  • Um serviço sem comando chamado my-cluster-kafka-brokers que permite a resolução de DNS dos endereços IP do pod do agente do Kafka diretamente. Esse serviço é usado para comunicação entre agentes.
  • Um serviço chamado my-cluster-zookeeper-client que permite que os agentes do Kafka se conectem aos nós do ZooKeeper como clientes.
  • Um serviço sem comando chamado my-cluster-zookeeper-nodes que permite a resolução de DNS dos endereços IP do pod do ZooKeeper diretamente. Esse serviço é usado para conectar-se entre as réplicas do ZooKeeper.
  • Uma implantação chamada my-cluster-entity-operator que contém o topic-operator e user-operator e facilita o gerenciamento dos recursos personalizados KafkaTopics e KafkaUsers.

Também é possível configurar dois NetworkPolicies para facilitar a conectividade com os listeners do Kafka de qualquer pod e namespace. Essas políticas também restringiriam as conexões com o ZooKeeper para agentes e permitiriam a comunicação entre os pods de cluster e as portas de serviço internas exclusivas para a comunicação de cluster.

Autenticação e gerenciamento de usuários

Esta seção mostra como ativar a autenticação e autorização para proteger os listeners do Kafka e compartilhar credenciais com os clientes.

O Strimzi fornece um método nativo do Kubernetes para gerenciamento de usuários usando um User Operator separado e o recurso personalizado do Kubernetes correspondente, KafkaUser, que define a configuração do usuário. A configuração do usuário inclui definições para autenticação e autorização e provisiona o usuário correspondente no Kafka.

O Strimzi pode criar listeners e usuários do Kafka compatíveis com vários mecanismos de autenticação, como autenticação baseada em nome de usuário e senha (SCRAM-SHA-512) ou TLS. Também é possível usar a autenticação do OAuth 2.0, que geralmente é considerada uma abordagem melhor do que o uso de senhas ou certificados para autenticação por causa do gerenciamento de segurança e de credenciais externas.

Implantar um cluster do Kafka

Esta seção mostra como implantar um operador do Strimzi que demonstra os recursos de gerenciamento de usuários, incluindo:

  • Um cluster do Kafka com autenticação baseada em senha (SCRAM-SHA-512) ativado em um dos listeners.
  • Uma KafkaTopic com três réplicas.
  • Uma KafkaUser com uma ACL que especifica que o usuário tem permissões de leitura e gravação no tópico.
  1. Configure seu cluster do Kafka para usar um listener com autenticação SCRAM-SHA-512 baseada em senha na porta 9094 e autorização simples:

    kubectl apply -n kafka -f kafka-strimzi/manifests/03-auth/my-cluster.yaml
    
  2. Crie um Topic, um User e um pod cliente para executar comandos no cluster do Kafka:

    kubectl apply -n kafka -f kafka-strimzi/manifests/03-auth/topic.yaml
    kubectl apply -n kafka -f kafka-strimzi/manifests/03-auth/my-user.yaml
    

    O Secret my-user com as credenciais de usuário é ativado no pod cliente como um Volume.

    Essas credenciais confirmam que o usuário tem permissões para publicar mensagens no tópico usando o listener com a autenticação baseada em senha (SCRAM-SHA-512) ativada.

  3. Crie um pod de cliente:

    kubectl apply -n kafka -f kafka-strimzi/manifests/03-auth/kafkacat.yaml
    
  4. Aguarde alguns minutos até que o pod cliente se torne Ready e se conecte a ele:

    kubectl wait --for=condition=Ready pod --all -n kafka --timeout=600s
    kubectl exec -it kafkacat -n kafka -- /bin/sh
    
  5. Produza uma nova mensagem com credenciais my-user e tente consumi-la:

    echo "Message from my-user" |kcat \
      -b my-cluster-kafka-bootstrap.kafka.svc.cluster.local:9094 \
      -X security.protocol=SASL_SSL \
      -X sasl.mechanisms=SCRAM-SHA-512 \
      -X sasl.username=my-user \
      -X sasl.password=$(cat /my-user/password) \
      -t my-topic -P
    kcat -b my-cluster-kafka-bootstrap.kafka.svc.cluster.local:9094 \
      -X security.protocol=SASL_SSL \
      -X sasl.mechanisms=SCRAM-SHA-512 \
      -X sasl.username=my-user \
      -X sasl.password=$(cat /my-user/password) \
      -t my-topic -C
    

    O resultado será assim:

    Message from my-user
    % Reached end of topic my-topic [0] at offset 0
    % Reached end of topic my-topic [2] at offset 1
    % Reached end of topic my-topic [1] at offset 0
    

    Digite CTRL+C para interromper o processo do consumidor.

  6. Saia do shell do pod

    exit
    

Backups e recuperação de desastres

Embora o operador do Strimzi não ofereça funcionalidade de backup integrada, é possível implementar estratégias de backup eficientes seguindo determinados padrões.

Use o Backup para GKE para fazer backup:

  • Manifestos do recurso do Kubernetes.
  • Recursos personalizados da API Strimzi e as definições extraídas do servidor da API Kubernetes do cluster que está em backup.
  • Volumes correspondentes aos recursos PersistentVolumeClaim encontrados nos manifestos.

Para mais informações sobre como fazer backup e restaurar clusters do Kafka usando o Backup para GKE, consulte Preparar-se para a recuperação de desastres.

Você também pode fazer um backup de um cluster do Kafka que foi implantado usando o operador do Strimzi. Faça o backup:

  • A configuração do Kafka, que inclui todos os recursos personalizados da API Strimzi, como KafkaTopics e KafkaUsers.
  • Os dados, armazenados nos PersistentVolumes dos agentes do Kafka.

Armazenar manifestos de recursos do Kubernetes, incluindo configurações do Strimzi, em repositórios Git pode eliminar a necessidade de um backup separado para a configuração do Kafka, porque os recursos podem ser reaplicados a um novo cluster do Kubernetes quando necessário.

Para proteger a recuperação de dados do Kafka em cenários em que uma instância do servidor do Kafka ou um cluster do Kubernetes em que o Kafka está implantado é perdida, recomendamos que você configure a classe de armazenamento do Kubernetes usada para provisionar volumes para agentes do Kafka. com a opção reclaimPolicy definida como Retain. Também recomendamos que você tire snapshots dos volumes do agente do Kafka.

O manifesto a seguir descreve um StorageClass que usa a opção reclaimPolicy Retain:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: premium-rwo-retain
...
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer

O exemplo a seguir mostra o StorageClass adicionado ao spec de um recurso personalizado do cluster do Kafka:

# ...
spec:
  kafka:
    # ...
    storage:
      type: persistent-claim
      size: 100Gi
      class: premium-rwo-retain

Com essa configuração, os PersistentVolumes provisionados usando a classe de armazenamento não são excluídos, mesmo quando o PersistentVolumeClaim correspondente é excluído.

Para recuperar a instância do Kafka em um novo cluster do Kubernetes usando a configuração atual e os dados da instância do agente:

  1. Aplique os recursos personalizados atuais do Strimzi Kafka (Kakfa, KafkaTopic, KafkaUser etc.) a um novo cluster do Kubernetes
  2. Atualize os PersistentVolumeClaims com o nome das novas instâncias do agente do Kafka para os PersistentVolumes antigos usando a propriedade spec.volumeName no PersistentVolumeClaim.