Como desligar graciosamente um contêiner Docker em execução prolongada

DockerBeginner
Pratique Agora

Introdução

Contêineres Docker são amplamente utilizados para executar diversos aplicativos e serviços, mas gerenciar o ciclo de vida de contêineres em execução prolongada pode ser um desafio. Este tutorial guiará você pelo processo de desligamento suave de um contêiner Docker em execução prolongada, garantindo uma transição suave e prevenindo possíveis perdas de dados ou problemas de aplicação.

Compreendendo o Ciclo de Vida de um Contêiner Docker

Contêineres Docker possuem um ciclo de vida bem definido que os desenvolvedores precisam compreender para gerenciar seus aplicativos de forma eficaz. Esta seção fornecerá uma visão geral do ciclo de vida do contêiner Docker, incluindo os diferentes estados pelos quais um contêiner pode passar e os conceitos-chave que fundamentam esse ciclo de vida.

Estados do Contêiner Docker

Contêineres Docker podem existir em vários estados diferentes durante sua vida útil:

  1. Criado: Um contêiner foi criado, mas ainda não iniciado.
  2. Executando: O processo principal do contêiner está sendo executado atualmente.
  3. Pausado: O processo principal do contêiner foi pausado, mas o contêiner ainda está em execução.
  4. Parado: O processo principal do contêiner foi parado.
  5. Reiniciando: O contêiner está sendo reiniciado.
  6. Saiu: O contêiner parou e seu processo principal encerrou.

Compreender esses estados é importante, pois eles determinam as ações que você pode executar em um contêiner e o comportamento que você pode esperar.

graph LR
    Criado --> Executando
    Executando --> Pausado
    Pausado --> Executando
    Executando --> Parado
    Parado --> Executando
    Parado --> Saiu

Eventos do Ciclo de Vida do Contêiner Docker

Além dos estados do contêiner, existem vários eventos-chave que ocorrem durante o ciclo de vida de um contêiner Docker:

  1. Criar: Um novo contêiner é criado.
  2. Iniciar: O processo principal do contêiner é iniciado.
  3. Parar: O processo principal do contêiner é parado.
  4. Reiniciar: O contêiner é reiniciado, manualmente ou automaticamente.
  5. Pausar/Despausar: O processo principal do contêiner é pausado ou despausado.
  6. Matar: O processo principal do contêiner é encerrado forçadamente.
  7. Deletar: O contêiner é removido do sistema.

Compreender esses eventos do ciclo de vida é crucial para gerenciar e automatizar o comportamento de seus contêineres Docker.

Lidando com o Ciclo de Vida de Contêineres com o Docker CLI

O Docker CLI fornece vários comandos para interagir com o ciclo de vida do contêiner:

  • docker create: Criar um novo contêiner.
  • docker start: Iniciar um contêiner parado.
  • docker stop: Parar um contêiner em execução.
  • docker restart: Reiniciar um contêiner.
  • docker pause: Pausar um contêiner em execução.
  • docker unpause: Despausar um contêiner pausado.
  • docker kill: Parar forçadamente um contêiner em execução.
  • docker rm: Remover um contêiner.

Esses comandos permitem que você gerencie o ciclo de vida de seus contêineres Docker de forma programática e automatize várias tarefas relacionadas ao gerenciamento de contêineres.

Parando Contêineres de Longa Duração de Forma Graciosa

Ao lidar com contêineres Docker em execução por longo período, é importante garantir que eles sejam parados graciosamente, permitindo que o processo principal do contêiner execute quaisquer tarefas de limpeza ou desligamento necessárias antes que o contêiner seja terminado. Esta seção explorará estratégias para parar contêineres Docker em execução por longo período de forma graciosa.

Entendendo o Sinal SIGTERM

O mecanismo principal para parar um contêiner Docker graciosamente é enviar o sinal SIGTERM para o processo principal do contêiner. Este sinal informa ao processo que ele deve iniciar o processo de desligamento e executar quaisquer tarefas de limpeza necessárias.

Por padrão, quando você executa o comando docker stop, o Docker enviará o sinal SIGTERM para o processo principal do contêiner e aguardará um período de tempo limite padrão (geralmente 10 segundos) para que o processo saia. Se o processo não sair dentro do período de tempo limite, o Docker enviará um sinal SIGKILL, que encerra o processo forçadamente.

Personalizando o Comportamento de Desligamento

Para personalizar o comportamento de desligamento de um contêiner Docker em execução por longo período, você pode usar as seguintes opções:

  1. --stop-signal: Especifique um sinal alternativo a ser enviado para o processo principal do contêiner durante o comando docker stop. Por exemplo, --stop-signal=SIGINT enviaria o sinal SIGINT em vez do SIGTERM padrão.

  2. --stop-timeout: Especifique o número de segundos para aguardar que o processo principal do contêiner saia antes de enviar o sinal SIGKILL. Por exemplo, --stop-timeout=30 daria ao processo 30 segundos para sair antes de ser encerrado forçadamente.

Aqui está um exemplo de como usar essas opções ao iniciar um contêiner em execução por longo período:

docker run -d --name my-app --stop-signal=SIGINT --stop-timeout=60 my-app:latest

Este comando iniciaria um contêiner em execução por longo período chamado my-app, e quando o comando docker stop for emitido, ele enviaria o sinal SIGINT para o processo principal do contêiner e aguardaria até 60 segundos para que ele saia antes de enviar o sinal SIGKILL.

Implementando Desligamento Gracioso em seu Aplicativo

Para garantir que seus contêineres Docker em execução por longo período sejam parados graciosamente, é importante projetar seu aplicativo para lidar com o sinal SIGTERM (ou alternativo) e executar quaisquer tarefas de limpeza necessárias antes de sair. Isso pode envolver tarefas como:

  • Salvar dados na memória em armazenamento persistente
  • Fechar conexões de rede ou conexões de banco de dados
  • Limpar logs ou outros outputs
  • Executar quaisquer outras tarefas de limpeza específicas do aplicativo

Ao implementar este tratamento de sinal em seu aplicativo, você pode garantir que seus contêineres sejam parados de forma controlada e previsível, minimizando o risco de perda de dados ou outros problemas.

Estratégias para Desligamento Gracioso

Quando se trata de desligar contêineres Docker em execução por longo período de forma graciosa, existem várias estratégias que você pode empregar para garantir um processo de desligamento suave e controlado. Esta seção explorará algumas das estratégias e melhores práticas-chave para desligamento gracioso.

Tratamento de Sinais em seu Aplicativo

Uma das estratégias mais importantes para desligamento gracioso é implementar o tratamento de sinais em seu aplicativo. Isso envolve escrever código que ouve o sinal SIGTERM (ou alternativo) e executa as tarefas de limpeza necessárias antes que o aplicativo saia.

Aqui está um exemplo de como você pode implementar o tratamento de sinais em um aplicativo Node.js:

process.on("SIGTERM", () => {
  console.log("Recebido sinal SIGTERM, iniciando desligamento gracioso...");
  // Execute tarefas de limpeza, como:
  // - Salvar dados na memória em armazenamento persistente
  // - Fechar conexões de rede ou conexões de banco de dados
  // - Limpar logs ou outros outputs
  // - Executar quaisquer outras tarefas de limpeza específicas do aplicativo
  console.log("Desligamento gracioso completo, saindo do processo.");
  process.exit(0);
});

Ao implementar este tratamento de sinais, seu aplicativo pode garantir que execute um desligamento controlado, minimizando o risco de perda de dados ou outros problemas.

Usando Healthcheck e Probes de Liveness

Outra estratégia para desligamento gracioso é usar os recursos de health check e liveness probe embutidos do Docker. Esses recursos permitem que você defina verificações que o Docker pode usar para determinar a saúde e prontidão do seu contêiner.

Durante o processo de desligamento, você pode usar esses probes para sinalizar ao Docker que seu contêiner está no processo de desligamento, permitindo que o Docker aguarde o término do desligamento antes de remover o contêiner.

Aqui está um exemplo de como você pode configurar um health check e um liveness probe em seu contêiner Docker:

## Dockerfile
FROM node:14-alpine
COPY . /app
WORKDIR /app
CMD ["node", "server.js"]
HEALTHCHECK --interval=5s --timeout=3s \
  CMD curl -f http://localhost:3000/healthz || exit 1
LABEL com.labex.shutdown.signal=SIGINT
LABEL com.labex.shutdown.timeout=60

Neste exemplo, a instrução HEALTHCHECK define um health check que verifica o endpoint /healthz no servidor web do contêiner. As instruções LABEL definem o sinal a ser usado para desligamento gracioso (SIGINT) e o período de tempo limite (60 segundos).

Durante o processo de desligamento, seu aplicativo pode atualizar o endpoint de health check para sinalizar que o desligamento está em andamento, permitindo que o Docker aguarde o término do desligamento antes de remover o contêiner.

Aproveitando Frameworks de Orquestração

Se você estiver executando seus contêineres Docker em um framework de orquestração como Kubernetes ou Docker Swarm, você pode aproveitar os recursos embutidos desses frameworks para ajudar no desligamento gracioso.

Por exemplo, no Kubernetes, você pode usar o hook preStop para executar um comando ou script que executa tarefas de limpeza antes que o contêiner seja terminado. Você também pode usar o campo terminationGracePeriodSeconds para especificar a quantidade de tempo que o contêiner deve ter para desligar graciosamente.

Aqui está um exemplo de como você pode configurar um deployment Kubernetes com um hook preStop e um período de término gracioso:

## kubernetes-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: my-app
          image: my-app:latest
          ports:
            - containerPort: 3000
          lifecycle:
            preStop:
              exec:
                command: ["/app/shutdown.sh"]
          terminationGracePeriodSeconds: 60

Neste exemplo, o hook preStop executa um script (/app/shutdown.sh) que executa quaisquer tarefas de limpeza necessárias antes que o contêiner seja terminado. O campo terminationGracePeriodSeconds dá ao contêiner 60 segundos para desligar graciosamente antes de ser encerrado forçadamente.

Ao aproveitar esses recursos de frameworks de orquestração, você pode aprimorar ainda mais a confiabilidade e previsibilidade do seu processo de desligamento de contêiner.

Resumo

Neste tutorial, você aprendeu a importância de compreender o ciclo de vida de um contêiner Docker e as estratégias para desligar contêineres em execução por longo período de forma graciosa. Seguindo as melhores práticas descritas, você pode garantir um processo de desligamento suave e confiável, minimizando o risco de perda de dados ou interrupções no aplicativo. Dominar a arte do desligamento gracioso de contêineres é uma habilidade crucial para qualquer desenvolvedor ou administrador Docker.