Hive Accessible spark in AWS without EMR (using glue meta-store)
emr을 통하면 쉽게 aws에서 spark 환경을 만들수 있지만, 독립된 ec2, 특히 docker/kubernetes 환경에서는 emr사용하지 않지만 glue metastore를 활용하는 spark 환경이 필요하기 때문에 이에 구성에 대해서 설명하도록 하겠습니다. emr hadoop에 필요한 jar를 만들 필요가 있기때문에 emr서버 한대는 필요합니다.
Jar 파일 준비
- 준비해야할 jar파일은 spark관련한 jar와 hadoop/emr 관련한 jar가 있으며
- spark 관련 jar
- $SPARK_HOME/jars (보통 /usr/lib/spark/jars) 에 있는 hadoop*.jar, hive*.jar, ht*.jar
- 수집된 파일을 emr-spark.tar 라고 가정
- hadoop/emr 관련 jar
- hadoop/emr관련 jar는 $SPARK_HOME/conf/spark-default.conf 에서 어떤 파일이 필요한지 확인할수 있는데, 실제로 emr서버에서 확인하셔서 관련 jar를 모두 수집합니다.
- 수집된 파일을 emr-lib.tar 라고 가정
- 참고 spark-default.con. emr-x.x 에 spark configuration 이며, 향후 변경될수도 있으니 현재의 설정을 확인하는것이 좋습니다. 아래는 참고로만 봐주세요.
spark.driver.extraClassPath :/usr/lib/hadoop-lzo/lib/*:/usr/lib/hadoop/hadoop-aws.jar:/usr/share/aws/aws-java-sdk/*:/usr/share/aws/emr/emrfs/conf:/usr/share/aws/emr/emrfs/lib/*:/usr/share/aws/emr/emrfs/auxlib/*:/usr/share/aws/emr/goodies/lib/emr-spark-goodies.jar:/usr/share/aws/emr/security/conf:/usr/share/aws/emr/security/lib/*:/usr/share/aws/hmclient/lib/aws-glue-datacatalog-spark-client.jar:/usr/share/java/Hive-JSON-Serde/hive-openx-serde.jar:/usr/share/aws/sagemaker-spark-sdk/lib/sagemaker-spark-sdk.jar:/usr/share/aws/emr/s3select/lib/emr-s3-select-spark-connector.jar spark.driver.extraLibraryPath /usr/lib/hadoop/lib/native:/usr/lib/hadoop-lzo/lib/native
Spark 구성
- spark설치 : https://www.apache.org/dyn/closer.lua/spark/spark-2.4.4/spark-2.4.4-bin-hadoop2.7.tgz 받아서 압축을 풀어줍니다.
- spark jar 교체
- $SPARK_HOME/jars 파일중 위에서 수집했던 hive*.jar, hadoop*.jar, ht*.jar파일을 삭제하고 emr-spark.jar 파일을 $SPARK_HOME/jars 폴더에 넣어줍니다. 반대로 emr spark 의 jar를 기본으로 apache spark 의 jar에서 필요한 파일만 넣어주는 방법도 가능하겠는데요 apache spark 에는 kubernetes 등에서 쓰는 다양한 library가 있어서 위에서 말한 hive, hadoop, ht* 관련 jar를 대체해서 hadoop version을 맞춰주는 방식이 안정적이 었습니다. 참고로 테스트한 apache spark은 hadoop 2.7.3 version이었고, hadoop 은 2.8.5 이었으며 version을 하나로 맞추지 않으면 hadoop version 충돌문제가 발생하게 됩니다.(access method org.apache.hadoop.metrics2.lib.MutableCounterLong - https://stackoverflow.com/questions/51527878/spark-job-reading-from-s3-on-spark-cluster-gives-illegalaccesserror-tried-to-ac)
- hadoop/emr jar는 임의의 폴더(emr-lib 이라고 정하고 이후예제에도 그렇게 사용합니다.) 를 만들고 emr-lib.tar를 풀어줍니다
- spark 설정
- $SPARK_HOME/conf/hive.xml : aws glue metastore를 사용하기 위한 설정
<configuration> <property> <name>hive.metastore.connect.retries</name> <value>15</value> </property> <property> <name>hive.metastore.client.factory.class</name> <value>com.amazonaws.glue.catalog.metastore.AWSGlueDataCatalogHiveClientFactory</value> </property> </configuration>
- $SPARK_HOME/conf/spark-default.conf
- 당연한 이야기지만 $SPARK_HOME환경변수가 설정하지 않고 절대경로를 넣으셔도 됩니다.
spark.driver.extraClassPath $SPARK_HOME/emr-lib/* spark.driver.extraLibraryPath $SPARK_HOME/emr-lib/native spark.executor.extraClassPath $SPARK_HOME/emr-lib/* spark.executor.extraLibraryPath $SPARK_HOME/emr-lib/native
- hadoop conf설정
- spark에서 hive를 사용하려면 hadoop설정을 해야하는데, 설정방법은 두가지가 있습니다.
- hadoop configuration file생성
- HADOOP_CONF_DIR 설정. 환경변수로 만들거나 (export HADOOP_CONF_DIR=path ) spark-env.sh 에 HADOOP_CONF_DIR에 hadoop conf path지정
- 해당폴더에 core-site.xml 을 만들고 아래 설정 및 정보를 입력합니다.
core-site.xml <configuration> <property> <name>fs.s3.impl</name> <value>com.amazon.ws.emr.hadoop.fs.EmrFileSystem</value> </property> <property> <name>fs.s3n.impl</name> <value>com.amazon.ws.emr.hadoop.fs.EmrFileSystem</value> </property> <property> <name>fs.s3n.awsAccessKeyId</name> <value></value> </property> <property> <name>fs.s3n.awsSecretAccessKey</name> <value></value> </property> <property> <name>fs.s3.awsAccessKeyId</name> <value></value> </property> <property> <name>fs.s3.awsSecretAccessKey</name> <value></value> </property> </configuration>
- spark실행 후 sparkConf 수정
val spark: SparkSession = SparkSession.builder(). appName("AppName"). config("hive.exec.dynamic.partition", "true"). config("hive.exec.dynamic.partition.mode", "nonstrict"). config("fs.s3a.impl", "org.apache.hadoop.fs.s3a.S3AFileSystem"). config("fs.s3a.access.key", ""). config("fs.s3a.secret.key", ""). config("fs.s3.impl", "com.amazon.ws.emr.hadoop.fs.EmrFileSystem"). config("fs.s3.awsAccessKeyId",""). config("fs.s3.awsSecretAccessKey", ""). config("fs.s3n.impl", "com.amazon.ws.emr.hadoop.fs.EmrFileSystem"). config("fs.s3n.awsAccessKeyId",""). config("fs.s3n.awsSecretAccessKey", ""). enableHiveSupport(). getOrCreate()
- $SPARK_HOME/conf/hive.xml : aws glue metastore를 사용하기 위한 설정
- ec2-설정
- glue metastore접속을 위해서는 ec2 에 EMR_EC2_DefaultRole (https://docs.aws.amazon.com/ko_kr/emr/latest/ManagementGuide/emr-iam-roles-glue.html) 을 주거나 환경변수에서 ACCESS_KEY, SECRET_KEY를 설정해주셔야합니다.
Kubernetes를 위한 docker생성
- Dockerfile 수정
- 설치된 spark내에 있는 Dockerfile에 다음 부분을 추가합니다.
-
# 1 RUN apk --update add coreutils # 2 RUN mkdir -p /opt/spark/spark-conf COPY conf/hive-site.xml /opt/spark/spark-conf/ # 3 RUN rm /opt/spark/jars/kubernetes-client-4.1.2.jar ADD https://repo1.maven.org/maven2/io/fabric8/kubernetes-client/4.4.2/kubernetes-client-4.4.2.jar /opt/spark/jars/ ADD https://repo1.maven.org/maven2/org/apache/hadoop/hadoop-aws/2.8.5/hadoop-aws-2.8.5.jar /opt/spark/jars/
- #1 - sparkThriftServer 실행시 nohup관련 버그관련https://stackoverflow.com/questions/44661274/crashloopbackoff-in-spark-cluster-in-kubernetes-nohup-cant-execute-no-s
- #2 - 설정파일추가. 위에서 설정한 hive-site.xml 를 $SPARK_HOME/conf에 위치해주시고, 파일 복사 script를 추가합니다.
- #3 - kubernetes-client jar 관련 bug. https://github.com/kubernetes/kubernetes/issues/82131
- entrypoint.sh
- entrypoint.sh 파일에서 다음 script로 변경을 합니다.
-
case "$SPARK_K8S_CMD" in driver) CMD=( "$SPARK_HOME/bin/spark-submit" --conf "spark.driver.bindAddress=$SPARK_DRIVER_BIND_ADDRESS" --conf "spark.driver.extraClassPath=/opt/spark/emr-lib/*" --conf "spark.driver.extraLibraryPath=/opt/spark/emr-lib/native" --conf "spark.executor.extraClassPath=/opt/spark/emr-lib/*" --conf "spark.executor.extraLibraryPath=/opt/spark/emr-lib/native" --deploy-mode client "$@" ) ;; export SPARK_CONF_DIR=/opt/spark/spark-conf/ # Execute the container CMD under tini for better hygiene /opt/spark/sbin/start-thriftserver.sh exec /sbin/tini -s -- "${CMD[@]}"
- spark-submit 실행설정을 수정하고(spark-default.conf에 넣을때는 설정이 반영이 안되어서 entrypoint를 수정했음),
- spark-conf folder설정. conf 폴더가 default설정 폴더이기는 하지만 k8s로 spark-submit하게되면 conf폴더를 kubernetes 설정으로 replace해서 Dockerfile에서 구성을 해도 사라지는 현상이 있어서 다른 폴더를 구성하고 spark-conf도 다른 폴더로 설정했습니다.
- ThriftServer 실행. Spark ThriftServer 제공목적으로 Hive ThriftServer2 대신해서 제공하능한 경우가 있으며, 사실 이 dockerize목적인 hudi에서 필요합니다.