Sistemas de controle e automação
Colocando recipientes Docker com sistema para inicialização automatizada
Table of Contents
Por que combinar sistema com Docker para implantação de produção
A infraestrutura moderna exige que os serviços contêinerizados sobrevivam a reinicialização inesperada, falhas de hardware ou atualizações de pacotes. Enquanto o Docker fornece políticas de reinicialização (, essas políticas só funcionam enquanto o daemon Docker estiver em execução. Systemd – o sistema de inicialização usado pelo Ubuntu, Debian, Fedora, CentOS e a maioria das distribuições modernas do Linux – leva isso adiante, gerenciando o ciclo de vida do daemon Docker em si e pode iniciar contêineres mesmo antes do socket Docker ficar disponível. Os benefícios do uso do systemd incluem:
- Ordem de arranque garantida através de directivas de dependência (por exemplo, após network.target, after docker.service)
- Registo unificado via , tornando a depuração simples
- Controlo de grãos finos sobre os limites de recursos (CPU, memória, E/S) utilizando directivas de unidades systemd
- Reiniciar automaticamente com o atraso configurável e os limites de ruptura
- Suporte para ativação do socket e inicialização cronometrada
Ao envolver cada recipiente Docker em um arquivo de serviço systemd, as equipes de operações ganham uma interface consistente para iniciar, parar e monitorar os recipientes, reduzindo a dependência em scripts ad-hoc e intervenção manual.
Criação de um serviço sistematizado para um recipiente de um único compartimento
A abordagem padrão envolve escrever um arquivo de unidade de serviço que chama os comandos do Docker para executar e parar o recipiente. Abaixo, nós caminhamos pelo processo passo a passo, começando com um exemplo básico e, em seguida, cobrindo os requisitos comuns de produção.
Passo 1: Escreva o arquivo de unidade de serviço
Criar um ficheiro chamado . Use o seguinte modelo como ponto de partida:
[Unit]
Description=My Application Container
After=network-online.target docker.service
Wants=network-online.target
Requires=docker.service
[Service]
Restart=always
RestartSec=10
StartLimitBurst=3
ExecStartPre=-/usr/bin/docker kill myapp
ExecStartPre=-/usr/bin/docker rm myapp
ExecStart=/usr/bin/docker run --rm --name myapp \
-e DB_HOST=10.0.1.50 \
-e DB_PORT=5432 \
-v /data/myapp:/app/data \
-p 8080:8080 \
myregistry/myapp:latest
ExecStop=/usr/bin/docker stop -t 10 myapp
ExecStopPost=-/usr/bin/docker rm myapp
[Install]
WantedBy=multi-user.target
Explicação das principais directivas:
- – garante que o daemon Docker está em execução antes de iniciar o recipiente.
- – se o Docker for interrompido, este serviço também pára.
- – limpa qualquer recipiente restante de uma execução anterior (o prefixo ] significa que as falhas aqui não são fatais).
- – usa para remover automaticamente o recipiente quando pára.
- – graciosamente pára o recipiente com um tempo limite (10 segundos).
- – reinicia o recipiente, independentemente do código de saída.
- ] – espera 10 segundos antes de reiniciar.
- – os limites reiniciam para 3 tentativas por intervalo (padrão 10 segundos) para evitar ciclos de reinicialização.
Passo 2: Activar e Iniciar o Serviço
sudo systemctl daemon-reload
sudo systemctl enable myapp.service
sudo systemctl start myapp.service
O diz ao systemd para reler os arquivos de serviço. cria o link simbólico para que o serviço comece no arranque.
Gerenciando o Serviço com Comandos Padrão Systemd
Uma vez que o serviço está em execução, você controla-o como qualquer outro serviço do sistema:
- Início:
- [[FLT: 0]] Pare: ]
- Reiniciar:
- Status: ]
- Logs: (seguir os registos ao vivo)
Padrões de Configuração Avançados
Implementos de produção muitas vezes exigem mais do que um simples . Abaixo estão melhorias comuns que você pode adicionar aos seus arquivos de serviço systemd.
Passando Variáveis de Ambiente
Não é recomendado o segredo ou configuração de codificação em disco no ficheiro de serviço. Em vez disso, use um ficheiro de ambiente separado:
[Service]
EnvironmentFile=-/etc/myapp/env.conf
ExecStart=/usr/bin/docker run --rm --name myapp \
--env-file /etc/myapp/env.conf \
myregistry/myapp:latest
O prefixo antes do caminho significa que o serviço irá começar mesmo que o arquivo não exista (útil durante a configuração inicial).
Ligação em rede e ligação a portos
Para os contentores que precisam de se comunicar no mesmo host, considere usar ou redes de ponte definidas pelo utilizador. Exemplo:
ExecStart=/usr/bin/docker run --rm --name web \
--network=my-net \
-p 443:443 \
-v /etc/ssl/certs:/etc/ssl/certs:ro \
myregistry/web:latest
Se usar uma rede personalizada, certifique-se de que a rede existe antes do início do serviço. Você pode adicionar um comando para criá-lo:
ExecStartPre=/usr/bin/docker network create my-net
Dependências entre os conteúdos
Quando um recipiente requer que outro esteja pronto antes de iniciar (por exemplo, uma aplicação Web à espera de um banco de dados), systemd pode forçar a encomenda. Crie um segundo ficheiro de serviço para o banco de dados e depois:
[Unit]
Description=Web App Container
After=network-online.target docker.service mydb.service
BindsTo=mydb.service
liga o ciclo de vida do aplicativo à caixa de dados – se o banco de dados parar, o aplicativo web também é parado.
Verificação de Saúde e Prontidão
Os controlos de saúde do Docker podem ser integrados com o sistema para evitar a disponibilidade prematura de serviços. Use com um script que pesquisa o ponto final de saúde:
ExecStartPost=/usr/local/bin/wait-for-health.sh http://localhost:8080/health 30
O script deverá sair de 0 apenas quando o recipiente estiver saudável. Se falhar, o sistema marca a unidade como falhada.
Limites de Recursos via Systemd
Você pode restringir a CPU e memória de um recipiente no nível do cgroup sem as próprias opções de recursos do Docker. Isto é especialmente útil quando rodando vários recipientes em uma única máquina:
[Service]
MemoryMax=512M
CPUQuota=50%
Essas configurações criam um limite rígido que o sistema impõe independentemente do Docker.
Gerenciando vários recipientes: Systemd vs. Docker Compose
Para um pequeno número de contentores (por exemplo, 2- 5), os ficheiros de serviço individuais são simples e mantendíveis. Contudo, quando um projecto envolve muitos serviços interligados, o Docker Compose torna- se mais conveniente. Você ainda pode usar o systemd para orquestrar toda a pilha de serviços do Docker Compose criando uma única unidade de serviço que chama . Exemplo:
[Unit]
Description=My Application Stack
After=network-online.target docker.service
Requires=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/myapp
ExecStart=/usr/local/bin/docker-compose up -d
ExecStop=/usr/local/bin/docker-compose down
[Install]
WantedBy=multi-user.target
Esta abordagem dá-lhe a simplicidade da Compose para definir serviços combinados com a gestão do ciclo de vida do systemd. Note que é usado porque sai imediatamente. mantém a unidade em estado “ativo” até ser chamada.
Que método deve escolher?
- Serviços individuais de sistema – melhor para aplicações legados, serviços com estrita ordem de inicialização, ou quando você precisa de limites de recursos por conteúdo.
- Docker Compose with systemd – ideal para pilhas de microservices onde as dependências são tratadas internamente pela Compose, e você quer uma única unidade para gerenciar todo o grupo.
Resolver Problemas Comuns
Mesmo com uma configuração cuidadosa, você pode encontrar problemas. Abaixo estão armadilhas frequentes e suas soluções.
O serviço falha com “Não é possível conectar ao daemon Docker”
Isto significa normalmente que o serviço começa antes do socket docker estar pronto. Certifique-se de que sua unidade contém e . Verifique também se o daemon do Docker está habilitado: .
Container Reinicia em um Loop
Se o recipiente sair imediatamente, o sistema irá continuar a reiniciar de acordo com e . Verifique os registos do contentor com . Aumente ] (por exemplo, 30 segundos) e defina para evitar um loop ocupado.
O serviço não pára de forma limpa
Um configurado incorretamente pode deixar o recipiente em execução. Verifique se usa o nome correto do recipiente. Use para remover o recipiente com força se a parada falhar.
Variáveis de Ambiente Não Carregadas
Se você usar , confirme que o arquivo existe e é legível por root. Evite citar problemas – citações de tiras systemd de valores variáveis. Para injeção secreta, considere usar credenciais systemd ou um gerenciador secreto dedicado.
Considerações sobre segurança
Correr os contentores Docker através do sistema levanta alguns pontos de segurança:
- Sempre execute o serviço systemd como um usuário não-root se possível (use as diretivas e , mas garanta que o usuário tenha acesso ao socket do Docker ou seja executado em modo sem-raiz).
- Evite usar em unidades systemd, a menos que absolutamente necessário.
- Use somente leitura vincular montagens () sempre que o recipiente não precisa escrever para o host.
- O sistema de alavancagem e ] para endurecer a unidade contra fugas.
[Service]
ProtectSystem=strict
ReadWritePaths=/var/log/myapp
PrivateTmp=true
User=myappuser
Recursos externos
Para mais informações, consultar as seguintes referências oficiais:
- Documentação sobre as Políticas de Reiniciação do Docker
- Manual da unidade de serviço sistematizado
- [[FLT: 0]]Docker Compose Overview
Conclusão
Integrando o sistema com os containers Docker, você pode controlar a ordem de inicialização, gerenciar dependências, definir limites de recursos e monitorar registros usando ferramentas que sua equipe de operações já sabe. Se você escolhe serviços individuais para cada container ou uma única unidade para orquestrar uma pilha Compose, systemd fornece a confiabilidade e previsibilidade que os ambientes de produção demandam.
Comece com um arquivo unitário simples, teste-o completamente, em seguida, a camada em opções avançadas, como arquivos de ambiente, verificação de saúde e endurecimento de segurança. Com esta abordagem, seus containers Docker sobreviverão a reinicialização, falhas e mudanças de configuração sem intervenção manual, libertando sua equipe para se concentrar em aplicativos de construção.