Trino iceberg connector의 권한을 제어하기 위해서 아래 3가지를 비교했었다.

OPA와 Apache Ranger는 외부 정책 엔진으로 Trino 외 다른 서비스(k8s, Spark, Kafka 등)에서도 사용 가능하며 Trino에 국한된게 아니다.

File-based AC + 자동 갱신 Watcher은 Trino 엔진에서 처리하는 Trino에 국한된 방식이다.

 

1. OPA (Open Policy Agent)

방식: 외부 OPA 서버 + Rego 정책 엔진 연동 (권한 판단 로직만 별도 관리)

장점: Rego 언어의 유연성으로 정책 설정을 최대한 건드리지 않아도 됨.

단순 A는 B를 할 수 있다/없다가 아니라 특정 시간대에만 접속 가능, 속성 기반, 컬럼 중에서 민감 정보가 포함되어 있는지 등 다양하게 체크 가능

문제점: 436 버전에서는 트리노 엔진 내부의 OPA Access Control 플러그인 지원 X

결론: Trino 446 버전으로 업그레이드

 

2. File-based AC + 자동 갱신 Watcher

방식: 사용자가 SQL로 권한 부여 시 Python 스크립트가 hive metastore를 폴링하여 rules.json 자동 갱신

문제점: iceberg connector는 기본적으로 allow all(규칙에 없으면 일단 허용)이라서 제어할 모든 계정, 권한을 적어야하고 누락 시 접근 허용이 된다. 그래서 사용자 계정, 스키마, 테이블 추가 시 rules.json 파일이 계속 변경되어야하고 rule.json 파일 변경 여부를 Trino 엔진이 설정된 주기마다 연동해야해서 지연이 발생한다.

결론: iceberg connector의 Default Policy의 방향성으로 인해 기각

 

3. Apache Ranger

방식: 데이터 보안 전용 프레임워크

문제점: 운영해야 할 컴포넌트(Admin, DB, Solr, Usersync 등)가 너무 많아 관리 포인트 늘어나고 리소스도 늘어남.

결론: Trino의 접근제어만을 위해서 도입하기엔 비효율적

 

 

Trino → OPA → Ownership Service → Metastore DB 순서로 Data flow가 흐른다.

 

 

1. 사용자 쿼리: 사용자가 Trino에 SELECT * FROM mydb.mytable 같은 쿼리를 날린다.

2. Trino의 권한 체크: Trino는 이 사용자가 해당 테이블에 접근 권한이 있는지 확인하기 위해 OPA에 "이 유저가 이 테이블 봐도 돼?"라고 HTTP 호출하여 물어본다.

3. OPA의 판단 유보: OPA는 정책(Policy)을 계산하다가, "어? 주인이 누군지 알아야 권한을 줄지 말지 결정하겠는데?"라고 판단한다.

4. Ownership Service 호출: OPA가  Ownership Service의 URL(예: http://ownership-service:8080/?schema=mydb&table=mytable)로 요청을 보낸다.

5. Metastore DB 조회 및 응답: Ownership Service는 Metastore DB에서 소유자 정보를 꺼내서 OPA에게 JSON({"owner": "admin"})으로 던져준다.

6. OPA의 최종 결정: OPA는 "방금 받은 소유자가 'admin'네? 그럼 이 쿼리 승인(Allow)!" 혹은 "소유자가 다른 사람이네? 거절(Deny)!"이라는 결과를 Trino에 보낸다.

7. Trino의 실행/차단: Trino는 OPA의 결정에 따라 쿼리를 실행하거나 Access Denied 에러를 보낸다.

 

트러블슈팅

1. Trino 448 버전 버그

Trino 버전을 448로 진행했었는데 Trino iceberg DB의 한글 컬럼을 조회하지 못하는 버그가 있어서 446 버전으로 다시 업그레이드를 진행했었다.

 

2. OPA health check port 구분

OPA는 정책 판단 쿼리가 몰리면서 부하가 있었다. 그래서 health check port를 따로 구분하여 진단용 port와 서비스용 port를 구분하여 잠시 OPA API가 느려지는 것에서 오는 k8s pod 재시작을 막았다.

 

 
airflow, kubeflow, trino의 cpu, memory 데이터는 Prometheus로 가져와서 telegraf를 통해서 postgresql db에 저장한다.
db에 저장하는 이유는 분기마다 데이터를 누적해서 보고싶기 때문이다. Prometheus는 기본 데이터 보관기간이 15일이다. 

 

Prometheus의 데이터 보관기간을 늘리면 되는데, 왜 Telegraf를 통해서 Postgresql에 데이터를 보관하는 것일까?

그 이유는 스토리지 용량 증가로 오래된 쿼리 조회 시 속도가 느려지고 디스크I/O 부하가 발생할 수 있다. 또한 Prometheus는 데이터를 영구적으로 쌓아두고 분석하는 데이터 웨어하우스(DW) 목적으로 설계되지 않았기에 장기보관에 적합하지 않다. Prometheus 로컬 스토리지는 백업과 복구가 DB만큼 유연하지 않아서 안정성을 위해서도 Postgresql를 선택했다. 나중에 여러 매트릭 데이터와 비즈니스 데이터를 조인해서 엑셀로 추출하기에도 DB에 저장해야 쿼리 유연성을 보장할 수 있다.
 
Trino에 테이블에 있는 airflow 내 특정한 dag와 kubeflow의 모델의 cpu, memory와 사용자 데이터를 postgre로 저장하기 위해서 sync-worker를 사용했다. 5분에 한번씩 데이터를 가져오게 하였고 postgre로 데이터를 insert하는 파이썬 코드다.
 
Grafana에서는 postgresql DB만 커넥션으로 연결하면 된다.
 

아키텍쳐 설계 시 고민한 점

1. 메트릭 데이터가 Postgre에 많이 쌓이면 grafana에서 조회 속도가 느려질텐데 이걸 어떻게 해결할 수 있을까?
-> postgre extension인 Timescale 형식으로 변경하면 된다. postgre config 파일에 추가만 하면 돼서 변경은 어렵지 않지만, 다만 Timescale로 변경하는 건 DB 재시작이 필요하다.
 
2. NAS 용량 데이터를 가져올때 왜 cronjob을 사용했을까?
-> telegraf sidecar로 스토리지를 가져오지 않고 cronjob을 선택한 이유는 스토리지 용량 계산에 순간적으로 메모리를 많이 쓰면서 I/O와 메모리 부하 문제가 발생할 경우,  telegraf sidecar는 telegraf pod에도 영향을 주기 때문이다. 보통 스토리지 용량 계산하는데 시간이 오래 걸린다. Trino와 연동된 스토리지 용량 확인은 자주 바뀌는 상태 데이터도 아니고 배치 이후에 용량 체크를 위해서 k8s 크론잡으로 딱 하루 한번 실행되게 하였고 shell 파일(mc du)로 용량을 influxdb 형식으로 telegraf에게 전달한다.
 

트러블 슈팅

1. trino의 cordinator와 woker를 구분해서 메트릭 데이터를 가져오려면 어떻게 해야할까??
-> Trino JMX Exporter의 k8s service의 셀렉터를 구분해서 띄워야한다. service를 각각 띄우기 위해서는 pod에 lable이 구분되어져야한다. 그래야 telegraf의 tag 필드에서 구분해서 cordinator와 woker를 구분해서 값이 들어온다. Trino는 기본적으로 JMX 형태로 메트릭을 제공하는데, 이를 프로메테우스가 pull 할 수 있도록 HTTP로 변환해주는 JMX Exporter가 필요하다.
 
2. 메트릭 이름을 지정해서 특정 메트릭만 가져오도록 telegraf.conf 파일을 수정하였는데 왜 메트릭을 안가져올까??
-> 프로메테우스에서 telegraf가 데이터를 가져올때 메트릭 이름과 태그로 분리한다. 그래서 딱 일치하는 값이 아니면 아예 수집이 안된다. Telegraf는 fieldpass에 적힌 이름과 100% 일치하는 필드만 가져오기 때문에, 글자 하나만 틀려도 데이터를 안가지고 온다. 그래서 fieldpass를 전부 없애서 Telegraf를 띄운 다음에 Postgresql에 데이터가 어떻게 들어오는지 확인하고 fieldpass를 다시 넣어줬다.
 

+ Recent posts