Skip to main content

Install

Scripts for installing the praxis-collector supervisor on a Kubernetes cluster.

The supervisor runs as a single Deployment in your cluster and manages child OpenTelemetry Collector instances via the OpenTelemetry Operator.


Prerequisites

The following must already be present in your cluster before running the install script. The script validates each one and exits with an error if any is missing.

cert-manager

Required by the OpenTelemetry Operator to manage webhook TLS certificates. Verify it is running:

kubectl get deployment cert-manager -n cert-manager
kubectl get deployment cert-manager-cainjector -n cert-manager
kubectl get deployment cert-manager-webhook -n cert-manager

All three deployments should show AVAILABLE > 0.

note

If cert-manager is not installed, follow the official installation guide before proceeding.

OpenTelemetry Operator

Manages child OpenTelemetryCollector custom resources that the supervisor creates. Verify it is running:

kubectl get crd opentelemetrycollectors.opentelemetry.io
kubectl get deployment -n opentelemetry-operator-system

The CRD should exist and the operator deployment should show AVAILABLE > 0.

note

If the OpenTelemetry Operator is not installed, follow the official installation guide before proceeding.

kubectl and helm

Both CLIs must be installed and available in your PATH.

kubectl version --client
helm version

Cluster access

Your kubeconfig must point to the target cluster with sufficient permissions (cluster-admin or equivalent RBAC to create ClusterRoles, ClusterRoleBindings, Deployments, ConfigMaps, and ServiceAccounts).

kubectl cluster-info

Installation

Run the install script with the credentials for your pipeline. Obtain these values from the Praxis UI (pipeline and collector settings).

sudo bash -c "$(curl -k -L https://storage.googleapis.com/download_dev/k8s_install.sh)" -- \
--url wss://<praxis-host>/v1/opamp \
--secret <oauth2-client-secret> \
--key <oauth2-client-key> \
--org <org-id> \
--tenant <tenant-id> \
--pipeline <pipeline-id>
note

The -k flag disables TLS certificate verification for the script download. If your environment requires verified downloads, fetch the script separately, inspect it, and run it directly instead of piping to bash.


What the Script Creates

The script provisions the following resources in order:

  1. Namespace — creates the target namespace (default: praxis-collector).
  2. ServiceAccount — identity used by the supervisor pod and passed to child collectors.
  3. ClusterRole + ClusterRoleBinding — grants the supervisor cluster-wide permissions to manage OTel Collector CRDs, read pods, and manage ConfigMaps.
  4. ConfigMap — stores config.yml (supervisor connection settings and credentials) and otel_config.yml (a no-op placeholder until Praxis pushes a real pipeline configuration).
  5. Deployment — runs the supervisor pod, mounts the ConfigMap at /app/config, and connects to Praxis over OpAMP.
warning

The install script provisions a quick-start layout: no persistent volumes are created. The supervisor's /app/metadata directory (instance UID, agent description, last health) lives in the container's writable layer, and child collector pods have no persistent file_storage volume. This means:

  • Every supervisor pod restart re-registers as a brand-new OpAMP agent.
  • Every child collector image upgrade silently re-tails inputs from the beginning (file/Splunk/Kafka receivers lose their offsets), causing duplicate ingestion.

For production deployments, use the Helm chart with supervisor.persistence.enabled=true and childCollector.fileStorage.enabled=true instead of this script.


Parameters

ParameterTypeRequiredDescription
urlstringYesPraxis OpAMP URL. Must start with wss:// (TLS) or ws://. The OAuth2 token endpoint is derived automatically by replacing wss:// with https:// and /v1/opamp with /oauth2/token. Example: wss://praxis.example.com/v1/opamp
keystringYesOAuth2 client key (machine-to-machine credential). Used by the supervisor to authenticate and obtain a short-lived access token. Obtain it from your pipeline configuration in the Praxis UI.
secretstringYesOAuth2 client secret paired with --key. Together they form the M2M credential. Keep this confidential — it is stored in the Kubernetes ConfigMap and injected as an environment variable.
orgstringYesOrganization ID in Praxis. Sent on every connection so Praxis can associate this supervisor with the right org. Must match the org the M2M client (--key / --secret) is bound to; mismatches are rejected by identity-service.
tenantstringYesTenant ID for your organization in Praxis. Sent on every connection so Praxis can associate this supervisor with the right tenant.
pipelinestringYesUUID of the pipeline this supervisor instance manages. Praxis uses this to push the correct pipeline configuration. Each supervisor should be associated with exactly one pipeline.
versionstringNo (default: latest)Docker image tag for the collector container image. In production, always pin to a specific version (e.g. v1.2.3) to ensure reproducible deployments and avoid unexpected upgrades on pod restarts.
imagestringNo (default: us-central1-docker.pkg.dev/adaptive-pipeline/downloads/praxis-collector)Container image repository for the supervisor. Override only if pulling from a private registry mirror or using a custom build.
kubeconfigstringNo (default: ~/.kube/config)Path to a specific kubeconfig file. When provided, every kubectl and helm call uses this file instead of the default. Useful when managing multiple clusters.
warning

Always pin --version to a specific tag in production. Using latest means the supervisor image can change on every pod restart, which can introduce unexpected behaviour or breaking changes.

warning

The --secret value is stored in a Kubernetes ConfigMap, not a Kubernetes Secret. ConfigMaps are not encrypted at rest by default. In production environments, consider enabling etcd encryption or using an external secrets manager to protect this credential.


Interactive Prompts

After argument parsing and prerequisite checks, the script interactively prompts you to confirm or customize the names of the Kubernetes resources it will create. Press Enter to accept the default shown in brackets, or type a custom name.

PromptDefaultDescription
Service Account Namepraxis-collector-manager-saThe ServiceAccount the supervisor pod runs as. Bound to the ClusterRole via the ClusterRoleBinding. Also passed to child collectors so they inherit the correct identity for RBAC (e.g. reading Kubernetes events, scraping kubelet stats).
Cluster Role Namepraxis-collector-managerUsed for both the ClusterRole and ClusterRoleBinding. The ClusterRole defines what the supervisor can do across the cluster (create/update/delete OTel Collector CRDs, read pods, manage ConfigMaps). Both resources receive the app.kubernetes.io/name=praxis-collector label for cleanup by the uninstall script.
ConfigMap Namepraxis-collector-configHolds config.yml (supervisor connection settings, credentials, namespace, log config) and otel_config.yml (a no-op placeholder until Praxis pushes the real pipeline). Mounted at /app/config.
Deployment Namepraxis-collector-supervisorThe Kubernetes Deployment running the supervisor pod. Use this name with kubectl get deployment, kubectl describe, or kubectl logs.
Namespacepraxis-collectorConfirmed interactively so you can review the full picture of where all resources will land before the script makes any changes.

Verifying the Installation

Run these commands after the script completes to confirm everything is healthy:

# Supervisor pod is in Running state
kubectl get pods -n praxis-collector -l app.kubernetes.io/component=supervisor

# Deployment shows AVAILABLE=1
kubectl get deployment praxis-collector-supervisor -n praxis-collector

# Supervisor logs — look for a successful connection to Praxis
# Use --tail to avoid following; add -f if you want to stream logs (press Ctrl+C to exit)
kubectl logs -n praxis-collector -l app.kubernetes.io/component=supervisor --tail=50

# ConfigMap exists and contains config.yml and otel_config.yml
kubectl get configmap praxis-collector-config -n praxis-collector

# ServiceAccount exists
kubectl get serviceaccount praxis-collector-manager-sa -n praxis-collector

# ClusterRole exists
kubectl get clusterrole praxis-collector-manager

# ClusterRoleBinding exists
kubectl get clusterrolebinding praxis-collector-manager

# Child collectors appear here once Praxis pushes a pipeline configuration
# An empty list is expected immediately after install
kubectl get opentelemetrycollectors -n praxis-collector

# Full deployment details including events — useful when a pod fails to start
kubectl describe deployment praxis-collector-supervisor -n praxis-collector

Production install (Helm)

For production environments, install via the Helm chart instead of the curl|bash script. The chart adds two persistence layers the script does not.

Supervisor state persistence

When supervisor.persistence.enabled=true, the chart provisions a PVC and mounts it at /app/metadata so the supervisor's instance UID, agent description, and last-known health survive pod restarts. Without this, every restart looks like a brand-new agent to Praxis Cloud and any state keyed on the previous instance UID is orphaned.

Child collector file-offset persistence

When childCollector.fileStorage.enabled=true, the supervisor automatically injects a backing volume into every OpenTelemetryCollector CR it deploys. Mode-aware:

Child CR modeBacking volumePurpose
daemonsethostPath on the nodeOffsets pinned alongside the host log files. Survives pod replacement on the same node.
statefulsetvolumeClaimTemplates (one PVC per replica)Offsets survive pod and node moves via the OTel Operator's PVC management.
deploymentSkipped with warningMulti-replica RWO PVCs collide; switch the CR to StatefulSet or declare your own volumes in the CR.

Without this, every collector image upgrade silently rewinds file/Splunk/Kafka receivers to their input start, causing duplicate ingestion at upgrade time.

Pod security context

The chart runs the supervisor as the unprivileged praxiscollector user (uid/gid 10001) with runAsNonRoot: true and sets fsGroup: 10001 on the pod so the kubelet chowns mounted volumes to a writable group on attach. Without fsGroup, the supervisor would log open /app/metadata/...: no such file or directory at startup and silently lose persistence.

Example values

# my-values.yaml
opamp:
address: "wss://praxis.example.com/v1/opamp"
tenantId: "YOUR_TENANT_ID"
pipeline: "YOUR_PIPELINE_ID"
key: "YOUR_CLIENT_KEY"
secret: "YOUR_CLIENT_SECRET"

supervisor:
persistence:
enabled: true
size: 100Mi
# storageClass: fast-ssd

childCollector:
fileStorage:
enabled: true
# storageClass: fast-ssd
helm install praxis-supervisor \
oci://us-central1-docker.pkg.dev/adaptive-pipeline/charts/praxis-collector \
--namespace praxis-collector --create-namespace \
-f my-values.yaml

See the chart README for the full values reference and troubleshooting guide.


Behind an HTTP/HTTPS Proxy

If the cluster's egress to Praxis and to your destinations must traverse a corporate proxy, configure the collector using the standard Go HTTP proxy environment variables — HTTPS_PROXY, HTTP_PROXY, and NO_PROXY.

Supervisor (curl|bash install) — at install time

The installer accepts proxy flags directly; the supervisor Deployment is created with the env vars already in place:

sudo bash -c "$(curl -k -L https://<artifact-host>/k8s_install.sh)" -- \
--url wss://<praxis-host>/v1/opamp \
--key <key> --secret <secret> \
--org <org-id> --tenant <tenant-id> --pipeline <pipeline-id> \
--https-proxy http://proxy.corp.local:3128 \
--no-proxy localhost,127.0.0.1,::1,.svc,.cluster.local,10.0.0.0/8,169.254.169.254

If --no-proxy is omitted but --https-proxy (or --http-proxy) is set, the installer applies a sensible cluster-internal default automatically.

Supervisor — after install

If the supervisor is already running, patch the Deployment in place:

kubectl -n praxis-collector set env deployment/praxis-collector-supervisor \
HTTPS_PROXY=http://proxy.corp.local:3128 \
HTTP_PROXY=http://proxy.corp.local:3128 \
NO_PROXY=localhost,127.0.0.1,::1,.svc,.cluster.local,10.0.0.0/8,169.254.169.254

The supervisor restarts automatically and picks up the new values.

Supervisor (Helm install)

Add an env block under the supervisor in your values.yaml:

supervisor:
extraEnv:
- name: HTTPS_PROXY
value: "http://proxy.corp.local:3128"
- name: HTTP_PROXY
value: "http://proxy.corp.local:3128"
- name: NO_PROXY
value: "localhost,127.0.0.1,::1,.svc,.cluster.local,10.0.0.0/8,169.254.169.254"

Child collectors

Child OpenTelemetryCollector CRs are templated by the supervisor. Set the same env vars on each child CR (or on the OTel Operator's pod template if you want a cluster-wide default), so exporter traffic also flows through the proxy.

NO_PROXY checklist

Always include the following so cluster-internal traffic is not routed through the proxy:

  • localhost, 127.0.0.1, ::1
  • .svc, .cluster.local (the Kubernetes API and in-cluster services)
  • Pod and Service CIDRs (for example 10.0.0.0/8)
  • 169.254.169.254 (cloud instance metadata, where applicable)
  • Internal hostnames of any on-cluster receivers your collector connects to

Auth-required proxies

Use HTTPS_PROXY=http://user:[email protected]:3128. URL-encode any special characters in the password.

TLS-inspecting (MITM) proxies

If the proxy terminates and re-issues TLS with a corporate root CA, mount that CA into the supervisor and child collector pods so outbound TLS handshakes validate. Either rebuild the image with the corporate CA copied into /etc/ssl/certs/ca-certificates.crt, or mount the CA via a ConfigMap/Secret into /etc/ssl/certs/ (or set SSL_CERT_FILE=/path/to/ca-bundle.crt on the supervisor and child CRs).

note

All outbound paths honor the standard proxy env vars, including the OpAMP WebSocket (supervisor ↔ Praxis), the OAuth2 token endpoint, the Google SecOps HTTP and gRPC exporters, Splunk HEC, Snowflake, the GitHub receiver, and other confighttp-based exporters.

The Google SecOps gRPC path uses gRPC-Go's built-in CONNECT tunneling, which inspects HTTPS_PROXY / https_proxy only — never HTTP_PROXY. Always set HTTPS_PROXY when proxying Chronicle gRPC traffic, even if you also set HTTP_PROXY for HTTP-based exporters.

Verify

kubectl -n praxis-collector exec deploy/praxis-collector-supervisor -- env | grep -i proxy
kubectl -n praxis-collector logs deploy/praxis-collector-supervisor | grep -iE "proxy|dial|connect"

Optional: Kubernetes Cluster, Events, and Kubeletstats Receiver RBAC

If you want to use the Kubernetes cluster receiver (k8s_cluster), Kubernetes events receiver (k8s_events), or kubeletstats receiver in your pipelines, the child collector pods need additional cluster-wide read permissions. Apply the following after installation:

kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: praxis-collector-k8s-receiver
labels:
app.kubernetes.io/name: praxis-collector
app.kubernetes.io/component: k8s-receiver
rules:
- apiGroups: [""]
resources:
- events
- namespaces
- namespaces/status
- nodes
- nodes/spec
- nodes/stats
- nodes/proxy
- nodes/metrics
- pods
- pods/status
- persistentvolumes
- persistentvolumeclaims
- replicationcontrollers
- replicationcontrollers/status
- resourcequotas
- services
verbs: [get, list, watch]
- apiGroups: [discovery.k8s.io]
resources: [endpointslices]
verbs: [get, list, watch]
- apiGroups: [apps]
resources: [daemonsets, deployments, replicasets, statefulsets]
verbs: [get, list, watch]
- apiGroups: [batch]
resources: [jobs, cronjobs]
verbs: [get, list, watch]
- apiGroups: [autoscaling]
resources: [horizontalpodautoscalers]
verbs: [get, list, watch]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: praxis-collector-k8s-receiver
labels:
app.kubernetes.io/name: praxis-collector
app.kubernetes.io/component: k8s-receiver
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: praxis-collector-k8s-receiver
subjects:
- kind: ServiceAccount
name: praxis-collector-manager-sa
namespace: praxis-collector
EOF
note

If you used non-default values during installation, update the name and namespace in the subjects block to match your chosen ServiceAccount name and namespace.

The app.kubernetes.io/name: praxis-collector label ensures these resources are automatically found and removed by the uninstall script.