Skip to content

v0.43.1 — o nó do cluster morria por delegação de cgroup, e ninguém dizia porquê

Choose a tag to compare

@github-actions github-actions released this 08 Aug 20:50
· 5 commits to main since this release

v0.43.1 — o nó do cluster morria por delegação de cgroup, e ninguém dizia porquê

Bug report real, com o ecrã inteiro como prova:

✗ Preparing nodes (1)
error invalid argument: timeout waiting for containerd on node 'kaeso-control-plane' (90s)

O containerd nunca chegou a arrancar porque o nó já tinha morrido. Os logs
do nó tinham a resposta em duas linhas:

INFO: running in a user namespace (experimental)
ERROR: UserNS: cpu controller needs to be delegated

Entre a causa e o sintoma havia um download de 425 MB e 90 segundos de espera
por um serviço que nunca teve hipótese.

O system setup reportava sucesso sobre a limitação

Pior: o comando que existe para diagnosticar isto dizia que estava tudo bem.

limits:   APPLY — --memory/--cpus/--pids-limit take effect
Nothing to do.

E estava certo naquilo que verificava — «consigo criar um filho e escrever o
subtree_control?», sim. Só que nunca olhou para QUAIS controladores existem.
Neste host havia memory pids e mais nada; o cpu, que é o que um nó
Kubernetes exige, não estava delegado. Um diagnóstico que responde à pergunta
ao lado da que interessa é pior do que nenhum, porque é acreditado.

Agora lista-os, nomeia os que faltam, e liga-os à consequência:

controllers: memory pids
missing:  cpu cpuset io  ← a Kubernetes node needs these

Container limits work, but `cpu`, `cpuset`, `io` is not delegated.
`delonix cluster create` (kind mode) will fail: the node's entrypoint refuses
to boot without it (`UserNS: cpu controller needs to be delegated`).

O drop-in que o --delegate escreve passou a nomear os controladores em vez
de Delegate=yes: neste host o yes produziu apenas memory pids, o que passa
em todas as verificações do motor e mata um nó kind na mesma.

Falhar em milissegundos, não em 90 segundos

cluster create (modo kind) ganhou um preflight. Sem o cpu delegado recusa
ANTES de puxar a imagem, com a causa e o remédio:

error invalid argument: the `cpu` cgroup controller is not delegated, and a
Kubernetes node cannot boot without it (its entrypoint exits with `UserNS: cpu
controller needs to be delegated`). Delegated here: memory pids. Run `delonix
system setup` for the diagnosis and `sudo delonix system setup --delegate` for
the fix, then log out and back in.

Só o cpu bloqueia. O cpuset/io faltam em muitos hosts onde o nó arranca
bem, e recusar por causa deles rejeitaria clusters que teriam funcionado.

E o timeout, quando acontece, deixou de ser a mensagem inteira: passa a dizer se
o nó ainda está vivo e a mostrar as últimas linhas do log dele. Era o que faltava
para ligar timeout waiting for containerd a cpu controller needs to be delegated.

vm create não descarregava a imagem oficial

delonix vm create <nome> sem --disk, num host sem imagens VM locais,
respondia:

no local VM images — run delonix image --vm build (or pull) first

O cluster kubeadm já descarregava sozinho nessa mesma situação. A imagem
dourada é publicada como artefacto OCI precisamente para não ter de ser
construída à mão, por isso a mesma imagem em falta era um download prestável num
comando e um beco sem saída no outro.

Fechado com um helper partilhado pelos dois: vm create passa a descarregar
a oficial, e quando o download não é possível o erro lista o que existe em
vez de mandar correr outro comando para descobrir:

error invalid argument: could not download 'ghcr.io/angolardevops/delonix-vm-k8s:9.99':
no such image. Published there: 1.34, 1.35 — pick one with
`delonix vm pull ghcr.io/angolardevops/delonix-vm-k8s:<tag>`, or build your own
with `delonix image --vm build`.

O erro de «várias imagens locais» também passou a nomeá-las. A resposta à
pergunta «qual delas?» tem três palavras e estava a um comando de distância sem
razão nenhuma.

Conformidade CRI: 77 → 79

Três specs fechados, e um deles confirma o que a v0.43.0 publicou como por medir
(o perfil seccomp localhost).

  • ReopenContainerLog era um no-op que devolvia sucesso. O kubelet roda o
    log renomeando o ficheiro e chamando isto; responder «feito» sem fazer nada
    mandava cada linha seguinte para um inode que ninguém voltaria a ler — em
    silêncio, e só para contentores que vivem o suficiente para serem rodados.
    Duas metades: o shim passa a SEGUIR O CAMINHO (compara inodes antes de cada
    lote) e a chamada recria o ficheiro, que é o que o chamador verifica.
  • list_container_stats deitava fora o label_selector. Um filtro que
    devolve mais do que foi pedido é pior que um que dá erro: as linhas a mais
    parecem respostas verdadeiras.
  • ContainerStatus devolvia mounts vazio — lê-se como «este contentor não
    tem volumes», o oposto da verdade para tudo o que um kubelet monta.

O motor corre dentro de um user namespace aninhado

TERCEIRO sítio onde geteuid() foi confundido com «root a sério»:
write_userns_maps mapeava um intervalo de 65536 uids quando euid era 0, o que
num userns aninhado — onde possuímos exactamente um uid — falha com EPERM e
nenhum contentor arranca. Com as correcções irmãs em is_rootless e
runtime_dir, o motor corre inteiro sob unshare --user --map-root-user.

Propagação de mounts medida nesse ambiente, com a origem rshared e uma
montagem feita DEPOIS de o contentor arrancar: rslave e rshared vêem-na,
rprivate não. A via normal mantém o intervalo de subuid intacto (--user 101
continua a funcionar).

Documentação

O README estava na 0.38.0 — cinco releases atrás. Alinhado, com a conformidade
CRI publicada e três linhas novas na tabela comparativa que só valem por serem
honestas. O --help do --security-opt ainda descrevia só o que existia antes
da v0.43.0; como a documentação embebe o help real, a lacuna era no código.
Mais 18 entradas no catálogo pt.po.

Correcção a um teste, não ao motor

iotune_das_vms_e_opt_in_e_so_no_disco_raiz falhava por ordem de escalonamento:
o teste irmão faz env::set_var e os testes correm em paralelo no MESMO
processo, por isso o opt-in lia o que o vizinho tinha acabado de pôr. O
comentário do próprio teste avisava disto enquanto o vizinho o fazia. A
composição do <iotune> passou a função pura e nenhum teste toca no ambiente.

Validação

658 testes verdes, clippy e fmt limpos. Conformidade CRI 79/103 num root
limpo. Os dois bugs deste relatório reproduzidos antes e verificados depois, no
host onde foram encontrados.