From 6755f567b1de700930f7c42c056bce2aec4b43a7 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Oct 2026 05:22:42 +0000 Subject: [PATCH 1/2] feat: route node-web-app through the ionic-platform Envoy Gateway ionic-platform is moving from ingress-nginx to Envoy Gateway. Replace the base's Ingress with an HTTPRoute on the shared ionic-platform Gateway and a per-route BackendTrafficPolicy that restates nginx's 1 MiB body cap and 60s limits, and closes idle upstream connections before Node's 5s keep-alive timeout. Overlays now patch /spec/hostnames/0 on HTTPRoute/server. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01UPgWv27ehWiLwH4a9MdeTF --- README.md | 4 +-- .../examples/local-overlay/kustomization.yaml | 4 +-- kustomize/node-web-app/README.md | 17 +++++++----- .../node-web-app/backendtrafficpolicy.yaml | 24 +++++++++++++++++ kustomize/node-web-app/httproute.yaml | 26 +++++++++++++++++++ kustomize/node-web-app/ingress.yaml | 21 --------------- kustomize/node-web-app/kustomization.yaml | 3 ++- .../deploy/overlays/prod/kustomization.yaml | 4 +-- 8 files changed, 68 insertions(+), 35 deletions(-) create mode 100644 kustomize/node-web-app/backendtrafficpolicy.yaml create mode 100644 kustomize/node-web-app/httproute.yaml delete mode 100644 kustomize/node-web-app/ingress.yaml diff --git a/README.md b/README.md index 2e4659f..14795bf 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ kit upgrade goes through the app repo's normal review. | Here | `electron/infra` (wg-infra) | App repo | | --- | --- | --- | -| Reusable deploy workflow | Namespace, ServiceAccount, ExternalSecret `server-secrets`, `origin-tls` | `Dockerfile` | +| Reusable deploy workflow | Namespace, ServiceAccount, ExternalSecret `server-secrets` | `Dockerfile` | | kustomize bases (`node-web-app`, `cronjob`) | Key Vault + secrets, CI-push identity, ArgoCD project/app | `deploy/` overlays | | Templates | Repo secrets/variables, Cloudflare DNS, migration runbook | Caller workflow | @@ -60,7 +60,7 @@ kit upgrade goes through the app repo's normal review. ``` .github/workflows/deploy.yml reusable workflow (workflow_call) .github/workflows/ci.yml this repo's own checks -kustomize/node-web-app/ remote base: Deployment + Service + Ingress +kustomize/node-web-app/ remote base: Deployment + Service + HTTPRoute kustomize/cronjob/ remote base: Heroku Scheduler replacement kustomize/examples/ local-path overlay used by CI templates/ files an app copies diff --git a/kustomize/examples/local-overlay/kustomization.yaml b/kustomize/examples/local-overlay/kustomization.yaml index 3c7fc58..8a82c7b 100644 --- a/kustomize/examples/local-overlay/kustomization.yaml +++ b/kustomize/examples/local-overlay/kustomization.yaml @@ -17,9 +17,9 @@ images: newTag: main patches: - target: - kind: Ingress + kind: HTTPRoute name: server patch: |- - op: replace - path: /spec/rules/0/host + path: /spec/hostnames/0 value: example.electronjs.org diff --git a/kustomize/node-web-app/README.md b/kustomize/node-web-app/README.md index ba4345d..bc4898d 100644 --- a/kustomize/node-web-app/README.md +++ b/kustomize/node-web-app/README.md @@ -1,12 +1,13 @@ # node-web-app -A `Deployment`, `Service` and `Ingress` for an HTTP service listening on -`$PORT` (8080). +A `Deployment`, `Service`, `HTTPRoute` and `BackendTrafficPolicy` for an HTTP +service listening on `$PORT` (8080). The route attaches to the shared +`ionic-platform` Gateway (Envoy Gateway). -Everything is named `server`: the Deployment, Service, Ingress, ServiceAccount -and the `server-secrets` Secret. That matches the `components = { server = … }` -entry in `electron/infra`, which provisions the ServiceAccount and the -ExternalSecret under those names. +Everything is named `server`: the Deployment, Service, HTTPRoute, +BackendTrafficPolicy, ServiceAccount and the `server-secrets` Secret. That +matches the `components = { server = … }` entry in `electron/infra`, which +provisions the ServiceAccount and the ExternalSecret under those names. ## Overlay contract @@ -23,7 +24,7 @@ Your overlays must set: | --- | --- | | Namespace | `namespace: ` | | Image | `images: [{ name: electronionicplatform.azurecr.io/PLACEHOLDER_APP, newName: electronionicplatform.azurecr.io/, newTag: main }]` | -| Hostname | JSON patch on `Ingress/server`: replace `/spec/rules/0/host` | +| Hostname | JSON patch on `HTTPRoute/server`: replace `/spec/hostnames/0` | | `part-of` label | `labels: [{ pairs: { app.kubernetes.io/part-of: }, includeTemplates: true }]` | Optional settings. Apart from replicas, these are JSON patches on @@ -46,3 +47,5 @@ Optional settings. Apart from replicas, these are JSON patches on * No service-account token, no capabilities, uid/gid 1000, `RuntimeDefault` seccomp. The cluster enforces these through Azure Policy, so an image that needs root won't run. +* Requests capped at 1 MiB of body and 60s in total, as ingress-nginx did + (`httproute.yaml`, `backendtrafficpolicy.yaml`). diff --git a/kustomize/node-web-app/backendtrafficpolicy.yaml b/kustomize/node-web-app/backendtrafficpolicy.yaml new file mode 100644 index 0000000..a65aa09 --- /dev/null +++ b/kustomize/node-web-app/backendtrafficpolicy.yaml @@ -0,0 +1,24 @@ +# Per-route on purpose: a Gateway-targeted BackendTrafficPolicy is not +# reliably enforced on routes (envoyproxy/gateway#9513), and a route-targeted +# one replaces the Gateway's rather than merging with it, so the Gateway's +# 60s stream idle limit is restated here. +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: server +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: server + # Envoy has no body limit of its own. 1 MiB is ingress-nginx's old default + # (client_max_body_size); Envoy buffers up to it and answers 413 above it. + requestBuffer: + limit: 1Mi + timeout: + http: + streamIdleTimeout: 60s + # Node closes idle keep-alive connections after 5s (keepAliveTimeout). + # Envoy would otherwise reuse one the server had just closed and fail + # that request with a 503, so close them from Envoy's side first. + connectionIdleTimeout: 4s diff --git a/kustomize/node-web-app/httproute.yaml b/kustomize/node-web-app/httproute.yaml new file mode 100644 index 0000000..af832e2 --- /dev/null +++ b/kustomize/node-web-app/httproute.yaml @@ -0,0 +1,26 @@ +# Route on the shared ionic-platform Gateway (Envoy Gateway). The listener is +# picked by hostname, so no sectionName. +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: server +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: ionic-platform + namespace: envoy-gateway-system + hostnames: + # Replaced by the app's prod overlay. + - PLACEHOLDER_HOST + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: server + port: 80 + # Envoy's default is a 15s total; ingress-nginx allowed 60s. + timeouts: + request: 60s diff --git a/kustomize/node-web-app/ingress.yaml b/kustomize/node-web-app/ingress.yaml deleted file mode 100644 index 444e1fb..0000000 --- a/kustomize/node-web-app/ingress.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: server -spec: - ingressClassName: nginx - tls: - # Shared origin certificate, delivered per namespace by the ionic-app module. - - secretName: origin-tls - rules: - # Replaced by the app's prod overlay. - - host: PLACEHOLDER_HOST - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: server - port: - name: http diff --git a/kustomize/node-web-app/kustomization.yaml b/kustomize/node-web-app/kustomization.yaml index f1228a5..5c0c21e 100644 --- a/kustomize/node-web-app/kustomization.yaml +++ b/kustomize/node-web-app/kustomization.yaml @@ -5,7 +5,8 @@ kind: Kustomization resources: - deployment.yaml - service.yaml - - ingress.yaml + - httproute.yaml + - backendtrafficpolicy.yaml labels: - pairs: app.kubernetes.io/managed-by: argocd diff --git a/templates/deploy/overlays/prod/kustomization.yaml b/templates/deploy/overlays/prod/kustomization.yaml index 34f6e91..783c528 100644 --- a/templates/deploy/overlays/prod/kustomization.yaml +++ b/templates/deploy/overlays/prod/kustomization.yaml @@ -9,11 +9,11 @@ replicas: count: 1 patches: - target: - kind: Ingress + kind: HTTPRoute name: server patch: |- - op: replace - path: /spec/rules/0/host + path: /spec/hostnames/0 value: my-app.electronjs.org # Optional: a health endpoint other than /healthz, plain (non-secret) env # vars and resource limits. Delete whatever you don't need. From 08c33f4b93d54ee954b7ec333b8b8b2333dafd54 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Oct 2026 06:16:27 +0000 Subject: [PATCH 2/2] fix(node-web-app): disable the route's total timeout, keep the 60s idle limit ingress-nginx's 60s was proxy_read_timeout, a gap between reads, not a cap on the whole response. A 60s request timeout would cut off streams and downloads that are still sending data. Set the HTTPRoute's timeouts.request to 0s (which also overrides Envoy's 15s default) and rely on the BackendTrafficPolicy's streamIdleTimeout: 60s as the idle limit, as sudowoodo's trail route and agent-workflows already do. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01UPgWv27ehWiLwH4a9MdeTF --- kustomize/node-web-app/README.md | 3 ++- kustomize/node-web-app/httproute.yaml | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/kustomize/node-web-app/README.md b/kustomize/node-web-app/README.md index bc4898d..8d2f04a 100644 --- a/kustomize/node-web-app/README.md +++ b/kustomize/node-web-app/README.md @@ -47,5 +47,6 @@ Optional settings. Apart from replicas, these are JSON patches on * No service-account token, no capabilities, uid/gid 1000, `RuntimeDefault` seccomp. The cluster enforces these through Azure Policy, so an image that needs root won't run. -* Requests capped at 1 MiB of body and 60s in total, as ingress-nginx did +* Requests capped at 1 MiB of body, and cut off after 60s without any data + flowing (as ingress-nginx did) but with no cap on total duration (`httproute.yaml`, `backendtrafficpolicy.yaml`). diff --git a/kustomize/node-web-app/httproute.yaml b/kustomize/node-web-app/httproute.yaml index af832e2..f55a093 100644 --- a/kustomize/node-web-app/httproute.yaml +++ b/kustomize/node-web-app/httproute.yaml @@ -21,6 +21,9 @@ spec: backendRefs: - name: server port: 80 - # Envoy's default is a 15s total; ingress-nginx allowed 60s. + # Envoy's default is a 15s total, which would cut off streams and + # downloads. ingress-nginx only enforced 60s between reads, so the total + # is disabled here ("0s" turns Envoy's route timeout off) and + # backendtrafficpolicy.yaml applies the same 60s idle limit instead. timeouts: - request: 60s + request: 0s