🇧🇷 Kubernetes Parte 3

🇬🇧 To read this article in English click here

Nesta série de artigos, exploramos o Kubernetes em 3 partes. Esta é a terceira e última parte (até então), onde explicarei sobre componentes do Kubernetes que são usados para automatizar tarefas, gerenciar configurações, lidar com segurança e garantir que os serviços rodem de forma organizada e escalável no cluster. 

Se ainda não viu os artigos anteriores, dá uma olhada antes de começar este. Eles ajudam a entender melhor o que vem por aqui.

Já falamos sobre:

Control-plane, Kube-apiserver, cloud-controller-manager, etcd, kube-proxy, pods, kubelet, kube-scheduler, minikube, YAML, ReplicaSets, KNI, namespaces, volumes, services, liveness probes e mais.

Vamos continuar?

DaemonSet 

Um DaemonSet no Kubernetes garante que um tipo específico de pod rode em todos (ou em alguns) nós do cluster. É útil para coisas que precisam estar sempre disponíveis em cada nó, como agentes de monitoramento, coleta de logs ou ferramentas de rede.

Exemplo 1: Coletor de logs (Fluentd)

Quer instalar o Fluentd para capturar logs de todos os nós? Um DaemonSet é perfeito.

apiVersion: apps/v1

kind: DaemonSet

metadata:

  name: fluentd

  namespace: kube-system

spec:

  selector:

    matchLabels:

      app: fluentd

  template:

    metadata:

      labels:

        app: fluentd

    spec:

      containers:

        – name: fluentd

          image: fluent/fluentd:v1.14

          resources:

            limits:

              memory: “200Mi”

              cpu: “0.5”

          volumeMounts:

            – name: varlog

              mountPath: /var/log

      volumes:

        – name: varlog

          hostPath:

            path: /var/log

Nesse exemplo, um pod com o Fluentd será criado em cada nó do cluster para coletar os logs locais.


Exemplo 2: Verificador de nó (Node Exporter)

Se você quer monitorar os recursos dos nós usando algo como o Node Exporter:

apiVersion: apps/v1

kind: DaemonSet

metadata:

  name: node-exporter

spec:

  selector:

    matchLabels:

      app: node-exporter

  template:

    metadata:

      labels:

        app: node-exporter

    spec:

      containers:

        – name: node-exporter

          image: prom/node-exporter:latest

          ports:

            – containerPort: 9100

              name: metrics

Aqui, cada nó terá um pod do Node Exporter expondo métricas para um sistema de monitoramento como o Prometheus.


Uso

Sempre que precisar de algo rodando em todos os nós (ou em nós específicos).

Para tarefas como monitoramento, proxy ou serviços locais obrigatórios.

Exemplo: Usando Node Selectors

Se você quer rodar um DaemonSet apenas em nós com a label disktype=ssd:

apiVersion: apps/v1

kind: DaemonSet

metadata:

  name: custom-daemonset

spec:

  selector:

    matchLabels:

      app: custom-daemon

  template:

    metadata:

      labels:

        app: custom-daemon

    spec:

      nodeSelector:

        disktype: ssd

      containers:

        – name: custom-container

          image: custom/image:latest

Nesse caso, o DaemonSet só será implantado em nós que possuem a label disktype=ssd.


Exemplo: Usando Node Affinity

Se precisa de algo mais flexível, como preferir nós de uma região específica (region=us-east), mas não obrigatoriamente:

apiVersion: apps/v1

kind: DaemonSet

metadata:

  name: affinity-daemonset

spec:

  selector:

    matchLabels:

      app: affinity-daemon

  template:

    metadata:

      labels:

        app: affinity-daemon

    spec:

      affinity:

        nodeAffinity:

          requiredDuringSchedulingIgnoredDuringExecution:

            nodeSelectorTerms:

              – matchExpressions:

                  – key: region

                    operator: In

                    values:

                      – us-east

      containers:

        – name: affinity-container

          image: custom/image:latest

Aqui, o DaemonSet será implantado apenas em nós na região us-east.


Exemplo: Usando Tolerations

Se você tem nós taintados para cargas específicas, pode usar Tolerations para que o DaemonSet rode nesses nós:

apiVersion: apps/v1

kind: DaemonSet

metadata:

  name: tainted-daemonset

spec:

  selector:

    matchLabels:

      app: tainted-daemon

  template:

    metadata:

      labels:

        app: tainted-daemon

    spec:

      tolerations:

        – key: “dedicated”

          operator: “Equal”

          value: “special-nodes”

          effect: “NoSchedule”

      containers:

        – name: tainted-container

          image: custom/image:latest

Este DaemonSet será implantado apenas nos nós que possuem a taint dedicated=special-nodes:NoSchedule.


Uso de DaemonSets em nós específicos

Para implantar agentes em nós com configurações específicas (ex.: GPUs, SSDs).

Para isolar workloads em ambientes híbridos.

Para rodar serviços que só fazem sentido em tipos de nós particulares.

Esses filtros ajudam a evitar que recursos sejam desperdiçados em nós onde o DaemonSet não é necessário.

Resumo: DaemonSets são simples: você escreve uma vez, e o Kubernetes cuida de colocar em todos os cantos necessários do cluster.

Jobs

Os Jobs no Kubernetes são tipo tarefas que você manda fazer uma vez ou de vez em quando, diferente dos pods normais que ficam rodando o tempo todo. É como pedir pra máquina fazer um trabalho específico, tipo processar um arquivo, gerar um relatório, ou rodar um script, e depois que termina, o job para.

Como funciona na prática:

Você cria o Job, ele sobe um pod que faz o que você mandou.

Quando o trabalho é concluído com sucesso, ele “marca como feito”.

Se der erro, o Kubernetes tenta rodar de novo, dependendo da configuração que você colocar.

Importancia do Jobs Technical Specifications

As Technical Specifications (TS) de um job no Kubernetes são como a lista de instruções claras e detalhadas que dizem ao cluster exatamente o que fazer e como fazer. Elas são essenciais porque ajudam a garantir que o trabalho seja executado de forma correta, previsível e eficiente. Sem especificações bem definidas, é como dar uma tarefa sem explicar os passos ou as ferramentas necessárias — o resultado pode ser imprevisível.

restartPolicy

A restartPolicy no Kubernetes basicamente diz ao cluster o que fazer com o pod se ele falhar. É tipo dar instruções claras pro Kubernetes sobre quando insistir e quando desistir. Essa configuração é super importante pra jobs, porque garante que você não fique preso em loops desnecessários ou que o trabalho pare de funcionar sem tentar novamente.

Os três valores principais:

Always

Faz o Kubernetes sempre tentar reiniciar o pod, não importa como ele terminou (erro ou sucesso).

Normalmente usado em Deployments, onde você quer que o serviço esteja sempre rodando.

Jobs não usam isso, porque não faz sentido pra um job reiniciar infinito.

OnFailure

Reinicia o pod só se ele falhar (exit code diferente de 0).

Isso faz muito sentido pra jobs, porque ele tenta de novo se algo deu errado, tipo um erro de rede ou um bug temporário.

Never

Se o pod falhar, deixa ele lá quieto, sem tentar nada.

Útil quando você quer debugar manualmente o que deu errado, sem o Kubernetes ficar interferindo.

Aqui vai um exemplo simples de um Job no Kubernetes que simula um sorteio de loteria. A ideia é gerar alguns números aleatórios e imprimir no log.


Arquivo YAML do Job

apiVersion: batch/v1

kind: Job

metadata:

  name: loteria-job

spec:

  template:

    spec:

      containers:

      – name: loteria

        image: python:3.9

        command: [“python”, “-c”]

        args:

          – |

            import random

            numeros = sorted(random.sample(range(1, 61), 6))

            print(f”Números sorteados: {numeros}”)

      restartPolicy: Never


Explicação:

metadata.name:
É o nome do job. Nesse caso, chamamos de loteria-job, só pra identificar que ele é o cara que roda o sorteio.

image:
A gente tá usando a imagem oficial do Python (python:3.9) pra rodar o script direto. Não precisa construir nada, é só plugar e usar.

command e args:
É onde a mágica acontece. Usamos o comando python -c pra rodar o script que gera 6 números aleatórios entre 1 e 60 (como uma Mega-Sena).

random.sample(range(1, 61), 6) pega 6 números únicos dentro do intervalo.

sorted() organiza os números em ordem crescente, só pra ficar bonitinho.

restartPolicy:
Configuramos como Never, porque um sorteio faz sentido ser rodado uma vez só. Se deu erro, você vai lá e corrige, sem tentar reiniciar automaticamente.


Como rodar isso no Kubernetes?

Salva o arquivo como loteria-job.yaml.

Roda o comando:
kubectl apply -f loteria-job.yaml

Pra ver o resultado:

kubectl logs job/loteria-job

Você vai ver algo como:

Números sorteados: [5, 12, 23, 34, 45, 56]

CronJobs

Um CronJob no Kubernetes é tipo aquele alarme que você coloca no celular pra lembrar de beber água, só que no mundo dos clusters. Ele é usado para agendar tarefas para rodar automaticamente em horários específicos, seguindo o mesmo esquema das crontabs no Linux. Coisas como: rodar um backup toda meia-noite ou limpar logs toda sexta.

Exemplo prático:

Digamos que você quer rodar um script que apaga arquivos antigos toda madrugada às 2h.

apiVersion: batch/v1

kind: CronJob

metadata:

  name: limpa-logs

spec:

  schedule: “0 2 * * *”  # 2h da manhã, todo dia

  jobTemplate:

    spec:

      template:

        spec:

          containers:

          – name: limpa-logs

            image: python:3.9

            command: [“python”, “-c”]

            args:

              – |

                import os

                import time

                pasta = “/tmp/logs”

                agora = time.time()

                for arquivo in os.listdir(pasta):

                    caminho = os.path.join(pasta, arquivo)

                    if os.path.isfile(caminho) and agora – os.path.getmtime(caminho) > 7 * 24 * 60 * 60:

                        os.remove(caminho)

                        print(f”Apagado: {caminho}”)

          restartPolicy: Never


Explicação:

schedule:
“0 2 * * *” diz pro CronJob rodar todo dia às 2h da manhã.

Primeiro número: minutos.

Segundo número: horas.

O resto são dias, meses e dia da semana.

jobTemplate:
É onde você define o que o job vai fazer. Aqui ele:

Usa Python para apagar arquivos na pasta /tmp/logs que não foram mexidos nos últimos 7 dias.

restartPolicy:
Configurado como Never, porque não tem por que reiniciar se deu erro. Melhor você verificar os logs manualmente.

Para suspender um CronJob:

Se você já aplicou o CronJob e quer suspendê-lo sem alterar o YAML, faz assim:

Suspender:

kubectl patch cronjob backup-diario -p ‘{“spec”: {“suspend”: true}}’

Voltar ao normal:

kubectl patch cronjob backup-diario -p ‘{“spec”: {“suspend”: false}}’

Paralellism

No Kubernetes, o parallelism em CronJobs (e Jobs) é tipo colocar vários funcionários pra resolver uma tarefa ao mesmo tempo. Ele define quantos pods podem rodar em paralelo para executar o mesmo job. É útil quando você tem uma tarefa grande que pode ser dividida em pedaços menores e processada mais rápido.


Como funciona:

spec.parallelism: Controla o número de pods que podem rodar ao mesmo tempo para o job.

Se for 1 (o padrão), os pods vão rodar um de cada vez.

Se for maior que 1, ele dispara múltiplos pods em paralelo, cada um fazendo sua parte do trabalho.

spec.completions: Define quantos pods precisam terminar para considerar o job completo.

Se você quer que 5 tarefas sejam feitas, define completions: 5.

Trabalho em equipe: O Kubernetes vai criar até parallelism pods de uma vez, mas só para quando atingir o total definido por completions.


Exemplo prático:

Digamos que você tenha um job para processar 100 arquivos e quer dividir isso entre 5 pods, com 2 rodando ao mesmo tempo:

apiVersion: batch/v1

kind: Job

metadata:

  name: processa-arquivos

spec:

  parallelism: 2  #Quantos pods podem rodar ao mesmo tempo

  completions: 5  #Quantos pods precisam terminar

  template:

    spec:

      containers:

      – name: processador

        image: python:3.9

        command: [“python”, “-c”]

        args:

          – |

            import os

            import random

            arquivo = f”arquivo_{random.randint(1, 100)}.txt”

            print(f”Processando: {arquivo}”)

            # Simula trabalho

            import time

            time.sleep(10)

      restartPolicy: Never

ConfigMaps

ConfigMaps e Secrets são recursos do Kubernetes usados para armazenar e gerenciar configurações e informações sensíveis separadamente da lógica da aplicação.


ConfigMaps

Objetivo: Armazena dados como configurações, parâmetros ou caminhos de arquivos.

Uso comum: Caminhos de arquivos, URLs de APIs, variáveis de ambiente padrão.

Exemplo básico:

apiVersion: v1

kind: ConfigMap

metadata:

  name: my-app-config

data:

  config.json: |

    {

      “db_host”: “localhost”,

      “db_port”: 5432

    }

Como acessar: Pode ser usado como variáveis de ambiente ou montado como volume.

Para recuperar uma ConfigMap no Kubernetes e usar seus valores, você pode acessá-la como uma variável de ambiente ou montá-la como volume em um pod. Abaixo, vou mostrar ambos os exemplos.

Exemplo: Recuperando uma ConfigMap como variável de ambiente

Neste exemplo, vamos criar uma ConfigMap chamada my-app-config e acessá-la como uma variável de ambiente no pod.

Usando ConfigMap como variável de ambiente

apiVersion: v1

kind: Pod

metadata:

  name: app-with-config

spec:

  containers:

  – name: my-app

    image: my-app-image

    env:

      – name: DB_HOST

        valueFrom:

          configMapKeyRef:

            name: my-app-config

            key: config.json

      – name: DB_PORT

        valueFrom:

          configMapKeyRef:

            name: my-app-config

            key: config.json

Neste exemplo, os valores DB_HOST e DB_PORT são retirados da ConfigMap my-app-config.

A atualização de uma ConfigMap no Kubernetes envolve modificar seus dados ou configurações para refletir mudanças necessárias no ambiente ou na aplicação.

Atualização de ConfigMap

Criar uma nova ConfigMap com os dados atualizados:
Para atualizar uma ConfigMap, você pode criar uma nova versão com as informações modificadas.
Exemplo: Atualizar uma ConfigMap usando YAML

apiVersion: v1

kind: ConfigMap

metadata:

  name: my-app-config

data:

  config.json: |

    {

      “db_host”: “novo-servidor”,

      “db_port”: 5432

    }

Aplicar a ConfigMap atualizada:
Após criar a nova ConfigMap, aplique-a ao cluster usando o comando kubectl apply.

kubectl apply -f updated-configmap.yaml

Verificar a ConfigMap atualizada:
Você pode verificar se a ConfigMap foi atualizada usando o comando kubectl get configmap.

kubectl get configmap my-app-config -o yaml


Atualização parcial de ConfigMap

Se precisar atualizar apenas alguns campos específicos da ConfigMap, você pode usar o comando kubectl patch.

Exemplo: Atualizar apenas o db_host

kubectl patch configmap my-app-config -p ‘{“data”: {“config.json”: “{\”db_host\”: \”nova-host\”}”}}’

Dessa forma, apenas o campo db_host será atualizado.

Usando a ConfigMap em um comando do container:

apiVersion: v1

kind: Pod

metadata:

  name: app-with-config

spec:

  containers:

  – name: my-app

    image: my-app-image

    command: [ “cat”, “/etc/config/app-settings.json” ]

    volumeMounts:

    – name: config-volume

      mountPath: /etc/config

  volumes:

  – name: config-volume

    configMap:

      name: my-app-config

Nesse exemplo, o comando cat /etc/config/app-settings.json será executado no container para acessar o conteúdo da ConfigMap.

Secrets

Imagina que você tem uma aplicação rodando em contêineres no Kubernetes e precisa acessar informações sensíveis como senhas, tokens de API, ou chaves SSH. É aí que entram os Secrets.

Os Secrets são objetos do Kubernetes que armazenam informações confidenciais de forma segura. Você pode criar um Secret e usá-lo em seus pods, volumes ou aplicações para acessar essas informações de maneira controlada e protegida.

Exemplo básico:

Criar um Secret:

kubectl create secret generic my-secret –from-literal=username=admin –from-literal=password=1234

Aqui estamos criando um Secret chamado my-secret com dois campos: username e password.

ou YAML para criar um Secret no Kubernetes:

apiVersion: v1

kind: Secret

metadata:

  name: my-secret

data:

  username: YWRtaW4=  # ‘admin’ em base64

  password: MTIzNA==  # ‘1234’ em base64

Usando o Secret em um Pod: Agora que criamos o Secret, precisamos vinculá-lo ao nosso pod. Isso pode ser feito através de um volume ou usando o ambiente.

apiVersion: v1

kind: Pod

metadata:

  name: my-app

spec:

  containers:

  – name: my-app-container

    image: my-app-image

    envFrom:

      – secretRef:

          name: my-secret

Nesse caso, o pod my-app estará acessando username e password como variáveis de ambiente, sem precisar expor essas informações diretamente no código ou no ambiente.

Usando Secrets em Volumes: Outra abordagem é montar os dados do Secret como um volume em vez de usar variáveis de ambiente.

apiVersion: v1

kind: Pod

metadata:

  name: my-app-with-volume

spec:

  containers:

  – name: my-app-container

    image: my-app-image

    volumeMounts:

      – mountPath: /etc/secrets

        name: my-secret-volume

  volumes:

    – name: my-secret-volume

      secret:

        secretName: my-secret

Agora, os dados do Secret estarão disponíveis como arquivos no caminho /etc/secrets.

Vantagens dos Secrets:

Segurança: Mantém as informações sensíveis longe de configurações expostas ou hard-coded.

Controle: Você pode gerenciar quem tem acesso a esses segredos através de RBAC (Role-Based Access Control).

Facilidade: Acessar Secrets é tão simples quanto referenciá-los no pod ou aplicação.

Então, essa é a ideia geral dos Secrets no Kubernetes. Eles ajudam a manter suas informações seguras e organizadas no ambiente de contêineres. 🙂

StateFulSets

StatefulSets no Kubernetes são usados para gerenciar aplicativos com estado, ou seja, aqueles que precisam de identidades únicas e persistentes entre os pods. Diferente de Deployments, os StatefulSets garantem que os pods sejam criados, atualizados, e excluídos em uma ordem específica e mantenham seus volumes de armazenamento associados.

Quando usar StatefulSets?

Aplicações que precisam de persistência de dados, como bancos de dados (PostgreSQL, MySQL, MongoDB).

Serviços que requerem identificação única, como Zookeeper, Kafka ou Redis.

Apps que precisam de uma ordem de inicialização específica.

Características principais:

Identidade persistente: Cada pod tem um nome único que não muda, mesmo se for recriado.

Ordem de criação/deleção: Os pods são criados e removidos em sequência.

Armazenamento persistente: Cada pod pode ter seu próprio PersistentVolume que não é compartilhado com outros.

Exemplo básico de um StatefulSet

Aqui está um exemplo de um StatefulSet que cria dois pods de um app simples:

apiVersion: apps/v1

kind: StatefulSet

metadata:

  name: my-app

spec:

  replicas: 2

  serviceName: “my-app-service”

  selector:

    matchLabels:

      app: my-app

  template:

    metadata:

      labels:

        app: my-app

    spec:

      containers:

      – name: my-container

        image: nginx

        ports:

        – containerPort: 80

  volumeClaimTemplates:

  – metadata:

      name: my-storage

    spec:

      accessModes: [“ReadWriteOnce”]

      resources:

        requests:

          storage: 1Gi

Explicação do exemplo:

replicas: 2: Cria dois pods.

serviceName: Deve estar associado a um Headless Service(vamos falar sobre) para permitir comunicação entre os pods.

volumeClaimTemplates: Cada pod terá seu próprio volume de 1Gi para persistência.

Vamos criar um cenário simples para explicar o que esse exemplo faz no cluster Kubernetes.

Cenário:

Você é responsável por gerenciar um sistema web que usa Nginx como servidor e precisa que cada instância do servidor mantenha seus próprios arquivos de configuração ou dados específicos. Esses dados devem ser persistentes, mesmo que os pods sejam recriados.

O que acontece quando você aplica o exemplo?

Criação do StatefulSet:

O Kubernetes cria dois pods com os nomes my-app-0 e my-app-1 (seguindo a ordem e usando o nome do StatefulSet como base).

Cada pod terá sua própria identidade, o que é importante para rastrear logs ou manter dados únicos.

Headless Service:

O campo serviceName: “my-app-service” exige um Headless Service (sem IP cluster) para habilitar a comunicação direta entre os pods usando nomes DNS como:

my-app-0.my-app-service

my-app-1.my-app-service

Isso é útil se o aplicativo precisar se comunicar entre os pods diretamente.

Volumes Persistentes:

Para cada pod, o Kubernetes provisiona um PersistentVolumeClaim (PVC) baseado no volumeClaimTemplates definido. No caso do exemplo:

O pod my-app-0 recebe um volume chamado my-storage-my-app-0.

O pod my-app-1 recebe um volume chamado my-storage-my-app-1.

Esses volumes são independentes e persistem mesmo que o pod seja reiniciado.

Execução do Nginx:

Cada pod executa um container com a imagem nginx e expõe a porta 80.

Passo a passo no cluster:

O Kubernetes cria os volumes:

O controlador do StatefulSet verifica o volumeClaimTemplates e cria PVCs para cada pod. Os volumes provisionados garantem persistência.

O Kubernetes cria os pods na ordem:

Primeiro, cria o pod my-app-0, aguarda até que ele esteja pronto e só então cria my-app-1.

Isso garante a ordem necessária caso o aplicativo dependa disso.

Os pods se comunicam entre si (se necessário):

Com o Headless Service, os pods podem se comunicar diretamente pelo DNS (útil em bancos de dados distribuídos ou serviços clusterizados).

Recriação dos pods:

Se o pod my-app-0 falhar, o Kubernetes recria o mesmo pod (com o mesmo nome e volume associado) sem afetar o my-app-1.


Exemplo de comportamento real:

Imagine que o my-app-0 escreve dados específicos no volume, como:

/data/logs/nginx.log

Esses dados permanecem intactos mesmo se o pod for reiniciado. Já o my-app-1 mantém outro conjunto de dados no seu volume persistente.

Isso resolve problemas como:

Manter logs separados para cada instância.

Garantir que cada pod tenha armazenamento persistente exclusivo.

Headless Service

Um Headless Service no Kubernetes é um tipo de serviço que não atribui um IP fixo. Ele é usado para acessar diretamente os pods de um StatefulSet ou Deployment. Em vez de criar um IP único para balancear o tráfego, ele gera nomes DNS para cada pod no formato <pod-name>.<service-name>.

Isso é útil para apps distribuídos, como bancos de dados, onde cada instância precisa se identificar. Você ativa isso colocando clusterIP: None no YAML do serviço. Assim, os pods podem se comunicar diretamente, sem passar por um IP central.

Aqui está um exemplo simples de Headless Service:

apiVersion: v1

kind: Service

metadata:

  name: my-app-service

spec:

  clusterIP: None  # Faz o serviço ser Headless

  selector:

    app: my-app

  ports:

    – port: 80

      targetPort: 80

O que faz:

Este serviço seleciona pods com o label app: my-app.

Não cria um IP único para o serviço.

Permite acessar os pods diretamente via DNS:

my-app-0.my-app-service

my-app-1.my-app-service.

Ideal para apps que precisam de comunicação direta entre instâncias, como clusters de banco de dados.

PersistentVolume e PersitentVolumeClaim

Persistent Volume (PV): É um recurso de armazenamento no cluster Kubernetes, configurado pelo administrador. Ele é independente dos pods e fornece armazenamento persistente.

Persistent Volume Claim (PVC): É um pedido de armazenamento feito pelos usuários. O PVC solicita características específicas, como tamanho e modo de acesso (ReadWriteOnce, ReadOnlyMany).

O PVC se conecta automaticamente a um PV compatível no cluster.

Exemplo prático: Um pod usa um PVC para salvar dados; mesmo que o pod seja excluído ou reiniciado, os dados permanecem no PV.

Aqui vão exemplos simples de Persistent Volume (PV) e Persistent Volume Claim (PVC):

1. Persistent Volume (PV)

apiVersion: v1

kind: PersistentVolume

metadata:

  name: my-pv

spec:

  capacity:

    storage: 1Gi

  accessModes:

    – ReadWriteOnce

  hostPath:

    path: “/data”

O que faz: Cria um volume de 1Gi no diretório local /data.


2. Persistent Volume Claim (PVC)

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

  name: my-pvc

spec:

  accessModes:

    – ReadWriteOnce

  resources:

    requests:

      storage: 500Mi

O que faz: Solicita 500Mi de armazenamento do PV.


3. Pod usando o PVC

apiVersion: v1

kind: Pod

metadata:

  name: my-pod

spec:

  containers:

    – name: my-container

      image: nginx

      volumeMounts:

        – mountPath: “/app-data”

          name: my-volume

  volumes:

    – name: my-volume

      persistentVolumeClaim:

        claimName: my-pvc

O que faz: O pod monta o volume solicitado pelo PVC em /app-data.

Pod Management Policy

A Pod Management Policy no Kubernetes controla como os pods de um StatefulSet são gerenciados em relação à criação, exclusão e atualização. A política “OrderedReady” é a configuração padrão usada no podManagementPolicy de um StatefulSet.

O que é “OrderedReady”?

Define que os pods do StatefulSet serão criados, atualizados e excluídos em ordem sequencial.

Um pod só será criado ou atualizado depois que o pod anterior estiver no estado Ready.

Quando usar?

Necessário em aplicativos que dependem de uma ordem específica para inicialização, como bancos de dados distribuídos (MongoDB, Cassandra).

Garantia de que cada instância estará pronta antes de passar para a próxima.

Exemplo no StatefulSet:

apiVersion: apps/v1

kind: StatefulSet

metadata:

  name: my-app

spec:

  replicas: 3

  podManagementPolicy: OrderedReady  # Política padrão

  selector:

    matchLabels:

      app: my-app

  template:

    metadata:

      labels:

        app: my-app

    spec:

      containers:

      – name: app

        image: nginx

        ports:

        – containerPort: 80

O que acontece?

Cria o pod my-app-0 e espera que ele esteja “Ready”.

Depois, cria my-app-1 e aguarda o mesmo estado.

Por fim, cria my-app-2.

Alternativa: “Parallel”

Se você quiser que todos os pods sejam criados ou atualizados simultaneamente, use a política “Parallel” no lugar de “OrderedReady”.

Endpoints

O que são Endpoints no Kubernetes?

Endpoints no Kubernetes representam os endereços IP e portas de um ou mais pods que estão associados a um Service. Eles são criados automaticamente quando um Service é configurado para direcionar tráfego para os pods correspondentes.

Os Endpoints são usados para conectar o Service aos pods que ele gerencia, permitindo que o tráfego chegue aos pods corretos.


Como funciona?

O Service seleciona pods com base nos labels configurados no seletor (selector).

O Kubernetes cria os Endpoints para listar os IPs e portas dos pods que atendem ao seletor.

Quando você acessa o Service, ele usa esses Endpoints para rotear o tráfego.


Exemplo simples:

Pod:

apiVersion: v1

kind: Pod

metadata:

  name: my-pod

  labels:

    app: my-app

spec:

  containers:

  – name: my-container

    image: nginx

    ports:

    – containerPort: 80

Service:

apiVersion: v1

kind: Service

metadata:

  name: my-service

spec:

  selector:

    app: my-app

  ports:

  – protocol: TCP

    port: 80

    targetPort: 80

Endpoints gerados: Quando você cria o Service, o Kubernetes cria automaticamente os Endpoints:

apiVersion: v1

kind: Endpoints

metadata:

  name: my-service

subsets:

– addresses:

    – ip: 192.168.1.10  # IP do pod

  ports:

    – port: 80


O que acontece:

O Service my-service usa os Endpoints para rotear o tráfego para o pod my-pod no IP 192.168.1.10 na porta 80.

Se outro pod com o mesmo label app: my-app for adicionado, ele será incluído automaticamente nos Endpoints.

EndpointSlices

EndpointSlices são uma alternativa mais eficiente aos Endpoints no Kubernetes para rastrear IPs e portas de pods associados a um Service. Eles foram criados para lidar melhor com clusters grandes e serviços com muitos pods, evitando o limite de tamanho dos objetos Endpoints tradicionais.


Características:

Divisão em fatias: Cada EndpointSlice pode conter até 100 endpoints (padrão).

Escalável: Reduz a sobrecarga em clusters grandes.

Automático: Criados e gerenciados pelo Kubernetes quando um Service é configurado.

Compatibilidade: Trabalham junto com serviços existentes sem precisar de mudanças manuais.


Exemplo prático:

Pod:

apiVersion: v1

kind: Pod

metadata:

  name: my-pod

  labels:

    app: my-app

spec:

  containers:

  – name: my-container

    image: nginx

    ports:

    – containerPort: 80

Service:

apiVersion: v1

kind: Service

metadata:

  name: my-service

spec:

  selector:

    app: my-app

  ports:

  – protocol: TCP

    port: 80

    targetPort: 80

EndpointSlice gerado (automático): O Kubernetes cria o seguinte EndpointSlice para o Service:

apiVersion: discovery.k8s.io/v1

kind: EndpointSlice

metadata:

  name: my-service-xyz

addressType: IPv4

endpoints:

– addresses:

  – 192.168.1.10

ports:

– port: 80


O que acontece:

O EndpointSlice lista o IP e a porta do pod my-pod de forma mais eficiente.

Se houver muitos pods, o Kubernetes divide os Endpoints em várias fatias para evitar sobrecarga.

Isso melhora a performance em clusters grandes, garantindo que serviços continuem responsivos.

EndpointSlices Labels e endereçamentos

EndpointSlices Labels e endereçamentos são importantes para organizar e otimizar como os endpoints são agrupados e acessados em Kubernetes.

EndpointSlices Labels

Os labels em EndpointSlices ajudam a categorizar e organizar os Endpoints em grupos específicos. Isso facilita a descoberta e a rotação do tráfego para pods de acordo com essas categorias.

Os labels podem ser usados para:

Segmentação: Criar grupos de Endpoints dentro de um EndpointSlice, como diferentes regiões ou zonas de disponibilidade.

Descoberta: Ajudar os controladores a identificar e acessar Endpoints específicos, como todos os pods de uma determinada versão ou serviço.

Exemplo de labels no EndpointSlice:

apiVersion: discovery.k8s.io/v1

kind: EndpointSlice

metadata:

  name: my-service-xyz

  labels:

    version: v1

    zone: us-west

addressType: IPv4

endpoints:

  – addresses:

    – 192.168.1.10

    ports:

    – port: 80

Neste exemplo, o EndpointSlice tem os labels version: v1 e zone: us-west, permitindo que os pods com esses rótulos sejam agrupados de forma eficiente.

Endereçamentos Suportados em EndpointSlices

EndpointSlices suportam dois tipos principais de endereços para os pods:

IPv4: Endereços IP tradicionais no formato IPv4 (por exemplo, 192.168.1.10).

IPv6: Endereços IP no formato IPv6 (por exemplo, fd00::1).

Esses tipos de endereços permitem que você use diferentes protocolos de rede conforme a necessidade de seu cluster, incluindo suporte para ambientes que requerem IPv6.

Exemplo de Endereço IPv6 em EndpointSlice:

apiVersion: discovery.k8s.io/v1

kind: EndpointSlice

metadata:

  name: my-service-xyz

addressType: IPv6

endpoints:

  – addresses:

    – fd00::1

    ports:

    – port: 80

Nesse exemplo, o EndpointSlice usa um endereço IPv6 para o pod.

Resumo:

Labels em EndpointSlices permitem organização e filtragem de Endpoints, o que facilita a gestão do tráfego e descoberta de serviços.

Endereços suportados incluem IPv4 e IPv6, permitindo flexibilidade na configuração de rede dos pods no cluster Kubernetes.

Rbac

RBAC (Role-Based Access Control) no Kubernetes é uma maneira de controlar quem pode fazer o quê dentro do cluster, baseando-se em papéis (roles). Ele permite definir permissões para usuários, grupos ou serviços para acessar recursos do Kubernetes, como pods, deployments ou namespaces.

Como funciona?

Roles (Papéis): Definem um conjunto de permissões dentro de um namespace ou no cluster.

RoleBindings e ClusterRoleBindings: Atribuem roles a usuários ou grupos.

Verbos: As permissões são dadas usando verbos como get, list, create, delete, etc.


Tipos de RBAC:

Role: Definido em um único namespace. Controla o acesso a recursos dentro desse namespace.

ClusterRole: Controla o acesso a recursos em todo o cluster (em qualquer namespace).

Exemplos:

Role (para um namespace específico):

apiVersion: rbac.authorization.k8s.io/v1

kind: Role

metadata:

  namespace: my-namespace

  name: pod-reader

rules:

  – apiGroups: [“”]

    resources: [“pods”]

    verbs: [“get”, “list”]

O que faz: Cria uma role chamada pod-reader que permite ler (get, list) pods no namespace my-namespace.

RoleBinding (para associar Role a um usuário):

apiVersion: rbac.authorization.k8s.io/v1

kind: RoleBinding

metadata:

  name: pod-reader-binding

  namespace: my-namespace

subjects:

  – kind: User

    name: “gustavo-santana”

    apiGroup: rbac.authorization.k8s.io

roleRef:

  kind: Role

  name: pod-reader

  apiGroup: rbac.authorization.k8s.io

O que faz: Atribui a Role pod-reader ao usuário john-doe dentro do namespace my-namespace.

ClusterRole (para acesso global):

apiVersion: rbac.authorization.k8s.io/v1

kind: ClusterRole

metadata:

  name: cluster-admin

rules:

  – apiGroups: [“”]

    resources: [“pods”, “services”, “deployments”]

    verbs: [“get”, “list”, “create”, “delete”, “update”]

O que faz: Cria uma ClusterRole que permite acesso total a pods, serviços e deployments em todo o cluster.

ClusterRoleBinding (para associar ClusterRole globalmente):

apiVersion: rbac.authorization.k8s.io/v1

kind: ClusterRoleBinding

metadata:

  name: admin-binding

subjects:

  – kind: User

    name: “admin-user”

    apiGroup: rbac.authorization.k8s.io

roleRef:

  kind: ClusterRole

  name: cluster-admin

  apiGroup: rbac.authorization.k8s.io

O que faz: Atribui a ClusterRole cluster-admin ao usuário admin-user em todo o cluster.


Resumo:

RBAC no Kubernetes controla quem pode acessar e fazer o quê dentro de um cluster.

Roles definem permissões e RoleBindings ou ClusterRoleBindings conectam essas permissões a usuários ou grupos.

Gerando chave e certificado de segurança para RBAC

Para gerar uma chave e um certificado de segurança para RBAC no Kubernetes, você pode criar um certificado que será usado para autenticação via ServiceAccount, que é frequentemente utilizada em conjunto com RBAC.

Aqui está um processo simples para gerar e usar uma chave e certificado.

Gerar a chave privada (private key) e o certificado (certificate)

Você pode usar o OpenSSL para gerar a chave e o certificado.

Gerar a chave privada (private key)

openssl genpkey -algorithm RSA -out my-key.pem

Gerar o certificado (certificate)

openssl req -new -key my-key.pem -out my-csr.csr

Durante a execução, você será solicitado a fornecer informações como país, estado e nome comum. Para um certificado de uso específico, como para o Kubernetes, você pode preencher esses campos conforme necessário.

Gerar o certificado assinado (signed certificate)

openssl x509 -req -in my-csr.csr -signkey my-key.pem -out my-cert.crt

Agora você tem:

my-key.pem: A chave privada.

my-cert.crt: O certificado.

Criar o ServiceAccount no Kubernetes

Com a chave e o certificado gerados, você pode criar um ServiceAccount no Kubernetes. Aqui está um exemplo de como criar o ServiceAccount que usará esse certificado.

Exemplo de ServiceAccount:

apiVersion: v1

kind: ServiceAccount

metadata:

  name: my-service-account

Salve isso em um arquivo, por exemplo service-account.yaml, e aplique com:

kubectl apply -f service-account.yaml

Criar uma Role ou ClusterRole e RoleBinding ou ClusterRoleBinding

Agora, você precisa configurar as permissões para esse ServiceAccount. Aqui está um exemplo de uma Role com permissões de leitura para pods e como vinculá-la ao ServiceAccount:

Exemplo de Role (no namespace default):

apiVersion: rbac.authorization.k8s.io/v1

kind: Role

metadata:

  namespace: default

  name: pod-reader

rules:

  – apiGroups: [“”]

    resources: [“pods”]

    verbs: [“get”, “list”]

Exemplo de RoleBinding (associando a Role ao ServiceAccount):

apiVersion: rbac.authorization.k8s.io/v1

kind: RoleBinding

metadata:

  name: pod-reader-binding

  namespace: default

subjects:

  – kind: ServiceAccount

    name: my-service-account

    namespace: default

roleRef:

  kind: Role

  name: pod-reader

  apiGroup: rbac.authorization.k8s.io

Salve isso em um arquivo role-binding.yaml e aplique com:

kubectl apply -f role-binding.yaml

Usar o ServiceAccount com a chave e o certificado

Agora que o ServiceAccount tem a Role associada, você pode usar a chave e o certificado para autenticação com a API do Kubernetes ou dentro de seus pods. Para isso, você deve associar a chave e o certificado ao ServiceAccount.

Resumo:

Gerar chave e certificado com OpenSSL.

Criar um ServiceAccount para o Kubernetes.

Criar uma Role ou ClusterRole e vincular ao ServiceAccount com RoleBinding ou ClusterRoleBinding.

Usar o ServiceAccount com a chave e o certificado em seus pods ou serviços.

Esse processo de chave e certificado é frequentemente usado para autenticação segura e autorizações em nível de acesso.

Finalizando:

Aqui finalizo nossa série de artigos sobre Kubernetes. Espero ter ajudado a entender melhor essa ferramenta e seus componentes. Se cometi algum erro ou se você gostaria de contribuir com mais informações, por favor, entre em contato comigo. Agradeço pela leitura e nos vemos nos próximos artigos. 🙂