Deploying on GCP (GKE)
Run Orbtrace on Google Kubernetes Engine the recommended way — Cloud SQL for Postgres+pgvector, a tunable Hyperdisk StorageClass, Doris via the operator or GCE, GKE Ingress with a managed certificate, and the GCP-specific gotchas.
This is the Kubernetes (Helm) path on Google Kubernetes Engine (GKE). The generic Helm mechanics — pull secret, my-values.yaml, helm install — are on Install with Helm; this page is the GCP-specific decisions, and it follows the same recommendation: run the stateful stores as managed services, let Helm deploy only the stateless app.
In GKE — Helm deploys
Google-managed / GCE — you run these
Apps send OTLP to your Collector, which writes to Doris. The stateless Orbtrace app in GKE reads telemetry back from Doris and keeps settings + cache in Cloud SQL + the bundled Valkey. Only the app and the ephemeral cache live in GKE; the data you can't lose (Cloud SQL, Doris) stays on managed services / VMs outside the pod lifecycle.
What runs where on GCP
| Component | GCP choice | Notes |
|---|---|---|
| Orbtrace app | GKE Standard (this chart) | Stateless — replicas + HPA. Prefer Standard over Autopilot (privileged DaemonSet + node-pool control) |
| Postgres + pgvector | Cloud SQL for PostgreSQL | Regional HA in prod; private IP; pgvector is a one-line CREATE EXTENSION |
| Valkey (cache) | bundled in-cluster | Ephemeral — leave it in GKE. Memorystore only if you want it managed |
| Apache Doris | doris-operator on GKE, or GCE VMs | Memory-heavy, IOPS-heavy — give it its own node pool / VMs |
| Collector | a container / your existing pipeline | Never in the chart — see Integration patterns |
| Ingress + TLS | GKE Ingress (GCLB) + ManagedCertificate | ingress-nginx is the alternative |
| Block storage | Hyperdisk Balanced (pd.csi.storage.gke.io) | Tune IOPS/throughput — Doris BE is IOPS-bound (step 2) |
Size the cluster from your workload first with the Capacity planning calculator — the node-pool shapes below follow from its Doris FE/BE counts and per-node CPU/memory/disk.
- 1
Create the GKE cluster — two node pools
Use a VPC-native (
--enable-ip-alias) Standard cluster — VPC-native is what gives you container-native load balancing later. Give Doris BE its own memory-optimized pool:gcloud container clusters create orbtrace --region <region> \ --machine-type e2-standard-4 --num-nodes 1 --enable-ip-alias gcloud container node-pools create dorisbe --cluster orbtrace --region <region> \ --machine-type n2-highmem-8 --num-nodes 1 \ --node-locations <single-zone> \ # pin the BE pool to ONE zone (see callout) --node-labels orbtrace.io/doris-be=true gcloud container clusters get-credentials orbtrace --region <region>On a regional cluster
--num-nodesis per zone, so without--node-locationsthe BE pool would spread one node into every zone and a BE's zonal disk couldn't follow it.--node-locations <single-zone>keeps the whole BE pool in one zone.Zonal Persistent Disks are zone-locked — pin Doris BE
A zonal PD/Hyperdisk lives in one zone; a pod bound to that PVC can only run on a node in the same zone. If a Doris BE pod is rescheduled to another zone it cannot mount its data and stays
Pending. Keep the BE pool in one zone, or run one BE pool per zone with the operator's anti-affinity. The stateless app has no such constraint. - 2
A tunable Hyperdisk StorageClass
GKE ships the PD CSI driver already — no IAM wiring for volumes. The default
premium-rwo(pd-ssd) has fixed per-size performance; Hyperdisk Balanced lets you set IOPS and throughput independently, which Doris BE compaction needs:hyperdisk-orbtrace.yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: hyperdisk-orbtrace annotations: { storageclass.kubernetes.io/is-default-class: "true" } provisioner: pd.csi.storage.gke.io parameters: type: hyperdisk-balanced provisioned-iops-on-create: "6000" provisioned-throughput-on-create: "250Mi" volumeBindingMode: WaitForFirstConsumer # binds the disk in the pod's zone allowVolumeExpansion: trueApplysave as
hyperdisk-orbtrace.yaml, then runkubectl apply -f hyperdisk-orbtrace.yaml(Hyperdisk must be supported by your machine type/region; if not, fall back to
premium-rwosized large enough for its baseline IOPS.) - 3
Provide Postgres — Cloud SQL + pgvector
Create a Cloud SQL for PostgreSQL (v16+) instance with a private IP on the cluster's VPC (Private Service Access / VPC peering) — or reach it through the Cloud SQL Auth Proxy as a sidecar. Cloud SQL supports pgvector; enable it once connected:
CREATE DATABASE orbtrace; CREATE USER orbtrace WITH PASSWORD 'a-strong-password'; GRANT ALL PRIVILEGES ON DATABASE orbtrace TO orbtrace; \c orbtrace CREATE EXTENSION IF NOT EXISTS vector;The
orbtracenames are a convention; setpostgres.database/postgres.usernameto match in step 6. (Cache: leave Valkey bundled — it's ephemeral; use Memorystore only if you want it managed.) - 4
Set the Doris node kernel prerequisite
Doris BE needs
vm.max_map_count ≥ 2000000on its nodes. Apply it with a small privileged DaemonSet targeted at the BE pool from step 1 (this is one reason to use Standard, not Autopilot — Autopilot blocks privileged containers):doris-sysctl.yaml apiVersion: apps/v1 kind: DaemonSet metadata: { name: doris-sysctl, namespace: kube-system } spec: selector: { matchLabels: { app: doris-sysctl } } template: metadata: { labels: { app: doris-sysctl } } spec: nodeSelector: { "orbtrace.io/doris-be": "true" } initContainers: - name: sysctl image: busybox:1.37 securityContext: { privileged: true } command: ["sh", "-c", "sysctl -w vm.max_map_count=2000000"] containers: - name: pause image: registry.k8s.io/pause:3.9Applysave as
doris-sysctl.yaml, then runkubectl apply -f doris-sysctl.yaml - 5
Stand up Doris
Run Doris with the doris-operator in the cluster — its
DorisClusteruses thehyperdisk-orbtraceStorageClass and anodeSelectorfor the BE pool — or on GCE VMs and point the chart at it. Full walkthrough is on Setting up Doris. Note the FE Service address fordoris.host. - 6
Install Orbtrace with Helm
Follow Install with Helm — pull secret and
helm installare identical on GCP. In yourmy-values.yaml:my-values.yaml global: imagePullSecrets: [{ name: ghcr }] # the secret from the Helm page postgres: mode: external host: 10.x.x.x # Cloud SQL private IP (or 127.0.0.1 via Auth Proxy) port: 5432 database: orbtrace username: orbtrace password: <your-postgres-password> # or existingSecret — see the Helm page doris: host: <your-doris-fe-host> # FE Service / GCE address from step 5 - 7
Expose it — GKE Ingress + ManagedCertificate
Reserve a global static IP, point DNS at it, then create a
ManagedCertificateand wire it plus a static-IP annotation onto the chart's ingress (chart ≥ 2.0.13):orbtrace-cert.yaml apiVersion: networking.gke.io/v1 kind: ManagedCertificate metadata: { name: orbtrace-cert, namespace: orbtrace } spec: { domains: [orbtrace.example.com] }my-values.yaml (add) orbtrace: ingress: enabled: true # className unset → the default GKE (GCLB) Ingress class annotations: kubernetes.io/ingress.global-static-ip-name: orbtrace-ip networking.gke.io/managed-certificates: orbtrace-cert hosts: - host: orbtrace.example.com paths: [{ path: /, pathType: Prefix }]GKE derives the health check from the readiness probe — no BackendConfig needed
On a VPC-native cluster the GKE Ingress uses container-native load balancing (NEGs) and reads the pod's readiness probe to configure the LB health check. Orbtrace's readiness probe is
/actuator/health/readiness, so the health check is correct automatically — you do not need aBackendConfig. (Add one only for advanced tuning: IAP, custom timeouts, Cloud CDN.) One caveat: the app pods must already be running when the Ingress is created — if the Deployment is scaled to 0, GKE falls back to a default/check, and a later readiness-probe edit doesn't propagate to an existing Ingress. Install the chart before (or with) the Ingress, which the step order here already does.Use ingress-nginx instead by setting
className: nginxandnginx.ingress.kubernetes.io/*annotations with a cert-managertlssecret. - 8
Verify
kubectl -n orbtrace get pods kubectl -n orbtrace logs deploy/orbtrace-app -c orbtrace | grep doris-migration # ready when you see: [doris-migration] complete kubectl -n orbtrace get managedcertificate # STATUS goes Provisioning → Active curl -fsSI https://orbtrace.example.com | head -1 # HTTP/2 200 once the cert is ActiveThen continue to First login.
Production hardening on GCP
- Cloud SQL: regional (HA) instance, automated backups + PITR, private IP only. Keep the password in Secret Manager; use Workload Identity so pods authenticate to Google APIs without keys.
- Doris backups: enable the chart's
backup.*CronJobs to push Postgres dumps and Doris snapshots to GCS (via its S3-compatible endpoint); the referenceDorisClustersupports a cold tier — see Capacity planning. - Nodes: a regional cluster spreads the app pool across zones; enable node auto-provisioning. The chart ships PodDisruptionBudgets.
- Private cluster: run a private GKE cluster with authorized networks; scope firewall rules so only the cluster reaches Cloud SQL.
Common GCP first-install snags
ManagedCertificatestuckProvisioning→ DNS must resolve to the static IP and the load balancer must be serving before Google can validate the domain. It can take 15–60 minutes; confirm the A record first.- Privileged DaemonSet rejected → you're on Autopilot, which blocks privileged pods. Use a Standard cluster (step 1) or set
vm.max_map_countanother way. - App un-Ready,
STORAGE_UNAVAILABLE→ Cloud SQL private IP peering isn't set up (or the Auth Proxy sidecar isn't running), ordoris.hostis wrong. See Troubleshooting. - A Doris BE pod stuck
Pendingafter a reschedule → it landed in a different zone from its PD. Pin the BE pool to one zone (step 1). - BE disk slow / compaction lagging →
premium-rwoat its per-size baseline. Use Hyperdisk with explicit IOPS (step 2).
Wire your telemetry in — stand up an OTel Collector, or add the doris exporter to your existing OTel pipeline (Integration patterns) — then finish with the post-install checklist.