Trino 설정
GenD의 Federation SQL 엔진인 Trino의 배포 구성과 접근 제어 설정입니다.
개요
Trino는 공식 Helm 차트(infra/helm/trino/values.yaml)로 배포되며, 접근 제어(rules.json)와 리소스 그룹(resource-groups.json)으로 역할별 권한과 쿼리 리소스를 관리합니다.
Helm 차트 주요 설정
설정 파일은 2단으로 적용된다 — infra/helm/trino/values.yaml(공용) 뒤에
infra/helm/trino/values-prod.yaml(prod/staging 전용)을 -f 로 덧붙인다
(infra/azure-deploy/scripts/02-deploy-k8s.sh). Kind 로컬은 base 만 쓴다.
| 설정 | 값 (2026-07-31 실측) | 설명 |
|---|---|---|
| chart / Trino 버전 | trino-1.42.2 / 480 | infra/helm/trino/CHART_VERSION 로 고정 |
server.workers | 1 | 워커 수. worker.replicas 는 차트 스키마에 없는 키로 조용히 무시된다 (#2683) |
server.autoscaling | enabled, max 3, CPU 80% | 워커 HPA. 렌더 이름은 trino-worker — 차트가 단일 소유자다 |
worker.gracefulShutdown | enabled, 120s | 종료 시 실행 중 태스크 드레인 (#2776) |
worker.terminationGracePeriodSeconds | 300 | 차트 요구: >= 2 × gracePeriodSeconds |
coordinator.jvm.maxHeapSize / worker.jvm.maxHeapSize | 3G / 3G | JVM 힙 |
server.config.query.maxMemory | 4GB | 쿼리당 최대 메모리 (camelCase 키만 인식 — #1287) |
catalog.management | dynamic | 동적 카탈로그 |
:::warning 워커 오토스케일 소유자는 차트 하나뿐
infra/hpa/trino-hpa.yaml 과 infra/keda/scaledobjects/trino-worker-scaledobject.yaml
은 배포되지 않은 참고 설계다. apply 하면 같은 Deployment 에 오토스케일 컨트롤러가
2개가 되어 replicas 를 서로 덮어쓴다 (#2683 의 소유권 충돌과 같은 계열). 두 파일에는
적용 금지 표시가 있고, scripts/test_cross_pr_consistency.py 가 그 표시를 고정한다.
:::
워커 graceful shutdown (#2776)
워커가 종료될 때 실행 중인 쿼리를 지키는 설정이다. 없으면 스케일다운 한 번에 쿼리가
실패한다 — 2026-07-30 prod 실측: 워커 pod 가 24시간에 24회 교체되는 동안
쿼리 38건이 PAGE_TRANSPORT_TIMEOUT / TOO_MANY_REQUESTS_FAILED 로 실패했고,
각 실패는 코디네이터가 죽은 워커를 재시도하는 305초를 소모했다.
동작 순서:
- K8s 가 워커 pod 에 SIGTERM 을 보내기 전에 preStop 훅이 워커의
/v1/info/state에"SHUTTING_DOWN"을 PUT 한다. - 워커는 새 태스크를 받지 않고 진행 중 태스크를 끝낸다 (
shutdown.grace-period=120s). terminationGracePeriodSeconds: 300안에 종료되면 SIGKILL 없이 정상 종료된다.
운영자가 체감하는 변화
- 워커 pod 종료가 즉시 끝나지 않고 최대 약 5분까지 걸린다.
kubectl delete pod, 노드 드레인,helm upgrade롤아웃이 그만큼 느려지는 것이 정상이다. - 스케일다운이 느려진다 — HPA
behavior.scaleDown안정화 창 600초 + 5분에 1대. 워커가 놀고 있어도 바로 줄지 않는다(의도된 동작). - 차트는 shutdown API 호출을 인가하려고 워커에만 파일 접근제어
(
system_information: write)를 붙인다. 코디네이터의 쿼리 인가 경로는 건드리지 않는다 (코디네이터 파일 ACL 활성화는 ADR-0030 Step 3 사안).
확인 방법:
kubectl -n gend get deploy trino-worker \
-o jsonpath='{.spec.template.spec.terminationGracePeriodSeconds}{"\n"}' # 300
kubectl -n gend exec deploy/trino-worker -- \
grep shutdown /etc/trino/config.properties # shutdown.grace-period=120s
kubectl -n gend get hpa trino-worker -o jsonpath='{.spec.behavior}{"\n"}' # scaleDown 600s
카탈로그 구성
values.yaml의 additionalCatalogs에서 정의합니다.
| 카탈로그 | 커넥터 | 용도 |
|---|---|---|
iceberg | iceberg (Hive Metastore) | 메인 데이터 레이크 |
nessie | iceberg (REST) | Git-like 브랜칭 카탈로그 |
tpch / tpcds | 벤치마크 | 성능 테스트 데이터 |
sourcedb | postgresql | CDC 소스 DB 연결 |
접근 제어 (rules.json)
역할별 카탈로그 접근 권한을 정의합니다 (infra/trino/rules.json).
| 역할 | 카탈로그 | 권한 |
|---|---|---|
| admin | .* (전체) | all |
| analyst | iceberg, nessie, tpch, tpcds | read-only |
| viewer | iceberg, tpch | read-only |
| etl | iceberg, nessie | all |
| etl | tpch, tpcds, sourcedb | read-only |
리소스 그룹 (resource-groups.json)
역할별 쿼리 리소스 제한입니다 (infra/trino/resource-groups.json).
| 그룹 | 메모리 | 동시 실행 | 대기열 |
|---|---|---|---|
| admin | 50% | 20 | 100 |
| analyst | 30% | 10 | 50 |
| viewer | 15% | 5 | 20 |
GenD API 연동 설정
GEND_TRINO_HOST=trino
GEND_TRINO_PORT=8080
GEND_TRINO_USER=gend-api
GEND_TRINO_RULES_JSON_PATH=/etc/trino/access-control/rules.json
GEND_TRINO_RESOURCE_GROUPS_JSON_PATH=/etc/trino/resource-groups/resource-groups.json
접근제어(ACL) 활성화·검증 (v1.1+)
파일 기반 ACL 을 켜거나 재배포할 때는 infra/trino/ 의 두 스크립트를 쓴다.
직접 helm upgrade 를 치지 말 것 — 아래 게이트를 건너뛰면 2026-08-06 과 같은
전 사용자 차단이 재현된다.
bash infra/trino/activate-acl.sh # 활성화 (게이트 5개 내장)
bash infra/trino/verify-acl.sh # 검증만 단독 실행
activate-acl.sh 가 강제하는 게이트
| # | 게이트 | 막는 사고 |
|---|---|---|
| 0 | 릴리스가 deployed 상태인가 + 롤백 리비전 기록 | 다른 배포가 진행 중일 때 잘못된 롤백 지점을 기억 |
| 1 | gend-api:latest 가 main HEAD 이후 빌드인가 | 옛 생성기가 만든 규칙으로 배포 |
| 2 | acl-sync Job 1회 실행 | ConfigMap 이 낡은 채로 반영 |
| 3 | 역할이 실제로 얻는 권한을 첫-매칭 시뮬레이션으로 검증 | ★ 2026-08-06 롤백의 정확한 지점 |
| 4 | checksum/coordinator-config 변경 + access-control.name=file + refresh-period 확인 | ConfigMap 만 바뀌고 재시작이 안 걸린 경우 |
| 5 | verify-acl.sh 전 프로브 통과 | 인가가 의도와 다르게 걸린 경우 |
게이트 3 이 핵심이다. 규칙 ConfigMap 은 trino-acl-sync CronJob 이 5분마다
data_grants 기준으로 재생성하므로, 리포에서 확인한 규칙과 배포 시점의
규칙이 다를 수 있다. 실제로 그래서 analyst 38명 + viewer 6명이 전면 차단됐다.
게이트 3 은 group 이름을 문자열로 세지 않는다 — admin-analyst 같은 이름이 통과하고
규칙의 catalog·privileges 가 깨져도 못 잡기 때문이다. 대신 각 역할이 실제로 무엇을
얻는가를 본다: iceberg 읽기 O · gendpg.public.intel_* O · ws_secrets 등 X ·
users/audit_logs 는 admin 포함 전원 X.
게이트 4 는 파드 annotation checksum/coordinator-config 를 본다. 묻고 싶은 것이
"파드가 바뀌었나" 가 아니라 "coordinator 설정이 바뀌었나" 이기 때문이다. 파드 UID 는
노드 드레인·eviction 으로도 바뀌고, pod-template-hash 는 설정과 무관한 템플릿 변경
(리소스·라벨)에도 바뀐다 — 둘 다 대리 지표다. 체크섬은 질문 그 자체다.
게이트 0 의 릴리스 상태 확인은 스텝 4 직전에 한 번 더 수행한다. 그 사이 acl-sync Job 등으로 수 분이 흐르므로, 처음 한 번만 보면 그동안 시작된 다른 배포를 놓친다.
verify-acl.sh 의 프로브 설계
음성(차단돼야 함)만 있으면 "전부 막아서 통과" 를 못 잡는다 — 롤백 사고가 정확히 그 형태였다. 그래서 양성 프로브를 함께 둔다.
- 양성: analyst/viewer/admin →
iceberg, analyst →gendpg.public.intel_*, 서비스 계정(gend-api·dagster) →iceberg - 음성: analyst →
gendpg.public.ws_secrets·access_policies, viewer →gendpg.public.gitea_pat·system.runtime.queries, 미등록 사용자 → 전부
gendpg.public.users 같은 테이블은 프로브로 쓰지 않는다 — PG 계층
(gend_trino_readonly)이 이미 숨겨서 ACL on/off 를 구분하지 못한다.
환경변수
두 스크립트 모두 기본값이 prod 를 가리킨다. 다른 클러스터에 쓰려면 아래를 덮어쓴다.
| 변수 | 기본값 | 적용 | 설명 |
|---|---|---|---|
GEND_KUBE_CONTEXT | aks-genos-prod | 양쪽 | kubectl/helm 컨텍스트 |
GEND_NAMESPACE | gend | 양쪽 | 대상 네임스페이스 |
GEND_EXEC_TIMEOUT | 60s | 양쪽 | kubectl --request-timeout |
TRINO_CHART_VERSION | 1.42.2 | activate | helm 차트 버전 |
TRINO_HELM_WAIT_MIN | 5 | activate | helm --timeout (분). 외부 상한은 여기서 유도(+2분). 양의 정수만 — 아니면 exit 2 |
REPO_DIR | 스크립트의 리포 루트 | activate | values*.yaml 위치 |
GEND_PROBE_TABLE | iceberg.bronze.intel_ohlcv_raw | verify | 양성 프로브용 테이블 |
GEND_PROBE_ANALYSTGEND_PROBE_VIEWERGEND_PROBE_ADMIN | 없음 | verify | 프로브에 쓸 Trino user. 미지정 시 trino-group-provider ConfigMap 에서 각 group 의 첫 멤버를 읽는다 |
NO_COLOR | — | activate | 설정 시 ANSI 색상 비활성 (비-TTY 에서는 자동) |
★ GEND_PROBE_* 에 기본값을 두지 않는 것이 의도다. 리포에 운영 사용자의 Keycloak
sub 를 평문으로 남기지 않기 위해서다. 멤버를 못 찾으면 exit 2(판정 불가)로 끝난다.
--help 로도 같은 목록을 볼 수 있다.
종료코드
자동화·알림 라우팅이 상황을 구분할 수 있도록 코드를 나눠 둡니다.
| 코드 | 의미 | 대응 |
|---|---|---|
| 0 | 성공 | — |
| 1 | 게이트/검증 실패 — 클러스터는 안전한 상태 (자동 롤백 성공했거나 애초에 미변경) | 원인 수정 후 재시도 |
| 2 | 인자 오류·준비 실패 등 판정 불가 | 환경 확인 (컨텍스트·권한·kubectl) |
| 3 | 자동 롤백까지 실패 | 사람이 즉시 개입 — 아래 수동 롤백 |
★ 1 과 3 을 섞으면 안 됩니다. 1 은 재시도 대상이고 3 은 호출 대상입니다.
롤백
helm --kube-context aks-genos-prod -n gend rollback trino <직전 리비전>
activate-acl.sh 는 시작할 때 그 번호를 출력한다.