Kubernetes 환경에서 SysMasterDB를 기동 / 로그 확인 / 종료 / 초기화하는 방법이다.
클러스터 사전 준비(setup-once.sh)가 완료된 상태에서, 배포 파일이 위치한 디렉터리에서 아래 명령을 수행하면 전체 스택이 배포된다.
kubectl apply -k kubernetes적용 전에 최종 결과를 확인하려면 아래 명령을 사용한다.
kubectl kustomize kubernetes # 병합된 매니페스트 출력
kubectl apply -k kubernetes --dry-run=server # 클러스터 검증만 수행기동 순서는 별도로 제어할 필요가 없다. 각 모듈이 자신의 의존 대상이 준비될 때까지 대기하므로 아래 순서로 준비된다.
1. metadb, repodb, zookeeper, client-tibero (의존 대상 없음)
2. broker (zookeeper 필요)
3. sdm (metadb, repodb 필요)
4. analyzer, dbmanager, chatbot (sdm 필요)
5. collector (sdm, analyzer 필요)
6. client-gateway (sdm, dbmanager, collector 필요)최초 배포는 데이터베이스 초기화로 인해 전체가 준비되기까지 수 분이 소요된다.
kubectl -n sysmasterdb get pods전 모듈에 헬스 체크가 설정되어 있어 1/1 Running으로 표시되면 실제로 요청을 처리할 수 있는 상태를 의미한다. 별도로 기동 완료 로그를 확인할 필요가 없다.
파드가 계속 준비되지 않는 경우 아래로 원인을 확인한다.
kubectl -n sysmasterdb describe pod {파드명} # Events 항목에서 실패 사유 확인
kubectl -n sysmasterdb logs {파드명}
kubectl -n sysmasterdb logs {파드명} --previous # 재시작 직전 로그kubectl -n sysmasterdb get svc client-gateway할당된 EXTERNAL-IP로 브라우저에서 https://{EXTERNAL-IP} 로 접속한다. 사이트 인증서를 반입하지 않은 경우 브라우저에 인증서 경고가 표시되는 것은 정상이다.
각 컨테이너의 표준 출력 로그는 아래 명령으로 확인할 수 있다.
다만 각 모듈의 애플리케이션 로그는 표준 출력이 아니라 컨테이너 내 /sysmaster/logs 경로의 로그 파일에 적재되므로, 상세 로그는 아래와 같이 확인한다.
셸 도구가 없는 파드를 조사할 때는 함께 제공되는 유틸리티 이미지를 사용한다. kubectl debug는 파드 이름만 받으므로 디플로이먼트 이름을 전달하면 실패한다.
라이선스는 sdm-license Secret으로만 주입되며, 파드를 재시작하지 않고 교체할 수 있다. 아래 세 단계를 순서대로 수행한다.
Secret을 교체해도 파드 안의 파일이 즉시 바뀌지는 않는다. 아래 명령의 출력이 새 라이선스의 값으로 바뀔 때까지 기다린다(반영에 최대 1분 소요).
정상적으로 갱신되면 아래와 같이 응답한다. expirationDate가 새 라이선스의 만료일과 일치해야 갱신이 완료된 것이다.
특정 모듈만 재시작하는 경우는 다음과 같다.
아래 명령으로 워크로드만 삭제하면 SysMasterDB가 종료된다. 이때 Meta DB와 Repository DB의 데이터 파일은 PVC에 유지되므로, 다시 기동하면 이전에 저장한 데이터를 사용할 수 있다.
다시 기동할 때는 kubectl apply -k kubernetes만 수행한다.
아래 데이터는 PVC 3종에만 존재하며 제품이 생성하는 사본은 없다. 초기화를 수행하기 전에 반드시 백업한다.
스토리지 계층 백업(CSI VolumeSnapshot 등)을 권장한다. 대상 볼륨은 아래로 확인한다.
아래 명령을 수행하면 네임스페이스를 포함한 모든 오브젝트와 데이터가 삭제되고, 최초 설치 상태와 동일하게 동작한다.
실수로 삭제하더라도 데이터가 보존되도록 하려면, 배포 직후 PVC에 연결된 PV의 reclaim 정책을 Retain으로 변경해 둔다.
sdm-license Secret
위 절차 (재시작 불필요)
meta.conf / repo.conf
데이터베이스 최초 초기화 시에만 사용된다. 이미 초기화된 데이터베이스에는 반영되지 않는다
sdm이 기동하지 않음
라이선스 Secret 없음, 또는 라이선스 version이 1.1이 아님
setup-once.sh 실행 여부 확인
sdm이 데이터베이스에 접속하지 못함
meta.conf / repo.conf의 암호와 Secret 값 불일치
두 값을 동일하게 설정
UI 접속 시 502
NGINX_RESOLVER가 실제 클러스터 DNS 주소와 다름
kubectl get svc kube-dns -n kube-system
sdm이 데이터베이스를 계속 대기
metadb / repodb 서비스에서 7000 포트 누락
서비스 정의 확인
배포가 완료되지 않음
strategy: Recreate 설정을 제거함
원복
파드가 계속 OOMKilled
메모리 limits 부족, 또는 JVM 힙이 limits에 비해 큼
두 값을 함께 상향
Kafka Too many open files
노드의 파일 디스크립터 한계가 낮음
노드에서 nofile 65536 이상으로 설정
대시보드가 비어 있음
TPM Agent가 collector-ingest에 도달하지 못함
EXTERNAL-IP와 Agent 설정 주소 확인
client-gateway만 CrashLoopBackOff
로그 볼륨 마운트를 제거했거나 HTTPS 인증서 파일명이 다름
kubectl logs deploy/client-gateway --previous 로 nginx 오류 메시지 확인
ConfigMap을 변경했는데 반영되지 않음
ConfigMap 이름이 동일해 파드 스펙이 변경되지 않음
kubectl -n sysmasterdb rollout restart deploy
오버레이 추가 후 기존 UI/수집이 중단
kustomization.yml에 resources: / patches: 키를 중복 작성
「Kubernetes 환경」 문서의 매니페스트 구성의 주의 사항 참고
kubectl -n sysmasterdb logs deployment/sdm # 터미널 출력 로그
kubectl -n sysmasterdb logs deployment/sdm -f # 실시간 확인kubectl -n sysmasterdb exec deployment/sdm -- ls /sysmaster/logs
kubectl -n sysmasterdb exec deployment/sdm -- sh -c 'tail -f /sysmaster/logs/sdm.v.{version}.log'POD=$(kubectl -n sysmasterdb get pod -l app=sdm -o jsonpath='{.items[0].metadata.name}')
kubectl -n sysmasterdb debug -it "$POD" \
--image={registry}/sysmaster-db-alpine-linux:{version} --target=sdmkubectl create secret generic sdm-license -n sysmasterdb \
--from-file=sysmaster-db-license.xml=/path/to/new-license.xml \
--dry-run=client -o yaml | kubectl apply -f -kubectl -n sysmasterdb exec deploy/sdm -- grep serial_id /sysmaster/license/sysmaster-db-license.xmlkubectl -n sysmasterdb exec deploy/sdm -- refresh-license{"message":"License store refresh succeeded.","licenseType":"SUBSCRIPTION","maxMonitoringCores":1000,"expirationDate":"..."}kustomization.yml (이미지, patch 등)
kubectl apply -k kubernetes
디플로이먼트의 env 항목
kubectl apply -k kubernetes (파드가 자동 교체된다)
smdb-env ConfigMap
kubectl -n sysmasterdb rollout restart deploy/sdm
kubectl -n sysmasterdb rollout status deploy/sdmkubectl -n sysmasterdb delete deploy,svc --all
kubectl -n sysmasterdb get pvc # 3개가 Bound 상태로 남아 있어야 한다metadb-data
설정, 계정, 관제 대상 목록
repodb-data
수집된 메트릭 전체
sdm-reports
kubectl -n sysmasterdb get pvc -o custom-columns=PVC:.metadata.name,PV:.spec.volumeNamekubectl delete -k kubernetes
kubectl get ns sysmasterdb # NotFound 가 될 때까지 대기한다
kubectl get pv | grep sysmasterdb # 남아 있으면 스토리지 정책에 따라 처리한다for pvc in metadb-data repodb-data sdm-reports; do
pv=$(kubectl -n sysmasterdb get pvc "$pvc" -o jsonpath='{.spec.volumeName}')
kubectl patch pv "$pv" -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
done전체 파드 ImagePullBackOff
레지스트리 주소 미수정, 또는 인증 필요
kustomization.yml의 newName 확인 / 사설 레지스트리 인증 구성 추가
전체 파드 Pending
PVC가 Pending (default StorageClass 없음)
kubectl -n sysmasterdb get events --sort-by=.lastTimestamp | tail -30kubectl apply -k kubernetes 후 kubectl -n sysmasterdb rollout restart deploy
생성된 리포트 파일
kubectl -n sysmasterdb get pvc
Kubernetes 환경에서 SysMasterDB를 설치하는 과정이다.
SysMasterDB 8.4의 Kubernetes 배포 파일은 kustomize 기반 매니페스트로 제공된다. 배포 파일의 kubernetes 디렉터리에 전체 스택 매니페스트(base)와 클러스터 1회 부트스트랩 스크립트(setup-once.sh)가 포함되어 있으며, kubectl apply -k 명령 한 번으로 전체 스택이 배포된다.
각 매니페스트 파일의 구성과 설정값의 의미는 「Kubernetes 환경」 문서의 매니페스트 구성 항목을, 환경에 따라 선택적으로 적용하는 구성은 아래 9. 선택 구성 항목을 참고한다.
설치 파일이 준비된 디렉터리에서 아래의 명령을 수행하여 도커 이미지를 로드한다.
docker load -i sysmaster-db-{version}.tar
이때 로드된 도커 이미지 중 Kubernetes 배포에 사용하는 이미지는 다음과 같다.
sysmaster-db-client:{version}
sysmaster-db-client-tibero:{version}
sysmaster-db-sdm:{version}
sysmaster-db-dbmanager:{version}
sysmaster-db-collector:{version}
sysmaster-db-analyzer:{version}
sysmaster-db-chatbot:{version}
sysmaster-db-tiberoopensql-postgres:{version}
sysmaster-db-kafka-loggable:{version}
sysmaster-db-zookeeper-loggable:{version}
sysmaster-db-alpine-linux:{version}
로드한 도커 이미지들을 해당 환경에서 사용 중인 이미지 레지스트리로 태그를 변경하여 푸시한다.
푸시가 완료되면 kubernetes/kustomization.yml 파일의 images 항목에서 각 이미지의 newName을 해당 레지스트리 주소로 수정한다. 이미지 태그는 base 매니페스트에 릴리스 버전으로 이미 지정되어 있으므로, 릴리스 버전과 다른 태그로 푸시한 경우에만 newTag를 추가로 지정한다.
kubernetes/base 디렉터리의 매니페스트가 전체 스택(11개 디플로이먼트)을 정의한다. 모든 오브젝트는 sysmasterdb 네임스페이스에 생성되며, 컴포넌트 간 통신은 서비스(Service) 이름으로 이루어진다. 각 디플로이먼트에 대한 설명은 다음과 같다.
배포 전에 반드시 kubernetes/setup-once.sh를 클러스터당 1회 실행해야 한다. 실행하지 않으면 파드가 기동하지 못한다.
스크립트가 수행하는 내용은 다음과 같다.
sdm-license Secret 생성 — 발급받은 라이선스 파일을 Secret으로 등록하며, sdm 파드가 이를 /sysmaster/license 경로로 마운트한다. SysMasterDB 8.4는 license version="1.1"만 허용하며, 다른 버전의 라이선스를 전달하면 스크립트가 실패한다.
smdb-credentials Secret 생성 — Admin 계정과 Meta DB/Repository DB 접속 암호를 Secret으로 등록한다. 매니페스트에는 암호가 평문으로 존재하지 않으며, 암호를 사용하는 파드(sdm/analyzer/chatbot)만 Secret을 주입받는다.
CoreDNS rewrite 등록 — client-gateway가 sdm 이름을 해석할 수 있도록 rewrite name sdm sdm.sysmasterdb.svc.cluster.local
스크립트는 kubectl과 python3를 사용하며, 두 명령이 PATH에 없으면 즉시 중단된다. 실행 메시지는 영문으로 출력된다.
kubernetes/base/foundation.yml 파일을 열어 smdb-env ConfigMap에서 전 모듈이 공통으로 사용하는 파라미터 값을 설정한다.
예를 들어 수집 정보 보관 주기를 7일에서 14일로 변경하는 경우, kubernetes/base/sysmaster/sdm.yml에서 metric_retention_day 값을 직접 수정한다.
변경 후 kubectl apply -k kubernetes를 수행하면 sdm 파드가 자동으로 교체된다. 반영 여부는 아래로 확인한다.
kubernetes/base/sysmaster/sdm.yml 파일을 열어 env 항목에서 계정, 세션, 접속 IP 제한, 이벤트 로그 보관에 필요한 파라미터 값을 설정한다.
해당 과정에서 설정하는 파라미터에 대한 설명은 다음과 같다.
변경 후 kubectl apply -k kubernetes를 수행하면 sdm 파드가 자동으로 교체된다.
kubernetes/base/meta.conf, kubernetes/base/repo.conf 파일이 Meta DB와 Repository DB의 초기화(사용자·데이터베이스·확장 생성)에 사용된다. 다음은 Meta DB에 대한 파라미터 설정 예시이다.
운영 환경에서는 pg_superuser_password(기본 postgres)와 pg_users 계정의 암호(기본 sysmaster)를 반드시 교체한다. 그 외 conf 파일은 기본적으로 제공되는 설정값을 사용하되, 다음에서 설명하는 파라미터는 필요 시 구동 환경에 맞게 사용자가 직접 설정해준다.
외부 노출이 필요한 서비스는 LoadBalancer 타입으로 구성되어 있다.
배포 후 아래 명령으로 할당된 주소를 확인한다.
collector-ingest에 할당된 EXTERNAL-IP의 8292 포트를 관제 데이터베이스의 TPM Agent에 수집 서버 주소로 등록한다. 이 주소에 Agent가 도달하지 못하면 수집 정보가 유입되지 않아 대시보드가 비어 있게 된다.
default StorageClass가 없거나 특정 StorageClass를 사용해야 하는 경우 kubernetes/base/foundation.yml의 PVC 3종에 storageClassName을 지정한다. 지정하지 않은 상태에서 default도 없으면 PVC가 Pending에 머물고 파드가 기동하지 못한다.
클러스터 DNS 주소가 다른 경우 kubernetes/base/client/client-gateway.yml의 NGINX_RESOLVER 값을 수정한다. 값이 틀리면 UI 접속 시 502 오류가 발생한다.
파일 디스크립터는 Kubernetes에서 파드 단위로 설정할 수단이 없다.
기본 배포에는 포함되지 않으며, 환경에 따라 필요한 것만 kubernetes/kustomization.yml에 추가한다.
아래 예시에서 [기존]이 표시된 줄은 이미 파일에 있는 줄이고, <- 추가가 표시된 줄만 새로 작성하는 것이다. resources: patches: 키를 새로 작성하면 기존 항목이 무효화된다(매니페스트 구성 문서의 주의 사항 참고).
레지스트리가 익명 pull을 허용하지 않는 환경에서 필요하다. 적용하지 않으면 전체 파드가 ImagePullBackOff 상태가 된다.
기본값 imagePullPolicy: Always는 파드가 기동될 때마다(노드 재부팅, 재스케줄 포함) 레지스트리를 조회한다. 릴리스 이미지는 태그가 버전으로 고정되어 내용이 변하지 않으므로, 운영 환경에서는 레지스트리가 점검·장애로 중단된 경우 이미지가 노드에 캐시되어 있어도 파드가 기동하지 못하는(ErrImagePull) 위험만 남는다. 이 구성은 정책을 IfNotPresent로 완화한다.
기본 상태에서 client-gateway는 이미지에 내장된 self-signed 인증서를 사용한다. 서비스는 정상 동작하지만 브라우저에 인증서 경고가 표시되므로, 사이트 도메인 인증서를 반입한다.
https-cert.yml의 내용은 다음과 같다.
기본 구성에서 로그는 파드 내부 임시 볼륨에 기록되므로, 파드가 교체되면(배포, 노드 drain, eviction) 로그가 사라진다. 장애 조사를 위해 로그를 보존해야 하는 환경에서는 전 모듈이 공유하는 영구 볼륨으로 전환한다.
두 파일은 한 쌍이다. logs-pvc.yml만 추가하면 파드는 여전히 임시 볼륨을 사용하고, patch만 추가하면 참조할 PVC가 없어 전체 파드가 기동하지 못한다.
ReadWriteMany를 지원하는 StorageClass가 필요하다(NFS, CephFS 등). 지원하지 않는 스토리지에 적용하면 PVC가 Pending에 머물고 전체 파드가 기동하지 못한다.
smdb-logs PVC는 storageClassName을 지정하지 않은 상태로 제공된다. 즉 클러스터의 default StorageClass로 생성되므로, default가 ReadWriteMany를 지원하지 않으면 그대로는 Pending이 된다. Kubernetes는 접근 모드를 보고 StorageClass를 선택해 주지 않으므로, ReadWriteMany를 제공하는 StorageClass가 default가 아니면 직접 지정해야 한다.
용량 기본값은 50Gi이다. 수집 규모와 보관 주기에 맞춰 조정한다. 가득 차면 로그 기록이 실패한다.
볼륨 안에서 각 모듈은 자기 이름의 하위 디렉터리에 기록한다(sdm/, analyzer/ 등). 모듈별로 디렉터리를 분리하지 않으면 압축 보관되는 오류 로그 파일명이 모듈 간에 겹쳐 서로 덮어쓰게 된다.
이 구성에는 로그 확인 전용 파드가 함께 포함된다. 공유 볼륨을 읽기 전용으로 마운트하고 대기만 하므로, 애플리케이션 파드가 기동하지 못하는 상태에서도 전체 모듈의 로그를 확인할 수 있다.
Kubernetes는 기본적으로 동일 네임스페이스의 모든 파드가 서로 자유롭게 통신할 수 있으며, 클러스터 내의 다른 워크로드에서도 파드 IP로 직접 접근할 수 있다. 클러스터를 다른 서비스와 공유하거나 내부망 격리 요건이 있는 환경에서는 이 구성으로 실제로 필요한 통신 경로만 허용한다.
허용되는 통신 경로는 다음과 같다. 구성이나 포트를 변경한 경우 이 규칙도 함께 갱신해야 한다.
적용 후 검증
CNI가 NetworkPolicy를 지원해야 한다. calico, cilium 등은 정상 동작하지만, 지원하지 않는 CNI는 정책을 오류 없이 무시한다. 적용된 것으로 오인하지 않도록 반드시 실제 차단 여부를 확인한다.
애플리케이션 이미지에는 nc 등의 네트워크 도구가 없으므로 python으로 확인한다. chatbot 파드에서 sdm 파드로 두 포트를 시도하며, 허용 경로인 8090은 OPEN, 차단 대상인 8080은 BLOCKED 여야 정상이다(8080은 client-gateway만 접근할 수 있는 포트이다).
8090이 OPEN이어야 이 판정이 유효하다. 두 포트 모두 BLOCKED로 표시되면 정책이 아니라 확인 절차 자체가 실패한 것이다(파드 IP 오기재, sdm 미기동 등). 이 경우 차단되었다고 판단하지 않는다.
8080이 OPEN이면 CNI가 정책을 무시하고 있는 것이다. 클러스터의 CNI 종류를 확인한다.
sdm
수집 정보를 조회하고 클라이언트와 통신하는 API 서버
8080(사용자), 8090(내부)
dbmanager
관제 데이터베이스에 대한 상태 확인과 Admin 기능을 수행하는 서버
8082
collector
관제 데이터베이스로부터 데이터를 수집하는 서버
8292(수집), 8083(내부)
analyzer
수집한 정보를 가공해 분석 및 저장하는 서버
8081
chatbot
AI 챗봇 서버
8089
metadb
UI 관련 설정 정보를 저장하는 DB 서버
5432, 7000(헬스)
repodb
관제 데이터베이스 수집 데이터를 저장하는 DB 서버
5432, 7000(헬스)
broker
Kafka 브로커
9092
zookeeper
Kafka 코디네이터
2181
DONT_CHECK_HTTP_HEALTH
컨테이너 내부 헬스 체크 스크립트 비활성화 여부
false
HEALTH_LOG_RETENTION_DAY
헬스 체크 로그 보관 주기
30
KAFKA_MESSAGE_MAX_BYTES
카프카 메시지 사이즈 설정, 1MB ~ 2GB 범위로 설정 가능 smdb-env 값은 broker에만 반영된다. collector가 보내는 메시지의 최대 크기는 collector 디플로이먼트의 broker_message-max-size이므로 함께 변경한다.
20971520 Byte
METADB_USER
chatbot 모듈의 Meta DB 접속 사용자 이름
sysmaster
REPODB_USER
chatbot 모듈의 Repository DB 접속 사용자 이름
sysmaster
각 디플로이먼트의 logging_file_max-size / logging_file_total-size-cap
LOG_LEVEL
각 디플로이먼트의 logging_level_root
CONTAINER_LOG_PATH
각 디플로이먼트의 logging_file_path 및 볼륨 마운트 경로
ADMIN_USERNAME
sdm 디플로이먼트의 application_admin_username
SQL_FLUSH_THRESHOLD / SQL_RS_FETCH_SIZE
collector 디플로이먼트의 sql-loading_flush-threshold / sql-loading_fetch-size
LIMIT_SQL_HASH_COUNT
collector 디플로이먼트의 sql-loading_limit-sql-hash-count
PERFORMANCE_LOGGING
sdm / analyzer / collector / dbmanager 각 디플로이먼트의 동일 이름 env 항목
ACCOUNT_INACTIVE_LOCK_DAYS
sdm 디플로이먼트의 account_inactive-lock-days
ACCOUNT_REFRESH_TOKEN_EXPIRE_DAYS
sdm 디플로이먼트의 account_refresh-token-expire-days
ACCOUNT_PASSWORD_HISTORY_COUNT
sdm 디플로이먼트의 account_password-history-count
ACCOUNT_PASSWORD_CHANGE_DAYS
sdm 디플로이먼트의 account_password-change-days
SESSION_MAX_CONCURRENT_SESSIONS
sdm 디플로이먼트의 security_session_max-concurrent-sessions
SESSION_IDLE_TIMEOUT_MINUTES
sdm 디플로이먼트의 security_session_idle-timeout-minutes (배포 파일에 기본 포함되지 않으며, 필요 시 env에 추가한다)
IP_RESTRICTION_TARGET
sdm 디플로이먼트의 security_ip-restriction_target
IP_RESTRICTION_SUPER_ADMIN_IP
sdm 디플로이먼트의 security_ip-restriction_super-admin-ip
IP_RESTRICTION_TRUSTED_PROXIES
sdm 디플로이먼트의 security_ip-restriction_trusted-proxies
ACCESS_LOG_RETENTION_DAY
sdm 디플로이먼트의 access-log_retention_day
account_password-history-count
이전 비밀번호 재사용 금지 비밀번호를 바꿀 때 직전 N개와 같은 값으로는 바꿀 수 없다. 1~24로 설정 가능하다.
1
account_password-change-days
비밀번호 주기적 변경 안내 마지막으로 비밀번호를 바꾼 뒤(값이 없으면 계정을 만든 뒤) 이 기간이 지나면 로그인 응답으로 변경을 안내한다. 0 이하면 기능을 끈다.
0
security_session_max-concurrent-sessions
계정당 동시 세션 수
0이면 제한하지 않는다(기본).
1 이상이면, 이 수를 넘는 로그인이 성공할 때 가장 먼저 로그인한 세션부터 끊는다.
0
security_session_idle-timeout-minutes
유휴 세션 자동 로그오프(분)
0 이하면 기능을 끈다.
[참고] sdm.yml에 기본 포함되지 않으며, 필요 시 env에 추가한다.
30
security_ip-restriction_target
허용IP 외 접속차단서비스
NONE: 비활성화.
ADMIN: super admin과, 어느 그룹에서든 ADMIN 이상 역할이 있는 사용자에게만 적용한다.
ALL: 모든 사용자에게 적용한다.
NONE
security_ip-restriction_super-admin-ip
super admin의 허용 IP super admin의 허용 IP는 이 값으로만 정한다.
빈값
security_ip-restriction_trusted-proxies
프록시 대역
Kubernetes 환경에서는 client-gateway 파드가 속한 Pod 대역을 넣어야 한다.
사내 PC 대역(10.0.0.0/8, 192.168.0.0/16 등)은 넣으면 안 된다. 그 대역의 PC가 헤더를 위조해 IP 제한을 피할 수 있다.
127.0.0.0/8, ::1, 172.16.0.0/12, 10.88.0.0/15
access-log_retention_day
이벤트 로그 보관 기간(일) ACCESS_LOG ( 로그인/ 로그아웃 로그) 보관 기간을 설정한다.
0 이하면 기록, 보관, 삭제 정책이 모두 멈춘다. 이미 쌓인 데이터는 그대로 둔다.
365
노드 여유 자원
메모리 16GB 이상, CPU 4코어 이상 권장
kubectl describe node
파일 디스크립터
nofile 65536 이상
배포 후 kubectl -n sysmasterdb exec deploy/repodb -- sh -c 'ulimit -n'
LimitNOFILEToo many open fileslogs-pvc.yml + logs-pvc-patch.yml
파드가 교체되어도 로그를 보존 (ReadWriteMany 스토리지 필요)
network-policy.yml
파드 간 통신 제한 (CNI 지원 필요)
collector
sdm
8083
sdm
client-gateway
8080, 8090
sdm
analyzer, collector, dbmanager, chatbot
8090
metadb, repodb
sdm, analyzer, chatbot, dbmanager
5432, 7000
broker
collector, analyzer, broker
9092
zookeeper
broker
2181
client-tibero
client-gateway
80
chatbot
sdm
8089
dbmanager
sdm
8082
analyzer
sdm
8081
client-gateway
사용자가 브라우저를 통해 접속하게 될 웹 진입점(리버스 프록시)
443
client-tibero
UI 정적 자원을 제공하는 웹 서버
TZ
SysMasterDB 서버 Time-Zone 설정
Asia/Seoul
USE_SENTRY
외부 오류 리포팅 사용 여부. 운영 환경에서는 false로 둔다
RETENTION_DAY (수집 정보 보관 주기)
sdm 디플로이먼트의 metric_retention_day
LOG_RETENTION_DAY
각 디플로이먼트의 logging_file_max-history
account_inactive-lock-days
장기 미사용 계정 잠금 마지막으로 사용한 뒤 이 기간(일)이 지나면 로그인, 토큰 재발급, 역할 변경을 막는다. 0 이하면 기능을 끈다.
0
account_refresh-token-expire-days
Refresh Token 유효기간 0 이하면 따로 정하지 않은 것으로 본다. 이때 유효기간은 장기 미사용 잠금이 켜져 있으면 잠금 기간과 상한이 같다.
pg_max_connections
Meta DB(또는 Repository DB)의 최대 커넥션 수
log_timezone
Meta DB(또는 Repository DB)의 로그 시간 Time-Zone
client-gateway
443
사용자 UI/API 접속(https)
collector-ingest
8292
default StorageClass
PVC 3종(metadb 10Gi / repodb 20Gi / sdm-reports 10Gi, ReadWriteOnce)이 이를 통해 생성된다
kubectl get sc
클러스터 DNS 주소
client-gateway의 NGINX_RESOLVER(기본 10.96.0.10)가 실제 값과 일치해야 한다
image-pull-secret.yml
레지스트리가 인증을 요구하는 경우
image-pull-policy.yml
폐쇄망 — 레지스트리 장애 시에도 노드 캐시로 파드가 기동되도록
https-cert.yml
client-gateway
제한 없음(브라우저)
443
collector
제한 없음(TPM Agent)
80
false
LOG_FILE_SIZE / LOG_TOTAL_SIZE
0
관제 데이터베이스의 TPM Agent가 수집 정보를 push하는 포트
kubectl get svc kube-dns -n kube-system
사이트 HTTPS 인증서로 교체
8292
docker tag sysmaster-db-sdm:{version} {registry}/sysmaster-db-sdm:{version}
docker push {registry}/sysmaster-db-sdm:{version}
# 나머지 이미지들도 동일하게 수행한다.ADMIN_PASSWORD=... METADB_PASSWORD=... REPODB_PASSWORD=... \
./setup-once.sh /path/to/sysmaster-db-license.xmlapiVersion: v1
kind: ConfigMap
metadata:
name: smdb-env
namespace: sysmasterdb
data:
TZ: "Asia/Seoul"
USE_SENTRY: "false"
DONT_CHECK_HTTP_HEALTH: "false"
HEALTH_LOG_RETENTION_DAY: "30"
KAFKA_MESSAGE_MAX_BYTES: "20971520"
METADB_USER: "sysmaster"
REPODB_USER: "sysmaster"# kubernetes/base/sysmaster/sdm.yml
env:
...
- { name: metric_retention_day, value: "14" } # 기본값 "7"kubectl -n sysmasterdb exec deploy/sdm -- printenv metric_retention_day# kubernetes/base/sysmaster/sdm.yml
env:
...
- { name: "account_inactive-lock-days", value: "0" }
- { name: "account_refresh-token-expire-days", value: "0" }
- { name: "account_password-history-count", value: "1" }
- { name: "account_password-change-days", value: "0" }
- { name: "security_session_max-concurrent-sessions", value: "0" }
- { name: "security_ip-restriction_target", value: "NONE" }
- { name: "security_ip-restriction_super-admin-ip", value: "" }
- { name: "security_ip-restriction_trusted-proxies", value: "127.0.0.0/8,::1,172.16.0.0/12,10.88.0.0/15" }
- { name: "access-log_retention_day", value: "365" }pg_superuser: "postgres"
pg_superuser_password: "postgres"
pg_data: "/pgdata"
pg_log: "/sysmaster/logs" # 로그 볼륨 마운트 경로와 일치해야 한다
pg_users:
- name: "sysmaster"
pass: "sysmaster" # setup-once.sh 의 METADB_PASSWORD 와 같은 값이어야 한다
role_attr_flags: LOGIN
pg_databases:
- name: metadb
owner: sysmaster
pg_extensions:
- name: "timescaledb"
database: "metadb"
state: present
- name: "vector"
database: "metadb"
state: present
pg_max_connections: 100
pg_owner_id: 1000
pg_group_id: 1000
pg_multi_params:
- name: "shared_preload_libraries"
value: "pg_stat_statements,timescaledb"
state: present
pg_postgres_conf_params:
- name: "shared_buffers"
value: "256MB"
- name: "effective_cache_size"
value: "768MB"
- name: "maintenance_work_mem"
value: "64MB"
- name: "work_mem"
value: "3276kB"
- name: "log_filename"
value: "metadb-%H%M.log"
- name: "log_timezone"
value: "Asia/Seoul"
- name: "log_min_duration_statement"
value: "2000"
- name: "log_min_messages"
value: "INFO"
- name: "log_parameter_max_length"
value: "0"
- name: "log_rotation_age"
value: "60"
- name: "log_rotation_size"
value: "100MB"
- name: "log_truncate_on_rotation"
value: "on"kubectl -n sysmasterdb get svc client-gateway collector-ingestkubectl -n sysmasterdb create secret docker-registry smdb-registry \
--docker-server={registry} --docker-username={계정} --docker-password={암호}patches:
- path: gateway-lb.yml # [기존] 삭제하지 않는다
target: { kind: Service, name: client-gateway }
- path: image-pull-secret.yml # <- 추가
target: { kind: Deployment }patches:
- path: gateway-lb.yml # [기존] 삭제하지 않는다
target: { kind: Service, name: client-gateway }
- path: image-pull-policy.yml # <- 추가
target: { kind: Deployment }kubectl -n sysmasterdb create secret generic smdb-https-cert \
--from-file=sysmaster-db-server.crt={인증서 파일} \
--from-file=sysmaster-db-server.key={개인키 파일}patches:
- path: gateway-lb.yml # [기존] 삭제하지 않는다
target: { kind: Service, name: client-gateway }
- path: https-cert.yml # <- 추가
target: { kind: Deployment, name: client-gateway }apiVersion: apps/v1
kind: Deployment
metadata:
name: client-gateway
namespace: sysmasterdb
spec:
template:
spec:
volumes:
- name: ssl
# base의 emptyDir를 지우지 않으면 볼륨 소스가 2개가 되어 API 서버가 거부한다.
emptyDir: null
secret:
secretName: smdb-https-certresources:
- base # [기존]
- collector-lb.yml # [기존] 삭제하면 수집 진입점이 사라진다
- logs-pvc.yml # <- 추가
patches:
- path: gateway-lb.yml # [기존] 삭제하지 않는다
target: { kind: Service, name: client-gateway }
- path: logs-pvc-patch.yml # <- 추가
target: { kind: Deployment }kubectl get sc # default 표시와 ReadWriteMany 제공 StorageClass 확인# kubernetes/logs-pvc.yml
spec:
storageClassName: {RWX-StorageClass} # <- 추가
accessModes: ["ReadWriteMany"]kubectl -n sysmasterdb exec -it deploy/smdb-logs-viewer -- sh
kubectl -n sysmasterdb exec deploy/smdb-logs-viewer -- sh -c 'tail -f /sysmaster/logs/sdm/sdm.v.*.log'resources:
- base # [기존]
- collector-lb.yml # [기존]
- network-policy.yml # <- 추가SDM_IP=$(kubectl -n sysmasterdb get pod -l app=sdm -o jsonpath='{.items[0].status.podIP}')
kubectl -n sysmasterdb exec deploy/chatbot -- python -c 'import socket,sys
ip=sys.argv[1]
for p in (8090, 8080):
try:
socket.create_connection((ip, p), 5).close(); print(p, "OPEN")
except Exception as e:
print(p, "BLOCKED", type(e).__name__)' "$SDM_IP"rewrite name sdm sdm.sysmasterdb.svc.cluster.localSysMasterDB 8.4는 Kubernetes 환경에 kustomize 기반 매니페스트로 배포된다. 배포 파일의 kubernetes 디렉터리에 전체 스택 매니페스트와 클러스터 부트스트랩 스크립트가 포함되어 있으며, kubectl apply -k 명령 한 번으로 11개 모듈이 배포된다.
이미지 로드 및 push — 배포 파일의 이미지를 사이트 레지스트리에 등록한다.
파라미터 설정 — 레지스트리 주소, 계정 암호, 데이터베이스 초기화 설정을 지정한다.
클러스터 사전 준비 — setup-once.sh로 라이선스 Secret, 크리덴셜 Secret, DNS 설정을 등록한다.
배포 — kubectl apply -k kubernetes
접속 — LoadBalancer에 할당된 주소로 접속한다.
배포 파일의 kubernetes 디렉터리 구성은 다음과 같다.
각 모듈 파일은 디플로이먼트(Deployment) 1개 + 서비스(Service) 1개로 구성된다.
배포 파일은 제공된 파일 구성 그대로 사용한다. 새 파일을 만들거나 제공된 파일을 삭제·이름 변경하지 않고, 제공된 파일 안에서 파라미터 값만 수정한다. 사이트별로 달라지는 값과 그 수정 위치는 아래 표와 같다.
base 디렉터리의 파일도 아래 표에 지정된 값은 직접 수정하되, 리소스(디플로이먼트·서비스·볼륨 등)를 추가하거나 삭제하지 않는다. 별도의 patch 파일은 만들지 않으며, 선택 구성 파일(image-pull-secret.yml 등)은 이미 제공된 파일이므로 kustomization.yml에 등록만 한다. 다음 버전으로 업그레이드할 때는 새 배포 파일에 변경했던 값을 다시 반영한다.
배포는 이 파일 하나에서 시작한다. 배포 파일에 포함된 상태는 다음과 같다.
영구 볼륨 3종
전체 모듈이 동일한 구조를 가진다. 다음은 base/sysmaster/sdm.yml의 예시이다.
전 모듈에 3종의 헬스 체크가 설정되어 있어, kubectl get pods에서 1/1 Running으로 표시되면 실제로 요청을 처리할 수 있는 상태를 의미한다.
기동 대기 한도는 periodSeconds × failureThreshold이며 모듈마다 다르다.
기동이 지속적으로 실패하는 환경에서는 failureThreshold 값을 늘린다. periodSeconds는 장애 감지 속도에 영향을 주므로 변경하지 않는 것을 권장한다.
requests 합계는 파드가 스케줄링되기 위해 노드에 남아 있어야 하는 최소 여유이다. 부족하면 파드가 Pending 상태에 머문다.
limits는 상한이며, 메모리가 이를 초과하면 컨테이너가 강제 종료(OOMKilled)된다.
CPU에는 상한을 두지 않았다. 수집이 몰리는 순간이나 기동 시 불필요하게 지연되는 것을 방지하기 위함이다.
전 모듈이 replicas: 1로 구성되어 있으며 이는 의도된 값이다.
같은 이유로 위 모듈에는 strategy: Recreate가 적용되어 있다. 이 설정을 제거하면 배포가 완료되지 않거나, zookeeper의 경우 오류 없이 Kafka 메타데이터가 초기화될 수 있다. 또한 자동 확장(HPA)은 사용할 수 없다.
파일 디스크립터
nofile 65536 이상 (노드 측 설정)
라이선스
SysMasterDB 8.4용 라이선스 파일 (version="1.1")
외부 노출
LoadBalancer 또는 NodePort / Ingress
전 모듈 공통 설정
base/foundation.yml의 smdb-env
DB 초기화 계정·암호
base/meta.conf, base/repo.conf
20Gi
수집된 메트릭 전체
관제 이력이 사라진다
sdm-reports
10Gi
생성된 TPR/ASH 리포트 파일
목록에는 남지만 리포트를 열 수 없다
livenessProbe
살아 있는가
컨테이너 재시작
collector, analyzer, dbmanager, chatbot, client-gateway
20분
HTTP 또는 포트 접속
zookeeper, broker, client-tibero
2분
포트 접속(client-tibero는 HTTP /)
1Gi / 200m
6Gi
4g
sdm
1Gi / 200m
3Gi
2g
collector
1Gi / 200m
3Gi
2g
metadb
512Mi / 100m
2Gi
–
broker
1Gi / 100m
2Gi
–
dbmanager
512Mi / 100m
1Gi
640m
chatbot
256Mi / 50m
1Gi
–
zookeeper
256Mi / 50m
512Mi
–
client-gateway
64Mi / 20m
256Mi
–
client-tibero
64Mi / 20m
256Mi
–
합계
약 7.6Gi / 1.2코어
35Gi
Meta DB / Repository DB의 공유 메모리(/dev/shm, 각 1Gi / 8Gi)는 메모리 limits에 포함된다. 공유 메모리를 늘리는 경우 limits도 함께 상향해야 한다.
analyzer, collector, dbmanager, chatbot
단일 인스턴스를 전제로 sdm에 등록·동작한다
Kubernetes
이미지 pull이 가능한 컨테이너 레지스트리
StorageClass
default StorageClass (ReadWriteOnce 영구 볼륨 3종, 총 40Gi)
노드 자원
kubernetes/
├── kustomization.yml 배포 진입점. 레지스트리 주소와 외부 노출 방식을 지정한다
├── setup-once.sh 클러스터 1회 부트스트랩 (라이선스·크리덴셜 Secret, CoreDNS)
├── base/ 전체 스택 매니페스트. 파일은 추가·삭제하지 않고 값만 수정한다
│ ├── kustomization.yml base 파일 목록
│ ├── foundation.yml Namespace + 공통 환경변수(ConfigMap) + PVC 3개
│ ├── meta.conf Meta DB 초기화 설정
│ ├── repo.conf Repository DB 초기화 설정
│ ├── db/metadb.yml, db/repodb.yml
│ ├── kafka/zookeeper.yml, kafka/broker.yml
│ ├── sysmaster/sdm.yml, collector.yml, analyzer.yml, dbmanager.yml, chatbot.yml
│ └── client/client-gateway.yml, client/client-tibero.yml
├── gateway-lb.yml 사용자 UI/API를 LoadBalancer로 노출 (기본 적용)
├── collector-lb.yml 수집 포트 8292를 LoadBalancer로 노출 (기본 적용)
├── image-pull-secret.yml [선택] 사설 레지스트리 인증
├── image-pull-policy.yml [선택] 폐쇄망 환경의 이미지 pull 정책
├── https-cert.yml [선택] 사이트 HTTPS 인증서 반입
├── logs-pvc.yml [선택] 로그 영속화
├── logs-pvc-patch.yml [선택] 위와 한 쌍
└── network-policy.yml [선택] 파드 간 통신 제한이미지 레지스트리 주소
kustomization.yml의 images[].newName
외부 노출 방식(LoadBalancer 등)
gateway-lb.yml, collector-lb.yml의 type
메모리·CPU 상한, 개별 환경변수
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- base # 전체 스택 매니페스트
- collector-lb.yml # 수집 포트(8292) 외부 노출
images: # 각 이미지의 레지스트리 주소를 사이트 값으로 교체한다
- { name: sysmaster-db-sdm, newName: {registry}/sysmaster-db-sdm }
- { name: sysmaster-db-dbmanager, newName: {registry}/sysmaster-db-dbmanager }
- { name: sysmaster-db-collector, newName: {registry}/sysmaster-db-collector }
- { name: sysmaster-db-analyzer, newName: {registry}/sysmaster-db-analyzer }
- { name: sysmaster-db-tiberoopensql-postgres, newName: {registry}/sysmaster-db-tiberoopensql-postgres }
- { name: sysmaster-db-zookeeper-loggable, newName: {registry}/sysmaster-db-zookeeper-loggable }
- { name: sysmaster-db-kafka-loggable, newName: {registry}/sysmaster-db-kafka-loggable }
- { name: sysmaster-db-client, newName: {registry}/sysmaster-db-client }
- { name: sysmaster-db-client-tibero, newName: {registry}/sysmaster-db-client-tibero }
- { name: sysmaster-db-chatbot, newName: {registry}/sysmaster-db-chatbot }
- { name: sysmaster-db-alpine-linux, newName: {registry}/sysmaster-db-alpine-linux }
patches:
- path: gateway-lb.yml # 사용자 UI/API 외부 노출
target: { kind: Service, name: client-gateway }apiVersion: v1
kind: Namespace
metadata:
name: sysmasterdb
---
# 전 모듈이 공통으로 주입받는 환경변수.
# 값을 변경한 뒤에는 파드를 교체해야 반영된다.
# kubectl -n sysmasterdb rollout restart deploy
# 계정 암호는 여기 두지 않는다 - setup-once.sh가 생성하는 smdb-credentials Secret이 담당한다.
apiVersion: v1
kind: ConfigMap
metadata:
name: smdb-env
namespace: sysmasterdb
data:
TZ: "Asia/Seoul"
USE_SENTRY: "false"
DONT_CHECK_HTTP_HEALTH: "false"
HEALTH_LOG_RETENTION_DAY: "30"
...
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: metadb-data
namespace: sysmasterdb
spec:
# storageClassName 미지정 -> 클러스터 default StorageClass로 프로비저닝된다.
# default가 없거나 특정 StorageClass를 써야 하면 이 위치에 storageClassName: {StorageClass} 를 추가한다.
# 미지정 상태에서 default도 없으면 PVC가 Pending에 머물고 파드가 기동하지 못한다.
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: repodb-data
namespace: sysmasterdb
spec:
# 수집 메트릭 저장소. 관제 대상 수와 보관 주기에 비례해 커진다.
# 배포 후 확장은 StorageClass가 볼륨 확장을 지원할 때만 가능하므로 최초에 넉넉히 잡는다.
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 20Gi
---
# sdm이 생성하는 TPR/ASH 리포트 파일 저장소(/sysmaster/reports).
# 리포트 목록은 Meta DB에 남으므로, 파일이 사라지면 목록에는 보이는데 열리지 않는 상태가 된다.
# 반드시 영구 볼륨이어야 한다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: sdm-reports
namespace: sysmasterdb
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gimetadb-data
10Gi
설정, 계정, 관제 대상 목록
초기 설치 상태로 돌아간다
apiVersion: apps/v1
kind: Deployment
metadata: { name: sdm, namespace: sysmasterdb }
spec:
replicas: 1
# 새 파드를 먼저 띄우지 않고 기존 파드를 내린 뒤 교체한다.
# 기본값(RollingUpdate)이면 배포 중 두 파드가 잠시 공존하는데,
# reports PVC가 ReadWriteOnce라 두 번째 파드가 볼륨을 붙지 못해 배포가 끝나지 않는다.
strategy: { type: Recreate }
selector: { matchLabels: { app: sdm } }
template:
metadata: { labels: { app: sdm } }
spec:
containers:
- name: sdm
image: sysmaster-db-sdm:{version}
imagePullPolicy: Always
envFrom: [{ configMapRef: { name: smdb-env } }] # 공통 설정 일괄 주입
env:
# JVM 힙. 아래 메모리 limits의 약 2/3이며, limits를 조정하면 함께 변경한다.
- { name: java_heap_size_max, value: "2g" }
- { name: java_heap_size_min, value: "512m" }
- { name: spring_datasource_hikari_jdbc-url, value: "jdbc:postgresql://metadb:5432/metadb" }
- { name: spring_datasource_hikari_username, value: "sysmaster" }
# 암호는 매니페스트에 두지 않고 Secret에서 참조한다.
- { name: spring_datasource_hikari_password,
valueFrom: { secretKeyRef: { name: smdb-credentials, key: METADB_PASSWORD } } }
- { name: metric_retention_day, value: "7" } # 수집 메트릭 보관 주기(일)
- { name: logging_file_max-history, value: "30" } # 로그 보관 일수
- { name: logging_file_max-size, value: "20MB" }
- { name: logging_file_total-size-cap, value: "1GB" }
# 업그레이드 시 수행되는 데이터 마이그레이션 패치 제어.
# 문제가 발생하면 해당 패치만 즉시 비활성화할 수 있다.
# kubectl -n sysmasterdb set env deploy/sdm patch_v8-2-1-to-v8-3-0-migration_skip=true
- { name: "patch_v8-2-1-to-v8-3-0-migration_skip", value: "false" }
ports:
- { containerPort: 8090 } # 내부 통신용
- { containerPort: 8080 } # 사용자 API용
# 기동이 완료될 때까지 readiness/liveness를 유예한다(기동 중 재시작 방지).
# 8080이 아니라 8090을 확인하는 이유: 8080에는 라이선스 상태 필터가 있어
# 라이선스 만료·코어 초과 시 401을 반환하며, 이 경우 정상 동작 중인 파드가
# 재시작되고 재기동도 실패해 복구가 불가능해진다.
startupProbe:
httpGet:
path: /actuator/health
port: 8090
httpHeaders: [{ name: Authorization, value: smdb-internal }]
periodSeconds: 5
failureThreshold: 180 # 대기 한도 15분 (5초 x 180)
readinessProbe: # 실패 시 트래픽만 차단, 재시작하지 않는다
httpGet:
path: /actuator/health
port: 8090
httpHeaders: [{ name: Authorization, value: smdb-internal }]
periodSeconds: 10
failureThreshold: 3
livenessProbe: # 실패 시 컨테이너를 재시작한다
httpGet:
path: /actuator/health
port: 8090
httpHeaders: [{ name: Authorization, value: smdb-internal }]
periodSeconds: 20
failureThreshold: 6
resources:
requests: { memory: 1Gi, cpu: 200m } # 스케줄링에 필요한 예약량
limits: { memory: 3Gi } # 초과 시 컨테이너가 강제 종료(OOMKilled)된다
volumeMounts:
- { name: license, mountPath: /sysmaster/license }
- { name: reports, mountPath: /sysmaster/reports }
# subPath로 모듈별 하위 디렉터리에 기록한다. 로그 영속화(선택 구성) 시
# 모듈 간 로그 파일명이 겹치는 것을 방지한다.
- { name: logs, mountPath: /sysmaster/logs, subPath: sdm }
volumes:
# setup-once.sh가 생성한 라이선스 Secret. 없으면 sdm이 기동하지 못한다.
- { name: license, secret: { secretName: sdm-license } }
- { name: reports, persistentVolumeClaim: { claimName: sdm-reports } }
# 로그는 파드 내부 임시 볼륨. 컨테이너가 재시작해도 남지만 파드가 교체되면 사라진다.
- { name: logs, emptyDir: {} }
---
apiVersion: v1
kind: Service
metadata: { name: sdm, namespace: sysmasterdb }
spec:
selector: { app: sdm }
ports:
- { name: master, port: 8090, targetPort: 8090 }
- { name: http, port: 8080, targetPort: 8080 }startupProbe
기동이 완료되었는가
한도 초과 시 컨테이너 재시작
readinessProbe
지금 요청을 받을 수 있는가
metadb, repodb
10분
헬스 응답 본문 확인(최초 기동은 DB 생성으로 수 분)
sdm
15분
repodb
2Gi / 200m
16Gi
–
sdm, metadb, repodb
영구 볼륨이 ReadWriteOnce — 두 번째 파드가 볼륨을 붙지 못한다
broker
브로커 ID가 고정되어 있어 두 파드가 동일 ID로 등록할 수 없다
zookeeper

메모리 16GB 이상, CPU 4코어 이상 권장
각 모듈 매니페스트(base/sysmaster/sdm.yml 등)의 resources, env
repodb-data
서비스에서 트래픽만 차단 (재시작 없음)
HTTP /actuator/health (8090)
analyzer
단일 노드 구성 — 두 파드가 동시에 기동하면 broker의 접속 대상이 불확정해진다
ImagePullBackOffkubectl kustomize kubernetes | grep -c 'type: LoadBalancer' # 2
kubectl kustomize kubernetes | grep -c 'name: collector-ingest' # 1