How to Customise KSML Application Deployments

This guide shows you how to deploy KSML applications into a different namespace from the Provisioner, supply your own ServiceAccount instead of the chart’s, apply custom Helm values to every KSML pod, and switch distributed tracing on.

Type

How-to guide

Goal

Change how the Runtime Provisioner deploys the applications it manages.

Audience

Platform Operator who can edit the Provisioner’s values.yaml and apply it.

When to use

Use this guide after the Provisioner is running, when its defaults do not match your cluster’s conventions.

Each section below is independent. Apply only the ones you need.

Prerequisites

Confirm the following before you begin. The sections after this one are independent, so a resource listed for a tweak you skip does not apply.

Access and permissions required

You need the following access and permissions:

  • Permission to edit the Provisioner’s values file and apply it.

  • Permission to create a Role and RoleBinding in the namespace KSML applications deploy into, unless you supply your own ServiceAccount.

Tools and versions required

You need the following tools:

  • helm >= 3.12, or access to the pipeline that applies the values.

  • kubectl >= 1.28, with access to the namespace KSML applications deploy into.

Resources that must exist before starting

The following must already exist:

  • A running Runtime Provisioner, installed with How to Deploy the Runtime Provisioner.

  • The target namespace, when it differs from the one the Provisioner runs in.

  • A ServiceAccount holding the permissions listed in Required Kubernetes permissions, only when you supply your own instead of the chart’s.

  • An OpenTelemetry collector endpoint, only when enabling distributed tracing.

Deploy into a different namespace

By default, the Provisioner deploys KSML applications into the namespace it runs in. To send them somewhere else, for example a namespace named ksml, set both values below:

env:
  - name: NAMESPACE
    value: "ksml"

rbac:
  namespace: "ksml"

NAMESPACE tells the Provisioner where to deploy KSML applications. rbac.namespace gives it the permissions it needs in that namespace.

Bring your own ServiceAccount and Role-Based Access Control (RBAC)

By default, the Helm chart creates a ServiceAccount and the RBAC resources that go with it. To use your own instead, set:

serviceAccount:
  create: false
  name: my-custom-sa
rbac:
  create: false

serviceAccount.name names the ServiceAccount the Provisioner then runs as. Giving it the permissions it needs is your job: create a Role carrying everything Required Kubernetes permissions lists, and a RoleBinding that binds the Role to that ServiceAccount.

Leave rbac.create at true unless you have a reason to own these resources yourself. The chart then creates the Role and the RoleBinding for you.

Apply custom values to every KSML pod

The Provisioner applies custom Helm values to every KSML application it deploys, which is how you set resources, security contexts, topology spread constraints, and any other Kubernetes-level customisation the KSML pods need. Put them under the customValues field in the Provisioner’s values.yaml:

customValues:
  securityContext:
    allowPrivilegeEscalation: false
    capabilities:
      drop:
        - all
  resources:
    requests:
      cpu: 100m
      memory: 256Mi
    limits:
      memory: 512Mi
  topologySpreadConstraints:
    - maxSkew: 1
      topologyKey: kubernetes.io/hostname
      whenUnsatisfiable: DoNotSchedule

Any valid Helm value for the KSML chart belongs under customValues. The Provisioner merges these with the KSML chart’s own defaults each time it deploys an application.

The CUSTOM_VALUES_FILE environment variable points at a file holding the same values, which works but splits the configuration across two places. Prefer the customValues field.

Enable distributed tracing

The Provisioner exports traces with OpenTelemetry. Switch tracing on, then give the exporter the collector endpoint and the name the traces arrive under:

env:
  - name: DISTRIBUTED_TRACING_ENABLED
    value: "true"
  - name: OTEL_EXPORTER_OTLP_ENDPOINT
    value: "http://<otel-collector-host>:<port>"
  - name: OTEL_SERVICE_NAME
    value: "runtime-provisioner"

The endpoint must start with http or https and include the port number. A collector that requires credentials takes them in OTEL_EXPORTER_OTLP_HEADERS, which is otherwise left unset. For the description of each variable, see OpenTelemetry exporter configuration.

Tracing covers the Provisioner itself. The Instance must also have KSML support switched on before it deploys anything; see How to Enable KSML Support for an Instance.

Verify the customisation

Apply the edited values, then deploy one KSML application from Self-Service and inspect the pod it produces. Each check below matches one of the four tweaks, so run only the ones you applied. <KSML_POD> is the pod name the first command prints.

Replace every <VALUE> placeholder with your own value before running a command.
  1. Confirm the application pod landed in the target namespace:

    kubectl get pods --namespace <TARGET_NAMESPACE>

    Expected result: the KSML application pod appears in <TARGET_NAMESPACE>, not in the namespace the Provisioner runs in. No pod anywhere means the Provisioner could not deploy it, which its own logs report.

  2. Confirm the pod runs under your ServiceAccount:

    kubectl get pod <KSML_POD> --namespace <TARGET_NAMESPACE> -o jsonpath='{.spec.serviceAccountName}'

    Expected result: the command prints the name you set in serviceAccount.name. A Kubernetes 403 in the Provisioner logs instead means the Role bound to that ServiceAccount is missing something Required Kubernetes permissions lists.

  3. Confirm the custom values reached the pod:

    kubectl get pod <KSML_POD> --namespace <TARGET_NAMESPACE> -o yaml

    Expected result: the pod spec carries the securityContext, resources and topologySpreadConstraints you set under customValues. A value missing here did not survive the merge with the KSML chart’s own values.

  4. Confirm the Provisioner exports traces by searching your tracing backend for the service name you set in OTEL_SERVICE_NAME.

    Expected result: spans from the Provisioner appear in the backend. Nothing arriving while DISTRIBUTED_TRACING_ENABLED is "true" points at OTEL_EXPORTER_OTLP_ENDPOINT, and the Provisioner logs name the endpoint it could not reach.