[ Infra ]/Data Platform

[Data Platform] Trino 구축 가이드 (feat. Hive Metastore + Delta Lake)

환이s 2026. 6. 5. 12:12
728x90
반응형


Intro

 

안녕하세요. 환이s입니다 👋

 

지난 글에서는 데이터 레이크하우스 플랫폼의

세 번째 스택으로 Strimzi Operator 기반의 Kafka 클러스터를

Kubernetes 위에 구축하는 과정을 정리해 봤습니다.

 

이번 글에서는 네 번째 스택,

Trino를 Kubernetes 환경에 구축하는 과정을 정리해보려고 합니다.

 

 

Kafka에서 수집되어 Spark Streaming으로 처리된 데이터는

Delta Lake 포맷으로 Ceph S3에 쌓이게 됩니다.

 

이 데이터를 SQL로 조회하려면 쿼리 엔진이 필요한데,

그 역할을 담당하는 것이 바로 Trino입니다.

 

Trino를 Kubernetes 위에서 운영하다 보면

단순히 쿼리 엔진 하나를 띄우는 것에서 끝나는 게 아니라,

  • Hive Metastore가 Delta Lake 테이블의 메타데이터를 관리하고
  • PostgreSQL이 Hive Metastore의 백엔드 DB 역할을 하고
  • Trino가 이 메타데이터를 참조해서 Ceph S3의 실제 데이터를 SQL로 조회하는

3계층 구조가 유기적으로 연결되어야 합니다.

 

이번 글에서는

  • Trino + Hive Metastore + PostgreSQL 3계층 아키텍처가 어떻게 동작하는지
  • 실제로 어떤 순서로 설치하면 되는지
  • Delta Lake 커넥터와 Hive 커넥터를 어떻게 구성하는지
  • Delta Lake 테이블을 등록하고 SQL로 조회하는 방법

까지 순서대로 확인해 보겠습니다.


1. Trino 아키텍처 개요

 

본격적인 설치에 앞서,

이번 구성의 전체 아키텍처를 먼저 이해하는 것이 중요합니다.

 

Ceph S3 (Delta Lake 데이터)
        ↑ 실제 데이터 읽기
      Trino 468
        ↑ 메타데이터 조회 (Thrift 프로토콜)
  Hive Metastore 3.1.3
        ↑ 메타데이터 저장
    PostgreSQL 13

 

각 컴포넌트의 역할은 아래와 같습니다.

컴포넌트 역할
Trino SQL 쿼리 실행 엔진 — Ceph S3의 실제 데이터를 읽고 결과 반환
Hive Metastore (HMS) 테이블 스키마, 위치, 파티션 정보 등 메타데이터 관리
PostgreSQL HMS의 메타데이터를 영구 저장하는 백엔드 DB

 

즉, 여기서 확인할 수 있는 핵심은

Trino는 데이터를 저장하지 않고, Hive Metastore를 통해 메타데이터를 참조한 뒤 Ceph S3에서 직접 데이터를 읽어 쿼리 결과를 반환한다는 점입니다.


2. 설치 순서

 

이번 구축에서 사용하는 매니페스트 디렉토리 구조는 아래와 같습니다.

 

manifests/
├── ms/       # PostgreSQL (Hive Metastore 백엔드 DB)
│   ├── 00_ms_pvc.yaml
│   ├── 10_ms_deployment.yaml
│   └── 20_ms_service.yaml
│
├── hms/      # Hive Metastore
│   ├── 10_hms_configmap.yaml
│   ├── 20_hms_deployment.yaml
│   └── 30_hms_service.yaml
│
└── trino/    # Trino
    ├── 10_trino_namespace.yaml
    ├── 20_trino_configmap.yaml
    ├── 30_trino_deployment.yaml
    ├── 40_trino_service.yaml
    └── 50_trino_secret.yaml

 

💡 참고: ms 디렉토리는 이름만 보면 MariaDB처럼 보이지만 실제로는 PostgreSQL입니다. Hive Metastore의 백엔드 DB로 PostgreSQL을 사용합니다.

 

설치 순서는 반드시 아래 순서를 지켜야 합니다.

trino(namespace) → ms(PostgreSQL) → ⏳ → hms(Hive Metastore) → ⏳ → trino(Trino)

 

반드시 지켜야 하는 대기 구간이 2곳 있습니다.

 

첫 번째

PostgreSQL이 완전히 Running 상태가 된 이후에 Hive Metastore를 apply 해야 합니다.

HMS가 기동 시 PostgreSQL에 스키마를 초기화하는 작업을 수행하기 때문입니다.

 

두 번째

Hive Metastore가 완전히 Running 상태가 된 이후에 Trino를 apply 해야 합니다.

Trino가 기동 시 HMS에 연결을 시도하기 때문입니다.


3. Namespace 설정

 

먼저 trino Namespace를 생성합니다.

# 10_trino_namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: trino
kubectl apply -f trino/10_trino_namespace.yaml

 

PostgreSQL, Hive Metastore, Trino 세 컴포넌트 모두

trino Namespace 하나에서 운영합니다.


4. PostgreSQL 설치 (Hive Metastore Back-End DB)

 

PostgreSQL

Hive Metastore테이블 메타데이터를 영구 저장하는 백엔드 DB입니다.


PVC 생성

# ms/00_ms_pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: metastore-db-pvc
  namespace: trino
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: rook-ceph-block
  resources:
    requests:
      storage: 10Gi

 

rook-ceph-block StorageClass를 사용해서 RWO 방식의 PVC를 생성합니다.

DB 데이터는 단일 노드에서만 읽고 쓰기 때문에 RWO 모드가 적합합니다.


Deployment

# ms/10_ms_deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: metastore-db
  namespace: trino
spec:
  replicas: 1
  selector:
    matchLabels:
      app: metastore-db
  template:
    metadata:
      labels:
        app: metastore-db
    spec:
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              preference:
                matchExpressions:
                  - key: eluna.role
                    operator: In
                    values:
                      - trino
      containers:
        - name: postgres
          image: harbor.local/library/metastore/postgres:13
          env:
            - name: POSTGRES_DB
              value: metastore
            - name: POSTGRES_USER
              value: hive
            - name: POSTGRES_PASSWORD
              value: YOUR_PASSWORD
          ports:
            - containerPort: 5432
          volumeMounts:
            - name: postgres-data-vol
              mountPath: /var/lib/postgresql/data
              subPath: pgdata
      volumes:
        - name: postgres-data-vol
          persistentVolumeClaim:
            claimName: metastore-db-pvc

⚠️ 운영 환경에서는 POSTGRES_PASSWORD를 Kubernetes Secret으로 분리해서 관리하는 것을 권장합니다.

 

nodeAffinity에서 eluna.role=trino 레이블이 있는 노드를 선호하도록 설정했습니다.


Service

# ms/20_ms_service.yaml
apiVersion: v1
kind: Service
metadata:
  name: metastore-db
  namespace: trino
spec:
  type: NodePort
  ports:
    - port: 5432
      targetPort: 5432
      nodePort: 30999
  selector:
    app: metastore-db
kubectl apply -f ms/

 

PostgreSQL Pod가 Running 상태인지 확인합니다.

kubectl get pods -n trino | grep metastore-db
metastore-db-xxxxxxxxxx-xxxxx   1/1   Running   0   ...

 

즉, 여기서 확인할 수 있는 핵심은

PostgreSQL이 완전히 Running 상태가 된 이후에만 Hive Metastore가 정상적으로 스키마를 초기화할 수 있다는 점입니다.


5. Hive Metastore 설치

 

Hive Metastore Delta Lake 테이블의 스키마, 위치, 파티션 정보PostgreSQL에 저장하고 Trino에 제공하는 메타데이터 서버입니다.


ConfigMap

# hms/10_hms_configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: hive-metastore-conf
  namespace: trino
data:
  hive-site.xml: |

        javax.jdo.option.ConnectionURL
        jdbc:postgresql://metastore-db:5432/metastore

        javax.jdo.option.ConnectionDriverName
        org.postgresql.Driver

        javax.jdo.option.ConnectionUserName
        hive

        javax.jdo.option.ConnectionPassword
        YOUR_PASSWORD
      
        fs.s3a.impl
        org.apache.hadoop.fs.s3a.S3AFileSystem

        fs.s3a.endpoint
        http://rook-ceph-rgw-ceph-store.rook-ceph.svc:80

        fs.s3a.access.key
        YOUR_ACCESS_KEY

        fs.s3a.secret.key
        YOUR_SECRET_KEY

        fs.s3a.path.style.access
        true

        hive.metastore.warehouse.dir
        /tmp/hive/warehouse

        hive.metastore.schema.verification
        false

Deployment

# hms/20_hms_deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hive-metastore
  namespace: trino
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hive-metastore
  template:
    metadata:
      labels:
        app: hive-metastore
    spec:
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              preference:
                matchExpressions:
                  - key: eluna.role
                    operator: In
                    values:
                      - trino
      containers:
        - name: hive-metastore
          image: harbor.local/library/hive:3.1.3
          securityContext:
            runAsUser: 0
          command: ["/bin/sh", "-c"]
          args:
            - |
              set -e

              # S3 라이브러리 복사
              cp /opt/hadoop/share/hadoop/tools/lib/hadoop-aws-3.1.0.jar /opt/hive/lib/
              cp /opt/hadoop/share/hadoop/tools/lib/aws-java-sdk-bundle-1.11.271.jar /opt/hive/lib/

              # 스키마 초기화 (최초 기동 시에만 실행)
              /opt/hive/bin/schematool -dbType postgres -info || \
              /opt/hive/bin/schematool -dbType postgres -initSchema

              # Hive Metastore 기동
              exec /opt/hive/bin/hive --service metastore
          ports:
            - containerPort: 9083
          volumeMounts:
            - name: hive-config
              mountPath: /opt/hive/conf/hive-site.xml
              subPath: hive-site.xml
      volumes:
        - name: hive-config
          configMap:
            name: hive-metastore-conf

 

HMS 기동 시 schematool 명령어로

PostgreSQL에 스키마가 있는지 확인하고, 없으면 자동으로 초기화합니다.

|| initSchema 패턴 덕분에 최초 기동과 재기동 모두 안전하게 처리됩니다.


Service

# hms/30_hms_service.yaml
apiVersion: v1
kind: Service
metadata:
  name: hive-metastore
  namespace: trino
spec:
  ports:
    - port: 9083
      targetPort: 9083
  selector:
    app: hive-metastore
kubectl apply -f hms/

 

HMS Pod가 Running 상태인지 확인합니다.

kubectl get pods -n trino | grep hive-metastore
hive-metastore-xxxxxxxxxx-xxxxx   1/1   Running   0   ...

 

HMS 로그에서 스키마 초기화가 완료됐는지도 확인합니다.

kubectl logs -n trino deploy/hive-metastore | grep -i "schema"
Initialization script completed
schemaTool completed

 

즉, 여기서 확인할 수 있는 핵심은

HMSRunning 상태이고 스키마 초기화가 완료된 이후에만 Trino를 apply 해야 한다는 점입니다.


6. Trino 설치

 

Secret (패스워드 인증)

Trino파일 기반 패스워드 인증을 사용합니다.

bcrypt해시된 패스워드를 Secret으로 관리합니다.

# trino/50_trino_secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: trino-password-file
  namespace: trino
type: Opaque
stringData:
  password.db: |
    admin:$2y$05$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

⚠️ 운영 환경에서는 반드시 패스워드를 변경해서 사용하세요.
bcrypt 해시는 아래 명령어로 생성할 수 있습니다.

htpasswd -nbBC 10 admin YOUR_PASSWORD

ConfigMap

ConfigMap에는 Trino의 전체 설정이 담겨 있습니다.

# trino/20_trino_configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: trino-config
  namespace: trino
data:
  # 코디네이터 설정
  config.properties: |
    coordinator=true
    node-scheduler.include-coordinator=true
    http-server.http.port=8080
    discovery.uri=http://localhost:8080

    # 패스워드 인증 설정
    http-server.authentication.type=PASSWORD
    http-server.authentication.allow-insecure-over-http=true
    internal-communication.shared-secret=YOUR_INTERNAL_SECRET

  # JVM 설정
  jvm.config: |
    -server
    -Xmx2G
    -XX:+UseG1GC
    -XX:G1HeapRegionSize=32M
    -XX:+UseGCOverheadLimit
    -XX:+ExplicitGCInvokesConcurrent
    -XX:+HeapDumpOnOutOfMemoryError
    -XX:+ExitOnOutOfMemoryError

  # 파일 기반 패스워드 인증
  password-authenticator.properties: |
    password-authenticator.name=file
    file.password-file=/etc/trino/auth/password.db

  # 노드 설정
  node.properties: |
    node.environment=production
    node.data-dir=/data/trino
    plugin.dir=/usr/lib/trino/plugin

  # tpch 커넥터 (테스트용)
  tpch.properties: |
    connector.name=tpch

  # Hive 커넥터
  hive.properties: |
    connector.name=hive
    hive.metastore.uri=thrift://hive-metastore:9083

    fs.native-s3.enabled=true
    s3.endpoint=http://rook-ceph-rgw-ceph-store.rook-ceph.svc:80
    s3.aws-access-key=YOUR_ACCESS_KEY
    s3.aws-secret-key=YOUR_SECRET_KEY
    s3.region=us-east-1
    s3.path-style-access=true

  # Delta Lake 커넥터
  delta.properties: |
    connector.name=delta_lake
    hive.metastore.uri=thrift://hive-metastore:9083

    delta.register-table-procedure.enabled=true

    fs.native-s3.enabled=true
    s3.endpoint=http://rook-ceph-rgw-ceph-store.rook-ceph.svc:80
    s3.aws-access-key=YOUR_ACCESS_KEY
    s3.aws-secret-key=YOUR_SECRET_KEY
    s3.region=us-east-1
    s3.path-style-access=true

 

주요 설정 포인트는 아래와 같습니다.


커넥터 2개 구성

hive.properties    → Hive 커넥터 (일반 Hive 테이블 조회)
delta.properties   → Delta Lake 커넥터 (Delta Lake 테이블 조회)

 

 

두 커넥터 모두

hive.metastore.uri=thrift://hive-metastore:9083으로 동일한 Hive Metastore를 참조합니다.


delta.register-table-procedure.enabled=true

delta.register-table-procedure.enabled=true

 

이 설정이 핵심입니다.

Spark StreamingCeph S3에 쌓은 Delta Lake 데이터를

Trino에서 CALL 프로시저로 테이블로 등록할 수 있게 해줍니다.


s3.path-style-access=true

Ceph RGW는 경로 방식 S3 URL을 사용하기 때문에 이 설정이 없으면 S3 연결이 실패합니다.


Deployment

# trino/30_trino_deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: trino-deployment
  namespace: trino
spec:
  replicas: 1
  selector:
    matchLabels:
      app: trino
  template:
    metadata:
      labels:
        app: trino
    spec:
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              preference:
                matchExpressions:
                  - key: eluna.role
                    operator: In
                    values:
                      - trino
            - weight: 40
              preference:
                matchExpressions:
                  - key: role
                    operator: NotIn
                    values:
                      - kafka
      containers:
        - name: trino-container
          image: harbor.local/library/trino:468
          resources:
            requests:
              memory: "4Gi"
              cpu: "1"
          securityContext:
            runAsUser: 0
          ports:
            - containerPort: 8080
          volumeMounts:
            - name: config-vol
              mountPath: /etc/trino/config.properties
              subPath: config.properties
            - name: config-vol
              mountPath: /etc/trino/jvm.config
              subPath: jvm.config
            - name: config-vol
              mountPath: /etc/trino/node.properties
              subPath: node.properties
            - name: config-vol
              mountPath: /etc/trino/catalog/tpch.properties
              subPath: tpch.properties
            - name: config-vol
              mountPath: /etc/trino/catalog/hive.properties
              subPath: hive.properties
            - name: config-vol
              mountPath: /etc/trino/catalog/delta.properties
              subPath: delta.properties
            - name: config-vol
              mountPath: /etc/trino/password-authenticator.properties
              subPath: password-authenticator.properties
            - name: trino-password-secret
              mountPath: /etc/trino/auth/password.db
              subPath: password.db
              readOnly: true
            - name: data-vol
              mountPath: /data/trino
      volumes:
        - name: config-vol
          configMap:
            name: trino-config
        - name: trino-password-secret
          secret:
            secretName: trino-password-file
        - name: data-vol
          hostPath:
            path: /home/cys/trino
            type: DirectoryOrCreate

 

ConfigMap각 파일을 subPath로 하나씩 정확한 경로에 마운트 합니다.

catalog 디렉토리 아래에 마운트 된. properties 파일이 Trino에서 커넥터로 자동 인식됩니다.

💡 참고: data-vol은 hostPath(/home/cys/trino)를 사용하고 있습니다.
운영 환경에서는 PVC로 교체하는 것을 권장합니다.

 


nodeAffinity 설정 포인트

# eluna.role=trino 노드 선호 (weight 100)
# role=kafka 노드 기피 (weight 40, NotIn)

 

 

Kafka 노드에 Trino가 올라가지 않도록 기피 설정을 함께 적용했습니다.


Service

# trino/40_trino_service.yaml
apiVersion: v1
kind: Service
metadata:
  name: trino-service
  namespace: trino
spec:
  type: NodePort
  selector:
    app: trino
  ports:
    - port: 8080
      targetPort: 8080
      nodePort: 30140
kubectl apply -f trino/

 

Trino Pod가 Running 상태인지 확인합니다.

kubectl get pods -n trino
NAME                                  READY   STATUS    RESTARTS   AGE
metastore-db-xxxxxxxxxx-xxxxx         1/1     Running   0          ...
hive-metastore-xxxxxxxxxx-xxxxx       1/1     Running   0          ...
trino-deployment-xxxxxxxxxx-xxxxx     1/1     Running   0          ...

 

Trino UI는 아래 주소로 접근할 수 있습니다.

http://<노드IP>:30140
ID: admin
PW: (50_trino_secret.yaml에서 설정한 패스워드)

 

즉, 여기서 확인할 수 있는 핵심은

ConfigMap의 각 설정 파일이 subPath정확한 경로에 마운트 되어야 Trino커넥터와 인증 설정을 정상적으로 읽어올 수 있다는 점입니다.


7. Delta Lake 테이블 등록 및 쿼리 테스트

 

TrinoRunning 상태가 되면

Spark StreamingCeph S3에 쌓은 Delta Lake 데이터를 테이블로 등록합니다.


Table Call

delta.register-table-procedure.enabled=true 설정 덕분에

아래 CALL 프로시저로 S3Delta Lake 데이터를 테이블로 등록할 수 있습니다.

-- Delta Lake 테이블 등록
CALL delta.system.register_table(
  schema_name => 'default',
  table_name  => 'syslog',
  table_location => 's3a://버킷명/syslog/'
);

Query Test

테이블이 정상적으로 등록되면 SQL로 조회할 수 있습니다.

-- 전체 스키마 확인
SHOW SCHEMAS FROM delta;

-- 테이블 목록 확인
SHOW TABLES FROM delta.default;

-- 데이터 조회 (전체)
SELECT * FROM delta.default.syslog LIMIT 10;

-- 파티션 기준 조회 (dt=날짜 파티셔닝 적용 시)
SELECT * FROM delta.default.syslog
WHERE dt = '2026-06-04'
LIMIT 100;

-- 커넥터 목록 확인
SHOW CATALOGS;

 

파티션 기준 조회 시 Trino가 해당 날짜 폴더만 읽기 때문에

전체 데이터를 스캔하지 않고 쿼리 성능이 크게 향상됩니다.

 

즉, 여기서 확인할 수 있는 핵심은

register_table 프로시저로 테이블을 등록하면

이후부터는 일반 SQL로 Delta Lake 데이터를 조회할 수 있다는 점입니다.


8. 전체 구성 검증

 

모든 설치가 완료된 후 최종 상태를 확인합니다.

# 전체 Pod 상태
kubectl get pods -n trino

# 서비스 확인
kubectl get svc -n trino

# PVC 확인
kubectl get pvc -n trino

 

정상적인 경우 아래처럼 보입니다.

NAME                                  READY   STATUS    RESTARTS   AGE
metastore-db-xxxxxxxxxx-xxxxx         1/1     Running   0          ...
hive-metastore-xxxxxxxxxx-xxxxx       1/1     Running   0          ...
trino-deployment-xxxxxxxxxx-xxxxx     1/1     Running   0          ...

 

커넥터 연결도 확인합니다.

# Trino CLI로 커넥터 확인
kubectl exec -n trino deploy/trino-deployment -- \
  trino --server http://localhost:8080 \
        --user admin \
        --execute "SHOW CATALOGS;"
Catalog
--------
delta
hive
tpch

 

즉, 여기서 확인할 수 있는 핵심은

delta / hive / tpch 세 가지 카탈로그가 모두 보이면

Trino + Hive Metastore + PostgreSQL 전체 연동이 완료된 것이라는 점입니다.


📝 마무리

 

이번 글에서는

Kubernetes 위에서 Trino + Hive Metastore + PostgreSQL 3계층 구조로

Delta Lake SQL 조회 환경을 구축하는 과정을 정리해 봤습니다.

 

정리하면,

  • PostgreSQL → Hive Metastore의 메타데이터 영구 저장
  • Hive Metastore → Delta Lake 테이블 스키마 및 위치 관리
  • Trino → HMS 참조 + Ceph S3 직접 접근으로 SQL 쿼리 실행

라고 볼 수 있습니다.

 

특히 이번 구축에서 주의해야 할 포인트는

  • PostgreSQL Running 확인 후 → Hive Metastore apply
  • Hive Metastore 스키마 초기화 완료 후 → Trino apply
  • delta.register-table-procedure.enabled=true 설정 필수
  • s3.path-style-access=true 설정 필수 (Ceph RGW 연동 시)
  • ConfigMap 각 파일을 subPath로 정확한 경로에 마운트

이 5가지였습니다.

 

다음 편에서는

이번에 구축한 Trino 위에서 Airflow로 데이터 파이프라인을 스케줄링하는 방법을 다뤄보겠습니다. 🙌

 

궁금한 점이나 추가로 다뤄줬으면 하는 내용이 있다면 언제든지 편하게 말씀해 주세요! 😊

728x90
반응형