Orbtrace

Deploying on AWS (EKS)

Run Orbtrace on Amazon EKS the recommended way — RDS for Postgres+pgvector, a throughput-tuned gp3 StorageClass, Doris via the operator or EC2, an ALB ingress with ACM TLS, and the AWS-specific gotchas (EBS AZ-locking, IRSA, security groups).

This is the Kubernetes (Helm) path on Amazon EKS. The generic Helm mechanics — pull secret, my-values.yaml, helm install — are on Install with Helm; this page is the AWS-specific decisions layered on top, and it follows the same recommendation as everywhere else: run the stateful stores as managed services, let Helm deploy only the stateless app.

In EKS — Helm deploys

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

AWS-managed / EC2 — you run these

Apache DorisTelemetry store · operator on EKS, or EC2
Postgres + pgvectorSettings + RCA vectors · RDS 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 EKS reads telemetry back from Doris and keeps settings + cache in RDS Postgres + the bundled Valkey. Only the app and the ephemeral cache live in EKS; the data you can't lose (RDS, Doris) stays on managed/EC2 services outside the pod lifecycle.

What runs where on AWS

ComponentAWS choiceNotes
Orbtrace appEKS (this chart)Stateless — replicas + HPA, spread across AZs
Postgres + pgvectorRDS for PostgreSQLMulti-AZ in production; pgvector is a one-line CREATE EXTENSION
Valkey (cache)bundled in-clusterEphemeral — leave it in EKS. ElastiCache only if you want it managed
Apache Dorisdoris-operator on EKS, or EC2 VMsMemory-heavy, IOPS-heavy — give it its own node group / instances
Collectora container / your existing pipelineNever in the chart — see Integration patterns
Ingress + TLSAWS Load Balancer Controller (ALB) + ACMNLB via a LoadBalancer Service is the alternative
Block storagegp3 (EBS CSI)Tune IOPS/throughput — Doris BE is IOPS-bound (step 2)

Size the cluster from your workload first: run the Capacity planning calculator and it returns the Doris FE/BE counts, per-node vCPU/memory, and disk — the node-group shapes below follow from it.

  1. 1

    Create the EKS cluster — two node groups

    Give Doris BE its own node group: it wants memory (16–32 GB+ each) and pinned, IOPS-heavy EBS, which you don't want competing with the stateless app. Spread the app group across AZs for HA. A eksctl config with both:

    cluster.yaml
    apiVersion: eksctl.io/v1alpha5
    kind: ClusterConfig
    metadata: { name: orbtrace, region: <region>, version: "1.30" }
    iam: { withOIDC: true }   # required for IRSA (step 2)
    managedNodeGroups:
      - name: app
        instanceType: m6i.xlarge
        desiredCapacity: 3
        availabilityZones: ["<az-a>", "<az-b>", "<az-c>"]
      - name: doris-be
        instanceType: r6i.2xlarge       # memory-optimized; size per capacity planning
        desiredCapacity: 3
        availabilityZones: ["<az-a>"]    # keep a BE group in ONE AZ — see the EBS note below
        labels: { "orbtrace.io/doris-be": "true" }

    Applysave as cluster.yaml, then runeksctl create cluster -f cluster.yaml

    EBS volumes are AZ-locked — pin Doris BE to one AZ

    An EBS volume lives in a single Availability Zone; a pod bound to that PVC can only run on a node in the same AZ. If a Doris BE pod is rescheduled to another AZ it cannot mount its data and stays Pending. Keep each BE node group in one AZ (as above), or run one BE node group per AZ with the operator's anti-affinity — never let a single BE's replicas float across AZs. The stateless app has no such constraint; spread it freely.

  2. 2

    EBS CSI driver (via IRSA) + a throughput-tuned gp3 StorageClass

    The EBS CSI driver needs its own IAM role through IRSA — without it, every PVC hangs Pending. Create the role, then install the add-on wired to that role (the --service-account-role-arn is the step people miss — --role-only creates the role but does not attach it to the driver):

    eksctl create iamserviceaccount --cluster orbtrace --region <region> \
      --namespace kube-system --name ebs-csi-controller-sa \
      --attach-policy-arn arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy \
      --role-only --role-name orbtrace-ebs-csi --approve
    eksctl create addon --cluster orbtrace --region <region> --name aws-ebs-csi-driver \
      --service-account-role-arn arn:aws:iam::<acct-id>:role/orbtrace-ebs-csi --force

    Then a gp3 StorageClass — and turn the IOPS and throughput up. gp3 defaults to 3000 IOPS / 125 MB/s, which starves Doris BE compaction; 6000+ IOPS and 250+ MB/s is a sane floor for a real workload:

    gp3-orbtrace.yaml
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: gp3-orbtrace
      annotations: { storageclass.kubernetes.io/is-default-class: "true" }
    provisioner: ebs.csi.aws.com
    parameters:
      type: gp3
      iops: "6000"          # gp3 default 3000 is too low for BE compaction
      throughput: "250"     # MB/s — gp3 default 125 is too low
    volumeBindingMode: WaitForFirstConsumer   # binds the volume in the pod's AZ

    Applysave as gp3-orbtrace.yaml, then runkubectl apply -f gp3-orbtrace.yaml

    WaitForFirstConsumer is important: it delays provisioning until the pod is scheduled, so the volume is created in the same AZ as the pod (the flip side of the AZ-lock above).

  3. 3

    Provide Postgres — RDS for PostgreSQL + pgvector

    Create an RDS for PostgreSQL 16+ instance in the cluster's VPC (Multi-AZ for production — automated failover and backups), in private subnets. Allow the EKS node security group to reach it on 5432:

    aws ec2 authorize-security-group-ingress \
      --group-id <rds-sg-id> --protocol tcp --port 5432 \
      --source-group <eks-node-sg-id>

    Then, connected to the instance, create the database and enable pgvector (RDS ships it — no parameter-group change needed):

    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 — any database/user works; you'll set postgres.database/postgres.username to match in step 6. (Cache: leave Valkey bundled — it's ephemeral; reach for ElastiCache 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 node group you labeled in step 1 (or bake it into a custom AMI's sysctl):

    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 gp3-orbtrace StorageClass and a nodeSelector for the BE node group — or on EC2 VMs and point the chart at it. Full walkthrough (operator install, the reference DorisCluster, BE PVC sizing) is on Setting up Doris. Note the FE Service address for doris.host.

  6. 6

    Install Orbtrace with Helm

    Follow Install with Helm — the pull secret and helm install are identical on AWS. In your my-values.yaml, point Postgres at RDS and Doris at the address from step 5:

    my-values.yaml
    global:
      imagePullSecrets: [{ name: ghcr }]     # the secret from the Helm page
    postgres:
      mode: external
      host: orbtrace.abc123.<region>.rds.amazonaws.com   # RDS 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 / EC2 address from step 5
  7. 7

    Expose it — ALB ingress with ACM TLS

    Install the AWS Load Balancer Controller (its own Helm chart + an IRSA role), then drive the ALB entirely from the chart's ingress annotations (chart ≥ 2.0.13):

    my-values.yaml (add)
    orbtrace:
      ingress:
        enabled: true
        className: alb
        annotations:
          alb.ingress.kubernetes.io/scheme: internet-facing
          alb.ingress.kubernetes.io/target-type: ip
          alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
          alb.ingress.kubernetes.io/ssl-redirect: "443"   # needs the HTTP:80 listener above to redirect from
          alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:<region>:<acct-id>:certificate/<id>
          alb.ingress.kubernetes.io/healthcheck-path: /actuator/health/readiness
        hosts:
          - host: orbtrace.example.com
            paths: [{ path: /, pathType: Prefix }]

    Point DNS (a Route 53 alias record) at the ALB, then upgrade the release. Alternative — NLB: if you'd rather front the app with a Network Load Balancer, set orbtrace.ingress.enabled: false and apply a small standalone Service of type: LoadBalancer carrying service.beta.kubernetes.io/aws-load-balancer-type: nlb, its selector pointing at app.kubernetes.io/name: orbtrace — TLS then terminates at an upstream proxy, not the LB. The ALB path above is the simpler, recommended one.

    On a chart older than 2.0.13 the ingress.annotations key isn't rendered — either upgrade, or apply a hand-written Ingress with these annotations pointing at the orbtrace-app Service.

  8. 8

    Verify

    kubectl -n orbtrace get pods
    # orbtrace-app ×N, orbtrace-valkey-0 Running; migration line:
    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 column shows the ALB DNS name once provisioned
    curl -fsSI https://orbtrace.example.com | head -1   # HTTP/2 200

    Then continue to First login.

Production hardening on AWS

  • RDS: Multi-AZ, automated backups + PITR, and a parameter group with a sane max_connections. Store the DB password in Secrets Manager and reference it via postgres.existingSecret.
  • Doris backups: enable the chart's backup.* CronJobs to push Postgres dumps and Doris snapshots to S3 (the reference DorisCluster supports an S3 cold tier too — see Capacity planning).
  • Nodes: run the app node group across ≥ 3 AZs; add the Cluster Autoscaler or Karpenter. The chart already ships PodDisruptionBudgets so a node drain never takes the last replica.
  • Least privilege: IRSA per controller (EBS CSI, Load Balancer Controller), private RDS subnets, security groups scoped to the node SG — no 0.0.0.0/0 on 5432.

Common AWS first-install snags

  • PVCs stuck Pending → the EBS CSI driver has no IAM role. Re-check the IRSA service account in step 2.
  • A Doris BE pod stuck Pending after a reschedule → it landed in a different AZ from its EBS volume. Pin the BE node group to one AZ (step 1).
  • Ingress has no ADDRESS → the AWS Load Balancer Controller isn't installed or its IRSA role is missing.
  • App un-Ready, STORAGE_UNAVAILABLE → node security group can't reach RDS on 5432, or doris.host is wrong. See Troubleshooting.
  • BE disk slow / compaction lagging → gp3 at default 3000 IOPS. Raise iops/throughput on the StorageClass (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.