Implementando as Melhores Práticas para a Confiabilidade de Contêineres
Agora que entendemos como solucionar problemas de saída de contêineres, vamos implementar as melhores práticas para tornar nossos contêineres mais confiáveis e robustos. Vamos aprimorar nosso aplicativo de exemplo com tratamento de erros adequado, verificações de integridade e registro (logging).
1. Melhorando o Tratamento de Erros
Vamos modificar nosso aplicativo Python para lidar com variáveis de ambiente ausentes de forma adequada:
cd ~/project/docker-exit-test
nano app.py
Atualize o conteúdo para:
import os
import time
import sys
## Get environment variables with defaults
database_url = os.environ.get('DATABASE_URL', 'sqlite:///default.db')
print(f"Connecting to database: {database_url}")
## Simulate a long-running process
try:
print("Application running... Press Ctrl+C to exit")
counter = 0
while True:
counter += 1
print(f"Application heartbeat: {counter}")
time.sleep(10)
except KeyboardInterrupt:
print("Application shutting down gracefully...")
sys.exit(0)
Salve e saia do arquivo.
Esta versão aprimorada:
- Usa
os.environ.get() com um valor padrão em vez de gerar uma exceção
- Implementa um loop de longa duração para manter o contêiner ativo
- Lida com o desligamento adequado quando é terminado
Vamos reconstruir a imagem:
docker build -t exit-test-app:v2 .
E execute o contêiner aprimorado:
docker run -d --name improved-app exit-test-app:v2
Verifique se o contêiner está em execução:
docker ps
Você deve ver seu contêiner em execução:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c1d2e3f4g5h6 exit-test-app:v2 "python app.py" 10 seconds ago Up 10 seconds improved-app
Visualize os logs para confirmar que está funcionando:
docker logs improved-app
Você deve ver uma saída como:
Connecting to database: sqlite:///default.db
Application running... Press Ctrl+C to exit
Application heartbeat: 1
Application heartbeat: 2
2. Implementando uma Verificação de Integridade no Dockerfile
As verificações de integridade permitem que o Docker monitore a integridade do seu contêiner. Vamos atualizar nosso Dockerfile para incluir uma verificação de integridade:
nano Dockerfile
Atualize-o para:
FROM python:3.9-slim
WORKDIR /app
## Install curl for health checks
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
COPY app.py .
## Add a health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
## Expose port for the health check endpoint
EXPOSE 8080
CMD ["python", "app.py"]
Agora precisamos adicionar um endpoint de verificação de integridade ao nosso aplicativo:
nano app.py
Substitua o conteúdo por:
import os
import time
import sys
import threading
from http.server import HTTPServer, BaseHTTPRequestHandler
## Get environment variables with defaults
database_url = os.environ.get('DATABASE_URL', 'sqlite:///default.db')
## Simple HTTP server for health checks
class HealthRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/health':
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()
self.wfile.write(b'OK')
else:
self.send_response(404)
self.end_headers()
def run_health_server():
server = HTTPServer(('0.0.0.0', 8080), HealthRequestHandler)
print("Starting health check server on port 8080")
server.serve_forever()
## Start health check server in a separate thread
health_thread = threading.Thread(target=run_health_server, daemon=True)
health_thread.start()
print(f"Connecting to database: {database_url}")
## Main application loop
try:
print("Application running... Press Ctrl+C to exit")
counter = 0
while True:
counter += 1
print(f"Application heartbeat: {counter}")
time.sleep(10)
except KeyboardInterrupt:
print("Application shutting down gracefully...")
sys.exit(0)
Salve e saia do arquivo.
Reconstrua a imagem com nossa verificação de integridade:
docker build -t exit-test-app:v3 .
Execute o contêiner com a nova versão:
docker run -d --name healthcheck-app -p 8080:8080 exit-test-app:v3
Após cerca de 30 segundos, verifique o status da integridade:
docker inspect --format='{{.State.Health.Status}}' healthcheck-app
Você deve ver:
healthy
Você também pode testar o endpoint de integridade diretamente:
curl http://localhost:8080/health
Isso deve retornar OK.
3. Usando as Políticas de Reinicialização do Docker
O Docker fornece políticas de reinicialização para reiniciar automaticamente os contêineres quando eles saem ou encontram erros:
docker run -d --restart=on-failure:5 --name restart-app exit-test-app:v3
Esta política reiniciará o contêiner até 5 vezes se ele sair com um código diferente de zero.
Políticas de reinicialização disponíveis:
no: Nunca reiniciar (padrão)
always: Sempre reiniciar, independentemente do status de saída
unless-stopped: Sempre reiniciar, a menos que seja parado manualmente
on-failure[:max-retries]: Reiniciar apenas em saída diferente de zero
4. Definindo Limites de Recursos
Para evitar falhas de contêineres devido ao esgotamento de recursos, defina limites de recursos apropriados:
docker run -d --name resource-limited-app \
--memory=256m \
--cpus=0.5 \
exit-test-app:v3
Isso limita o contêiner a 256MB de memória e metade de um núcleo de CPU.
5. Configuração de Registro (Logging) Adequada
Para uma melhor depuração, configure seu aplicativo para gerar logs estruturados:
docker run -d --name logging-app \
--log-driver=json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
exit-test-app:v3
Isso configura o contêiner para usar o driver de log JSON com rotação de log (máximo de 3 arquivos de 10MB cada).
Resumo das Melhores Práticas
Ao implementar essas melhores práticas, você aprimorou significativamente a confiabilidade de seus contêineres Docker:
- Tratamento de erros adequado com valores padrão
- Verificações de integridade do contêiner para monitoramento
- Políticas de reinicialização apropriadas para recuperação automática
- Limites de recursos para evitar o esgotamento de recursos
- Configuração de registro (logging) adequada para facilitar a solução de problemas
Essas técnicas o ajudarão a criar contêineres resilientes que podem se recuperar de falhas e fornecer melhor observabilidade quando ocorrem problemas.