Orbtrace

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

Orbtrace serverThe stateless app · replicas + HPA · GKE Ingress
ValkeyCache · bundled in-cluster (fine in prod)

Google-managed / GCE — you run these

Apache DorisTelemetry store · operator on GKE, or GCE VMs
Postgres + pgvectorSettings + RCA vectors · Cloud SQL for PostgreSQL
OTel CollectorTelemetry ingest · a container / your pipeline
Your appsOTLPOTel CollectorwriteApache DorisreadOrbtrace (in cluster)

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

ComponentGCP choiceNotes
Orbtrace appGKE Standard (this chart)Stateless — replicas + HPA. Prefer Standard over Autopilot (privileged DaemonSet + node-pool control)
Postgres + pgvectorCloud SQL for PostgreSQLRegional HA in prod; private IP; pgvector is a one-line CREATE EXTENSION
Valkey (cache)bundled in-clusterEphemeral — leave it in GKE. Memorystore only if you want it managed
Apache Dorisdoris-operator on GKE, or GCE VMsMemory-heavy, IOPS-heavy — give it its own node pool / VMs
Collectora container / your existing pipelineNever in the chart — see Integration patterns
Ingress + TLSGKE Ingress (GCLB) + ManagedCertificateingress-nginx is the alternative
Block storageHyperdisk 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. 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-nodes is per zone, so without --node-locations the 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. 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: true

    Applysave 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-rwo sized large enough for its baseline IOPS.)

  3. 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 orbtrace names are a convention; set postgres.database/postgres.username to match in step 6. (Cache: leave Valkey bundled — it's ephemeral; use Memorystore only if you want it managed.)

  4. 4

    Set the Doris node kernel prerequisite

    Doris BE needs vm.max_map_count ≥ 2000000 on 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.9

    Applysave as doris-sysctl.yaml, then runkubectl apply -f doris-sysctl.yaml

  5. 5

    Stand up Doris

    Run Doris with the doris-operator in the cluster — its DorisCluster uses the hyperdisk-orbtrace StorageClass and a nodeSelector for 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 for doris.host.

  6. 6

    Install Orbtrace with Helm

    Follow Install with Helm — pull secret and helm install are identical on GCP. In your my-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. 7

    Expose it — GKE Ingress + ManagedCertificate

    Reserve a global static IP, point DNS at it, then create a ManagedCertificate and 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 a BackendConfig. (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: nginx and nginx.ingress.kubernetes.io/* annotations with a cert-manager tls secret.

  8. 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 Active

    Then 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 reference DorisCluster supports 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

  • ManagedCertificate stuck Provisioning → 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_count another way.
  • App un-Ready, STORAGE_UNAVAILABLE → Cloud SQL private IP peering isn't set up (or the Auth Proxy sidecar isn't running), or doris.host is wrong. See Troubleshooting.
  • A Doris BE pod stuck Pending after a reschedule → it landed in a different zone from its PD. Pin the BE pool to one zone (step 1).
  • BE disk slow / compaction laggingpremium-rwo at 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.