Skip to main content

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:

EndpointProtocoloUso
https://10.150.19.63:2379TLSkube-apiserver
http://127.0.0.1:2379HTTPacesso 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

8589934592 bytes = 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âmetroValor
Quota configurada8GB
DB Size atual2.2GB
Margem disponível~5.8GB
Alarmnenhum
Clusteroperacional

Conceitos

compact vs defrag

Estas duas operações são complementares e frequentemente confundidas:

OperaçãoFerramentaModoO que faz
compactetcdctlonlineRemove revisões antigas do histórico MVCC do etcd
defragetcdctl ou etcdutlonline ou offlineReorganiza 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

FerramentaModoUso
etcdctlonlineOperações via API contra etcd em execução
etcdutlofflineOperações diretas nos ficheiros — defrag, snapshot restore, migration

quota-backend-bytes

ValorBytesUso recomendado
21474836482GBDefault — clusters pequenos/teste
42949672964GBClusters médios
85899345928GBProduçã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.


Referências