Repository navigation
fix(gcp): exclude uninitialized replicas from GKE and Cloud Run readiness - #1024
Conversation
…ness After LeanerCloud#488 added /ready (503 until the replica has completed initialization), the GCP traffic-admission probes still gated on /health, which answers 200 on a cold replica. - GKE: new readiness_probe_path variable (default /ready) drives the readinessProbe; the liveness probe stays on health_check_path (/health) so a slow init does not get the pod restarted. - Cloud Run: the startup probe (Cloud Run's admission gate) now checks /ready, with its failure budget widened from 3 to 24 x 10 s so a slow-but-recovering init (120 s default migration timeout plus connect retries) does not restart-loop the container. Liveness stays on /health. - Static Go tests over the module sources pin both probe paths, the variable defaults and the startup window bounds. Closes LeanerCloud#648 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reached
This review includes 5 billable files and costs up to $1.25. Or wait 38 minutes for your next included review. View limit details
Comment |
The GCP test-deployment job still used the pre-LeanerCloud#488 inline smoke gate: warm up /api/auth/check-admin, then parse the /health body. The two requests could land on different Cloud Run instances, and /health is a liveness signal. Replace both inline steps with scripts/wait-for-healthy.sh against /ready, with the same fast-gate (1 of 5) and rollout smoke-gate (3 in a row within 30) parameters deploy-azure.yml uses since LeanerCloud#646. Refs LeanerCloud#648 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Independent review verdict: CLEAN at aabe506Reviewer: fresh Claude agent (Sonnet, high effort), not the author, cold brief. Read-only, with no cloud credentials and no plan or apply. The PR body was treated as a claim. Coordinator: claude-cc-reviewer. Blocking findings: none. Probes vs the Go server. All of them hit port 8080.
Notes (non-blocking):
Deploy impact (merge held for an explicit owner OK):
What was attacked and how:
Gate:
|
|
Delta verdict: CLEAN at 683beaf (same reviewer, scope
This also resolves note 2 of the main verdict: #648's third bullet (the deploy smoke gate) is now covered, so |
|
New head: Change: in the
The script itself is untouched. The gate's URL is the Cloud Run Evidence (local; nothing ran against GCP):
🤖 Generated with Claude Code |
|
Merge driver status (claude-cc-pr-merge, 2026-10-17T15:25:27Z): code gate met, merge BLOCKED on @cristim's explicit OK for the dev deploy. Merging to main runs the existing deploy workflows (Terraform apply to dev), and merge-driver sessions are not permitted to trigger that, so a maintainer must merge or approve it (GKE/Cloud Run readiness on /ready). |
|
Independent delta review of 683beaf (previous: aabe506): clean Reviewer: fresh non-author agent, dispatched by claude-cc-pr-merge-2. The earlier CLEAN verdict at aabe506 stands for the rest of the PR.
|
|
Refreshing CI (claude-cc-pr-merge-2): main changed directories this PR also touches after its CI ran. The merge-tree with main is clean (see the delta review), but CI must re-run on the current merge ref. Reopening now. |
|
Refreshing CI (claude-cc-pr-merge-2, round 1): main changed directories this PR touches after its CI ran. Reopening now. |
|
Refreshing CI (claude-cc-pr-merge-2, round 2): main changed directories this PR touches after its CI ran. Reopening now. |
|
Gate record for 683beaf (claude-cc-pr-merge-2):
|
Root cause
#646 (issue #488) added
/ready, which answers 503 until the replica has finished initialization, and moved the Azure traffic-admission probes onto it. The GCP probes that decide traffic admission were not moved. They still checked/health, which answers 200 even on a cold replica, so pods and instances that were still initializing got traffic during rollouts and scale-out.readinessProbeandlivenessProbeboth usedvar.health_check_path(/health)./health.Fix
GKE (
terraform/modules/compute/gcp/gke): adds a new variablereadiness_probe_path, default/ready, validated to start with/. It drives thereadinessProbe. Liveness stays onhealth_check_path(/health), so a slow init never gets the pod killed. This is the liveness complication the issue calls out. The variable descriptions now say which probe each one controls.Cloud Run (
terraform/modules/compute/gcp/cloud-run): the startup probe now checks/ready. A startup probe that never passes shuts the container down, so its failure budget is raised from 3 to 24 attempts (24 x 10 s, about 250 s including the 10 s initial delay). That covers the 120 s default migration timeout (CUDLY_MIGRATION_TIMEOUT) plus database connect retries. It also stays inside Cloud Run's 600 s limit onfailure_threshold x period_seconds.timeout_seconds(3) is still at or belowperiod_seconds. Liveness stays on/health.GCP deploy smoke gate (
.github/workflows/deploy-gcp.yml,test-deploymentjob): the old inline gate warmed up/api/auth/check-adminand then parsed the/healthbody, and those two requests could land on different Cloud Run instances. It is replaced withscripts/wait-for-healthy.shagainst/ready, mirroringdeploy-azure.ymlafter fix(server): exclude uninitialized replicas from readiness #646. A sparse checkout fetches the script. The fast gate needs 1 ready response within 5 attempts, 10 s apart. The rollout smoke gate needs 3 ready responses in a row within 30 attempts, 5 s apart. This PR does not modifyscripts/wait-for-healthy.shor its test.cloud_run_service_url, which isgoogle_cloud_run_v2_service.uri, anhttps://...run.appURL. So no http-to-https redirect is involved.git merge-tree --write-tree HEAD fork/fix/issue-647is clean.Probe table
/health(var.health_check_path)/ready(var.readiness_probe_path)/health/health(unchanged)/health, 3 x 10 s (+10 s delay, about 40 s)/ready, 24 x 10 s (+10 s delay, about 250 s)/health/health(unchanged)deploy-gcp.yml)/api/auth/check-admin+ inline/healthbody parse (5 tries)scripts/wait-for-healthy.shon/ready(1/5 fast gate, then 3-in-a-row/30)modules/frontend/gcp)GKE Ingress health check caveat
The GCE Ingress load-balancer health check is inferred from the pod
readinessProbe, and only when the Ingress is created. GKE docs: "Changes to a Pod'sreadinessProbedon't affect the Ingress after it is created." So:/ready./ready, but the existing LB health check keeps the path it was created with. To move it, recreate the Ingress or edit the health check by hand.BackendConfigwould need akubernetes_manifestCRD resource (cluster API access at plan time). That is left as a possible follow-up rather than added here.Verification
Static checks only. Nothing was deployed or planned against a real account, and no real endpoint was called.
terraform/modules/compute/gcp/readiness_probe_test.go, in the same Go-over-TF-source style as the package's other tests:var.health_check_path,readiness_probe_pathis undeclared, the Cloud Run startup path is"/health", and the startup window is 30 s.var.readiness_probe_pathreadiness_probe_pathdefault set to/healthhealth_check_pathdefault set to/readyfailure_threshold = 61(610 s window, over the 600 s limit)timeout_secondsgreater thanperiod_seconds/readygo test ./terraform/modules/compute/gcp/passes.go vetis clean. golangci-lint v2.10.1 reports 0 issues on the package. gocyclo does not apply (no non-test Go changed).terraform fmt -check -recursive terraform/modules/compute/gcppasses.terraform init -backend=falseplusvalidatesucceeds on thegkeandcloud-runmodules (temporary data dirs, deleted afterwards).actionlint1.7.12 passes, both the local binary and the digest-pinned CI image.zizmor==1.29.0 --offline --persona=pedantic --min-severity=medium .github/workflows/reports no findings.shellcheckis clean onscripts/wait-for-healthy.shand its test.scripts/test-wait-for-healthy.shgives 19 pass, 0 fail./ready503 x3 then 200 passes both gates; never-ready fails both with exit 1 and prints the last 503 body.git merge-tree --write-tree HEAD fork/fix/issue-649is clean, so it does not conflict with fix(infra): gate deployment checks and GCP uptime on /health body status #1023.scripts/test-git-secrets-allowlist.shgives 42 PASS, 0 FAIL. checkov and tflint are not installed locally, so CI covers them.Operators: re-apply the GKE or Cloud Run stack for the probe changes to take effect. For GKE Ingress, see the caveat above.
Out of scope
/healthbody status). Per fix(infra): deployment checks and GCP uptime monitor gate on tautological /health 200 #649, uptime monitors should not point at/ready.Closes #648
🤖 Generated with Claude Code