티스토리 뷰
<목차>
작업한 Spring Boot 프로젝트를 AWS EC2에 배포하는 과정을 작성하였다. Django를 배포했을 때보다는 과정이 덜 복잡한 것 같다.
AWS에서 EC2 인스턴스 생성 과정은 이전에 Django를 배포하였을 때와 동일하게 진행하였다.
🍑 EC2 환경 셋팅
git 설치
$ sudo apt-get install -y git
Reading package lists... Done
Building dependency tree
Reading state information... Done
git is already the newest version (1:2.17.1-1ubuntu0.9).
git set to manually installed.
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
EC2에는 기본적으로 git이 설치되어 있다.
Java 설치 (version 11)
$ sudo apt-get install openjdk-11-jdk
🍑 Spring Boot 프로젝트 다운로드
배포용 브랜치(deploy) 생성
deploy 브랜치 프로젝트 clone
$ git clone -b deploy https://github.com/RIANAEH/project-mnm-main-server.git
프로젝트 디렉터리로 이동 후, gradle에 실행 권한 부여
~$ cd project-mnm-main-server/
~/project-mnm-main-server$ sudo chmod 777 ./gradlew
🍑 Spring Boot 프로젝트 Build
build
~/project-mnm-main-server$ ./gradlew build
❌ build error #1 ❌
> Task :compileTestJava FAILED
/home/ubuntu/project-mnm-main-server/src/test/java/com/project/mnm/repository/UserServiceTest.java:26: error: cannot find symbol
userService.joinUser(user);
^
symbol: method joinUser(User)
location: variable userService of type UserService
1 error
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':compileTestJava'.
> Compilation failed; see the compiler error output for details.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 2m 25s
6 actionable tasks: 6 executed
기존에 프로젝트의 변경된 내용이 적용되어 있지 않아 이를 다시 수정 후 pull
~/project-mnm-main-server$ git pull origin deploy
다시 bulid
~/project-mnm-main-server$ ./gradlew build
> Task :compileJava
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
BUILD SUCCESSFUL in 16s
6 actionable tasks: 5 executed, 1 up-to-date
build가 성공하면 build/libs 디렉터리에 빌드한 스프링 프로그램이 mnm-0.0.1-SNAPSHOT.jar 파일로 생성된다.
~/project-mnm-main-server$ cd build/libs/
~/project-mnm-main-server/build/libs$ ls
mnm-0.0.1-SNAPSHOT-plain.jar mnm-0.0.1-SNAPSHOT.jar
해당 파일의 이름은 gradle 파일에서 설정할 수 있다.
// build.gradle
version = '0.0.1-SNAPSHOT'
🍑 Spring Boot 프로젝트 실행
run
~/project-mnm-main-server/build/libs$ java -jar mnm-0.0.1-SNAPSHOT.jar
❌ run error #1 ❌
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-11-23 14:43:24.276 ERROR 20383 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
Action:
Consider the following:
If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).
프로젝트 배포 시 application.properties를 제거하고 배포해야해서 이를 .gitignore에 설정해주고 잊고 있었다.
application.properties 파일 추가 후 다시 build
~/project-mnm-main-server$ ./gradlew build
인바운드 규칙 편집
8080포트와 5050포트를 사용하므로 이를 인바운드 규칙에 추가해주어야한다.
📌 인바운드 규칙 변경 후 vscode에서 ssh 연결 에러가 나서 어렵사리 해결했다.
[트러블슈팅] VSCode SSH 연결 실패 해결 (Setting up SSH Host Initializing VSCode Server) (tistory.com)
다시 run
~/project-mnm-main-server$ cd build/libs/
~/project-mnm-main-server/build/libs$ java -jar mnm-0.0.1-SNAPSHOT.jar
❌ run error #2 ❌
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-11-23 15:32:19.156 ERROR 1503 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'chattingService': Lookup method resolution failed; nested exception is java.lang.IllegalStateException: Failed to introspect Class [com.project.mnm.service.ChattingService] from ClassLoader [org.springframework.boot.loader.LaunchedURLClassLoader@1ed6993a]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:289) ~[spring-beans-5.3.12.jar!/:5.3.12]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1302) ~[spring-beans-5.3.12.jar!/:5.3.12]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1219) ~[spring-beans-5.3.12.jar!/:5.3.12]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[spring-beans-5.3.12.jar!/:5.3.12]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.12.jar!/:5.3.12]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.12.jar!/:5.3.12]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.12.jar!/:5.3.12]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.12.jar!/:5.3.12]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.12.jar!/:5.3.12]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944) ~[spring-beans-5.3.12.jar!/:5.3.12]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.12.jar!/:5.3.12]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.12.jar!/:5.3.12]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.5.6.jar!/:2.5.6]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-2.5.6.jar!/:2.5.6]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434) ~[spring-boot-2.5.6.jar!/:2.5.6]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:338) ~[spring-boot-2.5.6.jar!/:2.5.6]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) ~[spring-boot-2.5.6.jar!/:2.5.6]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1332) ~[spring-boot-2.5.6.jar!/:2.5.6]
at com.project.mnm.MnmApplication.main(MnmApplication.java:10) ~[classes!/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[mnm-0.0.1-SNAPSHOT.jar:na]
at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[mnm-0.0.1-SNAPSHOT.jar:na]
at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[mnm-0.0.1-SNAPSHOT.jar:na]
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88) ~[mnm-0.0.1-SNAPSHOT.jar:na]
Caused by: java.lang.IllegalStateException: Failed to introspect Class [com.project.mnm.service.ChattingService] from ClassLoader [org.springframework.boot.loader.LaunchedURLClassLoader@1ed6993a]
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:481) ~[spring-core-5.3.12.jar!/:5.3.12]
at org.springframework.util.ReflectionUtils.doWithLocalMethods(ReflectionUtils.java:321) ~[spring-core-5.3.12.jar!/:5.3.12]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:267) ~[spring-beans-5.3.12.jar!/:5.3.12]
... 26 common frames omitted
Caused by: java.lang.NoClassDefFoundError: org/json/simple/JSONObject
at java.base/java.lang.Class.getDeclaredMethods0(Native Method) ~[na:na]
at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3166) ~[na:na]
at java.base/java.lang.Class.getDeclaredMethods(Class.java:2309) ~[na:na]
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:463) ~[spring-core-5.3.12.jar!/:5.3.12]
... 28 common frames omitted
Caused by: java.lang.ClassNotFoundException: org.json.simple.JSONObject
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:471) ~[na:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589) ~[na:na]
at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:151) ~[mnm-0.0.1-SNAPSHOT.jar:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522) ~[na:na]
... 32 common frames omitted
org.json.simple.JSONObject library를 읽어오지 못해서 발생하는 에러
보통 libs/ 디렉터리에 관련 라이브러리를 직접 추가하여 해결하는 것 같았다.
ref : 코끼리를 냉장고에 넣는 방법 :: [JAVA] java에서 JSON 데이터 다루기. google의 json-simple 사용 방법 (tistory.com)
하지만 생각해보니 굳이 JSONObject를 사용하지 않아도 Dto를 만들어 처리해주면 될 것 같아서 필요한 형태의 Dto를 새로 추가하여 JSONObject를 사용한 부분을 변경하였다.
프로젝트의 변경된 내용 pull
~/project-mnm-main-server$ git pull origin deploy
다시 build 후 run
❌ run error #3 ❌
http://3.35.26.150:5050/auth/login 요청 시 "java.lang.ClassNotFoundException: org.json.simple.JSONObject" 에러가 서버에 출력되었다.
login 관련 Controller와 Service를 까보니 여기서도 JSONObject를 사용하고 있었다. 이 또한 Dto를 사용하는 방식으로 변경하였다.
프로젝트의 변경된 내용 pull, 다시 build 후 run
다른 api도 요청을 테스트 해보았는데 모두 동작하였다.
🍑 Spring Boot 프로젝트 백그라운드로 실행
프로젝트 서버 백그라운드로 실행
$ nohup java -jar project-mnm-main-server/build/libs/mnm-0.0.1-SNAPSHOT.jar &
실행 종료
ref : 쉽게 설명한 nohup 과 &(백그라운드) 명령어 사용법 (tistory.com)
ps, jobs로 모두 실행중인 프로젝트 프로세스를 찾을 수 없었다. 따라서 일단 인스턴스를 재부팅하는 방식으로 프로젝트 서버를 종료시킨다.
출력에 대한 설정을 해주지 않으면 기본적으로 nohup.out 파일에 서버 로그가 출력된다. (표준 출력과 표준에러가 모두 출력된다.)
표준출력, 표준에러 출력 파일 설정
표준출력(1) : mnm.out, 표준에러(2) : mnm.err
~$ mkdir log
~$ ls
log project-mnm-main-server
$ nohup java -jar project-mnm-main-server/build/libs/mnm-0.0.1-SNAPSHOT.jar 1 > log/mnm.out 2 > log/mnm.err &
확인해보니 mnm.err에 표준출력과 표준에러가 모두 출력되고 mnm.out에는 아무것도 출력되지 않았다. 생각해보니 nohup 명령어를 실행했을 때 다음과 같이 출력되었는데 내가 입력한 설정을 무시한다는 메시지였다.
ubuntu@ip-172-31-47-33:~$ nohup java -jar project-mnm-main-server/build/libs/mnm-0.0.1-SNAPSHOT.jar 1 > ./log/mnm.out 2 > ./log/mnm.err &
[1] 2282
ubuntu@ip-172-31-47-33:~$ nohup: ignoring input and redirecting stderr to stdout
그래서 그냥 한 곳에 모두 출력하는 명령어로 바꾸어 실행하였다.
$ nohup java -jar project-mnm-main-server/build/libs/mnm-0.0.1-SNAPSHOT.jar > mnm.log 2>&1 &
[1] 3060
그리고 위의 출력에서 알 수 있듯이 내가 실행한 프로젝트 서버의 프로세스 ID가 2282임을 확인 할 수 있다. 이를 이용해 프로세스 종료 명령어를 수행해보았더니 프로세스가 잘 종료되었다. 참고하자.
$ kill -9 2282
[1]+ Killed nohup java -jar project-mnm-main-server/build/libs/mnm-0.0.1-SNAPSHOT.jar 1 2 > ./log/mnm.out > ./log/mnm.err
Ref.
[AWS] EC2: 배포하기 (spring boot) : 네이버 블로그 (naver.com)
'프로젝트' 카테고리의 다른 글
[프로젝트/mnm] Django 서버를 올린 EC2의 리전 변경하기 (0) | 2021.11.24 |
---|---|
[프로젝트/mnm] Django 서버 AWS EC2에 배포하기 (0) | 2021.11.22 |
[프로젝트/mnm] Vue - Spring 채팅 구현 (0) | 2021.11.21 |
[프로젝트/mnm] Spring - 매칭 정보 관리 REST API 구현 (2) | 2021.11.17 |
[프로젝트/mnm] Spring - 거주 정보 관리 REST API 구현 (0) | 2021.11.17 |