Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Kubernetes CronJob no Node.js

Atualizado em: 22 de setembro de 2026

Terminal Linux usado em desenvolvimento e administração de aplicações Node.js

O Kubernetes CronJob no Node.js cria Jobs em horários recorrentes usando uma expressão cron. Ele é adequado para relatórios, limpeza de dados, reconciliação, envio de lembretes, backups auxiliares e outras tarefas periódicas que terminam depois de executar.

Um CronJob não garante execução exatamente uma vez. A documentação do Kubernetes alerta que, em determinadas circunstâncias, duas execuções podem ser criadas ou uma execução pode ser perdida. Por isso, o comando Node.js precisa ser idempotente, registrar progresso e tolerar reinícios.

Neste guia, você aprenderá a definir schedule e timezone, escolher concurrencyPolicy, configurar deadlines, retries, recursos, segurança, histórico, suspensão, observabilidade e execução manual.

O que é um CronJob?

A documentação oficial de CronJob no Kubernetes explica que o recurso cria objetos Job em uma programação recorrente. Cada Job cria um ou mais Pods que executam até concluir ou falhar.

O CronJob controla quando criar o Job. O Job controla retries e conclusão. O Pod executa o processo Node.js.

Estrutura básica

apiVersion: batch/v1
kind: CronJob
metadata:
  name: orders-reconciliation
  namespace: production
spec:
  schedule: "0 */2 * * *"
  timeZone: "America/Sao_Paulo"
  concurrencyPolicy: Forbid
  startingDeadlineSeconds: 900
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 5
  jobTemplate:
    spec:
      backoffLimit: 3
      ttlSecondsAfterFinished: 86400
      template:
        spec:
          restartPolicy: Never
          containers:
            - name: reconciliation
              image: ghcr.io/acme/orders-api@sha256:abc123...
              command:
                - node
                - dist/jobs/reconcile-orders.js

Esse exemplo executa a cada duas horas no fuso de São Paulo e impede sobreposição.

Sintaxe cron

# ┌──────── minuto
# │ ┌────── hora
# │ │ ┌──── dia do mês
# │ │ │ ┌── mês
# │ │ │ │ ┌ dia da semana
# │ │ │ │ │
# * * * * *

Exemplos:

0 3 * * *       # todos os dias às 03:00
0 8 * * 1-5     # dias úteis às 08:00
*/15 * * * *    # a cada 15 minutos
0 0 1 * *       # primeiro dia do mês

Valide a expressão em testes e documentação interna. Uma expressão incorreta pode executar mais vezes que o esperado.

Timezone

timeZone: "America/Sao_Paulo"

Use spec.timeZone em vez de colocar TZ ou CRON_TZ dentro da expressão. O nome deve ser válido na base IANA.

Horário de verão e mudanças regulatórias podem alterar a relação com UTC. Para processamento financeiro, registre o instante UTC e o período lógico processado.

UTC ou fuso local?

Use UTC quando o processo não depende de calendário local. Use um fuso explícito para fechamento diário, relatórios comerciais e rotinas relacionadas à data local.

Nunca dependa silenciosamente do timezone do controller manager.

concurrencyPolicy Allow

concurrencyPolicy: Allow

É o padrão. Uma nova execução começa mesmo que a anterior continue ativa. Use quando os Jobs são independentes e o sistema suporta paralelismo.

concurrencyPolicy Forbid

concurrencyPolicy: Forbid

A próxima execução é ignorada enquanto a anterior estiver ativa. É adequada para reconciliação, limpeza e relatórios que não devem sobrepor.

Uma execução ignorada conta como missed schedule. Se o Job demora mais que o intervalo, várias execuções podem ser puladas.

concurrencyPolicy Replace

concurrencyPolicy: Replace

O Kubernetes encerra o Job atual e inicia o novo. Use somente quando interromper a execução antiga é seguro. O processo Node.js precisa tratar SIGTERM e deixar estado consistente.

Idempotência

Mesmo com Forbid, duplicatas continuam possíveis em condições de controle. Use uma chave lógica:

const executionKey = `orders-reconciliation:${periodStart.toISOString()}`;

const result = await pool.query(`
  INSERT INTO scheduled_executions(execution_key, started_at)
  VALUES ($1, now())
  ON CONFLICT DO NOTHING
  RETURNING execution_key
`, [executionKey]);

if (result.rowCount === 0) {
  console.log('Período já processado');
  process.exit(0);
}

Consulte Idempotência em APIs Node.js.

Lock transacional

Uma alternativa é usar advisory lock:

const { rows } = await pool.query(
  'SELECT pg_try_advisory_lock($1) AS acquired',
  [lockId]
);

if (!rows[0].acquired) {
  process.exit(0);
}

Libere o lock no finally. Locks de sessão exigem conexão dedicada e cuidado com PgBouncer transaction mode.

startingDeadlineSeconds

startingDeadlineSeconds: 900

Permite iniciar até quinze minutos depois do horário planejado. Se o controller esteve indisponível por mais tempo, a execução é pulada.

Escolha o prazo conforme utilidade. Um relatório diário pode aceitar horas de atraso; uma rotina a cada cinco minutos talvez não deva recuperar uma execução antiga.

Valores muito baixos

O controller verifica schedules periodicamente. A documentação alerta que valores menores que aproximadamente dez segundos podem impedir a criação. Não use deadline extremamente curto.

Suspendendo

suspend: true

Isso impede novas execuções, mas não interrompe Jobs já iniciados. Ao reativar, schedules perdidos podem ser criados imediatamente quando não existe deadline.

Antes de remover a suspensão, avalie startingDeadlineSeconds.

Job template

O jobTemplate aceita a especificação de um Job. A documentação de Jobs no Kubernetes detalha completions, parallelism, backoff e pod failure policy.

restartPolicy

restartPolicy: Never

Com Never, uma falha cria novo Pod conforme backoffLimit. Com OnFailure, o container pode reiniciar no mesmo Pod. Never costuma facilitar diagnóstico e logs por tentativa.

backoffLimit

backoffLimit: 3

O Job falha após exceder retries. O código precisa classificar erros:

  • timeout e 503 podem ser temporários;
  • dados inválidos são permanentes;
  • credencial ausente exige intervenção;
  • conflito de idempotência pode ser sucesso lógico.

activeDeadlineSeconds

activeDeadlineSeconds: 1800

Encerra o Job depois de trinta minutos, incluindo retries. Defina um limite para impedir execuções presas.

Timeouts no código

const signal = AbortSignal.timeout(10_000);

const response = await fetch(url, { signal });

O prazo total do Job não substitui timeouts por operação. Banco, HTTP e filas precisam de limites menores.

Recursos

resources:
  requests:
    cpu: 250m
    memory: 256Mi
  limits:
    cpu: 1
    memory: 512Mi

Requests permitem scheduling previsível. Limits evitam que uma rotina pesada afete outros workloads. Analise OOMKilled e throttling.

Imagem imutável

image: ghcr.io/acme/orders-api@sha256:abc123...

Não use latest. Um CronJob pode executar dias depois do deploy, e a tag móvel dificultaria saber qual código rodou.

Veja GitHub Container Registry no Node.js.

Comando separado

Organize cada job como uma entrada clara:

src/jobs/reconcile-orders.ts
src/jobs/delete-expired-sessions.ts
src/jobs/generate-daily-report.ts

Evite iniciar o servidor HTTP e depois chamar uma função interna. O processo deve executar a tarefa e sair com código adequado.

Códigos de saída

async function main() {
  await runJob();
}

main()
  .then(() => process.exitCode = 0)
  .catch(error => {
    console.error(error);
    process.exitCode = 1;
  });

Não chame process.exit() antes de logs e buffers terminarem, salvo caso controlado.

ConfigMap

envFrom:
  - configMapRef:
      name: orders-jobs-config
  - secretRef:
      name: orders-jobs-secrets

Use configuração externa e secrets protegidos. Consulte ConfigMaps e Secrets no Node.js.

ServiceAccount

serviceAccountName: orders-reconciliation
automountServiceAccountToken: false

Desative o token se o Job não chama a API do Kubernetes. Quando precisa, conceda RBAC mínimo.

Security context

securityContext:
  runAsNonRoot: true
  seccompProfile:
    type: RuntimeDefault
containers:
  - name: reconciliation
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop: ["ALL"]

Monte emptyDir somente nos caminhos graváveis necessários.

Histórico

successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 5

Manter falhas ajuda no diagnóstico. Muitos Jobs antigos aumentam objetos e ruído. Combine com TTL.

TTL após conclusão

ttlSecondsAfterFinished: 86400

O Job e seus Pods são removidos após um dia. Logs importantes devem estar em um sistema central antes da limpeza.

Executando manualmente

kubectl create job \
  --from=cronjob/orders-reconciliation \
  orders-reconciliation-manual-$(date +%s) \
  -n production

A execução manual usa o template atual. Ela não altera o schedule. Registre motivo e operador.

Inspecionando

kubectl get cronjobs -n production
kubectl describe cronjob orders-reconciliation -n production
kubectl get jobs -n production \
  -l batch.kubernetes.io/cronjob-name=orders-reconciliation
kubectl logs job/JOB_NAME -n production

Annotation de horário agendado

Versões modernas adicionam ao Job uma annotation com o timestamp originalmente agendado. Use-a para calcular atraso e o período lógico, quando disponível.

Não use a hora de início como período

Se o Job das 03:00 começa às 03:12, ele ainda pode precisar processar o dia anterior. Derive o período do schedule ou de parâmetros explícitos, não apenas de new Date().

Processamento em páginas

let cursor = null;

do {
  const page = await loadPage(cursor, 500);
  await processPage(page.items);
  cursor = page.nextCursor;
} while (cursor);

Use cursor estável e checkpoint quando a tarefa pode exceder o prazo. O reprocessamento precisa ser seguro.

Jobs muito longos

Se a rotina dura horas, considere:

  • dividir em jobs menores;
  • usar fila;
  • usar Indexed Job;
  • usar workflow durável;
  • salvar checkpoint;
  • reavaliar a frequência.

Fila em vez de CronJob

O CronJob pode descobrir trabalho e publicar itens em uma fila. Workers processam em paralelo, com retries e DLQ. Isso evita um único Pod gigantesco.

Leader election

O CronJob controller já decide a criação do Job. Não é necessário leader election dentro do processo apenas por haver várias réplicas, pois o Job normalmente cria um Pod. Em Jobs paralelos, coordene as unidades de trabalho.

Graceful shutdown

Com Replace, drain ou deadline, o Pod recebe SIGTERM. O processo deve parar de iniciar novos lotes, salvar checkpoint e fechar conexões.

Consulte Graceful Shutdown no Node.js.

Observabilidade

Monitore:

  • último schedule e último sucesso;
  • Jobs ativos;
  • falhas consecutivas;
  • atraso entre schedule e início;
  • duração;
  • itens processados e rejeitados;
  • execuções puladas por concorrência;
  • OOMKilled e evictions;
  • Jobs presos.

Alertas

Um alerta útil verifica ausência de sucesso dentro de uma janela:

time() - kube_job_status_completion_time{job_name=~"orders-reconciliation-.*"}
  > 3 * 60 * 60

A expressão real depende das métricas e labels. Evite alertar apenas por um Job falho quando uma tentativa posterior concluiu.

Testes

Teste a função do job fora do Kubernetes com banco e serviços reais em containers. Depois teste:

  • duas execuções simultâneas;
  • SIGTERM no meio de uma página;
  • timeout externo;
  • reexecução do mesmo período;
  • schedule perdido;
  • mudança de timezone;
  • falha permanente.

Erros comuns

  • Allow em job não idempotente: execuções se sobrepõem.
  • Sem deadline: schedules antigos iniciam tarde demais.
  • Replace sem shutdown: estado fica parcialmente gravado.
  • Timezone implícito: tarefa roda em horário inesperado.
  • Latest: a mesma definição executa código diferente.
  • Sem timeout: Job permanece ativo para sempre.
  • Histórico ilimitado: objetos e logs acumulam.
  • Confiar em execução única: duplicatas causam efeitos repetidos.

Conclusão

O Kubernetes CronJob no Node.js agenda Jobs recorrentes com cron e timezone explícito. Concurrency policy, deadline, backoff e limites controlam como execuções atrasadas, concorrentes e falhas são tratadas.

Escreva tarefas idempotentes, use imagem imutável e defina recursos e timeouts. Centralize logs, monitore último sucesso e teste reexecução. Assim, rotinas periódicas deixam de depender de um cron local e passam a ter estado, histórico e controle operacional no cluster.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita