Processos, sinais e systemd na prática
Objetivos desta aula
- Investigar processos, estados e portas em uso
- Enviar sinais corretamente e entender encerramento gracioso
- Escrever uma unit systemd resiliente e segura
- Operar o serviço: habilitar, reiniciar, verificar e ler logs
1. O que é um processo
Todo serviço em execução é um processo com PID (identificador), dono, processo pai, estado e consumo de recursos. Um container também é um processo — isolado por recursos do kernel, mas visível no host. Dominar processos é pré-requisito para entender containers e Kubernetes depois.
2. Ferramentas de inspeção
ps auxf— lista em forma de árvore, mostrando quem criou quem.ps -eo pid,ppid,stat,etime,pcpu,pmem,cmd --sort=-pcpu— visão sob medida, ordenada por CPU.top/htop— comportamento ao vivo.pgrep -af node— localizar por nome.ss -tulpn— quem escuta qual porta; resolve metade dos "a porta está em uso".
3. Estados que contam uma história
- R executando ou pronto para executar.
- S dormindo, esperando algo (normal).
- D espera ininterrupta de I/O — indica disco ou rede lenta, não CPU.
- Z zumbi: terminou, mas o pai não coletou o status.
- T parado.
Muitos processos em D com CPU baixa é assinatura clássica de gargalo de armazenamento.
4. Sinais: conversando com processos
Sinal é uma notificação enviada ao processo. Os que importam: SIGTERM (15) pede encerramento e deixa o programa fechar conexões e terminar requisições; SIGKILL (9) mata imediatamente, sem chance de limpeza; SIGHUP (1) costuma significar "recarregue a configuração"; SIGINT (2) é o seu Ctrl+C.
Comece sempre por TERM. kill -9 pode corromper estado, deixar arquivo de lock preso e derrubar requisições no meio.
5. Encerramento gracioso na aplicação
Do lado do código, a aplicação deve escutar SIGTERM, parar de aceitar novas conexões, terminar as que estão em andamento e fechar o banco. Sem isso, cada deploy gera erro para quem estava navegando — e é exatamente o que Kubernetes espera do seu container.
6. Órfãos, zumbis e PID 1
Se o pai morre, o filho é adotado pelo PID 1 (init/systemd). Zumbis aparecem quando o pai não faz a coleta; em containers, isso é comum quando o processo principal não é preparado para ser PID 1 — daí a recomendação de usar --init ou um init mínimo na imagem.
7. O papel do systemd
systemd garante que o serviço suba no boot, reinicie após falha, tenha logs centralizados e dependências respeitadas. Sem ele você depende de alguém logar no servidor às 3h da manhã para digitar um comando.
8. Anatomia de uma unit
[Unit]— descrição e ordem (After,Wants).[Service]— como executar:User,WorkingDirectory,EnvironmentFile,ExecStart, política deRestart.[Install]— em qual alvo o serviço é habilitado (multi-user.target).
9. Política de restart sem laço maluco
Restart=on-failure com RestartSec=3 evita reinício instantâneo em loop. Para falhas persistentes, StartLimitBurst e StartLimitIntervalSec impedem que a máquina gaste CPU reiniciando algo que nunca vai subir. Teste com reboot: confiança se comprova, não se supõe.
10. Endurecimento (hardening) de graça
Quatro linhas que reduzem muito o risco: NoNewPrivileges=true (impede escalar privilégio), PrivateTmp=true (tmp isolado), ProtectSystem=full (sistema em leitura), ProtectHome=true. Adicione ReadWritePaths apenas para os diretórios que o serviço realmente precisa escrever.
11. Variáveis de ambiente do jeito certo
Use EnvironmentFile=/etc/cloudshop/app.env com permissão 640 e dono do serviço. Nunca coloque segredo direto em ExecStart: a linha de comando é visível em ps para qualquer usuário da máquina.
12. Rotina de operação e diagnóstico
daemon-reload após editar a unit, enable --now para habilitar e iniciar, status para ver estado e últimas linhas, journalctl -u ... -n 50 para o log, systemctl is-enabled para confirmar boot. Se o serviço não sobe, leia o log antes de mudar qualquer coisa.
Na prática
Investigação de processos
bash
ps auxf | head -30
ps -eo pid,ppid,stat,etime,pcpu,pmem,cmd --sort=-pcpu | head
# PID PPID STAT ELAPSED %CPU %MEM CMD
# 1841 1 Ssl 02:14:11 87.4 6.1 /usr/bin/node /opt/cloudshop/server.js
ss -tulpn | grep :3000 # LISTEN 0 511 *:3000 users:(("node",pid=1841))
lsof -p 1841 | head # arquivos e sockets abertos pelo processo
kill -TERM 1841 # encerramento gracioso (sinal 15)
kill -KILL 1841 # ultimo recurso (sinal 9)
pgrep -af node # localizar por nome
ps -eo stat | grep -c '^Z' # quantidade de zumbisUnit systemd da API do CloudShop
ini
# /etc/systemd/system/cloudshop-api.service
[Unit]
Description=CloudShop API
After=network-online.target
Wants=network-online.target
[Service]
User=cloudshop
Group=cloudshop
WorkingDirectory=/opt/cloudshop/current
EnvironmentFile=/etc/cloudshop/app.env
ExecStart=/usr/bin/node /opt/cloudshop/current/server.js
Restart=on-failure
RestartSec=3
StartLimitBurst=5
StartLimitIntervalSec=60
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
ReadWritePaths=/var/log/cloudshop
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.targetNota de segurança: EnvironmentFile deve ser 640, dono do serviço. Variáveis passadas em ExecStart aparecem em ps para qualquer usuário.
Operar o serviço
bash
sudo systemctl daemon-reload # obrigatorio apos editar a unit
sudo systemctl enable --now cloudshop-api
systemctl status cloudshop-api --no-pager
# Active: active (running) since Sun 2026-09-06 10:22:01; 5s ago
sudo systemctl restart cloudshop-api
systemctl is-enabled cloudshop-api # enabled -> sobe no boot
journalctl -u cloudshop-api -n 50 --no-pager
# Teste de resiliencia: mate o processo e veja o systemd trazer de volta
sudo kill -9 "$(systemctl show -p MainPID --value cloudshop-api)"
sleep 5 && systemctl is-active cloudshop-api # active (reiniciado sozinho)Encerramento gracioso na aplicação (Node)
javascript
const server = app.listen(3000);
async function shutdown(signal) {
console.log(JSON.stringify({ level: "info", msg: "shutdown iniciado", signal }));
server.close(async () => { // para de aceitar novas conexoes
try {
await pool.end(); // fecha o pool do PostgreSQL
process.exit(0); // saida limpa: systemd registra sucesso
} catch (e) {
process.exit(1);
}
});
setTimeout(() => process.exit(1), 10000).unref(); // limite de espera
}
process.on("SIGTERM", () => shutdown("SIGTERM")); // systemd/Kubernetes enviam SIGTERM
process.on("SIGINT", () => shutdown("SIGINT")); // Ctrl+C no terminalPor que isso importa
Serviço que não volta sozinho depois de uma falha transforma incidente de dois minutos em madrugada perdida.
Erro comum
Sair matando com kill -9 e depois descobrir dados corrompidos ou lock file preso.
Dica de produção
Use Restart=on-failure com RestartSec para não entrar em laço de reinício agressivo, e sempre teste com reboot.
Pergunta de entrevista
Explique a diferença entre SIGTERM e SIGKILL e por que isso importa em deploy.
Glossário
- unit
- Arquivo de definição de um recurso gerenciado pelo systemd (serviço, timer, socket).
- zumbi
- Processo que terminou mas cujo status ainda não foi coletado pelo processo pai.
- SIGTERM
- Sinal que pede encerramento e permite ao programa finalizar com ordem.
- SIGKILL
- Sinal que encerra o processo imediatamente, sem oportunidade de limpeza.
- daemon-reload
- Comando que faz o systemd reler os arquivos de unit após uma alteração.
- encerramento gracioso
- Parar de aceitar novas requisições, concluir as em andamento e fechar recursos antes de sair.
- load average
- Média de processos prontos ou esperando execução, comparada ao número de núcleos.
- socket em escuta
- Porta aberta por um processo aguardando conexões, visível com ss -tulpn.
Conexão com o CloudShop
Colocar a API do CloudShop sob systemd, com restart automático e logs no journal.
Quiz da aula
1. Qual comando mostra qual processo está escutando a porta 3000?
2. Por que preferir SIGTERM a SIGKILL em um deploy?
3. Muitos processos em estado D com CPU baixa indicam o quê?
4. O que garante que o serviço volte sozinho depois de uma falha?
Minhas anotações
Salvo automaticamente neste navegador.