쿠버네티스 설치 (Helm)
PlantPulse 플랫폼을 쿠버네티스에서 운영하기 위한 Helm 차트가 있습니다.
무엇인가
Docker Compose 구성을 쿠버네티스로 번역한 차트입니다. 대체한 것이 아닙니다.
정본은 여전히 compose 입니다. 둘이 어긋나면 compose 가 맞고 차트가 틀린 것입니다. 새 설정이 생기면 compose 에 먼저 들어가고 차트가 따라옵니다.
| 차트 위치 | plantpulse-platform-k8s 저장소의 charts/plantpulse-platform |
| 이미지 레지스트리 | 플랫폼은 docker.kopens.io/pp, 서드파티는 docker.kopens.io/proxy-dockerhub |
| 실증 환경 | k3s v1.32.5+k3s1 · 단일 노드 |
| 다루는 compose 서비스 | 10개 전부 |
시작하기 전에
쿠버네티스 버전
그 OS 는 cgroup v1 이고, k3s v1.36(쿠버네티스 1.36)은 cgroup v1 지원을 뺐습니다. 올리면 kubelet 이 이렇게 말하고 뜨지 않습니다.
kubelet is configured to not run on a host using cgroup v1
스토리지 — RWX 가 필요합니다
TLS 자재를 담는 pp-security 볼륨에 여러 파드가 동시에 붙습니다. 그래서
ReadWriteMany 를 지원하는 프로비저너가 있어야 합니다 — NFS · CephFS · Longhorn RWX 등입니다.
local-path 는 노드가 하나일 때만 동작합니다local-path 는 RWO 이고 노드 로컬입니다. 단일 노드에서는 모든 파드가 그 노드에 앉으므로
동작하지만, 두 번째 노드가 붙는 날 조용히 깨집니다. 차트의 결함이 아니라 실제 제약이고,
고객사 설치 중에 발견하지 않도록 여기 적어 둡니다.
자원
플랫폼 자원 요건은 쿠버네티스라고 줄지 않습니다. 시스템 요구사항 을 그대로 따릅니다 — 최소 16 vCPU · 128GB RAM · 1TB SSD 입니다.
클러스터가 없다면 — K3s → Rancher → Helm
쿠버네티스를 이미 쓰고 계시면 이 절은 건너뛰고 설치로 가십시오. 없는 상태에서 시작한다면 K3s 로 클러스터를 만들고, Rancher 로 화면을 붙이고, 그 위에 PlantPulse 차트를 올리는 순서가 가장 짧습니다.
SUSE 의 오픈소스 쿠버네티스 관리 UI 입니다. 선택 사항이고, kubectl 만으로도
설치·운영이 전부 됩니다. 파드·로그·이벤트를 눈으로 보고 싶을 때 편해서 함께 적습니다.
1. K3s 설치
한 대짜리 클러스터입니다. 설치 스크립트가 컨테이너 런타임 · kubelet · 기본
스토리지(local-path)까지 함께 올립니다.
# 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
kubectl 을 일반 계정에서 쓰려면 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 는 6443(API) 을 씁니다. 다른 노드를 붙일 계획이면 노드 사이의 8472/udp(flannel VXLAN) · 10250(kubelet) 도 필요합니다. 플랫폼 자체가 쓰는 포트는 포트 구성에 있습니다.
2. Rancher 설치
Rancher 는 인증서가 필요해 cert-manager 를 먼저 올립니다.
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
브라우저로 https://<호스트> 에 들어가 위에서 준 bootstrapPassword 로 처음 로그인하고,
바로 비밀번호를 바꾸십시오. 로그인하면 이 K3s 클러스터가 local 로 잡혀 있습니다.
Rancher 는 hostname 으로 인증서를 만듭니다. IP 로 접속할 계획이면 그 IP 를 그대로
hostname 에 넣고, 브라우저가 자체서명 인증서를 경고하는 것을 받아들이십시오.
3. PlantPulse 설치
여기서부터는 kubectl 로 하든 Rancher 화면에서 하든 같습니다. 아래 설치 절을
그대로 따르십시오. Rancher 를 쓰신다면 Apps → Charts → Repositories 에 차트
저장소를 등록해 화면에서 설치할 수도 있지만, 값 파일을 다루기에는 helm 명령이 낫습니다.
Rancher 가 실제로 도움이 되는 자리는 설치 뒤입니다 — 파드의 Init 단계가 어디서 기다리는지, 로그가 무엇을 말하는지, 이벤트가 무엇을 거절했는지를 한 화면에서 봅니다. 기동 순서를 읽을 때 특히 그렇습니다.
설치
1. 비밀을 Secret 으로 만듭니다
차트는 비밀번호를 한 줄도 갖고 있지 않습니다. values.yaml 에도 없고, 기본 설정에서는
차트가 Secret 을 만들지도 않습니다 — 이미 있는 것을 참조만 합니다.
정본은 compose 와 같은 사이드카 파일입니다. 바이트 단위로 그대로 씁니다.
kubectl -n plantpulse create secret generic plantpulse-platform-env \
--from-env-file=/etc/kopens/plantpulse-platform.env
2. 설치합니다
helm install pp charts/plantpulse-platform -n plantpulse --create-namespace
기본 레지스트리(docker.kopens.io/pp · docker.kopens.io/pd)는 공개라 밖에서도 받을 수
있습니다. 그래서 평상시 설치에는 --set 이 필요 없습니다.
3. 사설 레지스트리로 미러링했다면
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=<레지스트리 시크릿>
registryThirdParty 를 빠뜨리지 마세요앞의 둘만 덮으면 기동 순서 대기용 busybox 를 못 받습니다. 그러면 의존하는 파드가 전부
Init:0/1 에서 영원히 서는데, 그 실패는 레지스트리 문제처럼 보이지 않습니다.
기동 순서 — 쿠버네티스가 지켜 주지 않습니다
플랫폼에는 지켜야 할 순서가 있습니다.
certs → datalake → server → batch
→ warehouse · opcua · aasx · ha
server → proxy
compose 의 depends_on 에 해당하는 것이 쿠버네티스에는 없습니다. 흔히 대신 쓴다고 하는
셋은 전부 다른 일을 합니다.
| 흔히 쓰는 것 | 실제로 하는 일 |
|---|---|
readinessProbe | 그 파드로 가는 트래픽을 막습니다. 남의 파드 기동은 못 늦춥니다 |
StatefulSet | 같은 StatefulSet 안의 파드 순서입니다. 다른 워크로드와는 무관합니다 |
Helm hook · --wait | 매니페스트 적용 순서입니다. 만들어진 것의 준비 상태가 아닙니다 |
그래서 차트는 쿠버네티스가 실제로 막아 주는 자리 — initContainer — 에 대기를 넣었습니다. 대기는 「파드가 떴나」가 아니라 「산출물이 있나」 를 봅니다.
Init:0/2 는 고장이 아닙니다차트가 동작 중이라는 뜻입니다. 무엇을 기다리는지 물어보세요.
kubectl -n plantpulse logs <파드> -c wait-for-certs
기본 대기 한도는 1800초이고 5초마다 확인합니다. 한도를 넘기면 대기가 종료되고 kubelet 이 다시 띄웁니다 — 이유가 파드 이벤트에 남게 하려는 것입니다. 영원히 막혀 있으면 아무것도 남지 않습니다.
컨테이너 «안»의 순서는 차트가 건드리지 않습니다. 데이터레이크 내부의 기동 순서는
pd 가 컨테이너 안에서 강제합니다. 차트가 그것을 다시 구현하면 정본이 둘이 되어 갈라집니다.
프록시가 502 를 답한다면
도커 네트워크는 컨테이너의 모든 포트를 전달하지만, 쿠버네티스 Service 는 «자기가 선언한 포트만» 전달합니다. compose 에서 되던 업스트림이 쿠버네티스에서 502 가 되는 경로가 여기입니다.
차트에 그것을 잡는 게이트가 있습니다 — 모든 프록시 업스트림이 «이 차트가 만드는 Service 의, 그 Service 가 선언한 포트» 를 가리키는지 검사합니다.
bash hack/run-gates.sh
| 게이트 | 검사하는 것 |
|---|---|
gate-render.sh | 렌더 결과의 프록시 업스트림이 실재하는 Service · 포트인지 |
gate-no-plaintext-secrets.sh | 렌더 결과에 평문 비밀이 섞이지 않았는지 |
prove-gates-fail.sh | 게이트가 «실패해야 할 때 실제로 실패하는지» (게이트의 게이트) |
설치 전에 돌려서 통과시키십시오. 502 의 대부분은 여기서 먼저 잡힙니다.
노드가 얼마나 커야 하나
🔴 차트가 선언한 requests 의 합이 곧 노드 요구량입니다. 모자라면 파드가 뜨지 않고
Pending 으로 남습니다 — 「설치가 멈춘 것처럼 보이는」 첫 번째 원인입니다.
| 워크로드 | CPU 요청 | 메모리 요청 | 메모리 상한 |
|---|---|---|---|
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 | 각 2 Gi | 각 4 Gi |
ha | 0.5 | 1 Gi | 4 Gi |
proxy | 0.2 | 256 Mi | 1 Gi |
certs | 0.1 | 256 Mi | 1 Gi |
| 합(기본 구성) | 약 14 | 약 92 Gi | — |
datalake 만으로 CPU 8 · 메모리 64 Gi 를 요청합니다(요청과 상한이 같습니다 —
Guaranteed 로 잡아 둔 것입니다). 단일 노드에 올리려면 그 노드가 넉넉히 CPU 16 ·
메모리 128 Gi 급이어야 합니다.
JVM 힙은 파드 한도와 «따로» 선언됩니다
자바 앱 셋은 힙 크기를 차트가 직접 넘겨 줍니다. 파드 한도에서 유도하지 않습니다 — 유도하면 한도를 내렸을 때 힙이 조용히 줄어들고, 실제로는 이미지 안 리터럴이 이겨서 한도를 어느 방향으로 고쳐도 JVM 이 모르는 상태가 됩니다.
| 앱 | 힙 | 다이렉트 | 넘어가는 변수 |
|---|---|---|---|
server | 8g | 4g | PP_SERVER_HEAP · PP_SERVER_DIRECT |
batch | 3g | — | PP_BATCH_HEAP |
warehouse | 1g | — | PP_WAREHOUSE_HEAP |
차트는 렌더할 때 아래를 단언합니다. 어기면 설치가 그 자리에서 실패합니다.
힙 + 다이렉트 + 여유(기본 2048Mi) ≤ limits.memory
힙 + 여유 ≤ requests.memory
🔴 힙만 키우고 파드 한도를 안 올리면 프리터치 중 OOMKill 이 납니다. -Xms=-Xmx 에
AlwaysPreTouch 라 힙은 기동 즉시 전부 상주하는데, 이때 로그에는 힙을 지목하는 줄이
남지 않습니다. 둘을 함께 올리십시오.
지금 어디까지 검증됐나
실증 환경은 k3s v1.32.5+k3s1 · 단일 노드이고, 전부 실제 레지스트리 이미지입니다.
| 컴포넌트 | 상태 |
|---|---|
plantpulse-certs | ✅ Ready. TLS 자재가 공유 볼륨에 실제로 생성됨 |
plantpulse-datalake | ✅ 이 차트로 클린 설치해 19/19 기동 (2026-09-09, 2026.09 이미지, PVC 삭제 후) |
| 기동 순서 대기 | ✅ 막히고 풀리는 것을 실물로 확인 |
server · batch · warehouse · proxy | ⬜ 띄워 본 기록이 아직 없습니다 — 다만 차트 쪽 결함은 계속 잡히고 있습니다(2026-09-09: 백업 볼륨 · JVM 힙 계약 · 프록시 업스트림) |
opcua · aasx · ha | ⬜ 대기 상태까지만 확인 |
mirror-maker | 기본 꺼짐 (compose 의 mirror 프로파일 대응) |
데이터레이크 파드는 이제 특권 없이 뜹니다 — 이미지에서 systemd 가 PID 1 에서 빠지면서
privileged · SYS_ADMIN · cgroup:host 가 모두 필요 없어졌습니다.
TOTAL SERVICES : 19 RUNNING / 0 STOPPED
/proc/1/comm : bash # systemd 아님
securityContext: (비어 있음) # privileged·SYS_ADMIN·cgroup 없음
server → batch → warehouse → proxy 순서가 실물로 지켜지는지는 아직 아무도 그 지점까지
가 보지 못했습니다.
「차트를 안 고치고 있다」는 뜻이 아닙니다. 2026-09-09 에 앱 티어 결함 셋을 잡았습니다 — 백업 볼륨이 서버에 안 붙던 것, JVM 힙이 쿠버네티스에서 설정 불가였던 것, 프록시 업스트림 일곱이 도커 모양 그대로였던 것. 다만 셋 다 렌더 대조와 코드로 찾은 것이고, 파드를 실제로 띄워 확인한 것은 아닙니다. 「고쳤다」와 「이 차트에서 뜬다」는 다른 사실입니다. 고객사 설치에는 원라인 설치 나 Docker 설치 를 쓰십시오.
막혔을 때
| 증상 | 먼저 볼 것 |
|---|---|
파드가 Init:0/1 에서 안 움직임 | busybox 를 못 받는 것일 수 있습니다 — registryThirdParty 확인 |
파드가 Init:0/2 에서 대기 | 정상입니다. kubectl logs <파드> -c wait-for-certs 로 무엇을 기다리는지 봅니다 |
| 프록시 경로가 502 | bash hack/run-gates.sh — 업스트림이 실재하는 Service·포트를 가리키는지 |
| 두 번째 노드를 붙인 뒤 TLS 관련 오류 | pp-security 가 RWX 가 아닙니다 |
| 웨어하우스 콘솔의 목록이 항상 비어 있음 | Iceberg 관련 필수 설정이 비었습니다. 빈 값은 렌더가 실패하도록 되어 있으니 설치 로그를 봅니다 |