Skip to content

Harden the appscode.com serving path against intermittent 5xx - #1328

Merged
ArnobKumarSaha merged 1 commit into
masterfrom
arnob-gw-5xx
Sep 1, 2026
Merged

Harden the appscode.com serving path against intermittent 5xx#1328
ArnobKumarSaha merged 1 commit into
masterfrom
arnob-gw-5xx

Conversation

@ArnobKumarSaha

@ArnobKumarSaha ArnobKumarSaha commented Aug 31, 2026

Copy link
Copy Markdown
Member

Customer saw recurring 5xx (503/504/522/520 + SSL-closed) on appscode.com/api/v1/dbaas/billing/reports/namespaces. The handler is fine (112ms–1.84s, 0 5xx in 48h of Envoy logs); the failures come from the serving path.

charts/platform-api/templates/statefulset.yaml — liveness/readiness had only httpGet, so k8s defaults gave a 30s kill window. /api/v1/version is served by the same process as everything else, so it stalls when the process does: ace-platform-api-2 logged 40s /api/v1/version responses, then exitCode: 1 — no panic, no OOM. All replicas share ace-db/ace-nats, so they stalled and got killed together. Liveness now needs ~2m (timeout 10, period 20, failureThreshold 6); readiness keeps a tight period with a 5s timeout so it sheds traffic first. startupProbe gets a 5s timeout too.

charts/service-gatewayenvoy.replicas was commented out, so envoy-gateway runs 1 replica with no PDB. Every appscode.com request transits that one pod; any reschedule is a domain outage (the customer's 522s stop exactly when the current envoy pod was created, Aug 6). Added envoyPDB.maxUnavailable: 1 and a hostname topology spread (ScheduleAnyway), both gated on replicas > 1 so single-replica and DaemonSet installs render unchanged. Default stays 1 — prod should set service-gateway.envoy.replicas: 3.

charts/ace/templates/gateway/route-main.yamlrequest/backendRequest were 2m in 5 places, above Cloudflare's 100s origin timeout, so anything in the 100–120s band surfaced as a Cloudflare 520/524 with nothing in our logs. Lowered to 55s.

New BackendTrafficPolicy — without one, a single connection failure during a pod restart was returned to Cloudflare as a 503 even with healthy replicas left. Retries are limited to connect-failure, refused-stream, reset-before-request — all three guarantee the backend never processed the request, so POST /api/v1/register and /api/v1/license/issue stay safe without per-rule scoping. Passive health checking ejects a failing replica faster than readiness.

Not in this PR

  • Resource requests are already fixed in the chart (ab61c162); prod runs platform-api-v2026.6.19 with resources: {}, hence BestEffort pods evicted first. Chart upgrade fixes it.
  • gotenberg sidecar still has no resources — follow-up.
  • nats: no responders available in the billing processor/aggregator is application-side; tracked separately.

Verification

  • helm template platform-api: probes render with intended thresholds.
  • helm template service-gateway: replicas=1 → no PDB/spread; replicas=3 → both; DaemonSet unchanged.
  • kubectl apply --dry-run=server accepts the new BackendTrafficPolicy, HTTPRoute, EnvoyProxy/GatewayClass.
  • helm lint passes on both charts.

A customer reported recurring 503/504/520/522/500 and premature connection
closures against /api/v1/dbaas/billing/reports/namespaces. Investigation of the
production cluster found three defects in the serving path.

platform-api probes: liveness and readiness declared only httpGet, so the
Kubernetes defaults applied (timeoutSeconds 1, failureThreshold 3). Since
/api/v1/version is served by the same process as every other request, it stalls
whenever the process does -- observed at 39.8s while the pod was otherwise
serving traffic -- and the kubelet killed the container 30s later. All replicas
share the same upstreams, so they stall and get killed together. Liveness now
fires only after ~2m of a genuinely wedged process; readiness keeps a tight
period so it drains traffic first.

Envoy data plane: replicas defaults to 1 with no disruption budget, making the
gateway a single point of failure for the whole domain. Add envoyPDB and a
hostname topology spread, both gated on replicas > 1 so single-replica installs
are unaffected. Deployments that need redundancy set envoy.replicas.

Route timeouts: 2m exceeds Cloudflare's 100s origin timeout, so a slow backend
surfaced as a Cloudflare 520/524 with no matching entry in our logs. Lowered to
55s across the ace routes.

Also add a BackendTrafficPolicy with retries limited to triggers where the
backend provably never saw the request, keeping non-idempotent POSTs such as
/api/v1/register safe, plus passive health checking to eject a failing replica
faster than readiness can.

Signed-off-by: Arnob Kumar Saha <arnob@appscode.com>
@ArnobKumarSaha
ArnobKumarSaha merged commit a366f84 into master Sep 1, 2026
8 checks passed
@ArnobKumarSaha
ArnobKumarSaha deleted the arnob-gw-5xx branch September 1, 2026 17:45
ArnobKumarSaha added a commit that referenced this pull request Sep 1, 2026
Signed-off-by: Arnob Kumar Saha <arnob@appscode.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant