Implementación de las Mejores Prácticas para la Fiabilidad de los Contenedores
Ahora que entendemos cómo solucionar los problemas de salida de los contenedores, implementemos las mejores prácticas para que nuestros contenedores sean más fiables y robustos. Mejoraremos nuestra aplicación de ejemplo con un manejo adecuado de errores, comprobaciones de estado y registro.
1. Mejora del Manejo de Errores
Modifiquemos nuestra aplicación Python para manejar las variables de entorno faltantes con elegancia:
cd ~/project/docker-exit-test
nano app.py
Actualice el contenido a:
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)
Guarde y salga del archivo.
Esta versión mejorada:
- Usa
os.environ.get() con un valor predeterminado en lugar de generar una excepción
- Implementa un bucle de larga duración para mantener el contenedor activo
- Maneja el apagado correcto cuando se termina
Reconstruyamos la imagen:
docker build -t exit-test-app:v2 .
Y ejecute el contenedor mejorado:
docker run -d --name improved-app exit-test-app:v2
Compruebe que el contenedor se está ejecutando:
docker ps
Debería ver su contenedor en ejecución:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c1d2e3f4g5h6 exit-test-app:v2 "python app.py" 10 seconds ago Up 10 seconds improved-app
Vea los registros para confirmar que está funcionando:
docker logs improved-app
Debería ver una salida como:
Connecting to database: sqlite:///default.db
Application running... Press Ctrl+C to exit
Application heartbeat: 1
Application heartbeat: 2
2. Implementación de una Comprobación de Estado en Dockerfile
Las comprobaciones de estado permiten a Docker monitorear el estado de su contenedor. Actualicemos nuestro Dockerfile para incluir una comprobación de estado:
nano Dockerfile
Actualícelo a:
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"]
Ahora necesitamos agregar un punto final de comprobación de estado a nuestra aplicación:
nano app.py
Reemplace el contenido con:
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)
Guarde y salga del archivo.
Reconstruya la imagen con nuestra comprobación de estado:
docker build -t exit-test-app:v3 .
Ejecute el contenedor con la nueva versión:
docker run -d --name healthcheck-app -p 8080:8080 exit-test-app:v3
Después de unos 30 segundos, compruebe el estado de salud:
docker inspect --format='{{.State.Health.Status}}' healthcheck-app
Debería ver:
healthy
También puede probar el punto final de estado directamente:
curl http://localhost:8080/health
Esto debería devolver OK.
3. Uso de Políticas de Reinicio de Docker
Docker proporciona políticas de reinicio para reiniciar automáticamente los contenedores cuando salen o encuentran errores:
docker run -d --restart=on-failure:5 --name restart-app exit-test-app:v3
Esta política reiniciará el contenedor hasta 5 veces si sale con un código distinto de cero.
Políticas de reinicio disponibles:
no: Nunca reiniciar (predeterminado)
always: Siempre reiniciar independientemente del estado de salida
unless-stopped: Siempre reiniciar a menos que se detenga manualmente
on-failure[:max-retries]: Reiniciar solo en la salida distinta de cero
4. Establecimiento de Límites de Recursos
Para evitar que los contenedores se bloqueen debido al agotamiento de los recursos, establezca los límites de recursos apropiados:
docker run -d --name resource-limited-app \
--memory=256m \
--cpus=0.5 \
exit-test-app:v3
Esto limita el contenedor a 256 MB de memoria y la mitad de un núcleo de CPU.
5. Configuración de Registro Adecuada
Para una mejor depuración, configure su aplicación para que genere registros estructurados:
docker run -d --name logging-app \
--log-driver=json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
exit-test-app:v3
Esto configura el contenedor para usar el controlador de registro JSON con rotación de registros (máximo 3 archivos de 10 MB cada uno).
Resumen de las Mejores Prácticas
Al implementar estas mejores prácticas, ha mejorado significativamente la fiabilidad de sus contenedores Docker:
- Manejo de errores correcto con valores predeterminados
- Comprobaciones de estado del contenedor para el monitoreo
- Políticas de reinicio apropiadas para la recuperación automática
- Límites de recursos para evitar el agotamiento de recursos
- Configuración de registro adecuada para facilitar la solución de problemas
Estas técnicas le ayudarán a crear contenedores resilientes que puedan recuperarse de fallos y proporcionar una mejor observabilidad cuando ocurren problemas.