Skip to content

Install Logster on OpenShift with Helm

Logster is delivered for OpenShift as two Helm charts, packaged as .tgz archives in your delivery:

Chart archive What it deploys Where it runs
logster-classifier-<ver>.tgz The LogsterClassifier LLM model server — the OpenAI-compatible endpoint that produces Logster's authoritative verdict. A GPU node (needs nvidia.com/gpu).
logster-<ver>.tgz The Logster application stack — normalizer, inference, alerts, API, dashboard, plus Kafka (operator-managed), Elasticsearch, Logstash and Redis. Any worker nodes, entirely under the default restricted-v2 SCC (no privileged access).

Install order matters. Deploy the classifier chart first: it publishes the model endpoint URL that the application chart's inference service must be given at install time. Then deploy the application chart and point it at that endpoint.

flowchart LR
    subgraph EP["Windows / Linux endpoints"]
        SH["Log shipper<br>(Winlogbeat / etc.)"]
    end
    subgraph OCP["OpenShift cluster"]
        subgraph APP["logster (application chart)"]
            KR["Kafka routes<br>(bootstrap + per-broker)"] --> N["normalizer → inference → alerts"]
            N --> DB["dashboard route"]
        end
        subgraph GPU["logster-classifier (model chart)"]
            CR["classifier route<br>(HTTPS :8000 /v1/chat/completions)"]
        end
        N -- "verdict requests" --> CR
    end
    SH -- "raw logs · TLS" --> KR
    U["Analyst browser"] -- "HTTPS" --> DB

Prerequisites

Item Notes
OpenShift 4.12+ a default StorageClass is required (the application chart requests ~27 GB of persistent storage)
oc CLI, logged in project-admin rights for the installs; cluster-admin only for the one-time operator installs (GPU Operator, Kafka operator)
helm CLI v3.8+ single static binary — see helm.sh/docs/intro/install
GPU node + NVIDIA GPU Operator provides the nvidia.com/gpu resource the classifier requests; install from OperatorHub together with Node Feature Discovery. See the GPU Node guide for hardware/driver requirements.
Registry credentials username/token with pull access to the eunomatix/logster-* image repositories (provided with your delivery)
License file license.lic, provided with your delivery
Both chart archives logster-classifier-<ver>.tgz and logster-<ver>.tgz from your delivery

Everything below installs into a single project named logster. Create it once and reuse it for both charts:

oc new-project logster

The Logster images are in private repositories. Create a pull secret named regcred and link it to the project's default service account. The application chart references regcred by name (its imagePullSecrets value, default regcred); linking the same secret to the default service account additionally covers the classifier pods, which pull via the service account. One secret then serves both charts:

oc create secret docker-registry regcred \
  --docker-server=docker.io \
  --docker-username=<your-registry-user> \
  --docker-password=<your-registry-token> \
  -n logster

oc secrets link default regcred --for=pull -n logster

[!NOTE] If your delivery expects a different secret name, pass it to the application install with --set imagePullSecrets[0].name=<name>.


Part A — Install the LogsterClassifier (model)

The classifier serves the bundled model over HTTPS on port 8000 and is the sole source of Logster's verdict. Install it first so you can capture its endpoint URL for Part B.

It is a self-contained model application — a single image (eunomatix/logster-classifier) with the model weights baked in and an OpenAI-compatible server on port 8000; nothing else from Logster is needed to run it. Deploy it either with its Helm chart (A1, recommended) or, if you prefer not to use Helm, as a plain Deployment + Route (A1b). Either way the outcome is the same: a reachable https://<host>/v1/chat/completions endpoint.

A1 — Install the classifier chart

Install directly from the .tgz archive. Enable a Route so the endpoint is reachable by name, and add GPU tolerations if your GPU nodes are tainted:

helm install logster-classifier ./logster-classifier-<ver>.tgz -n logster \
  --set route.enabled=true \
  --timeout 40m
  # GPU nodes are commonly tainted — usually also needed:
  #   --set tolerations[0].key=nvidia.com/gpu \
  #   --set tolerations[0].operator=Exists \
  #   --set tolerations[0].effect=NoSchedule \
  #   --set nodeSelector."nvidia\.com/gpu\.present"=true

[!NOTE] The route.enabled=true flag exposes the model outside the cluster so the application's inference service can reach it by hostname. The route is TLS passthrough — end-to-end HTTPS is preserved to the model's self-signed certificate on port 8000. If the classifier and the application run in the same namespace, you may skip the route entirely and point inference at the in-cluster Service instead (see the note in A3).

A1b — Deploy without Helm (optional)

If you would rather not use Helm, the classifier is self-contained enough to run as a plain Deployment with a Service and a passthrough Route. This is the OpenShift equivalent of the reference docker run (GPU, shared memory, port 8000) and is a complete substitute for A1 — skip it if you used the chart above.

oc apply -n logster -f - <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata: {name: logster-classifier, labels: {app: logster-classifier}}
spec:
  replicas: 1
  selector: {matchLabels: {app: logster-classifier}}
  template:
    metadata: {labels: {app: logster-classifier}}
    spec:
      securityContext: {runAsNonRoot: true, seccompProfile: {type: RuntimeDefault}}
      containers:
        - name: classifier
          image: eunomatix/logster-classifier:v1.0.2
          securityContext: {allowPrivilegeEscalation: false, capabilities: {drop: ["ALL"]}}
          env: [{name: HOME, value: /tmp}]   # vLLM cache dir; random UID has no $HOME
          ports: [{containerPort: 8000, name: http}]
          resources:
            limits: {nvidia.com/gpu: 1}       # --gpus device=0
          volumeMounts: [{name: shm, mountPath: /dev/shm}]   # --ipc=host --shm-size 16g
      volumes:
        - name: shm
          emptyDir: {medium: Memory, sizeLimit: 16Gi}
---
apiVersion: v1
kind: Service
metadata: {name: logster-classifier}
spec:
  selector: {app: logster-classifier}
  ports: [{port: 8000, targetPort: 8000, name: http}]   # -p 8000:8000
---
apiVersion: route.openshift.io/v1
kind: Route
metadata: {name: logster-classifier}
spec:
  to: {kind: Service, name: logster-classifier}
  port: {targetPort: http}
  tls: {termination: passthrough}   # preserve HTTPS end-to-end to :8000
EOF

Add nodeSelector / tolerations for your GPU nodes as needed. Then continue with A2 and A3 exactly as for the chart install.

A2 — Wait for the model to be ready

The image bakes the model weights in (tens of GB), so the first pull is long and the server then loads the weights into VRAM before it answers. The chart's startup probe allows up to ~30 minutes for this.

oc rollout status deploy/logster-classifier -n logster --timeout=40m
oc get pods -l app=logster-classifier -n logster       # READY 1/1 when the model is up

The pod is READY 1/1 only once the model server passes its /health check — i.e. the model is loaded and serving.

A3 — Capture the model endpoint

Read the classifier Route host and form the OpenAI-compatible chat endpoint URL. Note this URL down — you pass it to the application chart in Part B:

oc get route logster-classifier -n logster \
  -o jsonpath='https://{.spec.host}/v1/chat/completions{"\n"}'

The result looks like:

https://logster-classifier-logster.apps.<cluster-domain>/v1/chat/completions

[!IMPORTANT] The application's inference service must be able to resolve and reach this hostname. When both charts share the cluster this is automatic. If you run the classifier on a separate GPU cluster, confirm the application cluster can resolve the route host (DNS) and reach it over TLS (443) before continuing.

Same-namespace shortcut: when the classifier runs alongside the application, you can bypass the route and use the in-cluster Service directly — https://logster-classifier:8000/v1/chat/completions. Use whichever address the inference service can reach.


Part B — Install the Logster application

B1 — Install the Kafka operator (one-time, cluster-admin)

Logster's Kafka cluster is managed by Red Hat AMQ Streams (or the upstream Strimzi operator — both provide the same CRDs). Install it once per cluster, either from the console (Operators → OperatorHub → "Streams for Apache Kafka" → Install) or via CLI:

oc apply -f - <<'EOF'
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
  name: amq-streams
  namespace: openshift-operators
spec:
  channel: stable
  name: amq-streams            # community alternative: strimzi-kafka-operator
  source: redhat-operators     #                        community-operators
  sourceNamespace: openshift-marketplace
EOF

Wait until the operator reports Succeeded:

oc get csv -n openshift-operators | grep -Ei 'amq-streams|strimzi'

B2 — Install the application chart

Install from the .tgz archive, passing the classifier endpoint you captured in A3:

helm install logster ./logster-<ver>.tgz -n logster \
  --set elasticsearch.password="$(openssl rand -hex 16)" \
  --set-file license.file=./license.lic \
  --set inference.llmEndpoint="https://logster-classifier-logster.apps.<cluster-domain>/v1/chat/completions" \
  --set inference.llmUseHttps=true \
  --timeout 20m
  • inference.llmEndpoint — the classifier URL from Part A. This is the only wiring between the two charts.
  • elasticsearch.password — generated once here and stored in the logster-elasticsearch Secret. Retrieve it later with: oc get secret logster-elasticsearch -n logster -o jsonpath='{.data.ELASTIC_PASSWORD}' | base64 -d
  • license.file — the signed license from your delivery. The inference service will not start without it.

B3 — Watch it converge

oc get pods -n logster -w

Within ~5–10 minutes (image pulls dominate the first install) every pod reaches Running / READY 1/1, the elasticsearch-init Job shows Completed, and Kafka reports Ready:

oc get kafka logster -n logster        # READY: True
oc get kafkatopic -n logster           # 8 topics, READY: True

[!NOTE] It is normal for the inference pod to restart a few times while Kafka and Redis are still coming up — the stack converges on its own.

B4 — The routes the application creates

The application chart publishes three routes. List them:

oc get routes -n logster
Route Purpose Consumed by
dashboard The Logster web console (edge TLS). Analysts' browsers.
logster-kafka-bootstrap Kafka bootstrap listener (TLS passthrough). Endpoint log shippers — the initial connect address.
logster-kafka-0 Kafka per-broker listener (TLS passthrough, one per broker). Endpoint log shippers — brokers the client is redirected to.

The two logster-kafka-* routes are the Kafka routes your endpoints ship logs to. They are covered in B6. (Confirm the exact route names on your cluster with oc get routes -n logster — Strimzi names them <cluster>-kafka-bootstrap and <cluster>-kafka-<broker-id>.)

B5 — Open the dashboard

oc get route dashboard -n logster -o jsonpath='https://{.spec.host}{"\n"}'

Open that URL in a browser. First login is admin / admin; you are required to set a new password immediately.

B6 — Ship endpoint logs to Logster

Endpoints publish raw logs to Logster's Kafka external listener, exposed through the OpenShift router as TLS-passthrough routes. A Kafka client first connects to the bootstrap route, then is redirected to the per-broker route(s) advertised back to it — so your endpoints must be able to reach both Kafka routes.

1. Get the bootstrap address. This is the authoritative connect address (host + port 443):

oc get kafka logster -n logster \
  -o jsonpath='{.status.listeners[?(@.name=="external")].bootstrapServers}{"\n"}'

2. Point the endpoint shipper at the bootstrap route (over TLS). Update the log shipper's Kafka output on every endpoint. Using the Winlogbeat example from Connect Windows Endpoints, change the output.kafka block so hosts is the bootstrap route host on port 443 and enable TLS. The routes are TLS-passthrough with a self-signed (Kafka cluster CA) certificate; set ssl.verification_mode: none so the shipper connects over TLS without needing the CA distributed to every endpoint:

output.kafka:
  enabled: true
  # bootstrap route host from step 1 — NOT the appliance's :29092 address
  hosts: ["logster-kafka-bootstrap-logster.apps.<cluster-domain>:443"]
  topic: "sysmon-logs"
  partition.round_robin:
    reachable_only: true
  required_acks: 1
  compression: gzip
  codec.json:
    pretty: false
    escape_html: false
  ssl.enabled: true
  ssl.verification_mode: none

[!IMPORTANT] Both Kafka routes must resolve from the endpoint. After connecting to the bootstrap route, the Kafka client is redirected to the advertised per-broker route host (logster-kafka-0-...). Every endpoint must therefore be able to resolve both the bootstrap and per-broker route hostnames in DNS — both point to the OpenShift ingress/router IP — and reach them on port 443. If only the bootstrap name resolves, the initial handshake succeeds but no data flows. List the exact hostnames with oc get routes -n logster and add them to endpoint DNS (or hosts files) as needed.

Restart the shipper. Within a minute or two the endpoint appears on the Logster dashboard. See Connect Windows Endpoints for the full Sysmon + Winlogbeat installation.


Uninstall

helm uninstall logster -n logster              # application workloads
helm uninstall logster-classifier -n logster   # model server
oc delete pvc --all -n logster                 # additionally removes stored data