Deploying on Azure (AKS)
Run Orbtrace on Azure Kubernetes Service the recommended way — Azure Database for PostgreSQL with the pgvector allowlist, a tunable Premium SSD v2 StorageClass, Doris via the operator or Azure VMs, AGIC ingress, and the Azure-specific gotchas.
This is the Kubernetes (Helm) path on Azure Kubernetes Service (AKS). The generic Helm mechanics — pull secret, my-values.yaml, helm install — are on Install with Helm; this page is the Azure-specific decisions, and it follows the same recommendation: run the stateful stores as managed services, let Helm deploy only the stateless app.
In AKS — Helm deploys
Azure-managed / VMs — you run these
Apps send OTLP to your Collector, which writes to Doris. The stateless Orbtrace app in AKS reads telemetry back from Doris and keeps settings + cache in Azure Postgres + the bundled Valkey. Only the app and the ephemeral cache live in AKS; the data you can't lose (Flexible Server, Doris) stays on managed services / VMs outside the pod lifecycle.
What runs where on Azure
| Component | Azure choice | Notes |
|---|---|---|
| Orbtrace app | AKS (this chart) | Stateless — replicas + HPA, spread across zones |
| Postgres + pgvector | Azure Database for PostgreSQL — Flexible Server | Zone-redundant HA in prod; vector must be on the extension allowlist (step 3) |
| Valkey (cache) | bundled in-cluster | Ephemeral — leave it in AKS. Azure Cache for Redis only if you want it managed |
| Apache Doris | doris-operator on AKS, or Azure 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 | Application Gateway (AGIC) or app-routing (managed nginx) | cert-manager secret, or an App Gateway / Key Vault cert |
| Block storage | Premium SSD v2 (disk.csi.azure.com) | 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 AKS cluster — two node pools
Give Doris BE its own node pool — memory-optimized (E-series), with pinned tunable disk. Spread the system/app pool across zones:
az aks create -g <rg> -n orbtrace \ --node-vm-size Standard_D4s_v5 --node-count 3 --zones 1 2 3 \ --network-plugin azure --generate-ssh-keys az aks nodepool add -g <rg> --cluster-name orbtrace -n dorisbe \ --node-vm-size Standard_E8s_v5 --node-count 3 --zones 1 \ --labels orbtrace.io/doris-be=true az aks get-credentials -g <rg> -n orbtraceAzure managed disks are zone-locked — pin Doris BE to one zone
A managed disk lives in one availability 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 node pool in one zone (--zones 1above), or run one BE pool per zone with the operator's anti-affinity. The stateless app has no such constraint. - 2
A tunable Premium SSD v2 StorageClass
AKS ships the Azure Disk CSI driver already — no IAM/identity wiring needed for volumes. But the default
managed-csi-premium(Premium SSD) ties IOPS to disk size, which starves Doris BE compaction. Use Premium SSD v2, whose IOPS and throughput are set independently:premium2-orbtrace.yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: premium2-orbtrace annotations: { storageclass.kubernetes.io/is-default-class: "true" } provisioner: disk.csi.azure.com parameters: skuName: PremiumV2_LRS DiskIOPSReadWrite: "6000" # independent of size — set for BE compaction DiskMBpsReadWrite: "250" # MB/s volumeBindingMode: WaitForFirstConsumer # binds the disk in the pod's zone allowVolumeExpansion: trueApplysave as
premium2-orbtrace.yaml, then runkubectl apply -f premium2-orbtrace.yaml(Premium SSD v2 must be available in your region/zone; if not, fall back to
managed-csi-premiumand size disks large enough that their baseline IOPS suffices.) - 3
Provide Postgres — Flexible Server + the pgvector allowlist
Create an Azure Database for PostgreSQL — Flexible Server (v16+) with private access (VNet integration) into the AKS VNet (or a peered one). Azure gates extensions behind a per-server allowlist, so add
vectortoazure.extensionsfirst — this is the step people miss:az postgres flexible-server parameter set -g <rg> -s <server-name> \ --name azure.extensions --value vectorThen, connected to the server, create the database and enable the extension:
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 Azure Cache for Redis 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 node pool from step 1: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 thepremium2-orbtraceStorageClass and anodeSelectorfor the BE pool — or on Azure 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 Azure. In yourmy-values.yaml:my-values.yaml global: imagePullSecrets: [{ name: ghcr }] # the secret from the Helm page postgres: mode: external host: <server-name>.postgres.database.azure.com # Flexible Server endpoint port: 5432 database: orbtrace username: orbtrace password: <your-postgres-password> # or existingSecret — see the Helm page doris: host: <your-doris-fe-host> # FE Service / VM address from step 5 - 7
Expose it — AGIC (or app-routing) ingress
Enable the Application Gateway Ingress Controller add-on (
az aks enable-addons -a ingress-appgw …) — or the application-routing managed-nginx add-on (az aks approuting enable). Then drive it from the chart's ingress annotations (chart ≥ 2.0.13). AGIC example, terminating TLS with a cert-manager Kubernetes secret and health-checking Orbtrace's readiness path:my-values.yaml (add) orbtrace: ingress: enabled: true className: azure-application-gateway annotations: appgw.ingress.kubernetes.io/ssl-redirect: "true" appgw.ingress.kubernetes.io/health-probe-path: /actuator/health/readiness hosts: - host: orbtrace.example.com paths: [{ path: /, pathType: Prefix }] tls: - secretName: orbtrace-tls # a cert-manager-issued cert, or your own hosts: [orbtrace.example.com]For an App Gateway– or Key Vault–managed certificate instead of a K8s secret, drop the
tlsblock and addappgw.ingress.kubernetes.io/appgw-ssl-certificate: <cert-name>. With the app-routing managed-nginx add-on instead, setclassName: webapprouting.kubernetes.azure.com(the IngressClass the add-on registers — notnginx) and the standardnginx.ingress.kubernetes.io/*annotations; it integrates DNS and TLS through Azure DNS + Key Vault. Point DNS at the ingress address, then upgrade the release.On a chart older than 2.0.13 the
ingress.annotationskey isn't rendered — upgrade, or apply a hand-writtenIngress. - 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 ingress # ADDRESS shows the App Gateway public IP once provisioned curl -fsSI https://orbtrace.example.com | head -1 # HTTP/2 200Then continue to First login.
Production hardening on Azure
- Flexible Server: zone-redundant HA, automated backups + PITR, and private access (no public endpoint). Keep the password in Key Vault and mount it via the Secrets Store CSI driver, referenced through
postgres.existingSecret. - Doris backups: enable the chart's
backup.*CronJobs to push Postgres dumps and Doris snapshots to Blob Storage (S3-compatible via its S3 endpoint); the referenceDorisClustersupports a cold tier — see Capacity planning. - Nodes: system/app pool across ≥ 3 zones; enable the Cluster Autoscaler. The chart ships PodDisruptionBudgets.
- Identity: use Workload Identity for pods that call Azure APIs; scope NSGs so only the AKS subnet reaches Postgres on 5432.
Common Azure first-install snags
CREATE EXTENSION vectorfails →vectorisn't onazure.extensionsyet (step 3). Set the parameter, then retry.- App un-Ready,
STORAGE_UNAVAILABLE→ Flexible Server isn't reachable (VNet not peered / firewall rule missing) ordoris.hostis wrong. See Troubleshooting. - A Doris BE pod stuck
Pendingafter a reschedule → it landed in a different zone from its managed disk. Pin the BE node pool to one zone (step 1). - Ingress has no address → the AGIC add-on / App Gateway subnet isn't set up, or the add-on is disabled.
- BE disk slow / compaction lagging → default Premium SSD tiers IOPS to size. Use Premium SSD v2 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.