본문 바로가기
Data Science/Spark Basic

IntelliJ + Maven + Scala 으로 Jar 파일 만들기

by 알파해커 2017. 10. 27.
반응형

이전에 포스팅했던, "아파치 스파크 개발 환경 구축 및 예제 실습 - IntelliJ + Maven + Scala + Mac"에서

IntelliJ 상에서 프로그램을 실행(Run)시키는 것을 해보았다.


IDE 툴을 이용하여, 프로그램을 개발하고 간단한 테스트를 하는 것은 문제없지만

만약에 그 프로그램을 어딘가 배포시키려는 목적이 있다면, 아마도 Jar 파일 추출시켜야 할 것이다.


나의 경우에도 연구실의 내 자리에 있는 PC에서 개발 및 간단한 테스트를 진행하고,

본격적인 실험을 위해선 그 프로그램을 서버에 포팅시키는 작업이 필요했다.


그래서 Jar 파일을 만든 후, 터미널에서 spark-submit을 이용해 실행시키기 위한 긴.... 여정을 떠났다.


이번 포스팅에서 그 길었던 여정에 대해서 이야기해볼 참이다.


이전 포스팅에서도 잠깐 언급한 적있지만, 이번에도 인터넷을 검색했을 땐 수많은 다양한 케이스가 존재했고 내가 구축한 환경에 딱 맞는 것을 찾아내기에 쉽지 않았다.

일단 IntelliJ에서 Jar파일을 추출(빌드)하는 방법은 두가지가 있다.
  1. Build Artifacts
  2. Maven Build




1. Build Artifacts

첫 번째 시도해본 방법은 이거였다.

방법은 우선, 상단 메뉴의 File - Project Structure... 에 들어가면 아래와 같은 화면을 만날 수 있다. 좌측 Project Settings에서 Artifacts를 선택하면, 두번째 열이 처음에는 아마도 비어있을 것이다.

그러면, 두번째열 상단에 있는 '+' 버튼을 눌러서, 자신이 작성한 코드의 시작점이 되는 클래스를 선택하면 된다. 그리고 Apply, OK 를 차례로 클릭한다.


그리고 나서, 상단 메뉴 중 Build에 들어가 Build Artifacts를 클릭한다.



그러면 아래와 같은 버튼이, 화면 중간쯤에 조그만하게 나타나는데, 이때 Build를 클릭해주면, IntelliJ가 빌드를 시작하고 시간이 조금 지나 Jar 파일을 만들어낸다.


Jar 파일이 만들어진 경로와 파일명은 [프로젝트명] / out / artifacts / [프로젝트명]_jar / [프로젝트명].jar 가 된다.



(이 과정에서 내가 깜빡하고 빼먹은 부분이 있을지 모르겠다. 그러나 이 과정에 관해서는 구글링을 조금만 해봐도 넘쳐나게 자료가 많으니 참고하길 바란다)


어쨌든, 이렇게 만들어진 Jar 파일을 이제 spark-submit을 이용해서 실행을 시켜봤다.

그때 만났던 에러 메세지는 아래와 같다.




여기서 중요한 부분은 바로 이 부분이다.

"Exception in thread "main" java.lang.SecurityException: Invalid signature file digest for Manifest main attributes"


이와 관련해서 구글링도 하고, 스택오버플로우도 검색해봤더니


"maven-shade-plugin 으로 over-jar를 생성하는 과정에서 .RSA, .SF, .DSA 이 3가지 확장자 파일들이 병합이 안되는데 있다" 고 한다. 이제 막 시작하는 입장헤서 저 말이 솔직히 무슨 말인지도 잘모르겠다.


어쨌거나, Pom.xml 내용에 아래의 내용을 넣어주면 된단다.



<configuration>

    <filters>

        <filter>

            <artifact>*:*</artifact>

            <excludes>

                <exclude>META-INF/*.SF</exclude>

                <exclude>META-INF/*.DSA</exclude>

                <exclude>META-INF/*.RSA</exclude>

            </excludes>

        </filter>

    </filters>

    <!-- Additional configuration. -->

</configuration>



그러고 나서 다시 빌드를 하고, 실행을 시켜봤다.

똑같은 에러가 또 나왔다. Shit.


하다보니까 나중에 깨달은 사실이지만, 나는 지금 Maven을 이용해서 빌드를 시켜야 하는 상황인데 저렇게 빌드를 했을시에 내가 적용하려는 것들이 제대로 반영이 안되는 것 같았다. (어디까지나 경험적인 것이다. 이론적으로나 코드적(?)으로 맞는 말인지는 확신할 수 없다)


그래서 Maven Build를 찾아서 시도를 했다.

(지금 생각해보면 환경 자체를 Maven으로 빌드하게 만들어놨으니까, Maven Build를 하는 것이 당연한데, 처음에는 저렇게 해도 Maven으로 Build가 되는줄 알았다.. 그렇게 하루를 날려먹었다.)



2. Maven Build


Maven으로 빌드를 하기 위해서는 우선 Maven Project라는 창을 띄워야 한다.

아래 그림의 우측 화면에 Maven Project라는 뷰가 있어야하는데, 아마도 한번도 사용해보지 않았다면, 저렇게 뷰가 떠있지 않을 것이다.


그때는 상단의 메뉴에서 View - Tool Windows - Maven Projects를 클릭하면 저 화면이 나타난다.




빌드하는 방법은 간단하다.

우측 메뉴 중, Lifecycle에 있는 clean, compile, packages를 차례로 클릭하면 된다.


빌드가 완료되면, target이라는 디렉토리에 jar파일이 생성됐음을 확인할 수 있다.
마찬가지로 spark-submit을 이용해서 실행을 해봤고, 아래와 같은 에러 메세지를 만났다.


중요하게 봐야할 부분은 "java.lang.ClassNotFoundException: SimpleApp" 이다.

SimpleApp은 내가 작성한 클래스의 이름인데, 그 클래스를 찾을 수 없다는 것이다.


어떻게 이런 일이 벌어질 수가 있을까.

나는 분명이 내가 만든 프로젝트를 빌드했는데.


이와 관련해서 또 열심히 구글링하고, 스택오버플로우에 글도 올려가며 원인을 찾다가,

누군가 Jar 파일을 vi 편집기로 열어보라고 했다. 거기에 내가 만든 SimpleApp이 있는지 확인해보라고.


열어봤더니 정말 없었다.

도대체 어떻게 이런 일이 벌어질 수가 있을까.


열심히 구글링을 다시해보니, 누군가가 처음에 IntelliJ를 실행하고 프로젝트를 만들면서 내부적으로 Java가 메인 프로그램 소스로 설정이 되었을 수도 있다고 했다. 


처음 프로젝트를 생성할 때 Maven으로 프로젝트를 만들고, 기본으로 생성되는 디렉토리 또한 Java였던 것은 기억한다. (심지어 Scala 디렉토리는 아예 없어서 내가 직접 생성하고, Source root로 직접 지정해주는 과정이 있긴 했다. 이 과정은 이전 포스팅을 참고하길..)


그리고, Scala SDK도 나중에 적용하긴 했지..


그럼에도 불구하고, 어쨌거나 Scala 디렉토리를 Source root로 지정해주었고, Scala SDK를 적용해줬으니 아무 문제없을거라고 상상했으나 그것이 아니었던 것이다.


결과적으로 이 부분을 고쳐줘야한다. 어떻게 고쳐질 수 있을까 고민을 해봤는데, 자료를 수집하다 보니 역시 답은 pom.xml에 있었던 것 같다.


역시나 확실하진 않지만, 

scala-maven-plugin이라는 플러그인에서 scala를 우선적으로(?) 처리해주는 내용이 있었던 것 같다. 이 부분은 나중에 확실히 더 찾아보고 다시 포스팅 하겠다.


어쨌든 pom.xml 내용을 수정하고 다시 실행했다.


(참고로, 최종적으로 적용시킨 pom.xml 내용은 아래와 같다)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>alpha</groupId>
<artifactId>spark-example-test</artifactId>
<version>1.0</version>

<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>2.11.8</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>2.2.0</version>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.2</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<executions>
<execution>
<id>scala-compile-first</id>
<phase>process-resources</phase>
<goals>
<goal>add-source</goal>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>scala-test-compile</id>
<phase>process-test-resources</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>


사실은, 이 최종 pom.xml을 적용하기 전에 하나의 단계가 더 있었다.

저 내용 중 scala-library의 버전을 적는 부분에 처음에는 내 PC에 설치되어 있는 scala의 버전을 보고 작성했다. 2.12.3 버전이었다.


그 상태에서 실행을 시켜보니 아래와 같은 에러가 났다.

Caused by: java.lang.BootstrapMethodError: java.lang.NoClassDefFoundError: scala/runtime/LambdaDeserialize

at SimpleApp$.$deserializeLambda$(SimpleApp.scala)

... 33 more

Caused by: java.lang.NoClassDefFoundError: scala/runtime/LambdaDeserialize

... 34 more

Caused by: java.lang.ClassNotFoundException: scala.runtime.LambdaDeserialize

at java.net.URLClassLoader.findClass(URLClassLoader.java:381)

at java.lang.ClassLoader.loadClass(ClassLoader.java:424)

at java.lang.ClassLoader.loadClass(ClassLoader.java:357)

... 34 more



음. 역시 scala 문제인것같다.

검색을 해보니, 버전 문제가 있으니 2.11.8 버전으로 시도를 해보란다.


이전 포스팅에서도 설명했듯이, Spark와 Hadoop을 설치할 때도 버전을 챙겨서 확인했어야 했는데, 이 부분도 역시 마찬가지인 것이다.


어쨌든, 그렇게 바꾸고 다시 빌드 후 실행시켜봤다.

드디어 성공!


아....... 드디어, 며칠간의 삽질을 끝냈다.

부디 누군가 이 과정을 보고, 나보다 더 좋은 방법을 찾아내서 적용하거나.

나와 같은 상황이래도 금방 해결할 수 있길 바란다.


나는 이제 다음 단계로 넘어가야겠다.





반응형

댓글