v0.43.1 — o nó do cluster morria por delegação de cgroup, e ninguém dizia porquê
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(orpull) 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).
ReopenContainerLogera 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_statsdeitava fora olabel_selector. Um filtro que
devolve mais do que foi pedido é pior que um que dá erro: as linhas a mais
parecem respostas verdadeiras.ContainerStatusdevolviamountsvazio — 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.