본문 바로가기

Data&Processing

Spark Docker Image build on AWS using glue metastore for kubernetes

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()
        
  • ec2-설정

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에서 필요합니다.