Saltar al contenido

Capítulo 8 de 13

Publicar con HTTPS y poder volver atrás

Pasar de localhost a un servidor Linux con dominio, proxy, certificados, actualizaciones y rollback verificable.

#Desplegar es conservar un estado conocido

Una aplicación está desplegada cuando sus usuarios pueden usarla y tú puedes explicar qué versión corre, dónde están sus datos y cómo recuperarla. docker compose up -d es una pieza, no la definición completa. Antes de poner tráfico real, necesitas un servidor, acceso administrativo, dominio, configuración, credenciales, almacenamiento, backups y una forma de observar el resultado.

Usaremos un servidor Linux y Compose. Es un alcance razonable para un equipo pequeño: varios servicios conectados, cambios controlados y una máquina como dominio de fallo. Si esa máquina cae, los contenedores de la misma máquina no ofrecen alta disponibilidad; las copias y el procedimiento de recuperación determinan cómo vuelves.

#El recorrido desde Internet

Un dominio como catalogo.tudominio.com se resuelve mediante DNS hacia la IP pública del servidor. El navegador abre una conexión TLS al puerto 443. TLS cifra el tráfico y permite verificar el servidor mediante un certificado. El proxy recibe esa conexión, aplica límites y envía HTTP a api:8000 dentro de Docker.

Un proxy inverso es un servidor que recibe peticiones de los usuarios y las dirige a otro servicio. Aquí centraliza HTTPS para que la API no tenga que gestionar certificados. Usaremos Caddy, un servidor web con emisión y renovación automática de certificados. Se elige por simplificar esta operación concreta; Nginx con gestión de certificados separada también sirve.

Entrega de una imagen probada al servidor, comprobación de salud y retorno al artefacto previo si falla.Ampliar diagrama ↗

La imagen cambia; los datos permanecen. Volver a la imagen anterior solo es seguro si sigue entendiendo el esquema actual.

#Preparar la máquina y el dominio sin abrir la base

Instala Docker Engine y el complemento Compose siguiendo las instrucciones oficiales de la distribución. Entra con una cuenta administrativa mediante SSH, el protocolo para administrar una máquina a través de una conexión cifrada. Usa claves y conserva una vía de recuperación del proveedor antes de cambiar acceso o firewall.

Reserva una carpeta de despliegue, por ejemplo /opt/catalogo, con configuración y secretos accesibles solo por las cuentas necesarias. Tu cuenta de despliegue puede administrar Docker; trátala como una cuenta potente. No expongas el socket del motor en un puerto público para facilitar despliegues.

Configura un registro DNS A hacia la IPv4 del servidor. Si añades un registro AAAA para IPv6, esa ruta también debe llegar al servidor: un IPv6 mal configurado puede romper validación o acceso aunque IPv4 funcione. En el firewall del proveedor permite 80 y 443 para el proxy, y SSH solo según tu política de administración. La base no necesita un puerto público.

#Caddyfile de producción

En el proyecto final hay un Caddyfile local. Al publicar, sustituyes su sitio HTTP local por tu dominio real:

catalogo.tudominio.com {
    request_body {
        max_size 8KB
    }
    reverse_proxy api:8000 {
        transport http {
            dial_timeout 3s
            response_header_timeout 10s
        }
    }
    log {
        output stdout
        format json
    }
}

Reemplaza el dominio; este nombre es ilustrativo. request_body limita el cuerpo de las peticiones antes de que llegue a la aplicación. dial_timeout limita cuánto espera el proxy al conectar; response_header_timeout, cuánto espera a las primeras cabeceras. El límite del servidor y el de la consulta a base también deben ser coherentes. Los logs van a la salida que Docker recoge.

En Compose, el servicio se parece a esto:

services:
  proxy:
    image: caddy:2-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    networks: [entrada]
    restart: unless-stopped
volumes:
  caddy_data:
  caddy_config:
networks:
  entrada:

Es el bloque del proxy, para integrar con el resto de la aplicación; api también debe pertenecer a entrada. El volumen /data conserva certificados y claves. Si lo eliminas en cada entrega, pierdes estado de certificados y puedes generar emisiones innecesarias. TCP 443 basta para HTTPS en este recorrido; HTTP/3 añadiría otras decisiones, pero no es necesario para conseguir un despliegue correcto.

Caddy necesita que el dominio y los puertos de validación sean accesibles, y poder contactar con la autoridad de certificación. Si falla HTTPS, revisa DNS, IPv4/IPv6, entrada 80/443, salida y logs. No lo soluciones desactivando validación de certificados en los clientes. Puedes validar sintaxis con docker compose exec proxy caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile y recargar con caddy reload usando esos mismos argumentos cuando el archivo sea correcto.

#Construir una vez y entregar ese artefacto

En desarrollo construyes desde código. En el servidor conviene descargar una imagen que ya pasó pruebas. Un registro guarda ese artefacto. El nombre registro.ejemplo/equipo/catalogo representa el registro y repositorio que hayas creado; no es una dirección lista para usar.

docker build -t registro.ejemplo/equipo/catalogo:revision-abc123 .
docker push registro.ejemplo/equipo/catalogo:revision-abc123

Autentícate con el mecanismo de tu registro, sin poner tokens literales en comandos que queden en el historial. Registra el digest que devuelve la publicación. El servidor puede configurar APP_IMAGE=registro.ejemplo/equipo/catalogo@sha256:… en su archivo de entorno: sustituye los puntos por el digest completo real.

Conservar una etiqueta de revisión facilita identificar la fuente. Fijar el digest evita que alguien mueva esa etiqueta y cambie una entrega sin cambiar la configuración. Conserva también el digest anterior; un rollback no sirve si el artefacto anterior ya fue eliminado del registro.

#Una entrega pequeña y revisable

Supongamos que tu Compose de servidor usa image: ${APP_IMAGE:?Define APP_IMAGE} para api y que el archivo release.env contiene el digest objetivo.

docker compose --env-file release.env config --quiet
docker compose --env-file release.env pull api
docker compose --env-file release.env up -d --no-deps --wait --wait-timeout 90 api
curl --fail --max-time 15 https://catalogo.tudominio.com/health/ready

--no-deps limita la actualización a la API, evitando recrear la base sin necesitarlo. --wait espera el estado de salud de los servicios con healthcheck; no sustituye a una petición por el dominio público. Conserva exactamente los archivos y variables usados en todos los comandos: si cambias --env-file entre pull y up, puedes desplegar otra cosa.

No hagas down antes de cada entrega. Añade una caída de todos los servicios y no es necesario para reemplazar la API. Con una sola instancia de API puede haber una interrupción breve al recrearla. Medirla y aceptarla conscientemente es mejor que prometer "cero downtime" sin implementar una estrategia que lo sostenga.

#Migraciones que permiten volver atrás

Un cambio de código puede necesitar otro esquema de base. Imagina que renombras titulo a nombre y eliminas titulo: el código anterior dejaría de funcionar tras el rollback. La reversión de la imagen no revierte los datos ni el esquema.

Una secuencia compatible es ampliar, migrar, retirar:

  1. Añade la columna o estructura nueva conservando lo anterior.
  2. Publica código que pueda convivir con ambos formatos y rellena datos existentes mediante una tarea controlada.
  3. Comprueba lecturas y escrituras, y deja pasar tu ventana de rollback.
  4. Retira la estructura anterior en una entrega posterior.

La técnica exacta depende del cambio: no todos requieren escribir dos columnas. La idea es evitar que el primer despliegue destruya inmediatamente la compatibilidad. Ejecuta migraciones una vez por entrega, con errores visibles y backup reciente. No lances migraciones simultáneamente desde cada worker.

#Rollback de aplicación y recuperación de datos

Si la nueva API falla y el esquema sigue compatible, configura el digest anterior y aplica la misma secuencia pull + up --no-deps --wait api. Verifica lectura y una operación válida, no solo la página de inicio. Documenta qué versión quedó activa y conserva logs de la fallida.

Si el nuevo código ya corrompió datos, revertir la imagen detiene el origen del problema pero no repara lo escrito. Puede hacer falta una corrección de datos o restaurar a una nueva base y reconciliar operaciones posteriores. Restaurar una copia antigua sobre producción pierde escrituras nuevas: es una decisión de recuperación, no un paso automático de cualquier rollback.

Una entrega con dos grupos de API —el anterior y el candidato— permite verificar el candidato antes de cambiar el proxy. Ambos deben usar un esquema compatible y no duplicar tareas periódicas. Esa estrategia aumenta uso de recursos y operaciones; aplícala si la interrupción breve deja de ser aceptable, después de tener resueltos backups, healthchecks y compatibilidad.

#Qué comprobar desde fuera

Haz una petición HTTPS desde una conexión distinta a la del servidor, comprueba certificado y código esperado. Verifica que una ruta inexistente devuelve el error previsto, una escritura sin credencial es rechazada y la base no responde en un puerto público. Después reinicia la API para probar recuperación y planifica un ensayo de reinicio del host en un entorno de prueba.

No confundas lo que puedes validar en local con lo que requiere un dominio público real. La sintaxis de Caddy y el proxy HTTP se pueden probar en local. La emisión pública de certificados necesita dominio, DNS y conectividad configurados: compruébala en ese entorno y registra el resultado.

#Referencias para profundizar

    ↑ ↓ para recorrer · Intro para abrir · Ctrl K para buscar