Blog
trinokuberneteshelmautoscalingdevopsdata-platform

Trino 를 Kubernetes 에 배포하기 — Helm, 오토스케일링, Graceful Shutdown

공식 Helm 차트로 Trino 클러스터를 Kubernetes 에 올리는 실전 가이드. 코디네이터/워커 구성, 카탈로그 주입, 리소스·HPA 오토스케일링, graceful shutdown, FTE 를 위한 exchange manager, 스팟 인스턴스 운영까지 정리합니다.

Data Dynamics2026년 6월 5일13 min read

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-secrets
kubectl 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: 70

CPU 사용률 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 pauseJVM 힙 압박

코디네이터의 /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 엔지니어링 팀