<h2>ConfigMaps e Secrets em Kubernetes: Gerenciando Configuração</h2>
<p>Quando você trabalha com Kubernetes em produção, rapidamente percebe que hardcoding configurações dentro de imagens Docker é uma péssima prática. ConfigMaps e Secrets são os mecanismos nativos do Kubernetes para separar dados de configuração da lógica da aplicação. A diferença fundamental é simples: <strong>ConfigMaps armazenam dados não-sensíveis em texto plano</strong>, enquanto <strong>Secrets são projetados para dados sensíveis como senhas e tokens</strong>, com suporte a encoding em base64.</p>
<p>Neste artigo, vou guiá-lo através dos conceitos práticos, mostrando como estruturar suas aplicações para aproveitar essas ferramentas corretamente. Você aprenderá não apenas a sintaxe, mas o porquê dessas decisões arquiteturais importam para escalabilidade e manutenção.</p>
<h2>Entendendo ConfigMaps: Configuração sem Sensibilidade</h2>
<h3>O que é um ConfigMap e quando usá-lo</h3>
<p>Um ConfigMap é um objeto Kubernetes que permite armazenar dados de configuração em pares chave-valor. Pense nele como um arquivo de properties ou um arquivo <code>.env</code> que vive dentro do cluster e pode ser montado em seus Pods. O uso mais comum é para variáveis de ambiente que mudam entre ambientes (desenvolvimento, staging, produção), como URLs de bancos de dados, timeouts, níveis de log, e parâmetros de aplicação.</p>
<p>A vantagem de usar ConfigMaps é que você pode atualizar configurações sem recriar suas imagens Docker. Você muda o ConfigMap, e na próxima vez que o Pod for criado ou reiniciado, ele recebe a nova configuração. Isso acelera o ciclo de desenvolvimento e reduz significativamente o tamanho e complexidade das imagens.</p>
<h3>Criando ConfigMaps: Métodos Práticos</h3>
<p>Existem várias formas de criar um ConfigMap. A mais simples é usar argumentos diretos no <code>kubectl</code>:</p>
<pre><code class="language-bash">kubectl create configmap app-config \
--from-literal=DATABASE_URL=postgresql://db:5432/myapp \
--from-literal=LOG_LEVEL=info \
--from-literal=MAX_CONNECTIONS=100</code></pre>
<p>Para configurações mais complexas ou que vêm de arquivos, você pode usar a abordagem declarativa com YAML:</p>
<pre><code class="language-yaml">apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: production
data:
DATABASE_URL: postgresql://db:5432/myapp
LOG_LEVEL: info
MAX_CONNECTIONS: "100"
app.properties: |
server.port=8080
server.shutdown=graceful
cache.ttl.seconds=3600</code></pre>
<p>Note que chaves com conteúdo multilinhas (como <code>app.properties</code>) usam o pipe <code>|</code>. Quando você tem arquivos inteiros de configuração, pode referenciá-los diretamente:</p>
<pre><code class="language-bash">kubectl create configmap nginx-config --from-file=nginx.conf --from-file=default.conf</code></pre>
<h3>Consumindo ConfigMaps em Pods</h3>
<p>Uma vez criado, você pode injetar ConfigMaps em seus Pods de duas formas principais. <strong>Como variáveis de ambiente</strong> é a mais comum:</p>
<pre><code class="language-yaml">apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
containers:
- name: app
image: myapp:1.0
env:
- name: DATABASE_URL
valueFrom:
configMapKeyRef:
name: app-config
key: DATABASE_URL
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL</code></pre>
<p>Ou você pode expor <strong>todas as chaves</strong> do ConfigMap de uma só vez usando <code>envFrom</code>:</p>
<pre><code class="language-yaml">apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
containers:
- name: app
image: myapp:1.0
envFrom:
- configMapRef:
name: app-config</code></pre>
<p>A segunda abordagem é <strong>montar o ConfigMap como um volume</strong>, útil quando você precisa de arquivos completos de configuração:</p>
<pre><code class="language-yaml">apiVersion: v1
kind: Pod
metadata:
name: nginx-app
spec:
containers:
- name: nginx
image: nginx:latest
volumeMounts:
- name: config-volume
mountPath: /etc/nginx/conf.d
volumes:
- name: config-volume
configMap:
name: nginx-config</code></pre>
<p>Neste caso, cada chave do ConfigMap vira um arquivo dentro do diretório <code>/etc/nginx/conf.d</code>. Se o ConfigMap tiver uma chave <code>default.conf</code>, você encontrará um arquivo <code>/etc/nginx/conf.d/default.conf</code> dentro do container.</p>
<h2>Secrets: Protegendo Dados Sensíveis</h2>
<h3>Diferenças entre ConfigMap e Secret</h3>
<p>Enquanto ConfigMaps são para dados públicos, <strong>Secrets são para informações sensíveis</strong>: senhas de banco de dados, tokens de API, chaves SSH, certificados TLS. A principal diferença técnica é que Secrets são armazenados em base64 por padrão (etcd) e o Kubernetes oferece opções de criptografia em repouso. Porém, <strong>não confunda base64 com encriptação</strong>: base64 é apenas encoding, não oferece segurança real.</p>
<p>Se você precisa de segurança real, você deve configurar encriptação em repouso no etcd. Mas mesmo com base64, Secrets têm um propósito importante: separam dados sensíveis de dados públicos, facilitam rotação de credenciais, e permitem controles RBAC (Role-Based Access Control) mais granulares.</p>
<h3>Criando Secrets de Forma Segura</h3>
<p>Para criar um Secret com dados sensíveis, use <code>kubectl create secret</code>:</p>
<pre><code class="language-bash">kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password=mysecurepassword123</code></pre>
<p>Ou de forma declarativa (mas cuidado com o controle de versão!):</p>
<pre><code class="language-yaml">apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
data:
username: YWRtaW4= # base64 encoded "admin"
password: bXlzZWN1cmVwYXNzd29yZDEyMw== # base64 encoded "mysecurepassword123"</code></pre>
<p>Para gerar os valores em base64 seguramente (sem colocar senhas em histórico de shell):</p>
<pre><code class="language-bash">echo -n "mysecurepassword123" | base64</code></pre>
<p>Para Secrets de tipo <code>docker-registry</code> (credentials para puxar imagens privadas):</p>
<pre><code class="language-bash">kubectl create secret docker-registry registry-credentials \
--docker-server=docker.io \
--docker-username=myusername \
--docker-password=mypassword \
--docker-email=myemail@example.com</code></pre>
<p>E para certificados TLS:</p>
<pre><code class="language-bash">kubectl create secret tls tls-secret \
--cert=path/to/cert.crt \
--key=path/to/key.key</code></pre>
<h3>Consumindo Secrets em Pods</h3>
<p>Secrets são consumidos de forma idêntica aos ConfigMaps. Como variáveis de ambiente:</p>
<pre><code class="language-yaml">apiVersion: v1
kind: Pod
metadata:
name: app-with-db
spec:
containers:
- name: app
image: myapp:1.0
env:
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password</code></pre>
<p>Ou montado como volume (para quando você precisa de arquivos, como certificados):</p>
<pre><code class="language-yaml">apiVersion: v1
kind: Pod
metadata:
name: secure-app
spec:
containers:
- name: app
image: myapp:1.0
volumeMounts:
- name: tls-certs
mountPath: /etc/tls
readOnly: true
volumes:
- name: tls-certs
secret:
secretName: tls-secret</code></pre>
<p>Para usar Secrets como credenciais de pull de imagens privadas, configure-o no <code>imagePullSecrets</code>:</p>
<pre><code class="language-yaml">apiVersion: v1
kind: Pod
metadata:
name: private-image-app
spec:
imagePullSecrets:
- name: registry-credentials
containers:
- name: app
image: private.docker.io/myapp:1.0</code></pre>
<h2>Boas Práticas e Padrões Reais</h2>
<h3>Organizando ConfigMaps e Secrets por Ambiente</h3>
<p>Em um projeto real, você não quer um único ConfigMap gigante. Organize por função e ambiente. Exemplo de estrutura para uma aplicação com múltiplos serviços:</p>
<pre><code class="language-yaml"># namespace: production
---
apiVersion: v1
kind: ConfigMap
metadata:
name: api-config
labels:
app: api
env: production
data:
LOG_LEVEL: warn
CACHE_TTL: "3600"
API_TIMEOUT: "30"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: worker-config
labels:
app: worker
env: production
data:
QUEUE_WORKERS: "10"
RETRY_ATTEMPTS: "3"
---
apiVersion: v1
kind: Secret
metadata:
name: api-secrets
labels:
app: api
env: production
type: Opaque
data:
# Encode: echo -n "your-api-key" | base64
API_KEY: eW91ci1hcGkta2V5
JWT_SECRET: eW91ci1qd3Qtc2VjcmV0</code></pre>
<p>Isso facilita a manutenção e permite diferentes configurações para staging, development, etc.</p>
<h3>Validação e Versionamento</h3>
<p>Sempre valide seu YAML antes de aplicar:</p>
<pre><code class="language-bash">kubectl apply --dry-run=client -f config.yaml</code></pre>
<p>Use controle de versão para seus ConfigMaps e Secrets. Uma prática comum é manter versões numeradas:</p>
<pre><code class="language-bash">kubectl create configmap app-config-v1 --from-file=app.properties
kubectl create configmap app-config-v2 --from-file=app.properties # quando houver mudanças</code></pre>
<p>Depois, você atualiza os Pods para usar a nova versão. O Kubernetes não atualiza Pods automaticamente quando um ConfigMap ou Secret é modificado — você precisa recriar os Pods ou usar ferramentas como Reloader.</p>
<h3>Rotação de Secrets em Produção</h3>
<p>Nunca comita Secrets em Git. Use uma solução de gerenciamento de secrets como Vault, AWS Secrets Manager, ou Azure Key Vault, e sincronize com Kubernetes usando operadores como External Secrets Operator:</p>
<pre><code class="language-yaml">apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-backend
spec:
provider:
vault:
server: "https://vault.example.com"
path: "secret"
auth:
kubernetes:
mountPath: "kubernetes"
role: "my-app-role"
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: db-credentials
creationPolicy: Owner
data:
- secretKey: username
remoteRef:
key: myapp/db
property: username
- secretKey: password
remoteRef:
key: myapp/db
property: password</code></pre>
<p>Isso sincroniza secrets de forma dinâmica e segura, permitindo rotação centralizada.</p>
<h2>Conclusão</h2>
<p>Você aprendeu que <strong>ConfigMaps e Secrets são ferramentas essenciais para separar configuração de código</strong>, permitindo que suas imagens Docker sejam imutáveis e reutilizáveis em diferentes ambientes. A diferença não é apenas técnica — é arquitetural: ConfigMaps para dados públicos e Secrets para sensíveis.</p>
<p>A segunda lição é que <strong>encoding em base64 (usado por padrão em Secrets) não é encriptação real</strong>. Para produção, você deve configurar encriptação em repouso no etcd e considerar soluções externas de gerenciamento de secrets como HashiCorp Vault, que oferecem rotação automática, auditoria e controle de acesso mais granular.</p>
<p>Por último, <strong>organize ConfigMaps e Secrets por função, ambiente e app</strong>, valide sempre antes de aplicar, e automize a sincronização quando trabalhar com backends externos. Isso transforma Kubernetes de um orquestrador para um verdadeiro gerenciador de configuração corporativo.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://kubernetes.io/docs/concepts/configuration/configmap/" target="_blank" rel="noopener noreferrer">Kubernetes Official Documentation - ConfigMaps</a></li>
<li><a href="https://kubernetes.io/docs/concepts/configuration/secret/" target="_blank" rel="noopener noreferrer">Kubernetes Official Documentation - Secrets</a></li>
<li><a href="https://external-secrets.io/" target="_blank" rel="noopener noreferrer">External Secrets Operator - HashiCorp Vault Integration</a></li>
<li><a href="https://12factor.net/config" target="_blank" rel="noopener noreferrer">12 Factor App - Store config in the environment</a></li>
<li><a href="https://kubernetes.io/docs/concepts/configuration/secret/#security-properties" target="_blank" rel="noopener noreferrer">Kubernetes Security - Managing Secrets</a></li>
</ul>
<p><!-- FIM --></p>