::: IT인터넷 :::

Spark Application 실행을 위한 Livy 서버 설정

곰탱이푸우 2023. 10. 19. 08:20
Jar 형태로 패키징한 Spark Application을 Spark에 Submit 할 때 Livy를 사용하면 아주 편리하다.
환경 구축은 아래 문서를 참고한다.
그러나 Livy 서버의 기본 설정 만으로는 Jar 형태의 Spark Application을 실행하는데 아래와 같은 제약이 있다.
  • 의존성 라이브러리 다운로드를 위한 내부 저장소 접근 불가 (프록시, 인증서 이슈)
  • 의존성 라이브러리 다운로드에 시간 과다 소요 (프록시, ivy 설정)
  • HDFS 파일 접근과 쓰기 (livy 계정 권한 문제)
  • Livy 서버의 버그 존재 (상태 변경 불가)
 
외부 네트워크와 격리 된 프록시 환경과 자체적으로 구축한 저장소를 사용함으로 인해 발생하는 문제이다.
이러한 문제를 해결하기 위한 Livy 서버와 Spark의 설정 변경 방법을 정리한다.

 

 

Spark과 Livy 서버 접속

Spark과 Livy 서버의 설정을 변경하기 위해 하둡 클러스터의 해당 노드에 접속한다.
 
하둡 클러스터를 구축하면서 Spark과 Livy는 2번 노드에 구성했다.
하둡 클러스터 구축과 Livy 서버 설치는 아래 문서를 참고한다.
따라서 가상 환경의 2번 노드로 SSH 접속을 진행한다.
호스트 전용 어댑터로 192.168.56.202 IP를 부여 했으므로 해당 주소로 접근한다.
$ ssh -p 22 livy@192.168.56.202
 
또는 /etc/hosts에 FQDN을 등록했다면 해당 FQDN을 사용해도 된다.
 
현재 구성해 놓은 하둡 클러스터는 다음과 같다.
 
설정해야 하는 Livy 서버가 두 개의 클러스터이므로, Server 1과 Server 2의 해당 노드에서 모두 진행한다.
 
하둡 에지노드 구성은 아래 문서를 참고한다.
 

Livy 서버 설정 변경

Jar 형태의 Spark Application을 하둡 클러스터의 Spark에 Submit하기 위한 Livy 서버의 설정을 변경한다.
아래 사항들을 설정한다.
  • Livy 서버가 접근하는 저장소의 사설 인증서 설정
  • Livy 계정 설정
  • ivy2 저장소 설정 변경
  • Livy 서버 설정 변경과 버그 해결
 

Nexus 저장소 인증서 설정

Spark Application은 Scala로 작성 된 Jar 형태로 패키징하여, 내부에 구축한 Maven 사설 저장소에 배포한다.
Maven 사설 저장소 구성 방법은 아래 내용을 참고한다.
Maven 사설 저장소로 사용하는 Nexus에는 자체 서명 인증서가 적용되어 있다.
따라서 자체 서명 인증서를 자바 가상 머신 (JVM)의 신뢰 인증서 목록에 추가한다.
그렇지 않으면 자체 서명 인증서의 검증 오류로 인해 다운로드가 불가능하다.
 
아래 문서의 "JVM에 자체서명 인증서 설정" 항목을 참고하여 진행한다.
만약 자체 서명 인증서를 사용하지 않거나, 공식 저장소를 사용하는 경우에는 생략해도 무방하다.
 
Maven 저장소가 구축 된 Nexus 서버의 rootca 인증서 파일을 사전에 준비한다.
 
Nexus 저장소의 서버 인증서를 다운로드한다.
$ curl -O https://gist.githubusercontent.com/lesstif/cd26f57b7cfd2cd55241b20e05b5cd93/raw/InstallCert.java
$ javac InstallCert.java
$ java -cp ./ InstallCert 저장소주소:포트
 
InstallCert.java 도구 관련 내용은 아래 문서를 참고한다.
다운로드한 인증서 파일을 CRT 포맷으로 변환한다.
$ keytool -exportcert -keystore jssecacerts -storepass changeit \
  -file output.cert -alias 저장소주소-1
 
서버 인증서와 rootca 인증서를 자바가상머신(JVM)에 등록한다.
# 기존에 등록된 별칭 (alias)가 있으면 삭제한다.
$ sudo keytool -delete -alias 저장소주소-1 -keystore ${JAVA_HOME}/jre/lib/security/cacerts \
  -storepass changeit

# 서버 인증서 등록
$ sudo keytool -importcert -trustcacerts -keystore ${JAVA_HOME}/jre/lib/security/cacerts \
  -storepass changeit -file /home/ambari/certs/output.cert -alias 저장소주소-1

# rootca 인증서 등록
$ sudo  keytool -importcert -trustcacerts -keystore ${JAVA_HOME}/jre/lib/security/cacerts \
  -storepass changeit -file /home/ambari/certs/rootca.crt -alias 저장소주소
 
위의 cacerts 디렉토리 경로는 JDK 1.8 버전의 경로이며, 다른 버전을 사용할 경우 해당 경로를 사용한다.
 

Livy 계정 설정 변경

Spark에 Application을 submit 할 때 Livy 서버가 실행 중인 계정이 전달된다.
livy 서버를 실행 중인 계정이 root인 경우, 하둡 클러스터에서 root 계정으로 인식한다.
하둡 클러스터의 root 계정과 동일하기 때문에 livy 서버는 livy 계정으로 실행해야 한다.
 
또한 Livy 서버를 설치하면 livy 계정이 생성되지만, 홈 경로와 터미널 접근이 불가능하다.
이러한 이유로 livy 계정의 설정을 변경한다.
 

Livy 계정 로그인 설정

livy 계정의 홈 디렉토리를 생성하고, 터미널 접근이 가능하도록 설정을 변경한다.
 
아래와 같이 홈 디렉토리를 생성한다.
# livy 홈 디렉토리 생성
$ sudo mkdir /home/livy

# 폴더 소유자 변경
$ sudo chown livy:hadoop /home/livy

# .bashrc, .bash_profile 생성
$ sudo cp ~/.bash* /home/livy/

# .bashrc, .bash_profile 소유자 변경
$ sudo chown livy:hadoop /home/livy/*.bashrc
 
livy 계정의 터미널 로그인이 가능하도록 설정을 변경한다.
해당 계정의 홈 디렉토리를 지정하고, nologin 경로를 bash 경로로 수정한다.
$ sudo vi /etc/passwd
# 아래 내용을 수정하고 :wq로 저장
# before - livy:x:996:990:Livy:/var/lib/livy:/sbin/nologin
# after - 계정명/비밀번호/UID/GID/기타정보(사용자이름)/홈디렉토리/로그인쉘
livy:x:996:990:Livy:/home/livy:/bin/bash
 

Livy 계정 권한 추가

livy 계정에 관리자 권한을 추가한다.
visudo 파일을 수정하여 sudo 권한을 부여하고, 관리자 역할을 대행하는 wheel 그룹에 추가한다.
# root 계정으로 전환
$ sudo su

# 관리자 권한으로 실행 가능하도록 권한 추가
$ visudo
## Allow root to run any commands anywhere 아래에 livy 추가하고 :wq로 저장
livy    ALL=(ALL)      ALL

# wheel 그룹(관리자 역할 대행 그룹)에 livy 계정 추가
$ usermod -aG wheel livy

 

 

livy 로그 폴더 권한 변경

livy 서버의 로그 폴더의 권한이 없으면 livy 계정으로 Livy 서버를 실행하면 오류가 발생한다.
로그 파일을 생성하거나 기록할 수 없기 때문이다.
 
따라서 Livy 서버의 로그 폴더 권한을 livy 계정으로 변경한다.
$ sudo rm -rf /var/log/livy/*
$ sudo chown livy:hadoop /var/log/livy/
 

Ivy의 저장소 설정 파일 생성

Ivy는 Apache에서 관리되고 있는 프로젝트로 Apache Ant의 하위 프로젝트이다.
Maven은 빌드 기능까지 제공하지만 Ivy는 순수하게 의존성 관리를 위한 프로젝트로 sbt, grails, gradle 등에서 사용한다.
 
Livy 서버로 제출 된 Jar 형태의 Spark Application에서 필요로 하는 라이브러리 다운로드 (의존성 해소)는 Livy 서버에서 진행된다.
Livy는 ivy 저장소와 Maven 저장소에서 라이브러리를 다운로드한다.
 
Ivy를 통해 의존성을 관리하는 경우, 저장소 정보 설정은 /home/계정명/.ivy2 경로에 ivysettings.xml을 생성하여 관리한다.
프록시 서버 정보와 저장소 목록을 관리할 수 있으며, 인터넷 환경에서 공식 저장소를 사용하는 경우 생략해도 무방하다.
 
아래 내용은 프록시 서버와 사설 저장소를 사용하는 경우에 해당한다.
$ sudo /home/livy/.ivy2/ivysettings.xml

# 아래 내용을 저장하고
<ivysettings>
    # 캐시 파일 경로
    <caches defaultCacheDir="${user.home}/.ivy2/cache" />
    # 프록시 서버 정보
    <proxies>
        <proxy name="proxy" host="프록시서버주소" port="프록시서버포트"/>
    </proxies>
    # 프록시 적용 예외 목록
    <hosts>
        <host name="대상서비스IP" port="포트" />
        <host name="대상서비스FQDN" port="포트" />
    </hosts>
    # 저장소 목록 (ivy, maven) - 상단의 저장소부터 탐색
    <resolvers>
        <chain name="default" returnFirst="true">
            <url name="ivy-remote-scalasbt">
               
            </url>
            <url name="ivy-remote-sbt-ivy">
               
            </url>
            <url name="ivy-remote-typesafe-ivy">
               
            </url>
            <ibiblio name="maven-public" m2compatible="true" \
                root="https://저장소주소:포트/repository/maven-public/" />
            <ibiblio name="maven-release" m2compatible="true" \
                root="https://저장소주소:포트/repository/maven-releases/" />
            <ibiblio name="maven-snapshot" m2compatible="true" \
                root="https://저장소주소:포트/repository/maven-snapshots/" />
        </chain>
        <!-- 다른 저장소 추가 가능 -->
    </resolvers>
</ivysettings>
 
ivy와 maven 저장소 구성과 목록 관련 내용은 아래 문서를 참고한다.

Livy 서버 환경 설정

Livy 서버 실행과 하둡 클러스터 연동을 위한 환경 변수를 지정한다.
또한 Livy 서버의 특정 라이브러리 버전이 맞지 않아 발생하는 오류를 해결한다.
 

Livy 환경 변수 설정

Livy 서버 실행과 하둡 클러스터 연동을 위한 환경 변수를 지정한다.
Livy 서버가 실행 중인 2번 노드는 Spark, Hadoop, YARN (Resource Manager)의 클라이언트 역할을 수행한다.
따라서 해당 설정 파일들이 모두 노드 내부에 존재하며, 해당 디렉토리 경로들을 환경 변수로 지정한다.
 
나머지 사항들은 아래 내용과 주석을 참고한다.
$ sudo vi /etc/livy/conf/livy-env.sh

# 아래 내용 추가하고 :wq로 저장
export SPARK_HOME=/usr/lib/spark/
export SPARK_CONF_DIR=/etc/spark/conf/
export HADOOP_CONF_DIR=/etc/hadoop/conf/
export YARN_CONF_DIR=/etc/hadoop/conf/
# JVM의 기본 옵션 정보 전달 (자체 서명 인증서 검증 옵션 해제와 신뢰 인증서 목록 전달)
# 실제 코드를 작성할때는 줄바꿈 제거하고 한줄로 입력
export LIVY_SERVER_JAVA_OPTS="-Xmx2g -Dsbt.override.build.repos=true \
        -Dmaven.wagon.https.ssl.insecure=true -Dmaven.wagon.https.ssl.allowall=true \
        -Dmaven.wagon.http.ssl.ignore.validity.dates=true \
        -Djavax.net.ssl.trustStore=$JAVA_HOME/jre/lib/security/cacerts \
        -Djavax.net.ssl.trustStorePassword=changeit"
# Livy 서버에 사용할 계정 정보
export LIVY_IDENT_USER=livy
# Jar의 의존성 관리 도구인 ivy의 설정 파일 경로
export IVY_SETTINGS=/home/livy/.ivy2/ivysettings.xml
 

Livy 버그 해결

Spark Application을 Livy 서버를 통해 Submit 하면 Application이 Spark에 제출되고 YARN (리소스 매니저)에서 확인 가능하다.
YARN에서는 정상적으로 실행 된 것으로 나오는데, Livy 서버에서 배치 세션의 상태 (State)가 starting에서 Running으로 변경되지 않는 문제가 있다.
또한 YARN에서 확인되는 Application Id가 Livy 서버에서 표시되지 않는다.
 
아래는 실제로 상태가 변경되지 않는 화면을 캡처한 것이다.

 

 

Livy 서버의 로그를 확인해보면 아래와 같은 오류 로그가 발생한 것을 확인할 수 있다.
java.lang.NoClassDefFoundError: javax/ws/rs/ext/MessageBodyReader
Java.lang.NoClassDefFoundError: Could not initialize 
                                class org.apache.hadoop.yarn.util.timeline.TimelineUtils
 
특정 라이브러리의 클래스를 찾지 못해 YARN과 정보 교환이 되지 않아 상태 업데이트가 안되는 것으로 추정할 수 있다.
 
관련 내용으로 찾아보면서 해결 방법이 명시 된 아래 문서를 발견할 수 있었다.
JSR 관련 라이브러리 버전의 호환 이슈가 원인이다.
오류가 해결 된 버전의 jar 파일을 라이브러리 폴더에 넣어주면 해결된다.
 
아래와 같이 진행한다.
# jsr311 1.1.1 버전 다운로드
$ wget https://repo1.maven.org/maven2/javax/ws/rs/jsr311-api/1.1.1/jsr311-api-1.1.1.jar
# livy의 라이브러리 폴더에 복사
$ sudo mv jsr311-api-1.1.1.jar /usr/lib/livy/jars/
# 소유자와 권한 변경
$ sudo chown root:root /usr/lib/livy/jars/jsr311-api-1.1.1.jar 
$ sudo chmod -R 777 /usr/lib/livy/jars
 

Livy 서버 재시작

설정 변경이 완료되면 Livy 서버를 재시작한다.
root 계정이 아닌 livy 계정으로 재시작한다.
$ /usr/lib/livy/bin/livy-server stop
$ /usr/lib/livy/bin/livy-server start
 
 

Spark 설정 변경

Livy 서버 설정 변경이 완료되면 Spark 설정을 변경한다.
Spark 설정은 Ambari에서 진행한다.
 

Ambari 접속 및 설정 화면 이동

Ambari(http://192.168.56.201:8080/)에 접속하고 관리자로 로그인한다.
 
Ambari -> Spark -> Configs  -> Advanced 탭 클릭 순으로 이동하여 Spark 설정 화면으로 진입한다.
 

ivy와 저장소 설정

Custom spark-defaults에서 add properties 버튼을 클릭하고 아래 내용을 입력한다.
 
아래 항목들을 입력한다.
구분
ivy 설정 파일 경로
Spark 저장소 설정
Key
spark.jars.ivySettings
spark.jars.repositories
Value
/home/livy/.ivy2/ivysettings.xml
아래 내용을 한줄로 입력한다.

https://repo.scala-sbt.org/scalasbt/sbt-plugin-releases/
[organization]/[module]/(scala_[scalaVersion]/)
(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext],
https://scala.jfrog.io/artifactory/ivy-releases/
[organization]/[module]/(scala_[scalaVersion]/)
(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext],
https://scala.jfrog.io/ui/native/ivy-releases/
[organization]/[module]/(scala_[scalaVersion]/)(
sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext],
https://저장소주소:포트/repository/maven-public/,
https://저장소주소:포트/repository/maven-releases/,
https://저장소주소:포트/repository/maven-snapshots/
Type
TEXT
TEXT
 
예를 들어 spark.jars.ivySettings 항목의 경우 아래와 같이 입력하고 ADD를 클릭한다.
 

Spark 전체 재시작

화면 하단의 SAVE 버튼을 클릭하면 아래와 같이 재시작을 권고하는 메시지가 출력된다.
 
화면 UI의 안내에 따라 Spark의 전체 구성 요소를 재시작한다.
위의 안내 메시지가 표시 되지 않을 경우 ACTIONS - Restart All 을 눌러 전체 재시작을 진행한다.