Zebra Continuous Delivery
The continuous-delivery pipeline deploys every commit merged to main to the stage environment, every commit merged to next to the next environment, and every published release to prod, except pre-releases from next, on Google Cloud Platform. PR-triggered work uses the dev environment.
Topology: one zonal MIG per (environment, branch, network, zone)
The pipeline targets two GCP projects. Each network in stage, next, and prod deploys to three zonal Managed Instance Groups (MIGs) in us-east1 zones b, c, and d. Each zonal MIG holds one Zebra instance with one stateful cache disk and one static IP.
| Trigger | Environment label | GCP project | MIGs per network | MIG name | Stateful disk |
|---|---|---|---|---|---|
release | prod | zfnd-prod-zebra | 3 (one per zone) | zebrad-${network}-${zone-letter} | zebrad-cache-${network}-${zone-letter} |
push to main | stage | zfnd-dev-zebra | 3 (one per zone) | zebrad-main-${network}-${zone-letter} | zebrad-cache-main-${network}-${zone-letter} |
push to next | next | zfnd-dev-zebra | 3 (one per zone) | zebrad-next-${network}-${zone-letter} | zebrad-cache-next-${network}-${zone-letter} |
workflow_dispatch | dev, next, or prod | selected by env | 1 (user-chosen zone) | dev: zebrad-${branch}-${network}-${zone-letter}; next and prod: as above | dev: zebrad-cache-${branch}-${network}-${zone-letter}; next and prod: as above |
ADR 0006 records the rationale; the runbook covers day-to-day procedures.
Update mechanics
The event-driven workflow builds an image with its trigger-specific inputs, then passes the complete immutable GAR repository@sha256:... identity to the reusable deployment workflow. Release automation can call that same deployment boundary with an image prepared earlier. Each push to main or next and each release fans out to six deploy-nodes jobs (2 networks × 3 zones), while a workflow_dispatch runs one job for the selected zone. Every job runs the same flow for its zonal MIG:
- Build a new instance template with the commit’s container image.
- Ensure the zonal stateful disk exists. On first deploy, create it from the latest matching cache image. On subsequent deploys, attach the existing disk.
- If the zonal MIG exists, run
rolling-action start-update --max-unavailable=1. True per-zone rolling: this zone’s MIG replaces its instance while the other two zones keep serving. The stateful disk persists across the replace. - If the zonal MIG does not exist, create it with
--size=1and apply the stateful policy. - Assign the static IP when the caller passes
use_reserved_ip: always forpushandrelease, and for a workflow_dispatch that selectsprodornext, or runs from themainornextref. Other dev deployments get no external address (--no-address). Zone-to-IP mapping is deterministic: zoneb→ primary, zonec→ secondary, zoned→ tertiary.nextuses its ownzebra-next-*addresses, because it shares stage’s project.
Cache images come from zfnd-deploy-integration-tests-gcp.yml’s create-state-image job. Image names encode branch, commit, state-DB version, network, and timestamp. One image per network seeds all three zones. Lookup priority in gcp-get-cached-disks.sh: current branch, then main, then any branch; most recent first.
Each deploy-nodes cell holds its per-MIG concurrency lock through template rollout and optional application-health verification. See the runbook for details.
Triggers
The workflow runs on:
- a
pushtomainornextthat touches Rust code, dependencies, Docker files, or the workflow itself - a published
release, unless it is a pre-release fromnext: any pre-release version other than a release candidate - a
workflow_dispatchfrom any branch (dispatcher picksnetwork,zone, andenvironment)
Pull requests run only the Docker-configuration tests; they do not deploy.
For implementation details, see the deploy workflow.