Problema

En entornos de Cloud Run es frecuente que un servicio despliegue sin errores aparentes y, sin embargo, cualquier petición HTTP termine en la página genérica “404. That’s an error”. El contenedor está activo, la sonda de arranque pasa y los logs de Cloud Run no registran ninguna solicitud entrante. El síntoma se repite tanto con la URL canónica (*.run.app) como con la URL numérica, y ocurre tanto desde navegadores externos como desde Cloud Shell. En estos casos el problema no está en la aplicación (por ejemplo, Flask/Gunicorn) sino en la capa de control que dirige el tráfico hacia el contenedor.

Causa

Los 404 que nunca llegan al contenedor suelen deberse a una de las siguientes configuraciones:

  1. Restricciones de ingreso (Ingress) a nivel de servicio o de organización

    • INGRESS_TRAFFIC_ALL está desactivado o sobreescrito por una política de organización run.allowedIngress.
    • Un Service Perimeter (VPC Service Controls) que bloquea tráfico externo aunque la política de ingreso sea permisiva.
  2. Política de IAM del invocador

    • Cuando invokerIamDisabled está en false, Cloud Run verifica que el solicitante tenga el rol roles/run.invoker. En organizaciones con dominio‑restricted sharing, el rol no puede asignarse a allUsers, lo que provoca un rechazo implícito que se traduce en 404.
    • Incluso con invokerIamDisabled=true, una política organizacional run.managed.requireInvokerIam obligatoria anula la desactivación y vuelve a aplicar la verificación.
  3. Desactivación de la URL predeterminada

    • La bandera defaultUriDisabled evita que la URL *.run.app sea pública. Si está activada, solo la URL numérica funciona, pero si la política de IAM también falla, ambas devuelven 404.
  4. Perímetro de servicio o VPC Service Controls

    • Un perímetro que incluye el proyecto pero no permite tráfico de salida hacia la red pública puede hacer que el front‑end de Cloud Run rechace la petición antes de que llegue al contenedor.
  5. Configuración de rutas internas (Cloud Load Balancer / Cloud CDN)

    • En entornos donde Cloud Run está detrás de un balanceador HTTP(S) personalizado, una regla de ruta mal definida o un host‑rule que no incluye el dominio run.app redirige a una página 404 genérica.
  6. Problemas de propagación de cambios

    • Después de modificar políticas (IAM, ingress, perímetro) puede haber un lapso de varios minutos antes de que el front‑end de Cloud Run refleje la nueva configuración. Durante ese tiempo, las peticiones siguen recibiendo 404.

Solución

1. Verificar y normalizar la política de ingreso

gcloud run services describe SERVICE_NAME \
  --region REGION \
  --format="value(ingress)"
  • Si el valor no es INGRESS_TRAFFIC_ALL, actualízalo:
gcloud run services update SERVICE_NAME \
  --region REGION \
  --ingress=all
  • Revisa la política organizacional:
gcloud org-policies describe run.allowedIngress \
  --effective \
  --project PROJECT_ID

Si la política fuerza allowInternalOnly, elimínala o crea una excepción para el proyecto.

2. Confirmar que el control de IAM del invocador está desactivado

gcloud run services describe SERVICE_NAME \
  --region REGION \
  --format="value(spec.template.metadata.annotations.invokerIamDisabled)"
  • Si devuelve false, habilítalo:
gcloud run services update SERVICE_NAME \
  --region REGION \
  --no-invoker-iam
  • Verifica que la política organizacional run.managed.requireInvokerIam no esté forzada:
gcloud org-policies describe run.managed.requireInvokerIam \
  --effective \
  --project PROJECT_ID

Si está activa, solicite a la administración que la desactive o añada una excepción.

3. Revisar la flag de URL predeterminada

gcloud run services describe SERVICE_NAME \
  --region REGION \
  --format="value(spec.template.metadata.annotations.defaultUriDisabled)"
  • Si está true, desactívala:
gcloud run services update SERVICE_NAME \
  --region REGION \
  --no-default-uri-disabled

4. Inspeccionar perímetros de VPC Service Controls

gcloud access-context-manager perimeters list \
  --project PROJECT_ID
  • Si el proyecto pertenece a un perímetro, asegúrese de que la regla de acceso accessLevels incluya allUsers o que el perímetro permita tráfico de salida a *.run.app.
  • En caso contrario, añada una excepción o elimine temporalmente el perímetro para validar.

5. Validar rutas de balanceador (si aplica)

  • En la consola de Cloud Load Balancing, compruebe que el host‑rule incluya *.run.app.
  • Si usa un backend service de tipo “Serverless Network Endpoint Group”, confirme que la URL de backend sea la URL de Cloud Run y que el health check apunte a /healthz.

6. Forzar una nueva revisión del servicio

A veces la configuración se queda en caché. Una forma rápida de “refrescar” es crear una nueva revisión sin cambiar nada:

gcloud run services update-traffic SERVICE_NAME \
  --to-revisions=LATEST=100 \
  --region REGION

Esto fuerza al control plane a volver a leer la configuración y a regenerar los endpoints.

7. Revisar logs de Cloud Run y Cloud Logging

  • En Cloud Logging, filtre por resource.type="cloud_run_revision" y severity="ERROR" para detectar rechazos implícitos.
  • Si no aparecen entradas de request, el tráfico está siendo bloqueado antes de llegar al contenedor, confirmando una de las causas anteriores.

Cuándo aplicar esta solución

  • Síntomas: 404 genérico de Google, sin logs de request; la sonda de arranque pasa; el contenedor está escuchando en 0.0.0.0:8080.
  • Entorno: proyectos dentro de organizaciones con políticas restrictivas (dominio‑restricted sharing, VPC Service Controls).
  • Exclusiones: Si los logs muestran peticiones llegando al contenedor pero la aplicación responde 404, el problema está en la lógica de la aplicación, no en la capa de control.

Código

# 1. Verificar ingreso
gcloud run services describe $SERVICE \
  --region $REGION \
  --format="value(ingress)"

# 2. Habilitar ingreso total
gcloud run services update $SERVICE \
  --region $REGION \
  --ingress=all

# 3. Desactivar IAM del invocador
gcloud run services update $SERVICE \
  --region $REGION \
  --no-invoker-iam

# 4. Asegurar URL predeterminada habilitada
gcloud run services update $SERVICE \
  --region $REGION \
  --no-default-uri-disabled

# 5. Revisar políticas organizacionales
gcloud org-policies describe run.allowedIngress \
  --effective \
  --project $PROJECT_ID
gcloud org-policies describe run.managed.requireInvokerIam \
  --effective \
  --project $PROJECT_ID

# 6. Forzar nueva revisión
gcloud run services update-traffic $SERVICE \
  --to-revisions=LATEST=100 \
  --region $REGION

Verificación

  1. Prueba de salud: curl -i https://SERVICE_NAME-REGION.run.app/healthz
    • Respuesta esperada: 200 OK y cuerpo ok.
  2. Petición pública: Desde una ventana incógnita del navegador, acceda a la URL canónica. No debe aparecer la página de 404.
  3. Logs: En Cloud Logging, busque entradas request bajo resource.type="cloud_run_revision" y confirme que aparecen los registros de la petición.
  4. Tiempo de propagación: Espere 2‑3 minutos después de cada cambio y repita la prueba; la mayoría de los cambios de política se materializan en ese intervalo.

Notas adicionales

  • En organizaciones con dominio‑restricted sharing, es buena práctica crear un grupo de servicio (serviceAccount) con el rol roles/run.invoker y usar tokens de identidad para llamadas internas.
  • Si necesita exponer el servicio a internet pero no puede asignar allUsers, considere usar Cloud Identity‑Aware Proxy (IAP) o un API Gateway que gestione la autenticación.
  • Cuando el proyecto está dentro de un perímetro, la regla accessLevels debe incluir accessPolicies/*/accessLevels/* que permitan tráfico externo; de lo contrario, cualquier endpoint público quedará inalcanzable.
  • Los cambios de política pueden tardar más de lo esperado si la organización tiene múltiples niveles de herencia; siempre valide con gcloud org-policies list --effective.

Con estos pasos el ingeniero puede aislar rápidamente la causa de un 404 que nunca llega al contenedor y restablecer la disponibilidad del servicio Cloud Run sin necesidad de redeploys extensos.