Gerenciando Aplicações com Estado no Kubernetes Usando StatefulSets

O Kuberneets, por padrão, trata os Pods como recursos efêmeros e sem estado. Embora o uso de volumes possa persistir dados, a perda de um Pod em um Deployment resulta na criação de um novo Pod que não mantém uma ligação com o armazenamento anterior. Para aplicações que exigem identidade de rede estável, armazenamento persistente garantido e implantação ordenada, o StatefulSet é o conrtolador ideal.

Casos de Uso para StatefulSets

  • Armazenamento Persistente Estável: Garante que os dados persistam mesmo após a reinicialização ou realocação de um Pod, associando-os a PersistentVolumeClaims (PVCs).
  • Identidade de Rede Estável: Mantém nomes de Host e Pods consistentes, mesmo em caso de realocação, utilizando Services Headless.
  • Implantação e Escalabilidade Ordenada: Permite que os Pods sejam criados, atualizados e excluídos em uma ordem específica (de N-1 para 0), garantindo que cada Pod esteja em estado Running e Ready antes que o próximo seja iniciado.
  • Redução e Exclusão Ordenada: Assim como a implantação, a remoção de instâncias também segue uma ordem decrescente.

Componentes Essenciais de um StatefulSet

  • Service Headless: Um Service sem ClusterIP, que permite a descoberta direta de Pods e a resolução de DNS individual para cada Pod.
  • VolumeClaimTemplates: Modelos para a criação de PersistentVolumeClaims (PVCs), que, por sua vez, solicitam PersistentVolumes (PVs) para fornecer armazenamento dedicado a cada Pod.
  • StatefulSet Definition: O recurso principal que define o comportamento e os Pods da aplicação com estado.

A convenção de nomenclatura DNS para Pods em um StatefulSet é {nome-do-pod}-{indice}.{nome-do-service-headless}.{namespace}.svc.cluster.local. Por exemplo, web-0.web.default.svc.cluster.local.

Por que um Service Headless?

Diferente dos Deployments, onde os nomes dos Pods são gerados aleatoriamente e não há garantia de ordem, os StatefulSets requerem identificadores fixos e estáveis para cada Pod. Um Service Headless atua como um resolvedor DNS que aponta diretamente para os Pods, permitindo que cada Pod tenha um nome de host associado e persistente.

Por que VolumeClaimTemplates?

Aplicações com estado frequentemente necessitam de armazenamento isolado. Enquanto os Deployments podem usar volumes compartilhados, os StatefulSets garantem que cada Pod receba seu próprio volume persistente. O volumeClaimTemplates automatiza a criação de PVCs, que são então vinculados a PVs disponíveis, garantindo armazenamento dedicado para cada instância do Pod.

Definindo um StatefulSet

A criação de um ambiente para StatefulSet requer uma ordem específica:

  1. Configurar o armazenamento (ex: NFS).
  2. Criar PersistentVolumes (PVs) que representam o armazenamento físico.
  3. Definir o Service Headless.
  4. Criar o StatefulSet com volumeClaimTemplates.

Exemplo de Configuração

1. Preparação do Armazenamento NFS

Vamos cofnigurar um servidor NFS para fornecer armazenamento. Crie diretórios e exporte-os:

# Criar diretórios para os volumes
mkdir -p /data/volumes/v{1..5}

# Configurar o arquivo de exportação NFS (/etc/exports)
# Substitua 10.157.27.0/24 pelo IP da sua rede Kubernetes
/data/volumes/v1 *(rw,sync,no_subtree_check,no_root_squash)
/data/volumes/v2 *(rw,sync,no_subtree_check,no_root_squash)
/data/volumes/v3 *(rw,sync,no_subtree_check,no_root_squash)
/data/volumes/v4 *(rw,sync,no_subtree_check,no_root_squash)
/data/volumes/v5 *(rw,sync,no_subtree_check,no_root_squash)

# Aplicar as exportações
exportfs -arv

2. Criação de PersistentVolumes (PVs)

Defina um PV para cada diretório exportado pelo NFS. Cada PV deve ter um nome único e especificar o servidor NFS e o caminho.

# pv-nfs.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
 name: pv-nfs-001
spec:
 capacity:
   storage: 5Gi
 volumeMode: Filesystem
 accessModes:
   - ReadWriteOnce
   - ReadWriteMany
 persistentVolumeReclaimPolicy: Retain
 storageClassName: nfs-storage
 nfs:
   path: /data/volumes/v1
   server: 10.157.27.114 # Substitua pelo IP do seu servidor NFS
---
apiVersion: v1
kind: PersistentVolume
metadata:
 name: pv-nfs-002
spec:
 capacity:
   storage: 5Gi
 volumeMode: Filesystem
 accessModes:
   - ReadWriteOnce
 persistentVolumeReclaimPolicy: Retain
 storageClassName: nfs-storage
 nfs:
   path: /data/volumes/v2
   server: 10.157.27.114
---
apiVersion: v1
kind: PersistentVolume
metadata:
 name: pv-nfs-003
spec:
 capacity:
   storage: 5Gi
 volumeMode: Filesystem
 accessModes:
   - ReadWriteOnce
   - ReadWriteMany
 persistentVolumeReclaimPolicy: Retain
 storageClassName: nfs-storage
 nfs:
   path: /data/volumes/v3
   server: 10.157.27.114
---
apiVersion: v1
kind: PersistentVolume
metadata:
 name: pv-nfs-004
spec:
 capacity:
   storage: 5Gi
 volumeMode: Filesystem
 accessModes:
   - ReadWriteOnce
   - ReadWriteMany
 persistentVolumeReclaimPolicy: Retain
 storageClassName: nfs-storage
 nfs:
   path: /data/volumes/v4
   server: 10.157.27.114
---
apiVersion: v1
kind: PersistentVolume
metadata:
 name: pv-nfs-005
spec:
 capacity:
   storage: 5Gi
 volumeMode: Filesystem
 accessModes:
   - ReadWriteOnce
   - ReadWriteMany
 persistentVolumeReclaimPolicy: Retain
 storageClassName: nfs-storage
 nfs:
   path: /data/volumes/v5
   server: 10.157.27.114

# Aplicar os PVs
kubectl apply -f pv-nfs.yaml

3. Definição do Service Headless e StatefulSet

Crie um Service Headless para permitir a descoberta de Pods e, em seguida, o StatefulSet, que utilizará o volumeClaimTemplates para provisionar volumes.

# statefulset-nginx.yaml
apiVersion: v1
kind: Service
metadata:
 name: nginx-headless-svc
 labels:
   app: nginx
spec:
 ports:
 - port: 80
   name: web
 clusterIP: None # Essencial para um Service Headless
 selector:
   app: nginx-pod # Deve corresponder aos labels dos Pods criados pelo StatefulSet
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
 name: nginx-statefulset
spec:
 serviceName: "nginx-headless-svc" # Referencia o Service Headless
 replicas: 3 # Número de instâncias desejadas
 selector:
   matchLabels:
     app: nginx-pod # Seleciona os Pods gerenciados por este StatefulSet
 template:
   metadata:
     labels:
       app: nginx-pod # Labels para os Pods
   spec:
     terminationGracePeriodSeconds: 10
     containers:
     - name: nginx
       image: nginx:1.21.0 # Imagem do container
       ports:
       - containerPort: 80
         name: web
       volumeMounts:
       - name: nginx-data # Deve corresponder ao nome no volumeClaimTemplates
         mountPath: /usr/share/nginx/html
 volumeClaimTemplates:
 - metadata:
     name: nginx-data # Nome do volume que será montado no container
   spec:
     accessModes: ["ReadWriteOnce"] # Modo de acesso ao volume
     storageClassName: nfs-storage # Nome da StorageClass (opcional, se não definida, usa a padrão)
     resources:
       requests:
         storage: 5Gi # Tamanho solicitado do volume

# Aplicar os recursos
kubectl apply -f statefulset-nginx.yaml

4. Verificação dos Recursos Criados

Após a aplicação do YAML, verifique os Services, PVs, PVCs e Pods criados.

# Verificar o Service Headless
kubectl get svc nginx-headless-svc

# Verificar os Pods e seus volumes associados
kubectl get pods -o wide
kubectl get pvc

# Verificar o status dos PersistentVolumes
kubectl get pv

5. Teste de Exclusão

Ao excluir o StatefulSet, os Pods são removidos ordenadamente. No entanto, os PVCs criados a partir dos volumeClaimTemplates persistem por padrão (Retain), permitindo a re-associação com novos Pods.

kubectl delete statefulset nginx-statefulset
kubectl get pvc # Os PVCs ainda estarão presentes

6. Resolução de Nomes DNS

Com o Service Headless, cada Pod pode ser acessado por seu nome DNS estável.

# Exemplo de consulta DNS para um Pod específico
dig nginx-statefulset-0.nginx-headless-svc.default.svc.cluster.local @<IP-do-CoreDNS>

Escalabilidade de StatefulSets

A escalabilidade de um StatefulSet é semelhante à de um Deployment. Pode-se aumentar ou diminuir o número de réplicas usando kubectl scale ou kubectl edit.

# Escalar para 4 réplicas
kubectl scale statefulset nginx-statefulset --replicas=4

# Escalar para 2 réplicas usando patch
kubectl patch statefulset nginx-statefulset -p '{"spec":{"replicas":2}}'

A adição de réplicas ocorre em ordem crescente (0, 1, 2, ...), e a remoção em ordem decrescente (..., 2, 1, 0).

Estratégias de Atualização

O StatefulSet suporta atualizações graduais (rolling updates) e a capacidade de pausar atualizações.

Atualização Automática (Rolling Update)

Por padrão, as atualizações de imagem ou configuração em um StatefulSet são realizadas em modo de rolagem, processando os Pods em ordem decrescente.

# Atualizar a imagem de todos os Pods
kubectl set image statefulset nginx-statefulset nginx=nginx:1.22.0
# Observe a atualização dos Pods em ordem inversa
kubectl get pods -w

Atualização com Partição (Canary Release)

Para implementar uma estratégia de canary release, onde apenas um subconjunto de Pods é atualizado inicialmente, utiliza-se o campo partition dentro de spec.updateStrategy.rollingUpdate.

# Configurar a partição para atualizar apenas os Pods com índice >= 2
kubectl patch statefulset nginx-statefulset -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":2}}}}'
kubectl set image statefulset nginx-statefulset nginx=nginx:1.23.0

# Verifique que apenas nginx-statefulset-2 e nginx-statefulset-3 foram atualizados
kubectl get pods -l app=nginx-pod -o=custom-columns=NAME:.metadata.name,IMAGE:.spec.containers[0].image

# Para atualizar os Pods restantes (0 e 1), redefina a partição para 0
kubectl patch statefulset nginx-statefulset -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":0}}}}'
# Em seguida, observe a atualização dos Pods restantes.

Tags: kubernetes statefulset volumes persistentvolume persistentvolumeclaim

Publicado em 6-13 21:44 por Thomas