Os StatefulSets do Kubernetes são usados ​​para implantar aplicativos com estado em seu cluster. Cada Pod no StatefulSet pode acessar volumes persistentes locais que permanecem nele mesmo após o reagendamento. Isso permite que os pods mantenham o estado individual separado de seus vizinhos no pool.

Infelizmente, esses volumes vêm com uma grande limitação: o Kubernetes não fornece uma maneira de redimensioná-los de dentro do objeto StatefulSet. a spec.resources.requests.storage Propriedade StatefulSet volumeClaimTemplates O campo é imutável, o que impede que você aplique qualquer aumento de capacidade necessário. Este artigo irá mostrar-lhe como corrigir o problema.

Criando um StatefulSet

Copie este YAML e salve-o em ss.yaml:

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  selector:
    app: nginx
  ports:
  - name: nginx
    port: 80
  clusterIP: None
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 3
  serviceName: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - name: web
          containerPort: 80
        volumeMounts:
        - name: data
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 1Gi

Aplique YAML ao seu cluster com Kubectl:

$ kubectl apply -f ss.yaml
service/nginx created
statefulset.apps/nginx created

Você precisará de uma classe de armazenamento e um provisionador em seu cluster para executar este exemplo. Crie um StatefulSet que execute três réplicas de um servidor Web NGINX.

Embora isso não seja representativo de quando StatefulSets deve ser usado, é apropriado como uma demonstração dos problemas de volume que você pode encontrar. Uma declaração de volume com 1 Gi de armazenamento é montada no diretório de dados NGINX. Seu conteúdo da web pode exceder essa alocação relativamente pequena à medida que você dimensiona seu serviço. No entanto, tentar modificar o volumeClaimTemplates.spec.resources.requests.storage campo para 10Gi irá relatar o seguinte erro quando você executar kubectl apply:

$ kubectl apply -f ss.yaml
service/nginx unchanged
The StatefulSet "nginx" is invalid: spec: Forbidden: updates to statefulset spec for fields other than 'replicas', 'template', 'updateStrategy', 'persistentVolumeClaimRetentionPolicy' and 'minReadySeconds' are forbidden

Isso ocorre porque quase todos os campos no manifesto de um StatefulSet são imutáveis ​​após a criação.

Redimensionar manualmente os volumes do StatefulSet

Você pode ignorar a restrição alterando manualmente o tamanho da solicitação de volume persistente (PVC). Você precisará recriar o StatefulSet para liberar e religar o volume de seus pods. Isso acionará o evento de redimensionamento do volume real.

Primeiro, use o Kubectl para encontrar os PVCs associados ao seu StatefulSet:

$ kubectl get pvc
NAME           STATUS   VOLUME                                     CAPACITY   ACCESS MODES
data-nginx-0   Bound    pvc-ccb2c835-e2d3-4632-b8ba-4c8c142795e4   1Gi        RWO         
data-nginx-1   Bound    pvc-1b0b27fe-3874-4ed5-91be-d8e552e515f2   1Gi        RWO         
data-nginx-2   Bound    pvc-4b7790c2-3ae6-4e04-afee-a2e1bae4323b   1Gi        RWO

Existem três PVCs porque existem três réplicas no StatefulSet. Cada Pod tem seu próprio volume individual.

agora usa kubectl edit para ajustar a capacidade de cada volume:

$ kubectl edit pvc data-nginx-0

O manifesto YAML do PVC aparecerá em seu editor. Encontre o spec.resources.requests.storage campo e altere-o para a nova capacidade desejada:

# ...
spec:
  resources:
    requests:
      storage: 10Gi
# ...

Salve e feche o arquivo. O Kubectl deve relatar que a alteração foi aplicada ao seu cluster.

persistentvolumeclaim/data-nginx-0 edited

Agora repita essas etapas para os PVCs restantes no StatefulSet. A lista de volumes persistentes em seu cluster deve mostrar o novo tamanho em relação a cada um:

$ kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM               
pvc-0a0d0b15-241f-4332-8c34-a24b61944fb7   10Gi       RWO            Delete           Bound    default/data-nginx-2
pvc-33af452d-feff-429d-80cd-a45232e700c1   10Gi       RWO            Delete           Bound    default/data-nginx-0
pvc-49f3a1c5-b780-4580-9eae-17a1f002e9f5   10Gi       RWO            Delete           Bound    default/data-nginx-1

As reivindicações manterão o tamanho antigo por enquanto:

$ kubectl get pvc
NAME           STATUS   VOLUME                                     CAPACITY   ACCESS MODES
data-nginx-0   Bound    pvc-33af452d-feff-429d-80cd-a45232e700c1   10Gi       RWO         
data-nginx-1   Bound    pvc-49f3a1c5-b780-4580-9eae-17a1f002e9f5   10Gi       RWO         
data-nginx-2   Bound    pvc-0a0d0b15-241f-4332-8c34-a24b61944fb7   10Gi       RWO

Isso ocorre porque o volume não pode ser redimensionado enquanto os pods ainda o estiverem usando.

Recriando o StatefulSet

Conclua o redimensionamento liberando a declaração de volume do StatefulSet contido. Remova o StatefulSet, mas use o orphan mecanismo em cascata para que seus pods permaneçam em seu cluster. Isso ajudará a minimizar o tempo de inatividade.

$ kubectl delete statefulset --cascade=orphan nginx
statefulset.apps "nginx" deleted

Em seguida, edite seu arquivo YAML original para incluir o novo tamanho de volume na spec.resources.requests.storage procedimentos. então use kubectl apply Para recriar o StatefulSet em seu cluster:

$ kubectl apply -f ss.yaml
service/nginx unchanged
statefulset.apps/nginx created

O novo StatefulSet assumirá a propriedade dos pods anteriormente órfãos porque eles já atenderão aos requisitos. Os volumes podem mudar de tamanho neste momento, mas na maioria dos casos você precisará iniciar manualmente uma implantação que reinicie seus pods:

$ kubectl rollout restart statefulset nginx

A implantação é feita sequencialmente, visando um pod por vez. Isso garante que seu serviço permaneça sempre acessível.

Agora seus PVCs devem exibir o novo tamanho:

$ kubectl get pvc
NAME           STATUS   VOLUME                                     CAPACITY   ACCESS MODES
data-nginx-0   Bound    pvc-33af452d-feff-429d-80cd-a45232e700c1   10Gi       RWO         
data-nginx-1   Bound    pvc-49f3a1c5-b780-4580-9eae-17a1f002e9f5   10Gi       RWO         
data-nginx-2   Bound    pvc-0a0d0b15-241f-4332-8c34-a24b61944fb7   10Gi       RWO

Tente se conectar a um de seus Pods para verificar se a capacidade aumentada é visível por dentro:

$ kubectl exec -it nginx-0 bash
[email protected]:/# df -h /usr/share/nginx/html
Filesystem                                                                Size  Used Avail Use% Mounted on
/dev/disk/by-id/scsi-0DO_Volume_pvc-33af452d-feff-429d-80cd-a45232e700c1  9.9G  4.5M  9.4G   1% /usr/share/nginx/html

O Pod relata os 10 Gi esperados de armazenamento.

Resumo

Os StatefulSets do Kubernetes permitem que você execute aplicativos com estado no Kubernetes com volumes de armazenamento persistentes limitados a pods individuais. No entanto, a flexibilidade que isso permite termina quando você precisa redimensionar um de seus volumes. Este é um recurso ausente e atualmente requer que várias etapas manuais sejam concluídas em sequência.

Os mantenedores do Kubernetes estão cientes do problema. Há uma solicitação de recurso aberta para desenvolver uma solução que deve permitir que você inicie alterações no tamanho do volume editando o manifesto StatefulSet. Isso será muito mais rápido e seguro do que a situação atual.

Uma advertência final é que as alterações no tamanho do volume dependem de um driver de armazenamento que permite a expansão dinâmica. Esse recurso estava disponível apenas no Kubernetes v1.24 e nem todos os controladores, distribuições do Kubernetes e plataformas de nuvem o suportarão. Você pode verificar se o seu faz isso executando kubectl get sc e olhando true no ALLOWVOLUMEXPANSION coluna do controlador de armazenamento que você está usando com seus StatefulSets.