Trino 를 Kubernetes 에 배포하기 — Helm, 오토스케일링, Graceful Shutdown
공식 Helm 차트로 Trino 클러스터를 Kubernetes 에 올리는 실전 가이드. 코디네이터/워커 구성, 카탈로그 주입, 리소스·HPA 오토스케일링, graceful shutdown, FTE 를 위한 exchange manager, 스팟 인스턴스 운영까지 정리합니다.
Trino 는 코디네이터 1대와 워커 N대로 구성되는 stateless 에 가까운 분산 엔진입니다. 이 특성은 Kubernetes 와 잘 맞습니다 — 워커를 Pod 로 늘리고 줄이기 쉽고, 부하에 따라 오토스케일링할 수 있으며, 스팟 인스턴스로 비용을 낮출 수 있습니다. 다만 "쿼리 도중 워커가 사라지면?" 같은 분산 시스템 특유의 문제를 다뤄야 합니다.
이 글은 공식 Helm 차트를 기준으로 Trino 를 Kubernetes 에 안정적으로 배포하는 방법을, 오토스케일링과 graceful shutdown 같은 운영 관점까지 포함해 정리합니다.
1. 아키텍처 — Kubernetes 위의 Trino
Ingress / LoadBalancer (TLS 종단)
│
▼
┌───────────────────┐
│ Coordinator Pod │ (Deployment, replica 1)
│ Service :8080 │
└───────────────────┘
│ discovery + 내부 통신
┌───────────────────┼───────────────────┐
▼ ▼ ▼
Worker Pod Worker Pod Worker Pod (Deployment, replica N)
└──── HPA 가 부하에 따라 replica 조절 ────┘
카탈로그/설정: ConfigMap · Secret 으로 주입
FTE 스풀: S3/GCS (exchange manager)핵심 매핑:
| Trino 개념 | Kubernetes 리소스 |
|---|---|
| 코디네이터 | Deployment (replica 1) + Service |
| 워커 | Deployment (replica N) |
| 노드 디스커버리 | 코디네이터 Service DNS |
| 카탈로그/설정 | ConfigMap |
| 비밀번호·시크릿 | Secret |
| 오토스케일링 | HPA (또는 KEDA) |
| FTE 스풀 저장소 | 외부 오브젝트 스토리지 |
2. 공식 Helm 차트로 시작
Trino 는 공식 Helm 차트(trinodb/charts)를 제공합니다.
helm repo add trino https://trinodb.github.io/charts
helm repo update
# 기본 설치
helm install my-trino trino/trino --namespace trino --create-namespace기본값으로도 코디네이터 1 + 워커 2가 뜨지만, 프로덕션에서는 values.yaml 을 직접 작성합니다.
3. values.yaml — 핵심 구성
image:
tag: "version-pin" # 항상 버전을 고정. latest 금지
server:
workers: 4
config:
query:
maxMemoryPerNode: "12GB"
# 공유 시크릿(내부 통신) — 실제로는 Secret 으로 주입
coordinatorExtraConfig: |
query.max-memory=80GB
exchangeManager:
name: filesystem # FTE 용. 아래 7장 참고
coordinator:
jvm:
maxHeapSize: "16G"
resources:
requests:
cpu: 4
memory: 18Gi
limits:
memory: 18Gi
worker:
jvm:
maxHeapSize: "24G"
resources:
requests:
cpu: 8
memory: 28Gi
limits:
memory: 28Gi
# 워커가 쿼리 중 죽지 않도록 graceful shutdown (아래 6장)
terminationGracePeriodSeconds: 120
additionalCatalogs:
iceberg: |
connector.name=iceberg
iceberg.catalog.type=rest
iceberg.rest-catalog.uri=http://iceberg-rest:8181
fs.native-s3.enabled=true
s3.endpoint=https://s3.example.com
postgresql: |
connector.name=postgresql
connection-url=jdbc:postgresql://pg:5432/crm
connection-user=trino
connection-password=${ENV:PG_PASSWORD}JVM 힙과 컨테이너 메모리의 관계
가장 흔한 사고는 JVM 힙을 컨테이너 메모리 limit 과 같게 잡는 것입니다. Trino 는 힙 외에도 네이티브 메모리·메타스페이스·OS 버퍼를 씁니다. 컨테이너가 OOMKill 되지 않도록 여유를 둬야 합니다.
컨테이너 memory limit ≈ JVM maxHeapSize + 헤드룸(보통 20~30%)
예) limit 28Gi → maxHeapSize 24G (약 4Gi 헤드룸)추가로 query.max-memory-per-node 는 JVM 힙보다 작아야 하며, memory.heap-headroom-per-node 로 힙 내 예약 영역을 남깁니다.
4. 카탈로그와 시크릿 주입
카탈로그는 ConfigMap(위 additionalCatalogs)으로, 비밀번호는 Secret 으로 분리합니다.
# 워커/코디네이터에 환경변수로 Secret 주입 (values.yaml)
worker:
envFrom:
- secretRef:
name: trino-secrets
coordinator:
envFrom:
- secretRef:
name: trino-secretskubectl create secret generic trino-secrets -n trino \
--from-literal=PG_PASSWORD='***' \
--from-literal=LDAP_BIND_PASSWORD='***'카탈로그 properties 안에서 ${ENV:PG_PASSWORD} 로 참조하면, Git 에 올라가는 ConfigMap 에는 비밀이 남지 않습니다.
5. 오토스케일링 — 워커를 부하에 맞춰
워커는 stateless 에 가깝기 때문에 수평 확장이 자연스럽습니다.
5.1 HPA (CPU 기반)
# values.yaml
worker:
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 20
targetCPUUtilizationPercentage: 70CPU 사용률 70% 를 넘으면 워커 Pod 를 늘립니다. 간단하지만 한계가 있습니다 — Trino 쿼리는 버스트성이 강해서 CPU 가 튀었다 가라앉는 사이 스케일이 따라오지 못할 수 있습니다.
5.2 KEDA (쿼리/스케줄 기반)
더 똑똑한 스케일링은 KEDA 로 합니다. 대기 중인 쿼리 수나 큐 길이를 메트릭으로 삼거나, 업무 시간에만 워커를 늘리는 스케줄 스케일링을 구성할 수 있습니다.
# KEDA ScaledObject 예시 (개념)
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus:9090
query: trino_queued_queries # 대기 쿼리 수
threshold: "5"
- type: cron # 업무시간 최소 보장
metadata:
start: "0 8 * * 1-5"
end: "0 20 * * 1-5"
desiredReplicas: "8"스케일 아웃은 쉽지만, 스케일 인(축소)이 위험합니다. 줄어드는 워커가 쿼리를 실행 중이면 그 쿼리가 실패하기 때문입니다. 그래서 graceful shutdown 이 반드시 필요합니다.
6. Graceful Shutdown — 쿼리를 죽이지 않고 워커 내리기
Trino 워커는 graceful shutdown 을 지원합니다. 종료 신호를 받으면 새 태스크 수신을 멈추고(SHUTTING_DOWN 상태), 진행 중인 작업이 끝날 때까지 기다린 뒤 종료합니다.
Helm 차트의 워커 Pod 는 preStop 훅과 terminationGracePeriodSeconds 로 이를 구현합니다. 핵심은 유예 시간을 충분히 주는 것입니다.
worker:
# 진행 중 쿼리가 마무리될 시간. 워크로드의 최대 쿼리 시간보다 길게
terminationGracePeriodSeconds: 300
gracefulShutdown:
enabled: true
gracePeriodSeconds: 120동작 흐름:
1. K8s 가 Pod 에 SIGTERM + preStop 훅 실행
2. 워커가 SHUTTING_DOWN 으로 전환 → 코디네이터가 새 태스크를 안 보냄
3. 진행 중 태스크 완료 대기
4. terminationGracePeriodSeconds 안에 종료유예 시간이 쿼리 길이보다 짧으면 K8s 가 SIGKILL 로 강제 종료해 쿼리가 깨집니다. 장시간 ETL 이 있다면 유예 시간을 넉넉히 잡거나, 7장의 FTE 를 함께 씁니다.
7. Fault-tolerant Execution — 스팟 인스턴스를 안심하고 쓰려면
Graceful shutdown 은 "예고된 종료"를 다루지만, 스팟 인스턴스 회수처럼 갑작스러운 워커 소실은 막지 못합니다. 이때 필요한 것이 Fault-tolerant Execution(FTE) 입니다. 중간 결과를 외부 스토리지(exchange manager)에 스풀해 두고, 워커가 죽으면 해당 태스크만 다른 워커에서 재시도합니다.
# values.yaml
server:
config:
retryPolicy: "TASK" # 또는 QUERY
exchangeManager:
name: filesystem
baseDir: "s3://trino-exchange/spool"# exchange-manager.properties 로 생성됨
exchange-manager.name=filesystem
exchange.base-directories=s3://trino-exchange/spool| retryPolicy | 동작 | 적합 |
|---|---|---|
QUERY | 쿼리 전체를 재시도 | 짧은 대화형 쿼리 |
TASK | 실패한 태스크만 재시도 | 장시간 ETL 배치 |
FTE 를 켜면 워커를 100% 스팟으로 운영하면서도 배치 안정성을 확보할 수 있습니다. 비용 절감 효과가 큽니다. 다만 스풀 I/O 오버헤드가 있으므로, 초저지연 대화형 쿼리 전용 클러스터에는 끄는 편이 낫습니다.
8. 스케줄링 — 노드 배치와 안정성
# 코디네이터는 안정적인 온디맨드 노드에, 워커는 스팟에
coordinator:
nodeSelector:
node-pool: on-demand
worker:
nodeSelector:
node-pool: spot
tolerations:
- key: "spot"
operator: "Equal"
value: "true"
effect: "NoSchedule"
# 워커를 여러 노드에 분산해 동시 소실 위험 분산
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway원칙:
- 코디네이터는 스팟에 두지 않습니다. 코디네이터가 죽으면 클러스터 전체가 멈춥니다(SPOF). 온디맨드 노드 + 충분한 리소스를 보장하세요.
- 워커는 스팟 + FTE 조합이 비용 효율적입니다.
topologySpreadConstraints로 워커를 여러 노드/존에 분산해 한 번에 다수가 사라지는 상황을 완화합니다.
9. 관측성 — Prometheus 와 헬스체크
Trino 는 JMX 메트릭을 노출합니다. JMX exporter 사이드카 또는 내장 메트릭 엔드포인트로 Prometheus 에 수집합니다.
serviceMonitor: # Prometheus Operator 사용 시
enabled: true
labels:
release: prometheus주요 모니터링 지표:
| 지표 | 의미 |
|---|---|
trino_running_queries / trino_queued_queries | 실행/대기 쿼리 수 (스케일 트리거) |
| 클러스터 메모리 사용률 | OOM 위험 |
| 워커 노드 수 | 디스커버리 정상 여부 |
| 실패 쿼리율 | 안정성 |
| GC pause | JVM 힙 압박 |
코디네이터의 /v1/info 엔드포인트로 readiness 를 확인하고, starting 상태인 노드에는 트래픽을 보내지 않도록 readiness probe 를 구성합니다.
10. 배포 체크리스트
- 이미지 태그 고정(
latest금지) - 코디네이터는 온디맨드, 워커는 스팟 노드풀 분리
- JVM 힙 < 컨테이너 limit (헤드룸 20~30%)
- 카탈로그는 ConfigMap, 비밀은 Secret 으로 분리
- HPA/KEDA 오토스케일링 구성
- graceful shutdown 유예 시간 ≥ 최대 쿼리 시간
- 장시간 배치는 FTE(retryPolicy=TASK) + exchange manager
- 내부 통신 공유 시크릿 + TLS
- Prometheus 메트릭·readiness probe 구성
- PodDisruptionBudget 으로 동시 축소 제한
11. 정리
| 운영 과제 | Kubernetes 에서의 해법 |
|---|---|
| 부하 변동 | HPA(CPU) 또는 KEDA(쿼리/스케줄) |
| 예고된 워커 종료 | graceful shutdown + 충분한 유예 시간 |
| 갑작스러운 워커 소실(스팟) | FTE(retryPolicy=TASK) + exchange manager |
| 코디네이터 SPOF | 온디맨드 노드 + 리소스 보장 |
| 비밀 관리 | ConfigMap/Secret 분리, 환경변수 치환 |
| 동시 다수 소실 | topologySpread + PodDisruptionBudget |
Trino 의 stateless 한 구조는 Kubernetes 와 궁합이 좋지만, "분산 쿼리 도중 워커가 사라질 수 있다"는 현실을 graceful shutdown 과 FTE 두 장치로 다루는 것이 프로덕션 안정성의 핵심입니다. 여기에 스팟 인스턴스 + FTE 조합을 더하면, 안정성을 지키면서도 컴퓨트 비용을 크게 낮출 수 있습니다.
이 글은 공식 Trino Helm 차트와 Trino 440번대 기준으로 작성되었습니다. Kubernetes 위에서의 Trino 배포·오토스케일링·비용 최적화가 필요하시면 언제든 문의해 주세요.
— Data Dynamics 엔지니어링 팀