#El éxito incluye poder recuperarse
Esta tercera parte continúa con el catálogo conectado y al menos un libro insertado. Mantén la misma carpeta y proyecto de Compose. Vas a distinguir tres escenarios: dependencia temporalmente caída, aplicación reemplazada y datos restaurados en otra instancia. No son la misma prueba.
#Fase 8 · Hacer fallar una dependencia y recuperarla
docker compose stop db
curl --max-time 10 -i http://127.0.0.1:8080/health/live
curl --max-time 10 -i http://127.0.0.1:8080/health/ready
curl --max-time 10 -i http://127.0.0.1:8080/libros
Espera 200 en liveness y 503 en las operaciones dependientes de base. El proxy ya estaba arrancado; la condición inicial de Compose no lo detiene por sí sola. La aplicación puede explicar su indisponibilidad sin desaparecer ni esperar infinitamente.
docker compose start db
docker compose exec db pg_isready -h 127.0.0.1 -U postgres -d catalogo
Repite pg_isready hasta ver accepting connections. Solo entonces comprueba la API:
curl --fail --max-time 10 http://127.0.0.1:8080/health/ready
curl --fail http://127.0.0.1:8080/libros
No hay que reiniciar la API: cada petición abre una conexión nueva. Si tu siguiente versión usa un pool, deberá detectar conexiones rotas y renovarlas.
Ahora sustituye solo la API:
docker compose up -d --no-deps --force-recreate --wait api
curl --fail http://127.0.0.1:8080/libros
El libro debe permanecer. El contenedor de API no era su almacenamiento. Puede haber una interrupción breve durante la sustitución; el proyecto no promete alta disponibilidad con una única instancia.
#Fase 9 · Copiar y restaurar datos con evidencia
Primero crea una copia con el administrador, sin terminal interactivo:
docker compose exec -T db pg_dump -U postgres -d catalogo -Fc > backups/catalogo.dump
Comprueba que el comando terminó con éxito. En una automatización, escribe a un temporal y renombra solo si el comando pasa. El dump no incluye el rol y su contraseña; por eso conservamos la definición de roles y secretos por un mecanismo seguro aparte.
Vamos a restaurar en un contenedor y volumen separados que no publican puertos. Esta copia sirve para el ensayo y no sustituye guardar otra fuera del servidor:
docker volume create catalogo-restore-lab
docker run -d --name catalogo-restore-lab \
-e POSTGRES_USER=postgres -e POSTGRES_DB=catalogo \
-e POSTGRES_PASSWORD_FILE=/run/secrets/db_password \
--mount type=bind,src="$(pwd)/secrets/db_password",dst=/run/secrets/db_password,readonly \
--mount type=bind,src="$(pwd)/secrets/app_password",dst=/run/secrets/app_password,readonly \
--mount type=bind,src="$(pwd)/init-db.sh",dst=/docker-entrypoint-initdb.d/10-init.sh,readonly \
--mount type=volume,src=catalogo-restore-lab,dst=/var/lib/postgresql/data \
postgres:17-alpine
docker exec catalogo-restore-lab pg_isready -h 127.0.0.1 -U postgres -d catalogo
Espera a que acepte TCP. La inicialización crea rol y tabla vacía. Para restaurar el dump completo sobre este destino desechable, limpia sus objetos equivalentes:
docker exec -i catalogo-restore-lab pg_restore \
-U postgres -d catalogo --clean --if-exists --exit-on-error --no-owner \
< backups/catalogo.dump
docker exec catalogo-restore-lab psql -U postgres -d catalogo \
-c 'SET ROLE catalogo; SELECT id, titulo FROM libros ORDER BY id;'
--clean elimina los objetos que va a reconstruir en el destino. Aquí el destino es exclusivamente el laboratorio de restauración; no lo apuntes a tu única base de producción. SET ROLE catalogo comprueba permisos de lectura con el rol de aplicación. Para comprobar también la contraseña y TCP, ejecuta dentro del contenedor:
docker exec catalogo-restore-lab sh -c \
'PGPASSWORD="$(cat /run/secrets/app_password)" psql -h 127.0.0.1 -U catalogo -d catalogo -c "SELECT count(*) FROM libros;"'
La contraseña no está escrita literalmente en el comando; existe temporalmente en el entorno de psql dentro del contenedor de administración. No actives trazas de shell ni vuelques su entorno. Esperas el mismo número de filas, y tu libro debe estar presente. Eso verifica contenido, rol y autenticación; un archivo .dump con tamaño mayor que cero no lo habría demostrado.
Después elimina solamente la instancia y el volumen de restauración: docker stop catalogo-restore-lab, docker rm catalogo-restore-lab y docker volume rm catalogo-restore-lab. La aplicación principal continúa intacta.
#Fase 10 · Publicar una versión y operarla
Para pasar a un servidor, sigue Despliegue y HTTPS con estos mismos nombres de servicio. Construye y prueba una imagen, publícala en tu registro, fija su digest en APP_IMAGE, conserva el anterior y descarga ese artefacto en el servidor. El archivo Compose de servidor puede omitir build: . porque no necesita código ni herramientas de construcción allí.
Cambia Caddy a tu dominio real y publica únicamente 80/443 del proxy. Lleva los secretos por un canal de administración seguro y prepara copias fuera de la máquina. Verifica por HTTPS lectura, rechazo sin token, escritura y readiness; después mide memoria, logs y tiempo de recuperación.
El proyecto ya muestra contenedores complementarios, datos persistentes, permisos, observación y recuperación. Para crecer, decide qué necesidad apareció: paginar resultados, limitar conexiones con un pool, añadir usuarios en lugar del token compartido o ejecutar una tarea larga en un worker. Introduce cada pieza con su fallo esperado y su comprobación; no porque el diagrama "parezca más de producción".
#Qué queda fuera y por qué
El token compartido es un mecanismo sencillo de administración, no un sistema de cuentas ni permisos por usuario. No hay rate limiting distribuido, búsqueda compleja, backups programados a un proveedor ni despliegue multi-host. Su necesidad depende del servicio que construyas. Lo que sí debes conservar al extenderlo es la separación de permisos, los límites de tiempo, la compatibilidad de migraciones y el ensayo de restauración.
Al terminar la práctica, docker compose down elimina cómputo y redes conservando los volúmenes. docker compose down -v elimina también los datos de este proyecto: úsalo solo si has decidido descartar el laboratorio. Conserva una copia comprobada antes de cualquier operación de eliminación que afecte a datos útiles.
#Fuentes de las piezas que acabas de conectar
- Gunicorn y Docker: servidor, logs y señales; los valores concretos aquí son decisiones del ejemplo.
- Uso básico de Psycopg y parámetros SQL: conexiones, transacciones y valores separados del SQL.
- Inicialización de la imagen PostgreSQL: ejecución solo con datos vacíos y servidor temporal.
- Directiva
reverse_proxyde Caddy: conexión con la aplicación. - Contrato WSGI: interfaz entre aplicación Python y servidor HTTP.