Gerenciamento Avançado de Armazenamento no Kubernetes: ConfigMaps, Secrets, Volumes e Provisionamento Dinâmico

  1. Classificação de Armazenamento no Kubernetes

No ecossistema Kubernetes, os dados podem ser categorizados em duas classes principais: metadados e dados de aplicação reais.

  • Metadados: Incluem configurações de aplicações, credenciais e informações do próprio Pod. São gerenciados através de ConfigMap, Secret e Downward API.
  • Dados Reais: Arquivos de usuário, bancos de dados e logs. São gerenciados através de Volumes efêmeros ou PersistentVolumes (PV).
  1. ConfigMaps: Injeção de Configurações

O ConfigMap permite desacoplar configurações das imagens de contêiner, promovendo portabilidade. As configurações podem ser injetadas como variáveis de ambiente, argumentos de linha de comando ou mnotadas como arquivos no sistema de arquivos do contêiner.

2.1 Criação e Injeção como Variáveis de Ambiente

# Criando um ConfigMap a partir de literais
kubectl create configmap db-credentials \
  --from-literal=DB_USER=admin \
  --from-literal=DB_PASS=supersecret

# Criando a partir de um arquivo de propriedades
cat <<EOF > app.properties
LOG_LEVEL=debug
MAX_CONNECTIONS=100
EOF
kubectl create configmap app-settings --from-file=app.properties

Consumindo os dados no Pod:

apiVersion: v1
kind: Pod
metadata:
  name: config-env-pod
spec:
  containers:
    - name: app-container
      image: nginx:alpine
      command: ["/bin/sh", "-c", "env"]
      env:
        - name: DATABASE_USER
          valueFrom:
            configMapKeyRef:
              name: db-credentials
              key: DB_USER
      envFrom:
        - configMapRef:
            name: app-settings
  restartPolicy: Never

2.2 Montagem como Volume e Atualização Dinâmica

Ao montar um ConfigMap como volume, o Kubernetes utiliza links simbólicos para permitir atualizações sem corromper arquivos em uso. No entanto, a aplicação deve ser capaz de recarregar a configuração internamente (ex: nginx -s reload), pois o Kubelet apenas atualiza os arquivos no disco, não reinicia o processo do contêiner.

apiVersion: v1
kind: Pod
metadata:
  name: config-volume-pod
spec:
  containers:
    - name: web-server
      image: nginx:alpine
      volumeMounts:
        - name: config-vol
          mountPath: /etc/nginx/conf.d
  volumes:
    - name: config-vol
      configMap:
        name: nginx-config

Nota: ConfigMaps podem ser marcados como immutable: true para melhorar a performance do cluster (reduzindo a carga no kube-apiserver) e evitar alterações acidentais.

  1. Secrets: Proteção de Dados Sensíveis

Os Secrets funcionam de maneira similar aos ConfigMaps, mas são destinados a dados sensíveis. Os valores devem ser codificados em Base64 no manifesto YAML (embora o Kubernetes também suporte o campo stringData para texto plano que é codificado automaticamente na criação).

apiVersion: v1
kind: Secret
metadata:
  name: api-tokens
type: Opaque
data:
  # echo -n 'my-secret-key' | base64
  api-key: bXktc2VjcmV0LWtleQ==

Os Secrets são armazenados em memória nos nós (tmpfs) e, se configurado o cluster, criptografados em repouso no etcd. Podem ser montados como volumes com permissões restritas (ex: defaultMode: 256 para 0400 em octal).

  1. Downward API: Metadados do Pod

A Downward API expõe informações do próprio Pod (como nome, namespace, IP, limites de CPU e Memória) para os contêineres. Isso é fundamental para aplicações que precisam estar cientes de seu contexto de execução ou limites de recursos impostos pelo cluster.

apiVersion: v1
kind: Pod
metadata:
  name: downward-env-pod
spec:
  containers:
    - name: main-app
      image: busybox
      command: ["sh", "-c", "echo Pod IP: $POD_IP, CPU Limit: $CPU_LIMIT"]
      env:
        - name: POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        - name: CPU_LIMIT
          valueFrom:
            resourceFieldRef:
              resource: limits.cpu

  1. Volumes: emptyDir e hostPath

5.1 emptyDir

Um volume emptyDir é criado quando um Pod é atribuído a um nó e existe apenas enquanto o Pod estiver em execução naquele nó. É ideal para cache, compartilhamento de dados entre contêineres no mesmo Pod (padrão sidecar) ou espaço temporário. Pode ser configurado para usar memória RAM (medium: Memory), consumindo a cota de memória do contêiner e oferecendo alta performance de I/O.

5.2 hostPath

Monta um arquivo ou diretório do sistema de arquivos do nó host diretamente no contêiner. É útil para daemons que precisam acessar logs do host ou o socket do Docker (/var/run/docker.sock). Deve ser usado com extrema cautela devido a implicações de segurança e falta de portabilidade entre nós.

  1. Persistência com PV, PVC e StatefulSets

O modelo de armazenamento persistente no Kubernetes separa o provisionamento da infraestrutura (PV - PersistentVolume) do consumo pela aplicação (PVC - PersistentVolumeClaim).

  • PV: Recurso de cluster provisionado por um administrador ou dinamicamente.
  • PVC: Solicitação de armazenamento feita pelo usuário ou pelo controlador do Pod.

6.1 StatefulSet com volumeClaimTemplates

Para aplicações com estado (como bancos de dados distribuídos), o StatefulSet utiliza volumeClaimTemplates. Isso garante que cada réplica do Pod receba seu próprio PVC e PV exclusivo, mantendo a identidade de rede e a persistência de dados mesmo após reinicializações ou reschedulements.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: database-cluster
spec:
  serviceName: "db-headless"
  replicas: 3
  selector:
    matchLabels:
      app: database
  template:
    metadata:
      labels:
        app: database
    spec:
      containers:
        - name: db-engine
          image: postgres:15
          volumeMounts:
            - name: db-data
              mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:
    - metadata:
        name: db-data
      spec:
        accessModes: ["ReadWriteOnce"]
        storageClassName: "fast-ssd"
        resources:
          requests:
            storage: 10Gi

  1. Provitionamento Dinâmico com StorageClass

O provisionamento estático exige que administradores criem PVs manualmente para cada demanda. O StorageClass resolve isso permitindo o provisionamento dinâmico. Quando um PVC é criado referenciando um StorageClass, um provisionador (como o nfs-subdir-external-provisioner ou APIs de provedores de nuvem) cria automaticamente o volume de backend e o objeto PV correspondente.

7.1 Configuração do NFS Provisioner

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-nfs-storage
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:
  archiveOnDelete: "false"
  pathPattern: "${.PVC.namespace}/${.PVC.name}"
reclaimPolicy: Delete
volumeBindingMode: Immediate

Com essa configuração, ao criar um PVC solicitando managed-nfs-storage, o provisionador cria um subdiretório isolado no servidor NFS e o vincula dinamicamente ao Pod, eliminando a necessidade de intervenção manual e acelerando o ciclo de desenvolvimento.

  1. Otimização de CLI: Autocomplete para kubectl

Para melhorar a produtividade ao interagir com o cluster via terminal, habilite o autocompletar do bash para o kubectl:

# Instale o pacote de completamento (se necessário)
sudo apt-get install bash-completion  # Debian/Ubuntu
# ou
sudo yum install bash-completion      # RHEL/CentOS

# Adicione as regras ao seu ~/.bashrc
echo 'source <(kubectl completion bash)' >> ~/.bashrc
echo 'complete -o default -F __start_kubectl k' >> ~/.bashrc # Habilita para o alias 'k'

# Recarregue a sessão do shell
source ~/.bashrc

Tags: kubernetes ConfigMap Secret persistentvolume StorageClass

Publicado em 7-19 12:45