Kubernetes Installation (Helm)
PlantPulse provides a Helm chart for running the platform on Kubernetes.
I have confirmed a clean install up to the data lake with this chart (2026-09-09, services 19/19, no privileges).
However, server · batch · warehouse · proxy have not been tested with actual hardware yet.
For customer installations, use One-Line Installation or Docker Installation.
What It Is
This chart translates the Docker Compose configuration into Kubernetes. It is not a replacement.
The source of truth remains the compose file. If the two diverge, the compose file is correct and the chart is wrong. When new settings appear, they are added to the compose file first, and the chart follows.
| Chart Location | plantpulse-platform-k8s repository's charts/plantpulse-platform |
| Image Registry | docker.kopens.io/pp for the platform, docker.kopens.io/proxy-dockerhub for third-party |
| Test Environment | k3s v1.32.5+k3s1 · Single Node |
| Compose Services Covered | All 10 |
Before You Begin
Kubernetes Version
That OS uses cgroup v1, and k3s v1.36 (Kubernetes 1.36) has removed cgroup v1 support. If you upgrade, kubelet will say this and won't start.
kubelet is configured to not run on a host using cgroup v1
Storage — RWX is Required
Multiple pods attach simultaneously to the pp-security volume that holds the TLS materials. therefore,
a provisioner that supports ReadWriteMany is required — such as NFS, CephFS, or Longhorn RWX.
local-path only works with a single nodelocal-path is RWO and node-local. On a single node, all pods land on that node, so it works,
but the moment a second node joins, it silently breaks. This is not a flaw in the chart but an actual
constraint, and I note it here so it isn't discovered during a customer installation.
Resources
Platform resource requirements don't shrink just because you're using Kubernetes. Follow System Requirements as-is — a minimum of 16 vCPU · 128GB RAM · 1TB SSD.
If You Don't Have a Cluster — K3s → Rancher → Helm
If you're already using Kubernetes, skip this section and go to Installation. If you're starting from scratch, the shortest path is to create a cluster with K3s, add a UI with Rancher, and then deploy the PlantPulse chart on top.
It's SUSE's open-source Kubernetes management UI. It's optional, and kubectl alone is sufficient for
installation and operation. It's convenient when you want to see pods, logs, and events visually, so I've included it here.
1. Install K3s
This is a single-node cluster. The installation script brings up the container runtime, kubelet, and default
storage (local-path) all at once.
# Oracle Linux 8.10 은 cgroup v1 이라 v1.32 라인을 고정합니다 (위 경고 참고)
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=v1.32.5+k3s1 sh -
sudo systemctl status k3s # active (running)
sudo k3s kubectl get nodes # STATUS = Ready
To use kubectl from a regular account, copy the kubeconfig.
mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown "$(id -u):$(id -g)" ~/.kube/config
kubectl get nodes
K3s uses 6443 (the API). If you plan to attach other nodes, you'll also need 8472/udp (flannel VXLAN) and 10250 (kubelet) between nodes. The ports the platform itself uses are in Port Configuration.
2. Install Rancher
Rancher needs certificates, so cert-manager must be up first.
helm repo add rancher-stable https://releases.rancher.com/server-charts/stable
helm repo add jetstack https://charts.jetstack.io
helm repo update
# 1) cert-manager
helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --set crds.enabled=true
kubectl -n cert-manager rollout status deploy/cert-manager
# 2) Rancher — <호스트> 는 브라우저로 칠 주소입니다
helm install rancher rancher-stable/rancher --namespace cattle-system --create-namespace --set hostname=<호스트> --set bootstrapPassword=<초기비밀번호> --set replicas=1
kubectl -n cattle-system rollout status deploy/rancher
Go to https://<호스트> in your browser, log in for the first time with the bootstrapPassword you created above,
and change the password immediately. Once logged in, this K3s cluster will be registered as local.
Rancher creates certificates based on hostname. If you plan to access it via IP, put that IP directly into
hostname, and accept the browser's warning about the self-signed certificate.
3. Install PlantPulse
From here on, it's the same whether you use kubectl or the Rancher UI. Follow the Installation section
below exactly. If you're using Rancher, you can register the chart repository under Apps → Charts → Repositories and install from the UI, but the helm command is better for working with value files.
Where Rancher really helps is after installation — seeing in one screen where a pod is waiting in its Init stage, what the logs say, and what an event has rejected. That's especially true when reading Startup Order.
Installation
1. Create Secrets
The chart contains no passwords at all. They're not in values.yaml, and in the default configuration the chart
doesn't create Secrets either — it only references existing ones.
The source of truth is the same sidecar file as in compose. Write it byte-for-byte.
kubectl -n plantpulse create secret generic plantpulse-platform-env \
--from-env-file=/etc/kopens/plantpulse-platform.env
2. Install
helm install pp charts/plantpulse-platform -n plantpulse --create-namespace
The default registries (docker.kopens.io/pp · docker.kopens.io/pd) are public, so they can be pulled from outside.
This is why --set isn't needed for a routine installation.
3. If You've Mirrored to a Private Registry
helm install pp charts/plantpulse-platform -n plantpulse --create-namespace \
--set global.image.registry=<사설 레지스트리>/pp \
--set global.image.registryDatalake=<사설 레지스트리>/pd \
--set global.image.registryThirdParty=<사설 레지스트리>/proxy-dockerhub \
--set global.imagePullSecrets[0].name=<레지스트리 시크릿>
registryThirdPartyIf you cover just the first two, the busybox used for startup-order waiting can't be pulled. Then all dependent pods will sit in
Init:0/1 forever, and that failure doesn't look like a registry problem.
Startup Order — Kubernetes Won't Enforce It
The platform has a required order.
certs → datalake → server → batch
→ warehouse · opcua · aasx · ha
server → proxy
Kubernetes has no equivalent to the compose depends_on. The things people commonly say they use instead
all do something different.
| Common Approach | What It Actually Does |
|---|---|
readinessProbe | Blocks traffic to that pod. Can't delay another pod's startup |
StatefulSet | Order of pods within the same StatefulSet. Unrelated to other workloads |
Helm hook · --wait | Order of manifest application. Not the readiness of what's been created |
So the chart places the waiting where Kubernetes actually enforces it — in initContainers. The wait checks not "has the pod come up" but "is the output present."
Init:0/2 is not a failureIt means the chart is working. Ask what it's waiting for.
kubectl -n plantpulse logs <파드> -c wait-for-certs
The default wait timeout is 1800 seconds, checked every 5 seconds. If it times out, the wait ends and kubelet restarts the pod — so the reason is left in the pod events. If it were blocked forever, nothing would be left.
The chart doesn't touch ordering inside containers. The startup order within the data lake is
enforced by pd inside the container. If the chart re-implemented it, there would be two sources of truth that diverge.
If the Proxy Returns 502
A Docker network forwards all container ports, but a Kubernetes Service only forwards the ports it declares. This is where an upstream that worked in compose becomes a 502 in Kubernetes.
The chart has a gate to catch this — it checks that every proxy upstream points to a Service created by this chart, and to a port that Service declares.
bash hack/run-gates.sh
| Gate | What It Checks |
|---|---|
gate-render.sh | Whether the proxy upstreams in the rendered result point to real Services and ports |
gate-no-plaintext-secrets.sh | Whether the rendered result contains any plaintext secrets |
prove-gates-fail.sh | Whether the gates actually fail when they should (the gate's gate) |
Run it before installation and make it pass. Most 502s are caught here first.
How Big Should a Node Be
🔴 The sum of the requests declared by the chart is exactly the node's requirement. If it's short, pods won't start and will stay in
Pending — the first cause of "installation seems stuck."
| Workload | CPU Request | Memory Request | Memory Limit |
|---|---|---|---|
datalake | 8 | 64 Gi | 64 Gi |
server | 2 | 12 Gi | 16 Gi |
batch | 1 | 6 Gi | 8 Gi |
warehouse | 1 | 4 Gi | 8 Gi |
opcua · aasx | 0.5 each | 2 Gi each | 4 Gi each |
ha | 0.5 | 1 Gi | 4 Gi |
proxy | 0.2 | 256 Mi | 1 Gi |
certs | 0.1 | 256 Mi | 1 Gi |
| Total (default config) | ~14 | ~92 Gi | — |
datalake alone requests 8 CPUs · 64 Gi of memory (request equals limit — it's pinned as Guaranteed). To run on a single node, that node needs to be comfortably in the
CPU 16 · memory 128 Gi range.
JVM Heap Is Declared Separately from Pod Limits
The chart passes the heap size directly to the Java app set. It's not derived from the pod limit — if it were, lowering the limit would silently shrink the heap, and in practice a literal in the image would win, leaving the JVM unaware regardless of how you adjust the limit.
| App | Heap | Direct | Variables Passed |
|---|---|---|---|
server | 8g | 4g | PP_SERVER_HEAP · PP_SERVER_DIRECT |
batch | 3g | — | PP_BATCH_HEAP |
warehouse | 1g | — | PP_WAREHOUSE_HEAP |
The chart asserts the following at render time. If violated, the installation fails on the spot.
힙 + 다이렉트 + 여유(기본 2048Mi) ≤ limits.memory
힙 + 여유 ≤ requests.memory
🔴 If you raise the heap without raising the pod limit, you'll get an OOMKill during pre-touch. -Xms=-Xmx is set to
AlwaysPreTouch, so the heap is fully resident right at startup, and the logs won't point to the heap. Raise both together.
What Has Been Verified So Far
The test environment is k3s v1.32.5+k3s1 · single node, using entirely actual registry images.
| Component | Status |
|---|---|
plantpulse-certs | ✅ Ready. TLS materials actually created on the shared volume |
plantpulse-datalake | ✅ Clean install with this chart, 19/19 services up (2026-09-09, 2026.09 image, after PVC deletion) |
| Startup order waiting | ✅ Verified blocked and unblocked with actual hardware |
server · batch · warehouse · proxy | ⬜ No record of having been brought up yet — though chart-side flaws are still being caught (2026-09-09: backup volume, JVM heap contract, proxy upstreams) |
opcua · aasx · ha | ⬜ Verified only up to the waiting state |
mirror-maker | Default off (corresponds to the compose mirror profile) |
The data lake pod now comes up without privileges — with systemd removed from PID 1 in the image,
privileged · SYS_ADMIN · cgroup:host are all no longer needed.
TOTAL SERVICES : 19 RUNNING / 0 STOPPED
/proc/1/comm : bash # systemd 아님
securityContext: (비어 있음) # privileged·SYS_ADMIN·cgroup 없음
No one has yet gotten to the point of verifying the server → batch → warehouse → proxy order with actual hardware.
This isn't a claim that the chart is being left untouched. On 2026-09-09, I fixed a set of app-tier flaws — the backup volume not attaching to the server, the JVM heap being unavailable in Kubernetes, and the seven proxy upstreams still being in Docker's shape. But all three were found by render comparison and code review, not by actually bringing up the pods. "Fixed" and "runs with this chart" are different facts. For customer installations, use One-Line Installation or Docker Installation.
When You're Stuck
| Symptom | What to Check First |
|---|---|
Pod stuck in Init:0/1 | Might be unable to pull busybox — check registryThirdParty |
Pod waiting in Init:0/2 | This is normal. See kubectl logs <파드> -c wait-for-certs for what it's waiting on |
| Proxy route returns 502 | bash hack/run-gates.sh — whether the upstream points to a real Service and port |
| TLS-related errors after adding a second node | pp-security isn't RWX |
| Warehouse console lists always empty | A required Iceberg setting is empty. Empty values are supposed to fail rendering, so check the installation logs |
Related Documents
- One-Line Installation — the currently recommended installation method
- Docker Installation — detailed compose-based procedures
- System Requirements — resource requirements are the same
- Cluster Installation — horizontal scaling of master and worker nodes