TL;DR: o ephemeral-storage allocatable do kubelet é calculado a partir do filesystem que sustenta seu root-dir (padrão /var/lib/kubelet), não do --data-dir do k3s; neste cluster o root-dir ficava numa partição /var de 22G enquanto os dados reais viviam numa partição /srv de 838G, então o kubelet reportava ~20.5GiB de ephemeral storage disponível, disparava o threshold de eviction sob o churn normal de pods/logs/emptyDir, e evictava o Jellyfin — que usava 1.1MB — simplesmente por ter o ranking mais alto entre pods sem request. Mudar --kubelet-arg root-dir “corrige” o número mas quebra o CSI do Longhorn e o socket do device-plugin de GPU, ambos com paths fixos sob /var/lib/kubelet; a correção que funciona é montar (bind mount) o disco grande direto no path convencional /var/lib/kubelet (a mesma abordagem que a EKS usa por padrão) depois de um reboot completo do nó pra garantir zero mounts vivos por baixo, repetido um nó por vez.
Mensagens de eviction do Kubernetes dizem qual recurso cruzou um threshold e qual pod morreu por isso — nunca dizem que o threshold em si foi calculado contra o filesystem errado. Quando o root-dir do kubelet e o disco de dados real divergem, todo consumidor de ephemeral-storage allocatable fica silenciosamente errado, e o pod evictado quase nunca é o que causa a pressão. A correção segura não é a flag anunciada exatamente pra esse propósito — é um bind mount no path que essa flag deveria substituir.
O Jellyfin usava 1.1 megabyte de disco. O kubelet evictou mesmo assim, sem pestanejar: The node was low on resource: ephemeral-storage. Threshold quantity: 1160345207, available: 714168Ki. Container jellyfin was using 1112Ki.... Lida sozinha, essa mensagem manda você procurar vazamento de disco no cache de transcode do Jellyfin. Não tinha vazamento nenhum — o Jellyfin era a menor coisa no nó. Ele foi escolhido porque o Kubernetes eviction ordena pods por “o quanto você passou do seu request”, e um pod sem request nenhum perde essa disputa por definição no instante em que qualquer pressão existe.
Este post é pra quem roda k3s (ou kubeadm) com data directory fora do filesystem do /var, especialmente junto com Longhorn ou qualquer outro CSI driver. Pressupõe que você já sabe o que é node-pressure eviction e como o df -h mente sobre a coisa que realmente importa.
Esse é o terceiro modo de falha ligado a storage encontrado no mesmo cluster; veja também Travamento de Nó k3s: Stalls de iSCSI do Longhorn e Pressão de Memória e Mounts SMB no Kubernetes Vão Travar Seu Nó pros outros dois — as três são causas-raiz sem relação entre si que só coincidem de estar no mesmo nó.
Sumário
- O sintoma: pods Evicted num nó com disco livre em todo lugar
- O sinal real, enterrado sob uma pista falsa
- Causa raiz: root-dir e data-dir apontando pra discos diferentes
- A correção que quebra tudo: mover root-dir
- O quase-desastre: cp -a andando por dentro de mounts CSI vivos
- A correção que funciona: bind mount do disco grande no path convencional
- Procedimento por nó
- Consequências e o que esperar do Longhorn nesse meio tempo
- Quando isso não se aplica
Por que o Jellyfin estava sendo evictado num nó com disco livre em todo lugar?
Resumo: o Jellyfin ficava ciclando entre
EvictedeError, e okubectl describe podapontavaDiskPressure— mas odf -hno nó mostrava nenhum filesystem nem perto de cheio: partição raiz 34% usada,/var17%,/srv28%. Essa incompatibilidade é a armadilha inteira.DiskPressureé uma condição derivada do Kubernetes, calculada a partir da contabilidade interna de allocatable/requested deephemeral-storagedo kubelet, não de porcentagens crua de disco cheio, então umdf -hsaudável não diz nada sobre se existe pressão de eviction. A primeira teoria, razoável à primeira vista — que oemptyDirdo cache de transcode do Jellyfin estava enchendo o nó — estava errada, porque nada no nó estava de fato enchendo. O problema real vivia uma camada abaixo do que odf -hconsegue ver: em como o kubelet tinha calculado a capacidade total deephemeral-storagedisponível pra ele, um número baseado no disco errado.
kubectl get pods -n media neste cluster k3s de homelab (três nós control-plane/etcd: node A, node B, node C) mostrava o Jellyfin ciclando repetidamente entre Evicted e Error. kubectl describe pod dava a linha padrão de eviction:
Warning Evicted kubelet The node had condition: [DiskPressure].
df -h no node B dizia que nenhum filesystem estava sob pressão real: partição raiz 34% usada, /var 17% usada, /srv 28% usada. Essa é a armadilha. DiskPressure e as porcentagens do df -h descrevem dois sistemas de contabilidade diferentes, e a primeira investigação — “o emptyDir do cache de transcode do Jellyfin está enchendo o nó” — era razoável e errada. Percentual de ocupação numa partição não diz nada sobre a contabilidade interna de allocatable/requested/limit do kubelet pra ephemeral-storage, que é o que de fato dirige node-pressure eviction. Veja a documentação de node-pressure eviction pra lista completa de sinais que o kubelet observa — DiskPressure é uma condição derivada, não uma checagem crua de disco cheio.
Qual era o sinal real, enterrado sob a pista falsa?
Resumo: a mensagem de eviction trazia os números reais, fáceis de passar batido: threshold quantity 1160345207 bytes (~1.16GB), available 714168Ki (~697MB), e o uso do próprio Jellyfin de 1112Ki — um erro de arredondamento perto de qualquer um dos dois. A eviction nunca foi sobre o consumo de disco do Jellyfin; era sobre o
ephemeral-storageallocatable total do nó ser pequeno o bastante pra que o churn normal — logs, emptyDirs, contabilidade interna do kubelet — empurrasse o disponível abaixo do threshold de ~1.16GB, forçando o kubelet a matar alguém. O Kubernetes ordena candidatos a eviction por uso-acima-do-request, e um pod com request zero de storage fica em primeiro lugar no instante em que qualquer pressão existe, porque “o quanto você passou do seu request” é indefinido-mas-máximo em zero. O Jellyfin foi escolhido por ser descartável e não pedir nada, não por usar disco de forma relevante — inocente e descartável, nessa ordem.
Um evento de eviction posterior trouxe o número que importava:
Warning Evicted kubelet The node was low on resource: ephemeral-storage.
Threshold quantity: 1160345207, available: 714168Ki. Container jellyfin was using 1112Ki...
1160345207 bytes é ~1.16GB — o threshold de eviction. 714168Ki é ~697MB — o que o kubelet achava que ainda estava disponível. O uso do próprio Jellyfin, 1112Ki, é um erro de arredondamento perto de qualquer um dos dois números. A eviction não era sobre o Jellyfin. Era sobre o ephemeral-storage allocatable total do nó ser pequeno o bastante pra que o churn normal de pods — logs, emptyDirs, contabilidade interna do kubelet — empurrasse o disponível abaixo de um threshold de ~1.16GB, e o kubelet precisava matar alguém. O ranking é por uso-acima-do-request; um pod com request zero e uso quase zero ainda fica em primeiro lugar no instante em que o nó entra em pressão, porque “o quanto você passou do seu request” é indefinido-mas-máximo com request zero. O Jellyfin era inocente e descartável, nessa ordem.
Causa raiz: root-dir e data-dir apontando pra discos diferentes
Resumo: o kubelet calcula o
ephemeral-storageallocatable a partir do filesystem que sustenta seu diretório de trabalhoroot-dir(padrão/var/lib/kubelet), de forma totalmente independente da flag--data-dirdo k3s. Neste cluster, oroot-dirficava numa partição/varde 22G enquanto--data-dir /srv/k3stinha movido o estado real do k3s — etcd, manifests, imagens — pra uma partição/srvde 838G. Okubectl get node ... allocatable.ephemeral-storageretornava 22046558601 bytes (~20.5GiB), suspeitosamente perto do tamanho da partição pequena/var, porque era exatamente isso que estava sendo medido. Ninguém tinha movido oroot-dirpra acompanhar o--data-dir, então o kubelet continuava calculando capacidade contra uma partição que nunca foi instruído a abandonar, enquanto o disco com os dados reais ficava totalmente fora do radar do eviction manager. Os três nós compartilhavam o mesmo particionamento e o mesmo descuido — isso estava embutido no bootstrap dos nós, não era um erro isolado numa única máquina.
kubectl get node node-b -o jsonpath='{.status.allocatable.ephemeral-storage}'
# 22046558601 (~20.5GiB)
20.5GiB é suspeitosamente perto do tamanho de /dev/nvme0n1p3, a partição /var de 22G. Não é coincidência — é o bug inteiro. O kubelet calcula o ephemeral-storage allocatable a partir do filesystem que sustenta seu diretório de trabalho, o root-dir, documentado como uma flag --root-dir simples com padrão /var/lib/kubelet na referência de CLI do kubelet. O k3s estava configurado com --data-dir /srv/k3s, o que move o estado próprio do k3s (dados do etcd, manifests, imagens de container) pra partição de 838G /dev/nvme0n1p5 — mas --data-dir e o root-dir do kubelet são knobs independentes. Ninguém tinha movido o root-dir. O kubelet continuava calculando capacidade contra a partição pequena que nunca foi instruído a abandonar, enquanto todos os dados que o k3s realmente usava ficavam num disco que o eviction manager do kubelet não sabia que existia.
Os três nós (node A, node B, node C) tinham particionamento idêntico e o mesmo descuido idêntico — isso não era específico de um nó, estava embutido no processo de bootstrap dos nós desde o dia zero.
A correção que quebra tudo: mover root-dir
Resumo:
--kubelet-arg=root-dir=/srv/k3s/agent/kubelet— a flag que o k3s expõe especificamente pra isso — funcionou exatamente como anunciado: oephemeral-storageallocatable pulou pra ~796GiB. Também quebrou o CSI do Longhorn em minutos, porque olonghorn-csi-pluginfixa hostPath mounts e um--kubelet-registration-pathapontando pra/var/lib/kubelet/plugins/driver.longhorn.io/..., e o socket de registro do Longhorn nunca apareceu onde o kubelet passou a esperar. Todo pod novo que precisasse anexar um volume Longhorn falhava indefinidamente, o que derrubou o Postgres de um serviço Git self-hosted, depois o próprio serviço Git, depois dois serviços internos de build/ETL que puxavam imagens do registro desse serviço — uma flag mal aplicada, três serviços não relacionados fora do ar. Isso não é específico do Longhorn: quase todo CSI driver ou fixa/var/lib/kubeletou exige reconfiguração separada pra seguir umroot-dirmovido, e o diretório de socket do device-plugin do próprio kubelet não respeitaroot-direm nenhuma configuração — o que também teria quebrado o device plugin de GPU do qual o Jellyfin depende pra transcode por hardware.
Aplicando a flag: funciona, e o Longhorn quebra em minutos
A correção óbvia, e a que o k3s expõe explicitamente uma flag pra fazer, é --kubelet-arg=root-dir=/srv/k3s/agent/kubelet (conforme a referência de CLI do servidor k3s). Aplicada e reiniciada, funcionou exatamente como anunciado: o ephemeral-storage allocatable pulou pra ~796GiB, refletindo corretamente o /srv.
Também quebrou o CSI do Longhorn em minutos. O DaemonSet longhorn-csi-plugin do Longhorn fixa hostPath mounts e uma flag --kubelet-registration-path apontando pra /var/lib/kubelet/plugins/driver.longhorn.io/.... Com o diretório de trabalho real do kubelet agora em outro lugar, o socket de registro do Longhorn nunca apareceu onde o kubelet esperava, e todo pod novo que precisasse anexar um volume Longhorn falhava indefinidamente:
MountVolume.MountDevice failed ... driver name driver.longhorn.io not found
Isso derrubou o Postgres de um serviço Git self-hosted (PVC do Longhorn), que cascateou pro próprio serviço Git (sem banco de dados), que cascateou pra dois serviços internos de build/ETL que puxavam imagens do registro desse mesmo serviço Git — fora do ar, porque o serviço por trás do registro estava fora do ar. Uma flag mal aplicada, três serviços não relacionados fora do ar.
Isso é um bug específico do Longhorn?
Isso não é um bug específico do Longhorn — é o formato geral do problema de mover root-dir. Quase todo CSI driver ou fixa /var/lib/kubelet ou exige uma reconfiguração explícita e separada pra seguir o path movido: o Longhorn expõe um valor csi.kubeletRootDir no chart Helm, o csi-driver-smb expõe um valor linux.kubelet na documentação de instalação. Pior: o diretório de socket do device-plugin do próprio kubelet (/var/lib/kubelet/device-plugins/) é fixo e não respeita root-dir em nenhuma configuração — o que também teria quebrado o device plugin de GPU Intel do qual o Jellyfin depende pra transcode por hardware, além do Longhorn.
O quase-desastre: cp -a andando por dentro de mounts CSI vivos
Resumo: durante a reversão da mudança de
root-dir, ummv /var/lib/kubelet /srv/k3s/agent/kubelet-datafoi rodado num nó vivo — ummvcross-filesystem, que degrada pra cópia recursiva mais remoção, e uma cópia recursiva simples não para em fronteiras de mount. Ela entra dentro do que está montado na origem e copia o conteúdo vivo por baixo, e o/var/lib/kubelettinha cerca de 28 mounts vivos: volumes tmpfs por pod, Secrets montados, e — o crítico — os dispositivos de bloco ext4 do Longhorn e um share SMB da biblioteca de mídia do Jellyfin. Ocp -aduplicou conteúdo de volume Longhorn vivo antes de umset -eposterior abortar o processo, com 22G já copiados. Nenhum dado foi perdido — os mounts originais continuaram intactos — mas a cópia de 22G teve que ser identificada como lixo e descartada. A regra: nuncamvoucp -aum diretório com mounts vivos por dentro; parar tudo que o usa primeiro, completamente, não sósystemctl stop.
O mv que virou uma cópia de mounts vivos
Durante a reversão da mudança de root-dir, uma movimentação de diretório foi tentada no node C com pods ainda rodando: mv /var/lib/kubelet /srv/k3s/agent/kubelet-data. /var é nvme0n1p3; /srv é nvme0n1p5. Um mv cross-filesystem não é um rename — é uma cópia recursiva seguida da remoção da origem, e uma cópia recursiva simples não para em fronteiras de mount. Ela entra dentro de qualquer coisa montada dentro do diretório de origem e copia o conteúdo vivo por baixo, não o ponto de mount vazio.
/var/lib/kubelet tinha cerca de 28 mounts vivos por baixo dele na época:
| Mount | Tipo | O que era |
|---|---|---|
/var/lib/kubelet/pods/<uid>/volumes/kubernetes.io~projected/kube-api-access-* | tmpfs | volumes de token de service account por pod, um por pod rodando |
/var/lib/kubelet/pods/<uid>/volumes/kubernetes.io~secret/* | tmpfs | Secrets do Kubernetes montados (ex: longhorn-grpc-tls, memberlist) |
/var/lib/kubelet/plugins/kubernetes.io/csi/driver.longhorn.io/<hash>/globalmount | ext4 em /dev/longhorn/pvc-* | dispositivo de bloco por volume do Longhorn, um por PVC anexado ao nó |
/var/lib/kubelet/pods/<uid>/volumes/kubernetes.io~csi/pvc-*/mount | ext4 no mesmo /dev/longhorn/pvc-* | o mesmo volume Longhorn, bind-montado de novo dentro do diretório do pod |
/var/lib/kubelet/plugins/.../smb.csi.k8s.io/<hash>/globalmount e o mount de pod correspondente | cifs em //nas.lan/media-library | o share SMB de 1000Gi media-library-pvc usado pelo Jellyfin |
O cp -a foi direto nos mounts de dispositivo ext4 do Longhorn — backing stores reais de várias PVCs, não diretórios vazios — e começou a duplicar conteúdo de volume vivo. O script abortou (set -e disparou num passo posterior) depois que 22G já tinham sido copiados pra /srv/k3s/agent/kubelet-data, consistente com algumas das PVCs -config menores terem sido copiadas por completo antes do abort. O share SMB de 1000Gi ou ainda não tinha sido alcançado, ou o CIFS recusou a cópia em massa no meio do caminho. Checar du -xsh no /var/lib/kubelet original depois confirmou que o conteúdo real (222M, 42 diretórios de pod) e todos os mounts continuavam intactos e intocados — nenhum dado foi perdido — mas os 22G no destino tiveram que ser identificados como lixo copiado de PVCs vivas e descartados, não confundidos com um backup legítimo.
Em uma linha: nunca faça mv ou cp -a de um diretório que pode ter mounts vivos por dentro. Pare tudo que usa aquele diretório — completamente, não só systemctl stop — antes de tocar nele. A própria documentação do mv do coreutils é explícita que um move cross-device degrada pra copy-then-remove; ela não diz nada sobre fronteiras de mount porque essa é uma questão de semântica de filesystem que cp/mv nunca foram feitos pra resolver.
Por que o systemctl start não reaplicou a config revertida?
Um segundo erro, menor, apareceu durante o mesmo revert: systemctl start k3s foi rodado numa unit já ativa pra aplicar a config revertida. start numa unit que já está rodando é um no-op — não recarrega nada — então a config quebrada de root-dir continuou rodando silenciosamente em dois dos três nós até o erro ser percebido e systemctl restart ser usado no lugar.
A correção que funciona: bind mount do disco grande no path convencional
Resumo: a flag feita especificamente pra resolver isso — mover o
root-dir— é a ferramenta errada, porque metade do ecossistema CSI fixa exatamente o path que ela deveria substituir. A correção que de fato funciona é deixar oroot-dirno padrão/var/lib/kubelete mudar o que está fisicamente montado ali: bind mount do disco de 838G direto nesse path convencional. É a mesma abordagem que a EKS usa por padrão em seus nós. Nada do que Longhorn, csi-driver-smb ou o device plugin de GPU esperam muda — o path continua/var/lib/kubeletpra todos eles — só o dispositivo de bloco por baixo muda. É nisso que o incidente inteiro se resume: não mover o path que todo consumidor fixo assume, e sim mover o que está por baixo dele, deixando a indireção no nível de filesystem fazer o trabalho que uma flag de configuração não consegue fazer com segurança.
A flag que existe especificamente pra resolver esse problema é a ferramenta errada, porque metade do ecossistema fixa o path que ela deveria substituir. A correção que de fato funciona é deixar o root-dir no padrão e mudar o que está fisicamente montado ali: bind mount do disco de 838G direto em /var/lib/kubelet. É a mesma abordagem que a EKS usa por padrão em seus nós — o path que kubelet, Longhorn, csi-driver-smb e o device plugin de GPU esperam nunca muda. Só o dispositivo de bloco por baixo muda.
Procedimento por nó
Resumo: o procedimento seguro é desabilitar, depois reiniciar o nó, depois verificar que está vazio antes de tocar em qualquer coisa, repetido um nó por vez.
systemctl disable --now k3s(desabilitado, não só parado, pra não voltar a subir sozinho), depois um reboot completo — o passo que fecha a brecha exposta pelo quase-desastre anterior, porquesystemctl stopsozinho deixa processos containerd-shim órfãos rodando com os mounts de volume deles junto. Depois do reboot,mount | grep /var/lib/kubeletprecisa retornar vazio antes de qualquer outra coisa — o gate de segurança que faltou no incidente anterior. Depois, copiar (nunca mover) o diretório pequeno existente pra outro lugar, bind montar o disco grande no path convencional via/etc/fstab, reiniciar o k3s, e confirmar que oephemeral-storageallocatable já reflete a partição grande antes de force-deletar qualquer pod preso emUnknowne esperar todo volume Longhorn daquele nó voltar ahealthyantes de começar o próximo.
Repetido no node C, depois node A, depois node B — um nó por vez, totalmente verificado antes de começar o próximo:
systemctl disable --now k3s— desabilitado, não só parado, pra não voltar a subir no meio do procedimento.reboot. Esse é o passo que fecha a brecha exposta pelo quase-desastre anterior:systemctl stop k3ssozinho deixa processos containerd-shim rodando como órfãos (shims são feitos pra sobreviver à morte do processo pai), e os mounts de volume deles sobrevivem junto. Um reboot completo derruba tudo de forma limpa, garantido.- Depois do reboot, antes de tocar em qualquer coisa:
mount | grep /var/lib/kubeletprecisa retornar vazio. Esse é o gate de segurança que faltou no incidente anterior. - Copiar (não mover) o
/var/lib/kubeletpequeno existente pra/srv/k3s/agent/kubelet-root, verificar que tamanho e contagem de pods batem com o original, depoisrm -rf /var/lib/kubelet && mkdir -p /var/lib/kubelet. - Adicionar o bind mount no
/etc/fstabpra sobreviver a reboots futuros:
depois/srv/k3s/agent/kubelet-root /var/lib/kubelet none bind 0 0mount /var/lib/kubelet. systemctl enable --now k3s.- Esperar
Ready, então confirmar que oephemeral-storageallocatable agora reflete a partição grande (~796GiB — output esperado854501760354bytes). - Force-delete de qualquer pod preso em
Unknown(objetos de pod órfãos do reboot abrupto) pra que os controllers os recriem do zero. - Observar o
robustnessdos volumes Longhorn até todos voltarem ahealthynaquele nó antes de começar o próximo.
Resultado nos três nós: Ready, ephemeral-storage allocatable 854501760354 bytes (~796GiB), bind mount persistido no /etc/fstab, todo pod Running, todo volume Longhorn healthy. Zero mudanças na config do Longhorn, csi-driver-smb ou device plugin de GPU — porque do ponto de vista deles, nada mudou de lugar.
Consequências e o que esperar do Longhorn nesse meio tempo
Resumo: duas coisas parecem alarmantes durante essa recuperação e não são. Um “disks are unavailable” ou
ReplicaSchedulingFailuretransitório no Longhorn logo após o reboot de um nó é esperado — checar sestatus.diskStatusvolta praReady/Schedulablelogo depois e seguir em frente. E como o Longhorn serializa o rebuild de réplica por padrão (limiteconcurrent-rebuildde 1), a contagem de volumes degradados oscila em vez de cair de forma monotônica enquanto uma fila de volumes é reconstruída um por vez — só preocupar se travar por completo. Um achado não relacionado apareceu e foi deliberadamente deixado de lado: o Longhorn manager estava nav1.12.0enquanto a maioria dos volumes ainda referenciava engine imagesv1.11.0/v1.11.1. Um upgrade de engine ao vivo no meio do incidente, com volumes ainda se recuperando de três reboots de nó, teria tornado impossível diagnosticar corretamente qualquer sintoma novo — esse upgrade é uma tarefa separada, feita só depois que tudo estiver confirmadohealthyde forma independente.
Duas coisas parecem alarmantes e não são:
Um aviso de “disks are unavailable” depois do reboot é uma regressão?
Um “disks are unavailable” ou ReplicaSchedulingFailure transitório no Longhorn logo após um reboot de nó é esperado, não uma regressão — checar se status.diskStatus em kubectl get nodes.longhorn.io <node> -o yaml volta pra Ready/Schedulable logo depois, e seguir em frente.
Por que a contagem de volumes degradados oscila durante o rebuild?
O rebuild de réplica do Longhorn é serializado por padrão (limite concurrent-rebuild de 1), então a contagem de volumes degradados oscila em vez de cair monotonicamente enquanto uma fila de volumes é reconstruída um por vez. Ver ela cair pra zero e depois subir brevemente no meio de um rebuild é normal; só preocupar se travar por completo.
Um achado não relacionado, deixado de propósito de lado: descompasso de versão do engine
Um achado não relacionado apareceu durante esse incidente e foi deliberadamente deixado de lado: o Longhorn manager estava na v1.12.0, mas a maioria dos volumes ainda referenciava engine images v1.11.0/v1.11.1. Um upgrade de engine ao vivo — por volume, ou via concurrent-automatic-engine-upgrade-per-node-limit — é uma tarefa separada, e fazer isso no meio do incidente, com volumes ainda se recuperando de três reboots de nó, teria tornado impossível diagnosticar corretamente qualquer sintoma novo. Fazer isso só depois que tudo estiver confirmado healthy de forma independente.
Quando isso não se aplica?
Resumo: nada disso se aplica se o seu data directory e o
root-dirdo kubelet já compartilham o mesmo filesystem — o caso comum de quem não separou os dois deliberadamente no bootstrap — porque oephemeral-storageallocatable já reflete a capacidade real nesse cenário. E se você não roda nenhum CSI driver com paths fixos em/var/lib/kubelet— sem persistent volumes, ou um CSI driver que parametriza totalmente o path do kubelet e você já atualizou isso — mover oroot-dirdireto é mais simples que um bind mount e não tem desvantagem. A abordagem de bind mount existe especificamente pra interseção de duas condições:root-dire o disco de dados divergiram, e pelo menos um CSI driver assume o path padrão. Checar as duas antes de usar essa abordagem; se só uma for verdadeira, uma correção mais simples se aplica.
Se o seu data directory e o root-dir do kubelet já vivem no mesmo filesystem — o caso comum de quem não separou os dois deliberadamente no bootstrap — nada disso se aplica, e o ephemeral-storage allocatable já reflete a capacidade real. E se você não roda nenhum CSI driver com paths fixos em /var/lib/kubelet (sem persistent volumes, ou um CSI driver que parametriza totalmente o path do kubelet e você já atualizou isso), mover o root-dir direto é mais simples que um bind mount e não tem desvantagem. A abordagem de bind mount existe especificamente pra interseção de “root-dir e disco de dados divergiram” e “pelo menos um CSI driver assume o path padrão” — checar os dois antes de usar essa abordagem.
Referências
- Node-pressure eviction do Kubernetes — define como os thresholds de
DiskPressureeephemeral-storagefuncionam de fato, e por que o ranking por uso-acima-do-request escolhe o pod que escolhe. - Referência de CLI do kubelet — confirma que
root-dirtem padrão/var/lib/kubelete é a origem da contabilidade de capacidade de ephemeral-storage. - Referência de CLI do servidor k3s — documenta
--kubelet-argcomo o mecanismo usado (e revertido) pra moverroot-dir. - Documentação de instalação Helm do Longhorn — origem do valor
csi.kubeletRootDirque seria necessário se oroot-dirfosse movido em vez de bind-montado. - Documentação de instalação do csi-driver-smb — mesma classe de problema pro CSI driver de SMB, via o valor
linux.kubelet. - Manual do
mvdo GNU coreutils — confirma quemvcross-filesystem degrada pra copy-then-delete, o mecanismo por trás do quase-desastre.