컨테이너로 만든 Trino 를 Kubernetes 에 올릴 때 코디네이터나 워커가 기동 중에 죽는 경우가 있다. 로그에 찍히는 메시지별로 원인을 정리한다.
Error occurred during initialization of VM
java.lang.Error: A command line option has attempted to allow or enable the Security Manager.
Enabling a Security Manager is not supported.
Java 의 Security Manager 는 JEP 411 로 비권장이 된 뒤 이후 릴리스에서 제거됐다. 제거된 JDK 에서 관련 옵션이 남아 있으면 JVM 이 초기화 단계에서 그대로 종료한다.
grep -R "security.manager" /opt/trino/etc
jvm.config 에서 -Djava.security.manager 계열 옵션을 지운다. 옵션을 넣은 기억이 없다면 오래된 Trino 배포본의 기본 jvm.config 를 그대로 쓰고 있을 가능성이 크다. Trino 는 릴리스마다 요구하는 JDK 가 정해져 있으므로, 이미지의 JDK 버전을 그 릴리스가 지원하는 것으로 맞추는 것이 근본 해결이다. 지원 JDK 범위는 해당 릴리스 노트에서 확인한다.
java.lang.NullPointerException
at com.google.common.base.Preconditions.checkNotNull
...
at io.airlift.configuration.ConfigurationFactory
Airlift 가 프로퍼티를 자바 객체로 변환하는 중에 값이 비어 있어 생긴다. 대개 값이 없는 키가 파일에 남아 있는 경우다.
grep -Rn "=$" /opt/trino/etc
cat /opt/trino/etc/catalog/*.properties
discovery.uri= · node.environment= · hive.metastore.uri= 처럼 키만 있고 값이 빈 줄을 지운다. 환경 변수 치환을 쓰는 구성이라면 그 변수가 실제로 채워졌는지 본다.
컨테이너 진입 스크립트에 아래 패턴이 흔하다.
set -a
. ${TRINO_CONF_DIR}/trino-env.sh
set +a
set -a 는 이후 정의되는 변수를 자동으로 export 하고 set +a 로 되돌린다. 이 파일 안에 VAR= 처럼 빈 값이 있으면 그 빈 값이 그대로 환경에 실린다. ConfigMap 으로 이 파일을 주입할 때 값이 비어 들어가는 경우가 많다.
An exception was caught and reported. Message: Unknown authentication type: file
http-server.authentication.type 에 들어갈 수 있는 값과 인증기 구현 이름을 혼동한 것이다. 파일 기반 사용자 인증은 PASSWORD 타입에 파일 인증기를 붙이는 구조다.
# config.properties
http-server.authentication.type=PASSWORD
password-authenticator.config-files=/opt/trino/etc/password-authenticator.properties
# password-authenticator.properties
password-authenticator.name=file
file.password-file=/opt/trino/etc/password.db
password.db 는 bcrypt 해시를 담은 텍스트 파일이다. 인증은 코디네이터의 HTTP 종단에서 이뤄지므로 이 설정은 코디네이터에 필요하다. 다만 코디네이터와 워커가 같은 이미지·같은 ConfigMap 을 쓰는 구성이라면 워커에도 같은 파일이 있어야 설정 검증을 통과한다. 워커에서 파일을 찾지 못해 기동에 실패하는 형태로 나타난다.
Kubernetes 에서는 이 파일을 PVC 로 두지 않는다. 작고 모든 파드가 같은 내용을 읽기만 하므로 Secret 이 맞다.
kubectl create secret generic trino-password-db --from-file=password.db -n databases
volumeMounts:
- name: password-db
mountPath: /opt/trino/etc/password.db
subPath: password.db
volumes:
- name: password-db
secret:
secretName: trino-password-db
subPath 로 마운트하면 Secret 내용이 바뀌어도 파드 안 파일은 갱신되지 않는다. 변경하면 파드를 다시 띄운다.
WARN http-client-announcer io.trino.node.AnnounceNodeAnnouncer
Error announcing node state to http://trino-coordinator-service:8080/v1/announce:
Server refused connection
워커는 discovery.uri 로 코디네이터에 자신을 등록한다. 이 주소는 Kubernetes 서비스 이름이어야 하고 코디네이터가 실제로 그 포트를 듣고 있어야 한다.
# 워커 config.properties
coordinator=false
discovery.uri=http://trino-coordinator-service:8080
확인 순서는 서비스 → 엔드포인트 → 코디네이터 상태다. 코디네이터가 아직 기동 중이거나 죽어 있으면 엔드포인트가 비어 있다.
kubectl -n databases get svc trino-coordinator-service
kubectl -n databases get endpoints trino-coordinator-service
kubectl -n databases logs deploy/trino-coordinator
엔드포인트가 비었다면 서비스의 셀렉터와 코디네이터 파드의 라벨이 어긋난 것이다. 서비스 이름으로 접근하는 규칙은 서비스 이름으로 통신하기 에 있다.
코디네이터가 워커 없이도 질의를 처리하게 하려면 node-scheduler.include-coordinator=true 를 준다. 운영 클러스터에서는 코디네이터가 작업까지 맡게 되므로 끄는 것이 보통이다.