docs(docs-portal): reflejar fixes de MTU, gitea-runner y cosign digest/rekor
Nuevo playbook del blackhole MTU cross-node (diagnostico + fix + validacion). Actualiza el playbook de contencion de recursos Gitea/runner de 'pendiente' a 'aplicado'. Actualiza cosign.md: firma por digest ya implementada, Rekor como decision tomada (no TODO). Corrige 2 links que quedaron rotos al renombrar la seccion de limitaciones (validado con mkdocs build --strict). Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_011FqYP3Wf1W63Qmh7qgXAAT
This commit is contained in:
1 parent
86fcc88a1e
commit
b0b832f142
6 files changed
+200
-81
No files matched your search
@@ -79,51 +79,53 @@ localmente, sin tocar el transparency log público
|
|||||||
|
|
||||||
## Qué NO se firma
|
## Qué NO se firma
|
||||||
|
|
||||||
Solo se firma el tag versionado (`ecommerce-frontend:v1.0.X`), no
|
No se firma `:latest` en ningún caso — es un tag mutable, se re-apunta a
|
||||||
`:latest`. `:latest` es un tag mutable — se re-apunta a una imagen
|
una imagen distinta en cada build, así que firmarlo no significaría nada
|
||||||
distinta en cada build — así que firmarlo no significa nada útil: la
|
útil. Y desde 2026-08-16 (ver más abajo) tampoco se firma directamente
|
||||||
firma quedaría asociada al digest de turno, y la siguiente build la
|
por el tag versionado — se firma por **digest**, que es la única
|
||||||
volvería a mover. Cualquier verificación real de firma debería apuntar
|
referencia que realmente identifica una imagen de forma inequívoca.
|
||||||
siempre a un tag de versión específico (o, mejor todavía, al digest
|
|
||||||
exacto).
|
|
||||||
|
|
||||||
## Verificar manualmente
|
## Verificar manualmente
|
||||||
|
|
||||||
Con la llave pública del repo, cualquiera puede confirmar la firma de
|
Con la llave pública del repo, cualquiera puede confirmar la firma de
|
||||||
una imagen sin necesitar acceso a nada privado:
|
una imagen sin necesitar acceso a nada privado. Apuntando al digest
|
||||||
|
exacto (lo que firma el pipeline hoy):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cosign verify \
|
cosign verify \
|
||||||
--key workloads/ecommerce/cosign.pub \
|
--key workloads/ecommerce/cosign.pub \
|
||||||
--insecure-ignore-tlog=true \
|
--insecure-ignore-tlog=true \
|
||||||
gitea.cruzcloud.net/devops/ecommerce-frontend:v1.0.97
|
gitea.cruzcloud.net/devops/ecommerce-frontend@sha256:<digest>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
También funciona apuntando a un tag — Cosign lo resuelve al digest que
|
||||||
|
tenga en ese momento antes de verificar —, pero por eso mismo es menos
|
||||||
|
preciso: si el tag fue reasignado después de la firma original, estarías
|
||||||
|
verificando una imagen distinta de la firmada, sin que nada te avise.
|
||||||
|
|
||||||
Si la imagen fue firmada por este pipeline, el comando termina con
|
Si la imagen fue firmada por este pipeline, el comando termina con
|
||||||
`exit 0` y muestra el detalle de la firma. Si no — sea porque nunca se
|
`exit 0` y muestra el detalle de la firma. Si no — sea porque nunca se
|
||||||
firmó, porque la firmó otra llave, o porque la imagen fue modificada
|
firmó, porque la firmó otra llave, o porque la imagen fue modificada
|
||||||
después — termina con `exit 1` y un error explícito.
|
después — termina con `exit 1` y un error explícito.
|
||||||
|
|
||||||
## Limitaciones conocidas y mejoras futuras
|
## Decisiones ya resueltas (antes eran TODO)
|
||||||
|
|
||||||
Dos limitaciones identificadas al validar el pipeline end-to-end,
|
Dos preguntas abiertas al validar el pipeline end-to-end por primera vez,
|
||||||
documentadas a propósito como TODO — ninguna de las dos se implementó
|
ambas ya cerradas — se documentan igual, porque el *por qué* de cada
|
||||||
todavía.
|
decisión sigue siendo útil.
|
||||||
|
|
||||||
### 1. Se firma por tag, no por digest
|
### 1. Firma por digest, no por tag — implementado 2026-08-16
|
||||||
|
|
||||||
Hoy el pipeline firma `ecommerce-frontend:v1.0.104` — un **tag**, no un
|
Hasta el 2026-08-15, el pipeline firmaba `ecommerce-frontend:v1.0.104` —
|
||||||
**digest** (`sha256:...`). Un tag es una etiqueta mutable: nada impide
|
un **tag**, no un **digest** (`sha256:...`). Un tag es una etiqueta
|
||||||
que, después de firmada la imagen, alguien (o un bug en el propio
|
mutable: nada impedía que, después de firmada la imagen, alguien (o un
|
||||||
pipeline) vuelva a subir contenido distinto bajo el mismo tag
|
bug en el propio pipeline) volviera a subir contenido distinto bajo el
|
||||||
`v1.0.104`. La firma original seguiría "verificando" — porque Cosign,
|
mismo tag `v1.0.104`. La firma original seguiría "verificando" — porque
|
||||||
al verificar por tag, resuelve el tag al digest que tenga *en ese
|
Cosign, al verificar por tag, resuelve el tag al digest que tenga *en
|
||||||
momento*, no al que tenía cuando se firmó. Si el tag fue reasignado,
|
ese momento*, no al que tenía cuando se firmó.
|
||||||
se está verificando una imagen distinta de la que realmente se firmó,
|
|
||||||
sin que nada avise.
|
|
||||||
|
|
||||||
Esto no es hipotético: el propio Cosign lo advierte en cada firma de
|
Esto no era hipotético: el propio Cosign lo advertía en cada firma de
|
||||||
este pipeline (visto en los logs reales de cada run):
|
este pipeline:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
WARNING: Image reference gitea.cruzcloud.net/.../ecommerce-frontend:v1.0.102
|
WARNING: Image reference gitea.cruzcloud.net/.../ecommerce-frontend:v1.0.102
|
||||||
@@ -131,20 +133,20 @@ uses a tag, not a digest, to identify the image to sign.
|
|||||||
This can lead you to sign a different image than the intended one.
|
This can lead you to sign a different image than the intended one.
|
||||||
```
|
```
|
||||||
|
|
||||||
**TODO:** capturar el digest exacto que devuelve `docker push` (o
|
**Fix aplicado:** el pipeline ahora captura el digest real que devuelve
|
||||||
`docker buildx build --metadata-file`) y firmar/verificar contra ese
|
`docker push` (parseando `sha256:[a-f0-9]{64}` de la salida) y lo usa
|
||||||
digest en vez del tag —
|
tanto para firmar como para verificar —
|
||||||
`gitea.cruzcloud.net/devops/ecommerce-frontend@sha256:...` en vez de
|
`gitea.cruzcloud.net/devops/ecommerce-frontend@sha256:...` en vez de
|
||||||
`:v1.0.104`. No implementado todavía; requiere ajustar el step de
|
`:v1.0.104` — en los tres pipelines (`ecommerce-frontend`,
|
||||||
build para exponer el digest como output y pasarlo a los steps de
|
`commerce-backend`, `docs-portal`). Esa advertencia de Cosign ya no
|
||||||
firma y verificación.
|
debería aparecer en los logs.
|
||||||
|
|
||||||
### 2. El transparency log (Rekor) está deshabilitado
|
### 2. El transparency log (Rekor) se mantiene deshabilitado — decisión, no pendiente
|
||||||
|
|
||||||
Ya se explicó arriba, en la sección "Por qué `--tlog-upload=false`" de
|
Ya se explicó arriba, en la sección "Por qué `--tlog-upload=false`" de
|
||||||
esta misma página, la decisión consciente de no publicar en el
|
esta misma página, la decisión de no publicar en el transparency log
|
||||||
transparency log para este registry privado. Vale la pena nombrar en
|
para este registry privado. Vale la pena nombrar en simple qué es lo
|
||||||
simple qué es lo que se está dejando afuera:
|
que se está dejando afuera:
|
||||||
|
|
||||||
Un **transparency log** (Rekor, el de Sigstore) es un registro
|
Un **transparency log** (Rekor, el de Sigstore) es un registro
|
||||||
público, append-only, criptográficamente verificable, de "quién firmó
|
público, append-only, criptográficamente verificable, de "quién firmó
|
||||||
@@ -160,17 +162,18 @@ la generó quien tuviera la llave privada en el momento en que se
|
|||||||
verificó"* — pero no hay ningún registro externo e inmutable que
|
verificó"* — pero no hay ningún registro externo e inmutable que
|
||||||
demuestre *cuándo* se generó, ni una forma de detectar si alguien con
|
demuestre *cuándo* se generó, ni una forma de detectar si alguien con
|
||||||
acceso a la llave privada firmó algo por fuera del pipeline sin que
|
acceso a la llave privada firmó algo por fuera del pipeline sin que
|
||||||
quede rastro. Para un lab personal con un registry privado, ese
|
quede rastro.
|
||||||
trade-off es razonable (ver la advertencia más abajo en esta misma
|
|
||||||
página). Pero es una limitación real, no solo un detalle de
|
|
||||||
configuración — importa especialmente el día que este mismo patrón se
|
|
||||||
use en un contexto con más de una persona firmando, o con un registry
|
|
||||||
que deje de ser privado.
|
|
||||||
|
|
||||||
**TODO:** si en algún momento el registry deja de ser exclusivamente
|
**Revisado explícitamente el 2026-08-16** (ya no es un TODO sin decidir,
|
||||||
privado, o se suma más de una persona con acceso a la llave de firma,
|
es una elección consciente confirmada): se mantiene deshabilitado.
|
||||||
reevaluar habilitar el transparency log público de Sigstore (o correr
|
Motivos concretos para este lab — habilitarlo agregaría una llamada de
|
||||||
uno privado propio) en vez de mantenerlo deshabilitado.
|
red saliente por build hacia `rekor.sigstore.dev`, atravesando el mismo
|
||||||
|
túnel de Cloudflare que ya tuvo un incidente de SNI/orden de reglas con
|
||||||
|
Cosign (ver el playbook de [realm HTTP y Cosign](../playbooks/incidente-cosign-realm-http-tunnel-2026-08.md)) — más
|
||||||
|
superficie de fallo por build, sin beneficio real, ya que el registry es
|
||||||
|
privado y no hay a quién auditar públicamente. Revisar esta decisión de
|
||||||
|
nuevo si el registry deja de ser exclusivamente privado, o si se suma
|
||||||
|
más de una persona con acceso a la llave de firma.
|
||||||
|
|
||||||
## Qué falta (a propósito, todavía)
|
## Qué falta (a propósito, todavía)
|
||||||
|
|
||||||
|
|||||||
@@ -105,9 +105,6 @@ flowchart TD
|
|||||||
- Admission controller en el cluster que verifique la firma de Cosign
|
- Admission controller en el cluster que verifique la firma de Cosign
|
||||||
antes de dejar correr un Pod (ver [cosign.md](cosign.md)) — hoy la
|
antes de dejar correr un Pod (ver [cosign.md](cosign.md)) — hoy la
|
||||||
verificación es solo un smoke test dentro del propio pipeline.
|
verificación es solo un smoke test dentro del propio pipeline.
|
||||||
- Firmar por digest en vez de por tag, y reevaluar el transparency log
|
|
||||||
de Sigstore — ver
|
|
||||||
[limitaciones conocidas de Cosign](cosign.md#limitaciones-conocidas-y-mejoras-futuras).
|
|
||||||
- `commerce-backend` tiene dos CVE CRITICAL documentados como excepción
|
- `commerce-backend` tiene dos CVE CRITICAL documentados como excepción
|
||||||
puntual en `workloads/commerce-backend/.trivyignore` (Go stdlib
|
puntual en `workloads/commerce-backend/.trivyignore` (Go stdlib
|
||||||
embebido en el binario de esbuild, sin fix compatible con la versión
|
embebido en el binario de esbuild, sin fix compatible con la versión
|
||||||
|
|||||||
+25
-30
@@ -97,37 +97,32 @@ distintos: el `docker-compose.yml` de `gitea` y el script que crea
|
|||||||
job pesado, sin ningún límite de recursos, puede acaparar
|
job pesado, sin ningún límite de recursos, puede acaparar
|
||||||
suficiente CPU/memoria como para dejar sin aire al resto del host.
|
suficiente CPU/memoria como para dejar sin aire al resto del host.
|
||||||
|
|
||||||
## Fix propuesto (documentado, aplicación manual pendiente)
|
## Fix aplicado (2026-08-16)
|
||||||
|
|
||||||
1. **`scripts/deploy-lab.sh`** (rama `fix/gitea-runner-resource-limits`,
|
1. **`scripts/deploy-lab.sh`** (mergeado a `main`): agrega
|
||||||
pendiente de mergear): agrega `--cpu-shares 512 --memory 8g
|
`--cpu-shares 512 --memory 8g --memory-swap 10g` a la creación de
|
||||||
--memory-swap 10g` a la creación de `gitea-runner`. Solo afecta a la
|
`gitea-runner`. Solo afecta a la próxima vez que se recree el
|
||||||
próxima vez que se cree el contenedor (la función es idempotente),
|
contenedor (la función es idempotente) — el runner queda con menos
|
||||||
no al que ya está corriendo.
|
prioridad relativa que Gitea a propósito.
|
||||||
2. **Fix inmediato en caliente** (sin recrear contenedores), a aplicar
|
2. **`docker-compose.yml` de Gitea** (fuera de este repo, en el host
|
||||||
manualmente por fuera de este repo:
|
ZimaOS — `/DATA/.casaos/apps/gitea/docker-compose.yml`, acceso
|
||||||
|
root-only): `cpu_shares` de `gitea` subido de 90 a **1024**
|
||||||
```bash
|
(el default de Docker), aplicado en caliente con
|
||||||
docker update --cpu-shares 1024 gitea
|
`docker compose up -d` y validado con `docker inspect`.
|
||||||
docker update --cpu-shares 512 --memory 8g --memory-swap 10g gitea-runner
|
3. **Mover `gitea-runner` a k3d** como job/pod separado: sigue quedando
|
||||||
```
|
**solo como recomendación documentada**, no implementado — la
|
||||||
|
contención real ya se resolvió con los límites de arriba, así que
|
||||||
3. **`docker-compose.yml` de Gitea** (fuera de este repo, acceso
|
esto pasa a ser una mejora de aislamiento a futuro, no una urgencia.
|
||||||
root-only): subir `cpu_shares` de `gitea` de 90 a 1024 — pendiente
|
|
||||||
de aplicar manualmente, no se implementó todavía.
|
|
||||||
4. **Mover `gitea-runner` a k3d** como job/pod separado: evaluado como
|
|
||||||
posible mejora de fondo (aislamiento real vía Kubernetes en vez de
|
|
||||||
límites de Docker sueltos), pero queda **solo como recomendación
|
|
||||||
documentada** — no implementado en esta vuelta.
|
|
||||||
|
|
||||||
## Validación
|
## Validación
|
||||||
|
|
||||||
Con `gitea` y `gitea-runner` estables (sin reinicios) después del
|
Con el fix aplicado: `docker inspect gitea --format
|
||||||
incidente, se reintentó el mismo pipeline y corrió de punta a punta
|
'{{.HostConfig.CpuShares}}'` confirma `1024`, el contenedor se recreó
|
||||||
sin interrupciones — pero eso confirma que el host se estabilizó
|
limpio (`Started`, sin errores) y siguió sirviendo tráfico normal
|
||||||
después del pico de carga, **no** que el fix de límites de recursos ya
|
(`200 OK` en `http://127.0.0.1:3002/` inmediatamente después). La
|
||||||
esté aplicado (sigue pendiente, ver arriba). La correlación completa
|
correlación completa carga-pesada → reinicio se había visto en tres
|
||||||
carga-pesada → reinicio ya se había visto antes, en dos fechas
|
fechas distintas (2026-07-30, 2026-08-11 y el propio 2026-08-15 de este
|
||||||
distintas (2026-07-30 y 2026-08-11) además de esta — no es un evento
|
incidente) antes de este fix — no era un evento aislado. Queda por
|
||||||
aislado, es un patrón recurrente que ahora tiene una causa raíz
|
confirmar con el próximo pipeline pesado real que la contención no
|
||||||
concreta y una propuesta de fix.
|
vuelva a aparecer, pero la causa raíz (pesos de CPU desbalanceados y sin
|
||||||
|
techo de memoria en el runner) ya no está presente.
|
||||||
@@ -88,6 +88,6 @@ credenciales reales, de punta a punta.
|
|||||||
|
|
||||||
!!! note "Nota relacionada, no parte de este incidente"
|
!!! note "Nota relacionada, no parte de este incidente"
|
||||||
El mismo log de este fallo mostró, además del error de realm, el
|
El mismo log de este fallo mostró, además del error de realm, el
|
||||||
warning esperado de Cosign sobre firmar por tag en vez de por
|
warning de Cosign sobre firmar por tag en vez de por digest — ya
|
||||||
digest — ver
|
resuelto el 2026-08-16, ver
|
||||||
[limitaciones conocidas de Cosign](../devsecops/cosign.md#limitaciones-conocidas-y-mejoras-futuras).
|
[decisiones ya resueltas de Cosign](../devsecops/cosign.md#decisiones-ya-resueltas-antes-eran-todo).
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
# Incidente: blackhole de red cross-node por MTU/PMTUD (2026-08)
|
||||||
|
|
||||||
|
| | |
|
||||||
|
|---|---|
|
||||||
|
| **Ventana del incidente** | Detectado ~2026-08, causa raíz confirmada y resuelto 2026-08-16 |
|
||||||
|
| **Servicio afectado** | Cualquier pod que necesite hablar con `commerce-postgres-0` (StatefulSet, sin réplicas, fijo a un nodo) desde un pod agendado en otro nodo del cluster k3d |
|
||||||
|
| **Impacto** | `medusa-deploy` quedaba en `CrashLoopBackOff`/`Init` si el scheduler lo mandaba a un nodo distinto al de Postgres — ver [incidente de crashloop de Medusa](incidente-crashloop-medusa.md), donde este mismo blackhole ya había aparecido como sospechoso sin confirmar |
|
||||||
|
|
||||||
|
## Síntoma
|
||||||
|
|
||||||
|
El handshake TCP (SYN/ACK) entre un pod y `commerce-postgres-0` funciona
|
||||||
|
perfecto cuando quedan en nodos distintos — `nc -zv` conecta sin problema.
|
||||||
|
Pero el primer paquete de datos real de la sesión (protocolo Postgres,
|
||||||
|
`pg_isready` incluido) nunca llega. La conexión se cuelga hasta hacer
|
||||||
|
timeout, sin ningún error explícito de por medio.
|
||||||
|
|
||||||
|
## Diagnóstico
|
||||||
|
|
||||||
|
Aislando el nodo con `nodeName` en pods de prueba: **100% de fallos
|
||||||
|
(186/186 intentos)** agendando cross-node; 100% de éxito en el mismo nodo.
|
||||||
|
Reproducible al 100%, no es intermitente.
|
||||||
|
|
||||||
|
Un primer intento de diagnóstico con `ping` normal (sin bit DF) no mostró
|
||||||
|
pérdida de paquetes hasta payloads de 2000 bytes — porque sin el bit DF el
|
||||||
|
kernel simplemente fragmenta y todo "funciona", lo cual **enmascaró** el
|
||||||
|
problema real durante un tiempo. La pista real solo aparece forzando el bit
|
||||||
|
DF (`ping -M do`), que es exactamente lo que hace un socket TCP real con
|
||||||
|
Path MTU Discovery activo — y ahí es donde Postgres (y cualquier TCP con
|
||||||
|
payload grande) se cuelga en silencio.
|
||||||
|
|
||||||
|
!!! warning "Herramienta correcta importa"
|
||||||
|
El `ping` de BusyBox (el que trae la imagen de nodo de k3s,
|
||||||
|
`rancher/k3s`) **no soporta `-M do`** — no hay forma de reproducir esto
|
||||||
|
con un `kubectl exec` directo a cualquier pod. Hizo falta un pod de
|
||||||
|
debug con una imagen completa (`nicolaka/netshoot`, trae `ping` de
|
||||||
|
`iputils` con soporte real de DF) agendado explícitamente en un nodo
|
||||||
|
distinto al de Postgres.
|
||||||
|
|
||||||
|
Con esa herramienta, un sweep de payloads crecientes contra el pod de
|
||||||
|
Postgres dio el límite exacto:
|
||||||
|
|
||||||
|
| Payload interno | Resultado |
|
||||||
|
|---|---|
|
||||||
|
| 1420 bytes | 0% de pérdida |
|
||||||
|
| **1450 bytes** | **100% de pérdida, sin ningún ICMP "frag needed"** |
|
||||||
|
| 1472/1500 bytes | 100% de pérdida |
|
||||||
|
|
||||||
|
Ese límite coincide casi exactamente con el MTU que la propia interfaz
|
||||||
|
`flannel.1` (la interfaz VXLAN de flannel dentro de cada nodo) ya reportaba:
|
||||||
|
**1450**, calculado como `eth0` del nodo (1500) menos 50 bytes de overhead
|
||||||
|
VXLAN. La aritmética de flannel es correcta — pero en la práctica, el
|
||||||
|
último tramo de red no entrega el paquete VXLAN a tamaño completo, y en vez
|
||||||
|
de generar el ICMP "fragmentation needed" que permitiría a Path MTU
|
||||||
|
Discovery ajustarse solo, el paquete se pierde en silencio. Eso es
|
||||||
|
exactamente un **blackhole de PMTUD**: el mecanismo que debería avisar
|
||||||
|
"bajá el tamaño" está roto, no solo el límite en sí.
|
||||||
|
|
||||||
|
## Causa raíz
|
||||||
|
|
||||||
|
Un tramo de la red (probablemente el puente Docker externo que conecta los
|
||||||
|
contenedores de los distintos nodos k3d entre sí) tiene una capacidad real
|
||||||
|
menor a los 1500 bytes que todos los niveles superiores (Docker, flannel)
|
||||||
|
asumen — y ese tramo descarta los paquetes que no entran, en vez de
|
||||||
|
fragmentarlos o avisar por ICMP. flannel calcula su propio MTU
|
||||||
|
correctamente a partir de lo que Docker le reporta, pero lo que Docker le
|
||||||
|
reporta no refleja la capacidad real de punta a punta.
|
||||||
|
|
||||||
|
## Fix aplicado
|
||||||
|
|
||||||
|
`k3s` (usado en este lab) **embebe** flannel — no lo corre como un
|
||||||
|
DaemonSet/binario separado con su propio archivo de configuración editable
|
||||||
|
como en un cluster "vanilla". Confirmado con `k3s server --help`: no existe
|
||||||
|
un flag `--flannel-mtu` en esta versión. El único punto de ajuste real es
|
||||||
|
la interfaz `flannel.1` en vivo, dentro de cada contenedor de nodo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec <nodo> ip link set flannel.1 mtu 1400
|
||||||
|
```
|
||||||
|
|
||||||
|
1400 queda con margen de sobra debajo del límite real confirmado (1420
|
||||||
|
bueno / 1450 blackhole).
|
||||||
|
|
||||||
|
Este ajuste **no persiste** en ningún archivo — se pierde si el contenedor
|
||||||
|
del nodo se reinicia. Automatizado en `scripts/deploy-lab.sh`, función
|
||||||
|
`fix_flannel_mtu()`, llamada tanto en `create_cluster()` (cluster nuevo)
|
||||||
|
como en `start_cluster()` (cluster existente que arranca) — espera hasta
|
||||||
|
20s a que la interfaz `flannel.1` exista antes de ajustarla, por si el nodo
|
||||||
|
todavía no terminó de inicializar su red.
|
||||||
|
|
||||||
|
Bajar el MTU de `flannel.1` en el host **solo afecta pods nuevos** — la CNI
|
||||||
|
de flannel asigna el MTU del `veth` de cada pod al crearlo, leyendo el
|
||||||
|
`flannel.1` del nodo en ese momento. Los pods que ya estaban corriendo se
|
||||||
|
reciclaron con `kubectl rollout restart` sobre todo el namespace
|
||||||
|
`ecommerce` (`commerce-postgres`, `commerce-redis`, `commerce-minio`,
|
||||||
|
`medusa-deploy`, `catalog-api-deploy`, `frontend-deploy`) para que
|
||||||
|
tomaran el MTU nuevo de inmediato, en vez de esperar a que se recrearan
|
||||||
|
solos.
|
||||||
|
|
||||||
|
## Validación
|
||||||
|
|
||||||
|
El mismo sweep de `ping -M do` después del fix ya no se cuelga en
|
||||||
|
silencio — devuelve `Frag needed and DF set (mtu = 1400)`, la respuesta
|
||||||
|
ICMP correcta que le permite al kernel ajustar el tamaño de los paquetes
|
||||||
|
solo. Path MTU Discovery, en otras palabras, **ya funciona** en vez de
|
||||||
|
estar bloqueado.
|
||||||
|
|
||||||
|
Validación final con tráfico real, no solo `ping`: conexión TCP cross-node
|
||||||
|
exitosa hacia el puerto 5432 de Postgres, y una transferencia de 50 KB
|
||||||
|
(muy por encima del umbral que antes se colgaba) completada sin problema.
|
||||||
|
`shop.cruzcloud.net` y `commerce.cruzcloud.net/app` respondiendo `200`
|
||||||
|
después de reciclar todos los pods.
|
||||||
|
|
||||||
|
## Sobre el `podAffinity` en `medusa.yaml`
|
||||||
|
|
||||||
|
El workaround que existía antes de este fix —
|
||||||
|
`podAffinity`/`requiredDuringSchedulingIgnoredDuringExecution` forzando a
|
||||||
|
`medusa-deploy` a agendarse siempre en el mismo nodo que
|
||||||
|
`commerce-postgres-0` — **se deja tal cual está**, a propósito. Ya no es
|
||||||
|
necesario para evitar el blackhole, pero no tiene ningún costo real, y
|
||||||
|
sirve como red de contención si el MTU real del entorno vuelve a cambiar
|
||||||
|
en el futuro (por ejemplo, tras una actualización de ZimaOS o de Docker).
|
||||||
|
No se debe interpretar como deuda pendiente de "limpiar" — es una decisión
|
||||||
|
consciente.
|
||||||
@@ -98,6 +98,7 @@ nav:
|
|||||||
- Docker-in-Docker y Semgrep: playbooks/incidente-docker-in-docker-semgrep-2026-08.md
|
- Docker-in-Docker y Semgrep: playbooks/incidente-docker-in-docker-semgrep-2026-08.md
|
||||||
- Realm HTTP y Cosign: playbooks/incidente-cosign-realm-http-tunnel-2026-08.md
|
- Realm HTTP y Cosign: playbooks/incidente-cosign-realm-http-tunnel-2026-08.md
|
||||||
- Contención de recursos Gitea/runner: playbooks/incidente-contencion-recursos-gitea-runner-2026-08.md
|
- Contención de recursos Gitea/runner: playbooks/incidente-contencion-recursos-gitea-runner-2026-08.md
|
||||||
|
- Blackhole MTU cross-node: playbooks/incidente-mtu-blackhole-cross-node-2026-08.md
|
||||||
- Aprendizajes:
|
- Aprendizajes:
|
||||||
- Notas sueltas: aprendizajes/notas-sueltas.md
|
- Notas sueltas: aprendizajes/notas-sueltas.md
|
||||||
- Guía del estudiante:
|
- Guía del estudiante:
|
||||||
|
|||||||
Reference in new issue
Block a user