Dissecando o estado D no Linux: rastreamento de gargalos de I/O em Kubernetes com eBPF

      Rafael Junqueira 10 min de leitura
      Dissecando o estado D no Linux: rastreamento de gargalos de I/O em Kubernetes com eBPF

      Aprenda como diagnosticar pods travados no Kubernetes devido a gargalos de storage. Use eBPF para rastrear o estado D (Uninterruptible Sleep) e recupere seus SLOs.

      Compartilhar:

      O pager toca às três da manhã de uma terça-feira. O alerta indica uma queima acelerada do orçamento de erro (error budget) devido a picos de latência de cauda no serviço de checkout. Ao abrir os dashboards, a CPU dos nós do Kubernetes está ociosa e a memória tem folga. No entanto, dezenas de pods estão travados no estado Terminating. O instinto de muitos operadores seria culpar a aplicação ou reiniciar o agente do nó. Como Engenheiros de Confiabilidade (SRE), sabemos que o sistema está nos dizendo algo mais profundo. O gargalo não está na computação, mas sim nas profundezas da pilha de armazenamento de dados.

      Resumo em 30 segundos

      • O estado D (TASK_UNINTERRUPTIBLE) indica processos travados aguardando respostas do hardware de storage (NVMe, SAN, iSCSI).
      • Forçar a exclusão de pods no Kubernetes apenas mascara o problema real de saturação nas filas de I/O dos discos.
      • O eBPF permite instrumentar a camada de bloco do kernel para identificar exatamente qual volume persistente está destruindo seu SLO de latência.

      A queima do orçamento de erro por latência de cauda

      Na cultura de SRE, medimos a saúde do sistema através de Indicadores de Nível de Serviço (SLIs). Quando a latência do percentil 99 (p99) ultrapassa nosso limite aceitável, o orçamento de erro começa a queimar. Em arquiteturas baseadas em microsserviços, um único volume persistente (PV) com lentidão pode causar um efeito cascata. Se um banco de dados ou um serviço de mensageria não consegue confirmar a gravação no disco em tempo hábil, as requisições de rede se acumulam.

      O problema de investigar latência de storage em ambientes conteinerizados é a abstração. O Kubernetes provisiona volumes dinamicamente, muitas vezes mascarando a infraestrutura física subjacente. Pode ser um array all-flash saturado na rede de área de armazenamento (SAN), um disco EBS na nuvem atingindo seu limite de IOPS ou um SSD NVMe local lidando com amplificação de gravação excessiva.

      A falha não é do desenvolvedor que escreveu o código. A falha é sistêmica: nossa plataforma carece de observabilidade granular na camada de bloco. Precisamos descer ao nível do kernel para entender onde os milissegundos estão sendo perdidos.

      A mecânica do estado TASK_UNINTERRUPTIBLE e a saturação do storage

      No Linux, o ciclo de vida de um processo passa por vários estados. O estado R significa executando, enquanto o S indica um sono passível de interrupção. O verdadeiro vilão da latência de storage é o estado D, formalmente conhecido como TASK_UNINTERRUPTIBLE. Quando um processo entra neste estado, ele está dizendo ao escalonador do kernel que iniciou uma operação crítica de hardware e não pode ser interrompido por nenhum sinal, nem mesmo um SIGKILL.

      Na esmagadora maioria das vezes, essa operação crítica é uma requisição de entrada e saída (I/O) na camada de bloco. O processo enviou um comando de leitura ou gravação para o sistema de arquivos, que o repassou para a camada de bloco genérica, que por sua vez o colocou na fila do driver do dispositivo de armazenamento (como o driver NVMe ou SCSI). Se o disco físico ou o storage de rede demorar para responder, o processo fica congelado no estado D.

      💡 Dica Pro: Você pode identificar rapidamente processos travados em I/O no seu nó executando o comando ps aux | awk '{if ($8 ~ /D/) print $0}'. Se a lista for longa, seu storage está pedindo socorro.

      Diagrama do fluxo de I/O no kernel Linux mostrando um pod travado no estado D aguardando a fila do NVMe. Figura: Diagrama do fluxo de I/O no kernel Linux mostrando um pod travado no estado D aguardando a fila do NVMe.

      Quando múltiplos pods tentam acessar um volume persistente que está sofrendo de alta latência, as filas de submissão do hardware ficam saturadas. O kernel Linux, projetado para garantir a integridade dos dados, não abortará a operação. Ele esperará pacientemente que a controladora do disco confirme a gravação. Durante essa espera, os recursos do nó são consumidos de forma invisível.

      A ilusão do kubectl delete force e o mascaramento de falhas

      Durante um incidente, a pressão para restaurar o serviço é imensa. É comum ver operadores executando kubectl delete pod <nome> --force --grace-period=0 ao se depararem com pods travados. Em uma cultura de post-mortem sem culpa (blameless), entendemos por que isso acontece: a ferramenta parece resolver o sintoma imediato na interface do cluster. No entanto, essa ação é uma armadilha perigosa quando o problema raiz é o armazenamento.

      Forçar a exclusão de um pod apenas instrui o plano de controle do Kubernetes a remover o registro daquele objeto no banco de dados etcd. O kubelet no nó de trabalho recebe a ordem, mas o processo real do contêiner continua existindo no sistema operacional hospedeiro. Como o processo está no estado D aguardando o disco, o kernel ignora a tentativa de encerramento do kubelet.

      ⚠️ Perigo: O uso indiscriminado do delete force em cenários de gargalo de I/O cria processos zumbis que continuam segurando descritores de arquivos (file descriptors) e conexões com o storage. Isso pode levar à exaustão completa dos recursos do nó, exigindo um hard reset do servidor físico.

      Em vez de mascarar a falha do disco matando pods virtualmente, precisamos diagnosticar a saúde da camada de bloco. É aqui que as ferramentas tradicionais começam a falhar e precisamos de instrumentação moderna.

      Rastreando a latência do bloco de armazenamento com eBPF

      Historicamente, administradores de sistemas dependiam de ferramentas como o iostat para monitorar discos. Embora útil para ver a média de IOPS e a largura de banda, o iostat é cego para a latência de cauda. Ele agrega dados em intervalos de segundos, escondendo micro-explosões de latência que destroem os SLOs de serviços sensíveis. Para resolver isso, a engenharia de confiabilidade moderna adotou o eBPF (Extended Berkeley Packet Filter).

      O eBPF é uma tecnologia revolucionária que permite executar programas em um ambiente de sandbox dentro do kernel do sistema operacional. Ele oferece instrumentação segura e com baixíssimo overhead. Para o ecossistema de storage, isso significa que podemos anexar sondas (probes) exatamente nas funções do kernel que enviam e recebem comandos para os discos SSDs ou arrays de armazenamento.

      Característica Ferramentas Tradicionais (ex: iostat) Instrumentação com eBPF (ex: biolatency)
      Granularidade Agregada por segundos (médias). Por evento de I/O (microssegundos).
      Visibilidade de Cauda Baixa. Esconde picos de latência. Alta. Gera histogramas precisos do p99.
      Identificação de Causa Mostra apenas o disco afetado. Mapeia o PID, o contêiner e o volume exato.
      Overhead no Sistema Baixo. Extremamente baixo (seguro para produção).

      Utilizando a coleção de ferramentas BCC (BPF Compiler Collection), podemos usar utilitários como o biolatency. Esta ferramenta rastreia o tempo exato desde o momento em que a requisição de bloco é emitida para o driver do dispositivo até a sua conclusão (hardware completion). O resultado é um histograma claro que revela se a maior parte do seu I/O está respondendo em microssegundos (típico de um SSD NVMe saudável) ou se há uma cauda longa na casa dos milissegundos.

      Histograma de latência de bloco gerado por eBPF evidenciando a latência de cauda no storage. Figura: Histograma de latência de bloco gerado por eBPF evidenciando a latência de cauda no storage.

      Outra ferramenta inestimável é o biosnoop, que imprime uma linha para cada operação de I/O no disco, detalhando o processo solicitante, o setor do disco e a latência exata. Ao cruzar o PID fornecido pelo biosnoop com os cgroups do Kubernetes, o SRE consegue apontar com precisão cirúrgica qual Persistent Volume Claim (PVC) e qual pod estão sofrendo com o hardware subjacente.

      Validando a recuperação do SLO após o ajuste do storage

      Uma vez que o eBPF isola o gargalo na camada de bloco, a remediação torna-se baseada em dados. O problema pode exigir o ajuste do escalonador de I/O do Linux. Para discos NVMe modernos, o escalonador none ou kyber costuma ser mais eficiente que o antigo mq-deadline, pois evita enfileiramentos desnecessários em software quando o hardware já possui múltiplas filas de submissão nativas.

      Em cenários de nuvem, a análise do eBPF pode provar que o volume de armazenamento em bloco provisionado atingiu seu limite de burst de IOPS. A solução arquitetural passa a ser a migração do banco de dados para uma classe de storage superior (como io2 na AWS ou Ultra Disk no Azure) ou a implementação de um padrão de cache em memória para aliviar a pressão de leitura no disco.

      A validação do sucesso não ocorre quando o alerta é silenciado, mas sim quando o SLI de latência retorna aos níveis normais e o orçamento de erro para de queimar. A cultura SRE exige que o aprendizado do incidente seja incorporado ao sistema. Exportar métricas baseadas em eBPF para o Prometheus garante que o próximo gargalo de storage seja detectado antes que os pods comecem a travar no estado D.

      O imperativo da observabilidade profunda em storage

      A complexidade das infraestruturas de dados continuará crescendo. Com a adoção de tecnologias como CXL (Compute Express Link) para expansão de memória e storage, e a proliferação de drives NVMe PCIe Gen 5, os gargalos mudarão de lugar. O hardware está ficando tão rápido que a própria pilha de software do kernel pode se tornar o fator limitante.

      Depender de métricas de superfície ou de reinicializações arbitrárias de pods é uma estratégia insustentável para a confiabilidade em escala. A adoção do eBPF como padrão para telemetria de armazenamento não é mais um luxo de grandes empresas de tecnologia, mas uma necessidade fundamental para qualquer equipe que opere sistemas de dados críticos em Kubernetes. O sistema sempre nos diz onde está doendo, só precisamos usar as ferramentas certas para escutar a camada de bloco.


      Referências & Leitura Complementar

      • BPF Performance Tools (Brendan Gregg): O guia definitivo sobre o uso de eBPF e BCC para análise de performance de I/O e sistemas de arquivos.

      • NVM Express Base Specification (NVMe.org): Documentação técnica sobre a arquitetura de filas de submissão e conclusão que interagem com o driver de bloco do Linux.

      • Site Reliability Engineering (Google): Capítulos sobre Service Level Objectives (SLOs) e a importância de medir a latência de cauda em sistemas distribuídos.


      O que significa o estado D (Uninterruptible Sleep) no Linux? É um estado do kernel onde o processo aguarda a liberação de um recurso de hardware, quase sempre operações de I/O em discos (HDD, SSD, NVMe) ou storage de rede (NFS, iSCSI). Neste estado, o processo ignora qualquer sinal do sistema, incluindo o SIGKILL, pois interrompê-lo poderia causar corrupção de dados na camada de bloco.
      Como o eBPF ajuda a diagnosticar problemas de storage no Kubernetes? O eBPF permite instrumentar o kernel do Linux de forma segura e com baixíssimo overhead. Ele consegue medir o tempo exato que as requisições de bloco (block I/O) levam na camada de storage, apontando exatamente qual disco físico ou volume persistente está causando a latência de cauda, algo que ferramentas tradicionais como o iostat não conseguem detalhar.
      Por que o comando 'kubectl delete pod --force' não resolve pods travados em Terminating? Forçar a exclusão apenas remove o registro do pod no banco de dados etcd do Kubernetes. O processo subjacente no nó do cluster continua travado no estado D aguardando a resposta do hardware de storage. Essa prática cria processos zumbis, o que pode levar à exaustão de recursos (file descriptors, memória) e causar uma falha em cascata no nó físico.
      #eBPF #Kubernetes #Storage I/O #Linux Kernel #SRE #Performance de Disco
      Rafael Junqueira
      Assinatura Técnica

      Rafael Junqueira

      Engenheiro de Confiabilidade (SRE)

      "Transformo caos em estabilidade via observabilidade. Defensor da cultura blameless e focado em SLIs e SLOs. Se algo falhou, revisamos o sistema, nunca a pessoa."