Skip to main content
Migration Notice
We're migrating documentation from the old portal into this one. Some things may look a little different or out of place in the meantime — we know, and we're working to get it right. If something's unclear or doesn't look right, let us know.

Install on Kubernetes: OpenShift / OKD

This guide installs the product on OpenShift or OKD. The console is exposed through the OpenShift router. Everything runs as pods in one namespace: the console, its databases (PostgreSQL through the CloudNativePG operator and ClickHouse through the Altinity ClickHouse operator, both bundled with the chart) and the pipeline agents (extraction, linguistic coherence and classify), which the chart deploys and registers for you.

The values file in this guide is the validated configuration for OpenShift / OKD. Every setting in it can be changed to fit your cluster: how the console is exposed, storage, sizing, images and placement.

Before you begin​

Cluster and tools​

  • Kubernetes v1.31 to v1.37.
  • Helm 3.18.6 or newer on the machine you install from. The chart refuses to install with an older Helm. Check with helm version --short.
  • oc and kubectl with cluster-admin rights (or equivalent). The install creates custom resource definitions and cluster-wide roles for the bundled operators.
  • A DNS name for the console.

What the product needs from your cluster​

RequirementDetails
ExposureThe OpenShift router. The chart's Ingress is converted to a Route through the openshift-default ingress class, or the chart can create a Route itself.
StorageA StorageClass that provides ReadWriteOnce volumes, such as your cluster's CSI storage. This guide also shows the Rancher local-path provisioner for clusters without one.
SecurityThe nonroot-v2 security context constraint, standard on OpenShift 4.x and OKD (oc get scc nonroot-v2).
HTTPS on port 443Users must reach the console over HTTPS on port 443, through the ingress. Login fails with HTTP 403 when the browser reaches it on another port (for example a NodePort), or through a load balancer that terminates TLS and forwards plain HTTP. A load balancer in front of the ingress controller must pass TLS through or re-encrypt.

Capacity​

For the values file in this guide. Memory is the limit per pod, CPU the request per pod, and disk the persistent volume per pod.

ComponentPodsCPU (request)Memory (limit)Disk
Console146 GiB40 GiB
Extraction agent124 GiB10 GiB
Linguistic coherence (LC) agent24 each12 GiB each10 GiB each
Classify agent24 each8 GiB each10 GiB each
PostgreSQL10.51 GiB40 GiB
ClickHouse1430 GiB600 GiB

In total about 26.5 CPUs requested, 81 GiB of memory limits and 730 GiB of persistent volumes (8 volumes), plus the two operators. The nodes also need about 52 GiB of free ephemeral storage for the agents' scratch space, and at least one node must fit the 4 CPU / 30 GiB ClickHouse pod. Size ClickHouse's disk for your audit volume and retention. Each additional pipeline agent adds its row's figures.

Registry access​

The chart and the product images are on a public registry and need no pull secret. The cluster nodes, and the machine you install from, need outbound HTTPS (TCP 443) to the locations below. If the cluster can't reach them, for example on an air-gapped site, copy the chart and images into your own registry once: see Optional: install from your own registry.

WhatLocation
Helm chartoci://northamerica-northeast2-docker.pkg.dev/superna-579/oci/aisecurity/charts/aisec
Product images (aisec-console, extraction-agent, lc-agent, classify-agent)northamerica-northeast2-docker.pkg.dev/superna-579/oci/aisecurity
PostgreSQL, ClickHouse, the two operators, a busybox init image, and the bitnami/kubectl image the ClickHouse operator uses to install its definitionsghcr.io and docker.io

Cluster policies​

Check these before you install. Each one can block the install or leave pods stuck.

If your cluster hasDo this
Nodes still joining or not ReadyWait until kubectl get nodes shows every node Ready, and the capacity above is free, before you install.
CloudNativePG or the Altinity ClickHouse operator already installedCheck with kubectl get crd clusters.postgresql.cnpg.io clickhouseinstallations.clickhouse.altinity.com. If either exists, set its operators.*.install to false and make sure that operator watches the product's namespace. Don't install a second copy.
Pod Security and security context constraintsThe nonroot-v2 bindings in Prepare the cluster let the product's pods run. If your cluster enforces Pod Security restricted on new projects, label the project baseline: oc label ns aisec pod-security.kubernetes.io/enforce=baseline --overwrite.
An admission policy engine (Kyverno, OPA Gatekeeper)Allow the product's registries (or your mirror), user IDs 1000 and 101, emptyDir volumes, the bitnami/kubectl:latest image, and containers without CPU or memory settings (several helper containers and the two operators set none).
A ResourceQuota on the namespaceAdd a LimitRange that gives containers default CPU and memory requests and limits, because the helper containers set none and a quota rejects them. The quota must allow at least 29 CPUs and 83 GiB of memory limits, 730 GiB of storage and 8 volumes.
Default-deny NetworkPoliciesAllow traffic between the product's pods, from the ingress controller to the console (port 8081), from the Kubernetes API server to the CloudNativePG operator (port 9443), and the outbound traffic to the Kubernetes API and your storage systems. Contact Superna support for an example policy. Leave the chart's own networkPolicy.enabled off.
Tainted or dedicated nodesSet scheduling.nodeSelector and scheduling.tolerations, and the same settings under cloudnative-pg, altinity-clickhouse-operator and altinity-clickhouse-operator.crdHook. Leave at least one node without the taint: the job that creates the ClickHouse database doesn't take these settings.

Prepare the cluster​

Storage​

Check whether the cluster has a StorageClass:

oc get storageclass

If you'll use an existing class, note its name for your values file. Otherwise, install the local-path provisioner, give its helper pods the permission they need, and label its directory for SELinux on every node:

oc apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml
oc patch storageclass local-path -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
oc adm policy add-scc-to-user hostmount-anyuid -z local-path-provisioner-service-account -n local-path-storage
for node in $(oc get nodes -o jsonpath='{.items[*].metadata.name}'); do
oc debug node/$node -- chroot /host sh -c 'mkdir -p /opt/local-path-provisioner && chcon -Rt container_file_t /opt/local-path-provisioner'
done

Project and permissions​

Create the project:

oc new-project aisec

The product's pods run with fixed non-root user IDs. Allow them by binding the nonroot-v2 security context constraint to the two service accounts they use. The second one is <release>-console; with the release name aisec used in this guide it is aisec-console.

kubectl create rolebinding aisec-nonroot-v2 \
--clusterrole=system:openshift:scc:nonroot-v2 \
--serviceaccount=aisec:default -n aisec
kubectl create rolebinding aisec-console-nonroot-v2 \
--clusterrole=system:openshift:scc:nonroot-v2 \
--serviceaccount=aisec:aisec-console -n aisec

Get the chart​

Choose a directory for the installation files, on the machine you run helm from, for example /home/admin/aisec. This guide calls it <install-dir>: replace it with the full path of your directory everywhere it appears. Replace <version>-<build> with the version and build you are installing, from Releases.

Download the chart into a directory of its own for this release, so that later releases never overwrite or mix with it:

mkdir -p <install-dir>/charts/<version>-<build>
helm pull oci://northamerica-northeast2-docker.pkg.dev/superna-579/oci/aisecurity/charts/aisec \
--version <version>-<build> --untar --untardir <install-dir>/charts/<version>-<build>
ls <install-dir>/charts/<version>-<build>/aisec

The chart is now in this directory, and every helm command in this guide uses it by this full path:

<install-dir>/charts/<version>-<build>/aisec

The ls command lists the chart's files (Chart.yaml, values.yaml, templates and others). Every setting and its default is listed in the Helm Values Reference, or print them with helm show values <install-dir>/charts/<version>-<build>/aisec.

Create your values file​

Save the following as <install-dir>/override-values.yaml, then change it as described below. Keep it outside the chart directories: the same file is used again for every upgrade.

## Superna Data Security for Dell AI Infrastructure — Helm values for OpenShift / OKD
## Change the values marked REQUIRED. Everything else is the validated
## configuration; adjust it to fit your cluster.

default:
podSecurityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
image:
## Product image registry (public). Point it at your own mirror if you use one.
registry: northamerica-northeast2-docker.pkg.dev/superna-579/oci/aisecurity
pullPolicy: IfNotPresent

## StorageClass for every volume. The chart creates local-path-retain on top of
## the local-path provisioner. To use an existing class instead, set
## createStorageClass: false and className: <your-class>.
storage:
createStorageClass: true
className: local-path-retain

## Pull secret, only for a private mirror that requires authentication.
imageRegistry:
createPullSecret: false
server: ""
username: ""
password: ""
email: ""

console:
image:
name: aisec-console
tag: "<version>-<build>" # REQUIRED: version and build from the Releases page
initImage:
registry: docker.io
name: library/busybox
tag: "1.37"
service:
type: ClusterIP
heap:
initial: "1g"
max: "5g"
resources:
requests: { memory: "6Gi", cpu: "4" }
limits: { memory: "6Gi", cpu: "4" }
persistence:
size: 40Gi

pipeline:
waitForConsole: true
terminationGracePeriodSeconds: 30
commonEnv:
AGENT_LOG_LEVEL: "INFO"
WORKER_COUNT: "4"
stages:
extraction:
enabled: true
workload: StatefulSet
replicas: 1
image:
name: extraction-agent
tag: "<version>-<build>"
resources:
requests: { memory: "2Gi", cpu: "2" }
limits: { memory: "4Gi" }
persistence:
size: 10Gi
scratch:
enabled: true
type: emptyDir
sizeLimit: 20Gi
paths:
- /tmp/tika-scratch
- /tmp/argus-pipeline

classify:
enabled: true
workload: StatefulSet
replicas: 2
image:
name: classify-agent
tag: "<version>-<build>"
resources:
requests: { memory: "8Gi", cpu: "4" }
limits: { memory: "8Gi", cpu: "4" }
persistence:
size: 10Gi
scratch:
enabled: true
type: emptyDir
sizeLimit: 8Gi
paths:
- /tmp/argus-pipeline

lc:
enabled: true
workload: StatefulSet
replicas: 2
image:
name: lc-agent
tag: "<version>-<build>"
resources:
requests: { memory: "12Gi", cpu: "4" }
limits: { memory: "12Gi", cpu: "4" }
persistence:
size: 10Gi
scratch:
enabled: true
type: emptyDir
sizeLimit: 8Gi
paths:
- /tmp/argus-pipeline

## Keep the PostgreSQL, ClickHouse and operator image versions: they are the
## versions validated with this release. Only the registry may change.
postgres:
enabled: true
instances: 1
image:
registry: "ghcr.io/cloudnative-pg"
name: "postgresql"
tag: "17.10"
storage:
size: 40Gi
storageClass: "" # empty = storage.className
resources:
requests: { memory: "1Gi", cpu: "500m" }
limits: { memory: "1Gi" }

clickhouse:
enabled: true
image:
registry: docker.io
name: clickhouse/clickhouse-server
tag: "26.3.10.62"
storage:
size: 600Gi # size for your audit volume and retention
storageClass: "" # empty = storage.className
resources:
requests: { memory: "30Gi", cpu: "4" }
limits: { memory: "30Gi", cpu: "4" }
shardsCount: 1
retentionConfig: true
podSecurityContext:
runAsUser: 101
runAsGroup: 101
fsGroup: 101

## Recommended: set your own database passwords before the first install.
## Left empty, the chart generates random ones and keeps them across upgrades.
secrets:
create: true
postgres:
username: "aisec"
password: "" # recommended: your own password, in quotes
clickhouse:
password: "" # recommended: your own password, in quotes
enrollmentToken: ""
agentKek: ""

ingress:
enabled: true
className: openshift-default
host: "aisec.apps.<your-cluster-domain>" # REQUIRED: the console's DNS name
path: /
tls:
enabled: true
generate: true # self-signed; or secretName: <your-tls-secret> with generate: false

## Bundled operators, scoped to this namespace. Set install: false for an
## operator that already runs on the cluster and watches this namespace.
operators:
cnpg:
install: true
clickhouse:
install: true

cloudnative-pg:
containerSecurityContext:
runAsUser: null
runAsGroup: null
image:
repository: ghcr.io/cloudnative-pg/cloudnative-pg

altinity-clickhouse-operator:
operator:
image:
registry: ""
repository: altinity/clickhouse-operator
metrics:
image:
registry: ""
repository: altinity/metrics-exporter

podDisruptionBudget:
enabled: true
minAvailable: 1

topologySpread:
enabled: true

## Enable only with a CNI that enforces NetworkPolicy, after setting the CIDRs
## described in the chart's values.yaml.
networkPolicy:
enabled: false

What to change​

SettingWhen to change it
ingress.hostRequired. The console's DNS name. OpenShift needs a real host name: no Route is created without one.
console.image.tag, pipeline.stages.*.image.tagRequired. Replace <version>-<build> with the version and build you are installing, from Releases. Always give both: there is no latest tag.
secrets.postgres.password, secrets.clickhouse.passwordRecommended. Your own database passwords, in quotes. Set them before the first install and don't change them in the file afterwards: the databases keep the password they were created with. Left empty, the chart generates random passwords on the first install and keeps them on upgrades; but tools that render the chart without a cluster connection, such as helm template or Argo CD, generate new ones on every render. With such a tool, also set secrets.enrollmentToken and secrets.agentKek (any long random strings) and use your own TLS certificate (ingress.tls), because the chart regenerates those on every render too. Keep the values file somewhere safe.
ingress.classNameYour ingress class, if you don't use the OpenShift router's openshift-default.
route.*To use a native Route instead of the Ingress: ingress.enabled: false, route.enabled: true and route.host.
ingress.tlsYour own certificate: create a kubernetes.io/tls secret in the namespace, set secretName to it and generate: false.
storage.*An existing StorageClass: createStorageClass: false and className: <your-class> (or "" for the cluster default). Each component's storage / persistence block sets its size and can override the class.
*.resources, pipeline.stages.*.replicasSizing for your capacity and throughput. The console always runs as a single pod.
default.image.registry, *.image.registry, imageRegistry.*A private registry mirror, and its pull secret if it needs one.
operators.*.installfalse for an operator that already runs on the cluster and watches this namespace.
scheduling.*Node selectors, affinity and tolerations for all the product's pods (the setting isn't in the file; add it).
networkPolicy.*Restrict traffic to and from the product's pods, if your CNI enforces NetworkPolicy.

Any other setting can be added to the file: see the Helm Values Reference for every setting, its type and its default.

warning

Keep these as they are:

  • The image versions of PostgreSQL, ClickHouse, the operators and busybox. Only their registry may change.
  • The pods' user and group IDs (podSecurityContext).

Optional: install from your own registry​

If the cluster can't reach the public registry, for example on an air-gapped site, copy the chart and the images once into your own registry (Harbor, Artifactory, Nexus or any OCI-compatible registry) and install from there. The cluster then needs no access to the public registry.

Step 1. On a machine that can reach the public registry, create your values file and list the images it uses:

helm template aisec <install-dir>/charts/<version>-<build>/aisec \
-f <install-dir>/override-values.yaml | grep -E '^\s*(image|imageName):' | awk '{print $2}' | tr -d '"' | grep . | sort -u

Step 2. Copy each image into your registry, keeping its path after the registry host:

ImageCopy to
northamerica-northeast2-docker.pkg.dev/superna-579/oci/aisecurity/<image> (the four product images)<your-registry>/aisecurity/<image>
ghcr.io/cloudnative-pg/postgresql, ghcr.io/cloudnative-pg/cloudnative-pg<your-registry>/cloudnative-pg/<image>
docker.io/clickhouse/clickhouse-server, docker.io/library/busybox<your-registry>/clickhouse/clickhouse-server, <your-registry>/library/busybox
altinity/clickhouse-operator, altinity/metrics-exporter, bitnami/kubectl (Docker Hub; bitnami/kubectl is published only as latest)<your-registry>/altinity/<image>, <your-registry>/bitnami/kubectl

For example, with skopeo (keep each image's tag from the list):

skopeo copy --all docker://northamerica-northeast2-docker.pkg.dev/superna-579/oci/aisecurity/aisec-console:<version>-<build> \
docker://<your-registry>/aisecurity/aisec-console:<version>-<build>

If no machine can reach both registries, copy each image to a file with skopeo copy --all docker://<image> oci-archive:<file>.tar, move the files inside, and push them from there.

If the machine you install from can't reach the public registry, copy the chart directory <install-dir>/charts/<version>-<build> to the same path on it. The commands in this guide then work unchanged.

Step 3. Point the registry settings in <install-dir>/override-values.yaml at your registry. Change the existing settings and add the crdHook block:

default:
image:
registry: <your-registry>/aisecurity
console:
initImage:
registry: <your-registry>
postgres:
image:
registry: <your-registry>/cloudnative-pg
clickhouse:
image:
registry: <your-registry>
cloudnative-pg:
image:
repository: <your-registry>/cloudnative-pg/cloudnative-pg
altinity-clickhouse-operator:
operator:
image:
registry: <your-registry>
metrics:
image:
registry: <your-registry>
crdHook:
image:
repository: <your-registry>/bitnami/kubectl

Step 4. Run the command from step 1 again. Every image it lists must start with your registry.

Step 5. If your registry requires a login, give the cluster its credentials: add them to the cluster's global pull secret (pull-secret in the openshift-config namespace). The chart's imageRegistry settings create a pull secret only for the product's own pods, not for the databases and operators, so set the credentials on the cluster.

Install​

When the chart installs the operators, install in two phases. The operators' custom resource definitions must be registered before the databases that use them are created, and a single install on a fresh cluster can fail with no matches for kind "Cluster".

The project already exists, so the commands don't use --create-namespace.

# Phase 1: the operators and their custom resource definitions
helm install aisec <install-dir>/charts/<version>-<build>/aisec \
-n aisec -f <install-dir>/override-values.yaml \
--set postgres.enabled=false --set clickhouse.enabled=false --timeout 15m

# Wait until both definitions are established
kubectl wait --for=condition=established crd/clusters.postgresql.cnpg.io --timeout=120s
kubectl wait --for=condition=established crd/clickhouseinstallations.clickhouse.altinity.com --timeout=120s

# Phase 2: your full values, databases included
helm upgrade aisec <install-dir>/charts/<version>-<build>/aisec \
-n aisec -f <install-dir>/override-values.yaml --timeout 15m

If both operators already run on the cluster and you set both operators.*.install to false, a single install is enough:

helm install aisec <install-dir>/charts/<version>-<build>/aisec \
-n aisec -f <install-dir>/override-values.yaml --timeout 15m

To upgrade to a later release, download its chart into its own directory (Get the chart, with the new version), set the new image tags in <install-dir>/override-values.yaml, and run the phase 2 helm upgrade command with the new chart directory. Never upgrade from an older release's chart directory: that re-applies the old chart.

Verify the installation​

kubectl get pods -n aisec
kubectl get cluster -n aisec
kubectl get chi -n aisec
  • All pods reach Running. A cold start takes 3 to 5 minutes.
  • The PostgreSQL cluster reports Cluster in healthy state and the ClickHouse installation Completed.
  • The console pod stays in Init until PostgreSQL is reachable and the ClickHouse database aisec exists, normally a few minutes after the database pods start. The pipeline agents stay in Init:0/1 until the console runs, then start on their own. If the console stays at Init:2/3, see Troubleshooting.
  • The classify and LC agents load large language models at startup and can show 0/1 Running for 2 to 3 minutes.

Check that the product's pods run under the nonroot-v2 constraint. The command prints nonroot-v2:

kubectl get pod aisec-classify-0 -n aisec -o jsonpath='{.metadata.annotations.openshift\.io/scc}'; echo

Access the console​

The console's host name is normally under your cluster's apps domain, which your wildcard DNS already resolves to the router. Otherwise, point the name at a node running the router: oc get pods -n openshift-ingress -o wide.

oc get route -n aisec shows the Route created for the console.

  1. Browse to https://<ingress.host>/.
  2. If you kept the self-signed certificate, accept the browser's certificate warning.
  3. On the first visit, the console asks you to create its administrator account. Choose a username and a password of at least 8 characters. There is no default console login.
  4. Log in with the new account and continue with the Quick Start Setup Guide. Help → Quick Start in the console's top bar opens the same walkthrough.

Next steps​

The rest of the setup is done in the console. The Quick Start Setup Guide describes each step:

  • Install the license.
  • Add your Kubernetes clusters and storage systems, and open the firewall ports they need. Product Prerequisites lists the accounts, permissions and ports for each.

The pipeline agents already run as part of the chart. To change how many run, set pipeline.stages.<stage>.replicas in your values file and run the phase 2 helm upgrade command again.

Troubleshooting the installation​

The install fails with no matches for kind "Cluster"​

The operators' custom resource definitions weren't registered yet. Use the two-phase install.

The install fails with "aisec requires Helm v3.18.6 or newer"​

Upgrade Helm on the machine you install from, then run the command again.

Pods or volumes stay Pending​

Run kubectl get pvc -n aisec. If the volumes are Pending, the StorageClass has no provisioner behind it: check Prepare the cluster, or set storage.className to a class your cluster provides. If the pods are Pending with bound volumes, kubectl describe pod <pod> -n aisec shows whether the cluster lacks CPU or memory. Such pods start on their own once capacity is available; if that took more than 10 minutes, also check The console pod stays at Init:2/3.

The console pod stays at Init:1/3​

PostgreSQL isn't ready yet. Check kubectl get cluster -n aisec and kubectl get pvc -n aisec.

The console pod stays at Init:2/3​

The console waits for the ClickHouse database aisec. A job creates it, but the job gives up 10 minutes after the install, so the database is missing when ClickHouse started late (for example, pods waited for capacity). If PostgreSQL and ClickHouse are running, check whether the database exists:

CHPASS=$(kubectl get secret -n aisec aisec-clickhouse-user -o jsonpath='{.data.password}' | base64 -d)
kubectl exec -n aisec chi-aisec-clickhouse-ch-0-0-0 -c clickhouse -- \
clickhouse-client --user aisec --password "$CHPASS" -q "SHOW DATABASES"

If aisec isn't listed, create it. The console continues within seconds, and the pipeline agents follow:

kubectl exec -n aisec chi-aisec-clickhouse-ch-0-0-0 -c clickhouse -- \
clickhouse-client --user aisec --password "$CHPASS" -q "CREATE DATABASE IF NOT EXISTS aisec"

Login seems to succeed but returns to the login page​

The console is being reached over plain HTTP. Use https://: the console's session cookie is only sent over HTTPS.

Login fails with HTTP 403​

The console is reached on a port other than 443, or through a load balancer that terminates TLS and forwards plain HTTP to the ingress controller. Reach the console on port 443, and set the load balancer to pass TLS through or to re-encrypt.

The console pod is never created​

kubectl describe statefulset aisec-console -n aisec shows a FailedCreate event when the nonroot-v2 binding for the aisec-console service account is missing. Create it as shown in Prepare the cluster.

The CloudNativePG operator pod isn't created​

The values file must keep cloudnative-pg.containerSecurityContext.runAsUser: null and runAsGroup: null, and Helm must be 3.18.6 or newer.

No Route is created​

ingress.host must be set and ingress.className must be openshift-default: OpenShift doesn't create a Route for an ingress without a host name. If kubectl get secret aisec-tls -n aisec returns NotFound, run the helm upgrade command from phase 2 again.