Quando dezenas de sites em um servidor começam a apresentar lentidão simultânea e o webmail passa a demorar minutos para carregar, o primeiro instinto costuma ser culpar ataques de rede (DDoS) ou picos anômalos de acessos. No entanto, em ambientes que combinam cPanel e CloudLinux, os sintomas visíveis quase sempre são apenas o estágio final de um efeito dominó que começou muito mais fundo.
Recentemente acompanhei um diagnóstico clássico onde múltiplos indicadores pareciam se contradizer: o processador estava ocioso, nenhum usuário estourava limites de recursos e as conexões do banco de dados estavam empilhadas em espera.
Abaixo está o roteiro forense passo a passo que desvendou a verdadeira causa raiz: desde a triagem do CloudLinux até a análise da latência de escrita em nível de bloco nos discos NVMe e o alívio do I/O através da memória RAM.
1. O alerta inicial e o descarte de limites individuais no CloudLinux#
O problema começou com clientes reportando timeouts intermitentes em sites WordPress e lentidão extrema ao tentar autenticar no Roundcube.
Em servidores com CloudLinux, a primeira verificação obrigatória é conferir se alguma conta específica está monopolizando a máquina e atingindo seu teto de processos de entrada (Entry Processes - EP) ou CPU (SPEED). O comando lveinfo (ou top-lve) mostra a fotografia em tempo real:
ID EP PNO TNO SPEED MEM IO IOPS
cliente1 0 1 1 0% 8 0 0
cliente2 0 1 1 0% 7 0 0
cliente3 0 2 2 0% 18 0 0
portal4 2 5 5 0% 14 0 0
loja5 1 3 3 0% 11 3 0
site6 3 7 7 2% 17 2 0
Todas as contas estavam com consumo de CPU praticamente nulo e a contagem de processos de entrada estava muito distante dos limites configurados (geralmente entre 20 e 40).
Nenhum usuário isolado estava sendo estrangulado pelo LVE. O problema era generalizado e afetava o servidor inteiro.
2. A avalanche de conexões em sleep no MySQL#
Quando a lentidão é coletiva, o motor de banco de dados costuma ser o suspeito número um. Ao verificar os processos ativos no MariaDB:
mysqladmin proc status
O terminal retornou uma lista com centenas de conexões travadas:
+---------+-----------------------+-----------+-----------------------+---------+------+----------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+---------+-----------------------+-----------+-----------------------+---------+------+----------+------------------+
| 2319092 | app_usuario1 | localhost | app_banco1 | Sleep | 16 | | |
| 2319246 | app_usuario2 | localhost | app_banco2 | Sleep | 29 | | |
| 2319268 | app_usuario3 | localhost | app_banco3 | Sleep | 27 | | |
| 2319394 | portal_blog | localhost | portal_blog | Sleep | 18 | | |
| 2319413 | loja_db | localhost | loja_db | Sleep | 17 | | |
| 2319681 | root | localhost | | Query | 0 | starting | show processlist |
+---------+-----------------------+-----------+-----------------------+---------+------+----------+------------------+
Uptime: 51430 Threads: 144 Questions: 20479295 Slow queries: 266 Opens: 182740 Open tables: 2000 Queries per second avg: 398.197
O que essa saída revela:#
- Quase tudo em
Sleep: O estadoSleepsignifica que a consulta SQL já terminou de ser executada pelo motor do banco. No entanto, o processo PHP que abriu a conexão ainda não a encerrou porque estava aguardando outros recursos (como I/O de disco ou buffers de rede do servidor web). - Cache de tabelas no teto: O valor
Open tables: 2000estava rigidamente cravado no limite máximo definido na configuração (table_open_cache = 2000). - Alto volume de requisições: Uma média de quase 400 consultas por segundo (QPS) operava sobre o motor.
O MySQL estava sendo bombardeado e acumulando filas, mas ele era apenas a primeira vítima visível do efeito dominó.
3. Investigando CPU e tráfego de rede#
Ao checar o uso geral de processamento com o top, o consumo de CPU estava relativamente baixo (mais de 90% ocioso), com apenas processos de auditoria e workers do Apache no topo:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
2993897 nobody 20 0 2123948 387044 18404 S 28.6 0.3 1:05.26 /usr/sbin/httpd -k start
2086 root 20 0 4204892 127552 17380 S 14.3 0.1 26:44.25 /usr/sbin/imunify-auditd-log-reader
2994423 nobody 20 0 2123952 376868 18368 S 14.3 0.3 1:09.60 /usr/sbin/httpd -k start
Para verificar se algum IP estava tentando saturar a porta 80 com requisições concorrentes:
netstat -anp | grep :80 | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -n | tail -10
O resultado mostrou que as conexões estavam amplamente distribuídas entre visitantes normais, com nenhum endereço isolado sustentando mais de 10 conexões simultâneas. Ataques externos de camada 7 estavam descartados.
4. O gargalo real: latência assimétrica no espelhamento RAID NVMe#
Se o processador está livre, a rede está calma e o banco está acumulando conexões esperando resposta, o próximo ponto de inspeção obrigatório é o subsistema de armazenamento.
Ao executar o utilitário iostat com intervalo de amostragem detalhado:
avg-cpu: %user %nice %system %iowait %steal %idle
1.39 0.00 0.78 0.07 0.00 97.76
Device r/s rkB/s rrqm/s %rrqm r_await rareq-sz w/s wkB/s wrqm/s %wrqm w_await wareq-sz f_await aqu-sz %util
md127 0.00 0.00 0.00 0.00 0.00 0.00 41.00 776.50 0.00 0.00 9.68 18.94 0.00 0.40 14.00
nvme0n1 0.00 0.00 0.00 0.00 0.00 0.00 73.00 735.00 0.00 0.00 0.29 10.07 0.31 0.03 1.20
nvme1n1 0.00 0.00 0.00 0.00 0.00 0.00 76.00 807.00 0.00 0.00 9.16 10.62 8.70 0.98 25.50
A discrepância técnica entre os discos:#
O servidor utilizava um arranjo de espelhamento RAID 1 por software (md127) composto por dois discos NVMe (nvme0n1 e nvme1n1).
Repare na coluna w_await (tempo médio de espera para gravação de blocos):
- O disco
nvme0n1completava as gravações em rápidos 0.29 ms, com utilização de apenas 1.2%. - O disco
nvme1n1demorava absurdos 9.16 ms (mais de 30 vezes mais lento!) e ficava com 25.5% de utilização gravando míseros 800 KB/s.
Em um arranjo RAID 1 (espelhamento síncrono), o sistema operacional só considera a gravação de um bloco finalizada quando ambos os discos confirmam a operação. O servidor inteiro estava engasgando porque a velocidade máxima de I/O da máquina ficava limitada ao disco mais lento.
Inspecionando o modelo do disco problemático#
Ao rodar o smartctl sobre a unidade nvme1n1:
smartctl -a /dev/nvme1n1
O relatório exibiu:
Model Number: KINGSTON SNV3S4000G
Total NVM Capacity: 4,000,787,030,016 [4.00 TB]
SMART overall-health self-assessment test result: PASSED
Percentage Used: 2%
A integridade SMART estava perfeita e o disco tinha apenas 2% de vida útil consumida. O problema não era defeito de hardware, mas sim de arquitetura de componente:
O modelo Kingston NV3 é um SSD projetado para uso doméstico. Ele utiliza memórias do tipo QLC e arquitetura sem chip de memória dedicado (DRAMless), dependendo exclusivamente de um pequeno cache SLC dinâmico.
Quando submetido à carga de um servidor cPanel corporativo (com dezenas de bancos MySQL gravando logs transacionais e milhares de arquivos de sessão sendo criados por segundo (I/O randômico agressivo)), o cache SLC da unidade se esgota rapidamente. A velocidade de gravação cai para níveis inferiores aos de um disco mecânico antigo, gerando picos brutais de latência.
5. Contenção imediata: usando a memória RAM como escudo contra o disco#
A troca física do disco por uma unidade NVMe corporativa com DRAM dedicada (como linhas Enterprise com memória TLC) exige janela programada de manutenção em datacenter. Para normalizar a máquina em produção de imediato, a saída foi blindar o subsistema de I/O usando a memória RAM.
Verificando a memória disponível:
free -h
total used free shared buff/cache available
Mem: 125Gi 26Gi 18Gi 407Mi 81Gi 98Gi
Swap: 15Gi 0B 15Gi
O servidor contava com massivos 125 GB de memória RAM, dos quais 98 GB estavam completamente livres.
Ao checar o arquivo de configuração /etc/my.cnf que estava em vigor:
[mysqld]
innodb_buffer_pool_size = 4294967296
max_connections = 500
wait_timeout = 60
O MySQL estava configurado para alocar apenas 4 GB (4294967296 bytes) de buffer pool. Isso forçava o motor a ler e gravar constantemente nos NVMes lentos, mesmo com quase 100 GB de memória sobrando no sistema.
Aplicando a otimização no /etc/my.cnf:#
Multipliquei o buffer pool por 8 e aumentei o cache de descritores para manter todas as tabelas em memória:
[client-server]
!includedir /etc/my.cnf.d
[mysqld]
# --- OTIMIZAÇÃO DE MEMÓRIA (Bypass do Disco) ---
# Expandindo o Buffer Pool para manter bases ativas na RAM
innodb_buffer_pool_size = 32G
table_open_cache = 10000
table_definition_cache = 10000
# --- MITIGAÇÃO DE LATÊNCIA DE I/O ---
# Reduz a frequência de fsync síncrono para o disco em transações corriqueiras
innodb_flush_log_at_trx_commit = 2
# --- CAPACIDADE DE CONEXÕES ---
max_allowed_packet = 268435456
open_files_limit = 69000
max_connections = 1000
wait_timeout = 60
interactive_timeout = 60
long_query_time = 5
innodb_file_per_table = 1
sql_mode = ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
Ao reiniciar o MariaDB (systemctl restart mariadb), o banco passou a responder quase 100% das leituras direto da memória RAM, aliviando de imediato a pressão sobre o array NVMe.
6. O gargalo no Apache: broken pipe e travas no splitlogs#
Embora as consultas SQL tenham acelerado brutalmente, o log de erros do Apache ainda acusava falhas:
[log_config:warn] [pid 3922139:tid 3922562] (32)Broken pipe: [client 198.51.100.39:37482] AH00646: Error writing to |/usr/local/cpanel/bin/splitlogs --dir=/etc/apache2/logs/domlogs...
[lsapi:warn] [pid 4030908:tid 4031167] Backend log: PHP Warning: session_start(): open(/opt/alt/php56/var/lib/php/session/sess_..., O_RDWR) failed: Disk quota exceeded (122)
Dois problemas estavam ocorrendo aqui:
- Travamento no
splitlogs: O utilitário do cPanel responsável por separar o log geral do Apache nos logs individuais de cada domínio (domlogs) não conseguia processar a rajada de conexões pendentes que foram liberadas pelo MySQL. Quando o buffer do pipe estoura, o Apache trava a resposta ao cliente e cospe o erroBroken pipe. - Quota estourada em conta legada: A mensagem
Disk quota exceedednão era da partição do sistema (que possuía mais de 1.2 TB livres com apenas 7% de inodes usados), mas sim de uma conta cPanel específica que atingiu o limite de armazenamento do plano e não conseguia salvar sessões do PHP.
Para destravar os pipes e recarregar a configuração do Apache de forma limpa:
# Finaliza eventuais processos zumbis do splitlogs
killall -9 splitlogs 2>/dev/null
# Reconstrói a configuração virtual do Apache
/scripts/rebuildhttpdconf
# Executa o restart forçado do serviço
/scripts/restartsrv_httpd --hard
O Apache voltou a responder imediatamente com a fila zerada. A conta que estava sem espaço teve sua cota ampliada no WHM, eliminando o erro de gravação de sessões.
7. Validando a ponta final com cURL#
Com os serviços restabelecidos, era hora de conferir se a resposta na ponta do usuário final estava normalizada.
Para inspecionar detalhadamente cada etapa da conexão HTTP e do aperto de mão criptográfico (TLS), usei o curl em modo detalhado (-Iv):
curl -Iv https://webmail.meudominio.com.br
O retorno confirmou a recuperação completa da velocidade:
* Host webmail.meudominio.com.br:443 was resolved.
* Connected to webmail.meudominio.com.br (198.51.100.246) port 443
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / RSASSA-PSS
* ALPN: server accepted http/1.1
< HTTP/1.1 200 OK
< Content-Length: 37940
< Set-Cookie: webmailsession=...; HttpOnly; path=/; port=443; secure
O handshake SSL em TLSv1.3 foi completado em milissegundos e a página de login do webmail respondeu com HTTP 200 sem nenhum atraso.
Roteiro de sobrevivência para evitar o efeito dominó em servidores#
Este incidente reforça lições técnicas valiosas para quem gerencia servidores de alta demanda:
- Hardware doméstico não aguenta I/O randômico de servidor: SSDs DRAMless com memória QLC (como o Kingston NV3) esgotam o cache SLC rapidamente sob gravações contínuas de bancos de dados. Em espelhamento RAID 1, um único disco lento obriga o disco rápido a operar na velocidade do pior membro.
- Não economize RAM no buffer pool do MySQL: Se o servidor tem 64 GB ou 128 GB de memória livre, manter o
innodb_buffer_pool_sizeem 2 GB ou 4 GB é um desperdício grave de recursos e uma sobrecarga desnecessária sobre os discos. - Monitore
w_awaitnoiostat: Olhar apenas%utilou taxas de transferência em MB/s pode esconder gargalos severos de latência por bloco. - Pipes de log também causam timeout: Daemons auxiliares de log como o
splitlogspodem congelar workers do Apache se a taxa de escrita for superior à capacidade de esvaziamento do buffer.
Com as camadas de I/O, memória e servidor web devidamente afinadas, a infraestrutura opera com folga e sem surpresas desagradáveis de timeout global.
Este artigo foi útil?
Deixe uma reação rápida para apoiar o conteúdo:
Este post está licenciado sob CC BY-NC.



Comentários
Participe da discussão abaixo.
0 comentários