Nueva seccion 'Templates' con la pagina principal de platform-templates (diagrama Mermaid del flujo codigo->produccion, los 3 arquetipos, el hallazgo real de Gitea sobre uses: cross-repo). Nuevo playbook de la migracion de docs-portal/frontend/medusa, con los 3 bugs reales encontrados en produccion (runAsNonRoot sin imagen no-root, capabilities rompiendo el chown de nginx, runAsNonRoot sin runAsUser numerico) y los 2 gotchas de Kustomize (rename via patch, anular un campo heredado). Nota: el directorio se llama docs/plantillas/ y no docs/templates/ -- MkDocs excluye silenciosamente cualquier carpeta llamada 'templates' del build (confirmado en vivo con mkdocs build --strict, sin ningun error/warning que lo explique, solo un INFO 'excluded from built site'). Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_011FqYP3Wf1W63Qmh7qgXAAT
5.1 KiB
Migración a platform-templates: docs-portal, frontend, medusa (2026-08)
| Ventana | 2026-08-17 |
| Servicios migrados | docs-portal, ecommerce-frontend (frontend-deploy), commerce-backend (medusa-deploy) |
| Impacto en producción | Cero downtime en las 3 — cada rollout mantuvo el pod viejo sirviendo tráfico hasta que el nuevo estuvo sano |
Qué cambió
Las 3 apps pasaron de manifiestos Kustomize sueltos y propios de cada
una a un overlay sobre el base común de
platform-templates
(k8s-manifests/base/, referenciado por URL de git remota, no una
copia local). El base agrega por default lo que ninguna de las 3 tenía
completo: readinessProbe/livenessProbe (docs-portal y frontend no
tenían ninguna) y securityContext a nivel pod y contenedor.
Ningún nombre de recurso cambió (docs-portal-deployment,
frontend-deploy, medusa-deploy, sus Services, todo se mantuvo
idéntico) — importante para no romper el tracking de Argo CD ni la
resolución DNS interna de otros servicios que dependen de esos nombres
(ej. MEDUSA_BACKEND_URL: http://medusa-svc:9000 en el frontend).
Por qué: 3 bugs reales encontrados aplicando esto en producción
Los 3 se encontraron con el pod viejo todavía sirviendo tráfico
(maxUnavailable: 0 en la estrategia de rollout, y Kubernetes excluye
automáticamente del Service cualquier pod que no pase su
readinessProbe) — cero impacto real en ningún momento, pero sí
bugs de verdad, no hipotéticos.
1. runAsNonRoot: true contra una imagen que corre como root (docs-portal)
docs-portal usa nginx:1.27-alpine sin ningún USER en su
Dockerfile — corre como root. Aplicar el default del base
(runAsNonRoot: true) rompió el pod al instante:
CreateContainerConfigError. Fix: override explícito
runAsNonRoot: false para este container puntual, documentado como
temporal hasta que docs-portal adopte
dockerfiles/static-nginx.Dockerfile
(nginxinc/nginx-unprivileged, no-root de verdad) — cambio de imagen
aparte, no hecho todavía.
2. capabilities: drop: [ALL] rompe el propio arranque de nginx (docs-portal)
Con runAsNonRoot: false ya resuelto, el pod siguió fallando:
2026/08/17 05:30:33 [emerg] 1#1: chown("/var/cache/nginx/client_temp", 101) failed (1: Operation not permitted)
El entrypoint oficial de la imagen de nginx hace un chown() sobre su
propio directorio de cache en cada arranque — necesita CAP_CHOWN.
El base saca todas las capabilities por default; incluso corriendo
como root, sin CAP_CHOWN ese chown() falla igual. Fix: sacar la
restricción de capabilities entera para este container
(capabilities: null en el patch — es la única forma real de anular
un drop: [ALL] heredado, ver nota técnica más abajo). No es peor que
el comportamiento actual en producción, solo no está endurecido
todavía — se resuelve del todo con el mismo cambio de imagen del punto
1.
3. runAsNonRoot: true sin runAsUser numérico (frontend)
ecommerce-frontend sí corre no-root (USER nextjs en su Dockerfile)
— pero el pod nuevo falló igual:
Error: container has runAsNonRoot and image has non-numeric user (nextjs),
cannot verify user is non-root
El Dockerfile define el usuario por nombre (useradd ... nextjs),
no por UID numérico. Kubernetes no puede confirmar que ese nombre
resuelve a un UID distinto de 0 sin arrancar el contenedor, así que por
seguridad se niega a intentarlo. Fix: runAsUser: 1001 explícito
(el UID real del useradd --uid 1001 del Dockerfile).
Aplicado ya con este aprendizaje: para medusa-deploy, se confirmó
el UID real del usuario node de la imagen con docker run --rm <imagen> id node (→ 1000) antes de escribir el overlay, en vez de
asumirlo — la migración de Medusa no tuvo ningún bug, arrancó sana en
el primer intento, initContainers (wait-for-postgres, migrations)
incluidos.
Nota técnica: 2 gotchas de Kustomize (no de Kubernetes) encontrados armando esto
- Renombrar un recurso vía patch de merge normal no funciona —
aunque uses
target:para seleccionar el recurso del base por su nombre original, cambiarmetadata.namedentro del contenido del patch no lo renombra en el resultado final. Hace falta un patch JSON6902 aparte (op: replace, path: /metadata/name). - Anular un campo heredado necesita
nullexplícito — para sacar elcapabilities: {drop: [ALL]}del base (bug #2 de arriba), ni omitir el campo ni un objeto vacío{}alcanzan; solocapabilities: nullen el patch realmente lo borra.
Ambos confirmados corriendo kubectl kustomize antes de aplicar nada
al cluster — no son suposiciones.
Validación
Cada una de las 3 migraciones se confirmó con un curl real al dominio
público correspondiente después del rollout:
docs.cruzcloud.net→200shop.cruzcloud.net→200commerce.cruzcloud.net/app→200
Y con el estado del pod nuevo (kubectl get pods, sin reinicios,
1/1 Ready) antes de dar cada migración por cerrada.