Instancia dockerizada de Koha 25.11 (Ubuntu 20.04) para el catálogo del Instituto Humboldt. Incluye OPAC, intranet, Plack, Zebra, Memcached y RabbitMQ corriendo en un solo contenedor, con perfiles para alternar entre BD local (MariaDB) y BD en la nube (RDS).
El contenedor está diseñado para correr detrás de un reverse proxy con terminación TLS. En local se simula con un servicio `nginx`; en producción se reemplaza por un ALB de AWS.
## Requisitos
- Docker Desktop con Docker Compose v2
- OpenSSL (para generar el certificado autofirmado del proxy local)
- Permisos de administrador para editar `C:\Windows\System32\drivers\etc\hosts`
- Permisos de administrador para importar el certificado raíz local
## Setup inicial (una sola vez)
### 1. Copiar el `.env`
```bash
cp .env.example .env
```
Editar `.env` y ajustar las credenciales según el entorno:
| Variable | Valor para BD local | Valor para RDS |
### Modo local (MariaDB en Docker + proxy nginx con HTTPS)
```bash
docker compose --profilelocal up --build
```
Activa los servicios `db`, `koha` y `proxy`. El **primer arranque tarda 10-20 minutos**: importa el dump SQL, ejecuta `koha-upgrade-schema`, reindexar Zebra y aplica todos los parches. Los reinicios posteriores son rápidos gracias al `INIT_FLAG` persistido en el volumen.
### Modo RDS (BD en la nube, sin proxy local)
```bash
docker compose up --build
```
Sin `--profile local` solo arranca el contenedor `koha`. Útil para probar contra RDS antes de subir a ECS.
## Acceso a la aplicación
### Con perfil local (recomendado)
| URL | Descripción |
|-----|-------------|
| `https://koha.local` | OPAC (catálogo público) — pasa por nginx, simula el ALB |
| `https://koha-intra.local` | Intranet — pasa por nginx |
| `http://koha.local:8080` | OPAC directo a Apache (sin SSL) — útil para debug |
| `http://koha-intra.local:8080` | Intranet directo a Apache |
### Sin proxy local
| URL | Descripción |
|-----|-------------|
| `http://koha.local:8080` | OPAC directo a Apache |
| `http://koha-intra.local:8080` | Intranet directo a Apache |
> El acceso por `http://localhost:8080` muestra la página default de Apache porque el header `Host` no coincide con ningún VirtualHost. Usar siempre los dominios configurados.
## Comandos útiles
```bash
# Ver logs en vivo
docker compose --profilelocal logs -f koha
# Ver estado de los servicios
docker compose --profilelocal ps
# Entrar al contenedor
docker compose --profilelocal exec koha bash
# Verificar Plack
docker compose --profilelocal exec koha koha-plack --status prod_humboldt
# Forzar reindexación de Zebra
docker compose --profilelocal exec koha koha-rebuild-zebra -f-v prod_humboldt
# Parar sin borrar volúmenes (preserva BD local + INIT_FLAG)
docker compose --profilelocal down
# Borrar todo y forzar reimportación
docker compose --profilelocal down -v
```
## Health check
El contenedor expone dos endpoints de health check según la fase del ciclo de vida:
| Fase | Puerto | Quién responde | Cuánto dura |
|------|--------|----------------|-------------|
| FIRST_RUN (importación de BD, schema upgrade, reindex) | `8099` | Python `http.server` en background | 10–20 min |
| Operación normal | `80` | Apache → Plack → Koha | indefinido |
El servidor en `:8099` arranca como primera acción del entrypoint y se mata justo antes de que Apache tome el control. Esto evita que ECS marque el task como unhealthy y lo reinicie durante el FIRST_RUN, que es lo más caro de reproducir.
### Verificación local
```bash
# Mientras el contenedor inicializa (FIRST_RUN)
docker compose exec koha wget -qO- http://localhost:8099/
# → starting
# Después de que arrancan los servicios
docker compose exec koha wget -qO--S http://localhost/ 2>&1 | head -1
El `||` encadena las dos pruebas: durante el FIRST_RUN responde `:8099`; cuando Apache toma `:80` el primer comando falla y cae al fallback. `startPeriod: 60s` da margen al proceso Python para hacer `bind()`.
### Configuración ALB (target group)
| Parámetro | Valor |
|-----------|-------|
| Protocol | HTTP |
| Port | 80 |
| Path | `/` |
| Healthy threshold | 2 |
| Unhealthy threshold | 5 |
| Interval | 30s |
| Timeout | 10s |
| Success codes | `200-399` |
El ALB chequea `:80`, no `:8099`. Durante el FIRST_RUN el target aparece `unhealthy` en el ALB → simplemente no recibe tráfico (el `startPeriod` del task health check evita que ECS lo reinicie). Cuando Apache arranca, el ALB lo marca `healthy` y empieza a enrutar.
> Importante: el container health check (ECS) y el target group health check (ALB) son independientes. El primero controla el ciclo de vida del task; el segundo, el routing de tráfico.
## Decisiones de diseño relevantes
-**Health check temporal en puerto 8099**: durante el FIRST_RUN (10-20 min) un servidor HTTP en Python responde 200 en `:8099` para que ECS no reinicie el task por timeout. Apache toma `:80` cuando termina.
-**mpm_itk + LD_PRELOAD**: Docker no otorga `CAP_SETGID`, así que `mpm_itk` falla al llamar `initgroups()`. La librería `libfakegroups.so` compilada en el Dockerfile intercepta esa llamada y devuelve 0.
-**mysql-client-8.0**: Ubuntu 20.04 instala el cliente MariaDB por default, que no soporta `caching_sha2_password` de MySQL 8. Se reemplaza con `mysql-client-8.0` para compatibilidad con RDS MySQL 8.
-**Filtros en el dump**: el SQL dump original tiene FKs y GRANTs incompatibles. El entrypoint filtra `CONSTRAINT ... FOREIGN KEY`, `ADD CONSTRAINT`, `GRANT`, `CREATE USER`, etc. antes de pasarlo a `koha-mysql`.
-**Log files pre-creados**: `opac-error.log` e `intranet-error.log` se tocan con ownership `prod_humboldt-koha` antes de iniciar servicios, para que Apache (root) y Plack (instance-user) puedan escribir en los mismos archivos.
-**ALB headers**: `mod_remoteip` confía en `X-Forwarded-For` de RFC 1918; `X-Forwarded-Proto: https` se mapea a la variable `HTTPS=on` para que Koha emita cookies `Secure` y URLs absolutas con esquema correcto.
## Despliegue a producción (ECS)
Resumen — para detalles ver la conversación de migración.
1.**ECR**: build + push de la imagen
2.**RDS MariaDB/MySQL 8**: crear DB y usuario `koha_prod_humboldt` con `ALL PRIVILEGES` (sin `WITH GRANT OPTION`)
3.**EFS**: filesystem con 2 access points (`/koha_data`, `/koha_logs`) con UID/GID 1000
4.**Secrets Manager**: guardar `DB_PASSWORD`
5.**ECS Task Definition**: Fargate, 2 vCPU / 4 GB RAM, montar EFS. Health check según la sección [Health check](#health-check).
6.**ALB**: HTTPS:443 → target group puerto 80 (un solo TG sirve ambos dominios — Apache enruta por `Host` header). HTTP:80 → redirect 301 a HTTPS.
7.**ACM**: certificado con SANs `catalogo.humboldt.org.co` + `intranet.humboldt.org.co`
8.**DNS**: dos CNAME apuntando al DNS name del ALB
## Troubleshooting
### Las URLs se quedan cargando
Revisar los logs de Plack:
```bash
docker compose --profilelocal exec koha tail -20 /var/log/koha/prod_humboldt/plack-error.log
```
Si dice `Permission denied` en `opac-error.log`, los archivos de log quedaron con ownership root. Fix manual: