Appearance
Imágenes Docker
Todas las imágenes usan build multi-etapa: una etapa de construcción con las herramientas de compilación y una etapa final mínima con solo lo necesario para ejecutar.
Las imágenes se publican en el registry de GitLab:
git.eje.gob.bo:5050/proceso-seleccion/<repositorio>:<tag>Backends (Django)
Aplica a auth-backend y backend-seleccion-postulantes.
Etapas
| Etapa | Base | Contenido |
|---|---|---|
builder | python:3.11-slim | Instala requirements.prod.txt en /install (pip --prefix) |
| runtime | python:3.11-slim | Copia /install a /usr/local, el código fuente, nginx y entrypoint.sh |
Paquetes de sistema en runtime:
auth-backend:nginx,curl.backend-seleccion-postulantes:nginx,curl,ghostscripty librerías nativas que WeasyPrint y OpenCV cargan en tiempo de ejecución (libpango,libpangocairo,libgdk-pixbuf,libcairo2,libffi8,libglib2.0-0,libgl1,libsm6,libxext6,libxrender1,shared-mime-info, fuentesliberationydejavu). La etapabuilderinstala además las librerías de desarrollo necesarias para compilar estas dependencias.
Usuario y permisos
El contenedor corre como appuser (no root). nginx (www-data) y gunicorn (appuser) comparten acceso a los volúmenes:
dockerfile
RUN useradd -m appuser \
&& mkdir -p /app/public_files /app/private_files \
&& chown -R appuser:www-data /app \
&& chmod -R 775 /app/public_files /app/private_filesTIP
Si en el host los directorios storage/public o storage/private tienen otro propietario, el backend no podrá escribir archivos. Deben ser escribibles por el UID de appuser dentro del contenedor.
Procesos dentro del contenedor
Cada contenedor de backend ejecuta nginx + gunicorn:
- gunicorn: 2 workers, timeout 60 s, logs de acceso y error a stdout/stderr.
- nginx:
proxy_read_timeout 60sy cabecerasHost,X-Real-IP,X-Forwarded-For,X-Forwarded-Protohacia gunicorn. - La ruta
/private_files/esinternal: solo se sirve cuando Django responde con la cabeceraX-Accel-Redirecttras validar el acceso.
Health check
dockerfile
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD curl -fsS http://localhost/health/ || exit 1El endpoint /health/ está definido en Django (auth_interop_backend/urls.py y seleccion_backend/urls.py).
entrypoint.sh
Se ejecuta en cada arranque del contenedor.
auth-backend
sh
python manage.py migrate --noinput
python manage.py collectstatic --noinput --clear -v 0
python manage.py rotate_signing_keys
python manage.py create_default_scopes
python manage.py create_claim_definitions
nginx -g "daemon off;" &
exec gunicorn auth_interop_backend.wsgi:application --bind 127.0.0.1:8000 ...backend-seleccion-postulantes
sh
python manage.py migrate --noinput
python manage.py collectstatic --noinput --clear -v 0
# Bucle en segundo plano: cada 30 min
python manage.py limpiar_segmentos_huerfanos
nginx -g "daemon off;" &
exec gunicorn seleccion_backend.wsgi:application --bind 127.0.0.1:8000 ...INFO
Las migraciones se aplican automáticamente al desplegar. Los comandos de arranque se describen en Scripts útiles de los backends.
Frontends (Vue + Vite)
Aplica a los portales del IDP, del SPS y a la estación de examen.
| Etapa | Base | Contenido |
|---|---|---|
builder | node:24-alpine | pnpm@11.3.0 vía corepack, pnpm install --frozen-lockfile, pnpm run build |
| runtime | nginx:alpine | Copia /app/dist a /usr/share/nginx/html y nginx.conf |
Variables de build (VITE_*)
Las variables VITE_* se incrustan en el bundle durante el build (se pasan como --build-arg). No pueden cambiarse en runtime: cualquier cambio requiere un nuevo build.
| Repositorio | Build args |
|---|---|
auth-authorization-frontend | VITE_TITLE_APP, VITE_API_ENDPOINT, VITE_API_ENDPOINT_FILES, VITE_PUBLIC_AUTH_PORTAL, VITE_ADMIN_AUTH_PORTAL |
auth-frontend-admin | VITE_TITLE_APP, VITE_API_ENDPOINT, VITE_API_ENDPOINT_PATH, VITE_API_ENDPOINT_FILES, VITE_LOGIN_REDIRECT_URL, VITE_MANUAL_TECNICO_URL, VITE_MANUAL_USUARIO_URL |
frontend-public-auth | VITE_TITLE_APP, VITE_API_ENDPOINT, VITE_API_ENDPOINT_PATH, VITE_API_ENDPOINT_FILES, VITE_API_FILES_PRIVATE, VITE_LOGIN_REDIRECT_URL, VITE_JACOBITUS_API_URL |
frontend-portal-admin-seleccion-postulantes | VITE_TITLE_APP, VITE_API_ENDPOINT, VITE_API_ENDPOINT_PATH, VITE_API_ENDPOINT_FILES, VITE_AUTH_ORIGIN_PORTAL, VITE_API_FILES_PRIVATE, VITE_MANUAL_USUARIO_URL, VITE_MANUAL_TECNICO_URL, VITE_AUTH_PUBLIC_URL |
frontend-portal-comisiones-seleccion-postulantes | VITE_TITLE_APP, VITE_API_ENDPOINT, VITE_API_ENDPOINT_PATH, VITE_API_ENDPOINT_FILES, VITE_API_FILES_PRIVATE, VITE_AUTH_ORIGIN_PORTAL, VITE_AUTH_PUBLIC_URL |
frontend-portal-postulantes-seleccion-postulantes | VITE_TITLE_APP, VITE_API_ENDPOINT, VITE_API_ENDPOINT_PATH, VITE_API_ENDPOINT_FILES, VITE_API_FILES_PRIVATE, VITE_LOGIN_REDIRECT_URL, VITE_AUTH_ORIGIN_PORTAL, VITE_DOMAIN_PUBLIC_URL |
frontend-estacion-examen | VITE_TITLE_APP, VITE_API_ENDPOINT, VITE_API_ENDPOINT_PATH, VITE_API_FILES_PRIVATE |
Los valores se toman de las variables CI/CD DEV_VITE_* o PROD_VITE_* según la rama. Ver Variables y secretos de CI/CD.
nginx de los frontends
nginx
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
gzip on;
gzip_types text/plain text/css application/javascript application/json image/svg+xml;
location /health {
access_log off;
default_type text/plain;
return 200 'OK';
}
# SPA: rutas del router resuelven a index.html
location / {
try_files $uri $uri/ /index.html;
}
location ~* \.(js|css|png|jpg|svg|ico|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}Manuales (VitePress)
Aplica a frontend-manual-tecnico y frontend-manual-de-usuario.
Misma estructura que los frontends, con estas diferencias:
- El build ejecuta
pnpm run docs:build. - Se copia
/app/.vitepress/disten lugar de/app/dist. - No reciben build args.
- Usan la misma configuración de nginx que los frontends.
Construcción local
Para validar una imagen antes de subir cambios:
sh
# Backend
docker build -t auth-backend:local ./auth-backend
# Frontend (los build args son obligatorios para un bundle funcional)
docker build \
--build-arg VITE_TITLE_APP="..." \
--build-arg VITE_API_ENDPOINT="..." \
-t frontend-portal-admin:local ./frontend-portal-admin-seleccion-postulantes