etcd NOSPACE — Cluster Kubernetes em Read-Only
Contexto
Durante a instalação do Metrics Server no cluster de staging, uma cadeia de erros revelou que o etcd havia atingido o limite padrão de quota de 2GB após 377 dias de operação, disparando o alarm NOSPACE. Com o alarm ativo, o etcd passa a recusar todas as operações de escrita, tornando o cluster efetivamente read-only.
Topologia
main01 ← master (etcd como serviço systemd)
work01
work02
work03
work04
work05
work06
Importante: O etcd neste cluster corre como serviço systemd, não como static pod gerido pelo kubeadm. Por isso não aparece em
kubectl get pods -n kube-system.
Configuração relevante do etcd:
--listen-client-urls https://10.15.10.60:2379,http://127.0.0.1:2379
--data-dir=/var/lib/etcd
--quota-backend-bytes (não definido — usando default de 2GB)
Cadeia de Erros
1. Metrics Server sem resposta
kubectl top nodes
# error: Metrics API not available
Logs do pod:
dial tcp 10.96.0.1:443: connect: connection refused
Failed to scrape node "w01": 401 Unauthorized
2. Erro ao tentar corrigir o kube-proxy
kubectl rollout restart daemonset/kube-proxy -n kube-system
# error: failed to patch: etcdserver: mvcc: database space exceeded
Este foi o erro que revelou a causa raiz.
3. Confirmação via etcdctl
ETCDCTL_API=3 etcdctl \
--endpoints=http://127.0.0.1:2379 \
endpoint status --write-out=table
+-----------------------+------------------+---------+---------+-----------+------------+--------------------+--------------------------------+
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | RAFT TERM | RAFT APPLIED INDEX | ERRORS |
+-----------------------+------------------+---------+---------+-----------+------------+--------------------+--------------------------------+
| http://127.0.0.1:2379 | d5da0b695e3e0ae0 | 3.5.13 | 2.1 GB | true | 9 | 157472813 | alarm:NOSPACE |
+-----------------------+------------------+---------+---------+-----------+------------+--------------------+--------------------------------+
ETCDCTL_API=3 etcdctl \
--endpoints=http://127.0.0.1:2379 \
alarm list
# memberID:15409641622182169312 alarm:NOSPACE
Causa Raiz
O parâmetro --quota-backend-bytes do etcd define o tamanho máximo da base de dados BoltDB. O valor padrão é 2GB. Quando atingido, o etcd dispara automaticamente o alarm NOSPACE e bloqueia todas as operações de escrita.
O cluster acumulou 377 dias de objetos Kubernetes (Longhorn, leases, events, secrets) até atingir o limite.
Como conectar ao etcdctl neste cluster
O etcd expõe dois endpoints:
| Endpoint | Protocolo | Uso |
|---|---|---|
https://10.150.19.63:2379 | TLS | kube-apiserver |
http://127.0.0.1:2379 | HTTP | acesso local (sem certs) |
Para operações manuais no master, usar sempre o endpoint HTTP local:
ETCDCTL_API=3 etcdctl --endpoints=http://127.0.0.1:2379 <comando>
Resolução
Passo 1 — Aumentar a quota para 8GB
Editar o unit file do systemd:
vi /etc/systemd/system/etcd.service
Adicionar --quota-backend-bytes no final do bloco ExecStart:
[Unit]
Description=etcd
Documentation=https://github.com/etcd-io/etcd
[Service]
ExecStart=/usr/local/bin/etcd \
--name 10.150.19.63 \
--cert-file=/etc/etcd/kubernetes.pem \
--key-file=/etc/etcd/kubernetes-key.pem \
--peer-cert-file=/etc/etcd/kubernetes.pem \
--peer-key-file=/etc/etcd/kubernetes-key.pem \
--trusted-ca-file=/etc/etcd/ca.pem \
--peer-trusted-ca-file=/etc/etcd/ca.pem \
--peer-client-cert-auth \
--client-cert-auth \
--initial-advertise-peer-urls https://10.150.19.63:2380 \
--listen-peer-urls https://10.150.19.63:2380 \
--listen-client-urls https://10.150.19.63:2379,http://127.0.0.1:2379 \
--advertise-client-urls https://10.150.19.63:2379 \
--initial-cluster 10.150.19.63=https://10.150.19.63:2380 \
--initial-cluster-state new \
--data-dir=/var/lib/etcd \
--quota-backend-bytes=8589934592
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
8589934592bytes = 8GB (8 × 1024³). Este é o limite máximo suportado pelo etcd — valores superiores não são aceites.
Aplicar e reiniciar:
systemctl daemon-reload
systemctl restart etcd
# Aguardar o etcd carregar a DB (~1-2 minutos com 2GB)
watch -n 3 'ETCDCTL_API=3 etcdctl --endpoints=http://127.0.0.1:2379 endpoint status --write-out=table'
Passo 2 — Remover o alarm NOSPACE
Aumentar a quota não remove o alarm automaticamente. O etcd continua em read-only até o alarm ser desativado manualmente:
ETCDCTL_API=3 etcdctl \
--endpoints=http://127.0.0.1:2379 \
alarm disarm
# Output esperado:
# memberID:15409641622182169312 alarm:NOSPACE
Verificar que o alarm foi removido:
ETCDCTL_API=3 etcdctl \
--endpoints=http://127.0.0.1:2379 \
endpoint status --write-out=table
+-----------------------+------------------+---------+---------+-----------+--------+
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | ERRORS |
+-----------------------+------------------+---------+---------+-----------+--------+
| http://127.0.0.1:2379 | d5da0b695e3e0ae0 | 3.5.13 | 2.2 GB | true | |
+-----------------------+------------------+---------+---------+-----------+--------+
Campo ERRORS vazio — o cluster voltou a aceitar escritas.
Passo 3 — Desfragmentação offline
O defrag online deu timeout com 2.2GB de dados:
ETCDCTL_API=3 etcdctl \
--endpoints=http://127.0.0.1:2379 \
--command-timeout=300s \
defrag
# Failed to defragment etcd member: context deadline exceeded
Solução: defrag offline com o etcdutl, que opera diretamente nos ficheiros sem precisar do serviço ativo.
# 1. Remover o kube-apiserver do controlo do kubelet
mv /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/kube-apiserver.yaml
sleep 15
# 2. Parar o etcd
systemctl stop etcd
# 3. Desfragmentar (pode demorar vários minutos com DB grande)
etcdutl defrag --data-dir=/var/lib/etcd
# 4. Subir o etcd
systemctl start etcd
sleep 15
# 5. Restaurar o kube-apiserver
mv /tmp/kube-apiserver.yaml /etc/kubernetes/manifests/kube-apiserver.yaml
# 6. Aguardar o apiserver inicializar
watch -n 3 'kubectl get nodes'
Output do defrag:
2026-04-16T10:28:07Z info defragmenting {"current-db-size": "2.2 GB", "current-db-size-in-use": "2.2 GB"}
2026-04-16T10:43:30Z info finished {"current-db-size-bytes-diff": -7159808, "current-db-size": "2.2 GB", "took": "15m23s"}
O defrag recuperou apenas ~7MB, confirmando que os dados são genuínos — não havia fragmentação significativa. O volume acumulado em 377 dias de operação é real.
Estado Final
ETCDCTL_API=3 etcdctl \
--endpoints=http://127.0.0.1:2379 \
endpoint status --write-out=table
+-----------------------+------------------+---------+---------+-----------+--------+
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | ERRORS |
+-----------------------+------------------+---------+---------+-----------+--------+
| http://127.0.0.1:2379 | d5da0b695e3e0ae0 | 3.5.13 | 2.2 GB | true | |
+-----------------------+------------------+---------+---------+-----------+--------+
| Parâmetro | Valor |
|---|---|
| Quota configurada | 8GB |
| DB Size atual | 2.2GB |
| Margem disponível | ~5.8GB |
| Alarm | nenhum |
| Cluster | operacional |
Conceitos
compact vs defrag
Estas duas operações são complementares e frequentemente confundidas:
| Operação | Ferramenta | Modo | O que faz |
|---|---|---|---|
compact | etcdctl | online | Remove revisões antigas do histórico MVCC do etcd |
defrag | etcdctl ou etcdutl | online ou offline | Reorganiza o ficheiro BoltDB no disco, recupera espaço físico fragmentado |
O compact limpa as entradas do histórico; o defrag recupera o espaço em disco que essas entradas ocupavam. O compact deve ser executado antes do defrag para maximizar o espaço recuperado.
etcdctl vs etcdutl
| Ferramenta | Modo | Uso |
|---|---|---|
etcdctl | online | Operações via API contra etcd em execução |
etcdutl | offline | Operações diretas nos ficheiros — defrag, snapshot restore, migration |
quota-backend-bytes
| Valor | Bytes | Uso recomendado |
|---|---|---|
2147483648 | 2GB | Default — clusters pequenos/teste |
4294967296 | 4GB | Clusters médios |
8589934592 | 8GB | Produção / alta atividade (máximo suportado) |
Prevenção
Monitorização com Prometheus
groups:
- name: etcd
rules:
- alert: EtcdDatabaseSizeWarning
expr: etcd_mvcc_db_total_size_in_bytes > 6442450944
for: 5m
labels:
severity: warning
annotations:
summary: "etcd DB acima de 6GB no cluster {{ $labels.job }}"
- alert: EtcdDatabaseSizeCritical
expr: etcd_mvcc_db_total_size_in_bytes > 7516192768
for: 5m
labels:
severity: critical
annotations:
summary: "etcd DB acima de 7GB — intervenção necessária"
Manutenção periódica (mensal)
# 1. Compact — remover revisões antigas
REV=$(ETCDCTL_API=3 etcdctl \
--endpoints=http://127.0.0.1:2379 \
endpoint status --write-out=json | jq -r '.[0].Status.header.revision')
ETCDCTL_API=3 etcdctl \
--endpoints=http://127.0.0.1:2379 \
compact $REV
# 2. Defrag online
ETCDCTL_API=3 etcdctl \
--endpoints=http://127.0.0.1:2379 \
--command-timeout=300s \
defrag
# 3. Verificar tamanho após manutenção
ETCDCTL_API=3 etcdctl \
--endpoints=http://127.0.0.1:2379 \
endpoint status --write-out=table
Identificar maiores consumidores de espaço
ETCDCTL_API=3 etcdctl \
--endpoints=http://127.0.0.1:2379 \
get / --prefix --keys-only | \
sed 's|/[^/]*$||' | sort | uniq -c | sort -rn | head -20
Suspeitos habituais: events, secrets, recursos do Longhorn e leases do Kyverno.