
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
즉, 여기서 확인할 수 있는 핵심은
HMS가 Running 상태이고 스키마 초기화가 완료된 이후에만 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 Streaming이 Ceph 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 테이블 등록 및 쿼리 테스트
Trino가 Running 상태가 되면
Spark Streaming이 Ceph S3에 쌓은 Delta Lake 데이터를 테이블로 등록합니다.
Table Call
delta.register-table-procedure.enabled=true 설정 덕분에
아래 CALL 프로시저로 S3의 Delta 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로 데이터 파이프라인을 스케줄링하는 방법을 다뤄보겠습니다. 🙌
궁금한 점이나 추가로 다뤄줬으면 하는 내용이 있다면 언제든지 편하게 말씀해 주세요! 😊
'[ Infra ] > Data Platform' 카테고리의 다른 글
| [Data Platform] Prometheus + Grafana 구축 가이드 (feat. kube-prometheus-stack) (0) | 2026.06.09 |
|---|---|
| [Data Platform] Airflow 구축 가이드 (feat. Spark on Kubernetes) (0) | 2026.06.08 |
| [Data Platform] Apache Kafka 구축 가이드 (feat. KRaft 모드) (0) | 2026.06.04 |
| [Data Platform] Apache Spark 구축 가이드(Spark Operator + Delta Lake) (1) | 2026.06.03 |
| [Data Platform] Rook-Ceph 구축 가이드(Block / FileSystem / Object Store) (0) | 2026.06.02 |