Elastic Beanstalk - (2) War 파일 + Tomcat을 통한 Web Application Deploy
이전 포스팅에 이어 Spring Boot Application을 War Packge로 배포하는 과정을 정리해 보았다.
- Jar 파일을 이용한 배포 및 내부 구조 이해
- Tomcat + War 파일을 이용한 배포 및 내부 구조 이해
- .extension 폴더 및 각종 환경 설정을 더한 Spring Boot Application 배포 (by Jar 파일)
Application 수정
Spring Boot Application을 War Package로 생성하기 위해, 이전에 작성했던 Code중에서 일부를 아래와 같이 수정하였다.
gradle.build 수정
- 'war' plugin 추가
- Version 변경 : 0.1-SNAPSHOT ==> 0.2-SNAPSHOT
- Dependency 추가
- spring-boot-starter-tomcat : 실행시 Tomcat 적용 (jar build시에는 Dependency 설정없이 자동으로 포함되게 된다.)
- spring-boot-starter-thymeleaf(Optional) : Root page의 thymeleaf template 사용을 위해 추가
plugins {
...
id 'war'
}
group = 'com.browndwarf'
version = '0.2-SNAPSHOT'
sourceCompatibility = '1.8'
...
dependencies {
...
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
...
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
}
SpringBootServevletInitializer 구현체 추가
SpringBootServeletInitializer를 상속받아 main 함수가 포함된 Class를 source로 지정해야 한다.
...
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(BoardserviceApplication.class);
}
}
Default Controller & index.html
그리고 root page 활성화를 위해 index.html과 contoller를 추가하였다.
...
@Controller
@RequestMapping("/")
public class DefaultController {
@GetMapping
public String checkHeartBeat(Model model){
String hostName;
String memorySize;
try {
hostName = InetAddress.getLocalHost().getHostName();
}catch(Exception e) {
hostName = "FAIL TO GET HOSTNAME";
}
memorySize = String.format("%d mb", (Runtime.getRuntime().totalMemory() / (1024 * 1024)));
model.addAttribute("hostName", hostName);
model.addAttribute("memory", memorySize);
return "index";
}
}
...
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Hello. Browndwarf World.</h1>
<p th:text="'Host Name : ' + ${hostName}"/>
<p th:text="'Memory : ' + ${memory}"/>
</body>
</html>
Application 생성
Application 생성 과정은 이전 Posting에서와 유사하지만, 환경 설정 시 <pic 1>과 같이 플랫폼을 Tomcat으로 설정 해야한다. Tomcat 플랫폼에는 몇 개의 브랜치가 있는데 이 포스팅에서는 Java 8을 기반으로 하는 브랜치를 선택했다.
그리고 build 후에 만들어진 version 0.2의 war file을 upload 했다.
정상적으로 launching이 되면 <pic 3>와 같은 결과 화면이 나오지만, 접속을 해도 <pic 4>와 같이 Root Page가 정상적으로 연결되지 않았다.
Debug
Launching한 Application의 상태를 확인하기 위해 Log를 확인하기로 했다. Elastic Beanstalk > 환경 > {생성된 환경} > 로그로 진입하면 <pic 5>와 같은 화면이 나온다. 여기에서 우측 상단의 '로그 요청'버튼을 클릭한다. 마지막 100줄이나 전체 Log를 가져올 수 있는데, 이 예에서는 마지막 100줄만을 가져왔다.
로그가 정상적으로 가져오게 되면 아래 List에 로그 조회 이력이 추가되고, 제일 왼쪽 칼럼에 '다운로드'를 클릭하면 Log를 Download해서 확인할 수 있다. 아래는 Download 받은 Log에서 catalina log 부분이며, 내용을 확인해보면 Application 초기화 작업 중에 h2 databse Data File을 생성하는 과정에서 Permission denied 오류가 발생해 Application 구동에 실패했다고 유추할 수 있다.
-------------------------------------
/var/log/tomcat8/catalina.2020-06-08.log
-------------------------------------
...
Caused by: java.io.FileNotFoundException: /usr/share/tomcat8/browndwarfboard.mv.db (Permission denied)
at java.io.RandomAccessFile.open0(Native Method)
at java.io.RandomAccessFile.open(RandomAccessFile.java:316)
at java.io.RandomAccessFile.<init>(RandomAccessFile.java:243)
at java.io.RandomAccessFile.<init>(RandomAccessFile.java:124)
at org.h2.store.fs.FileNio.<init>(FilePathNio.java:43)
at org.h2.store.fs.FilePathNio.open(FilePathNio.java:23)
at org.h2.mvstore.FileStore.open(FileStore.java:153)
... 76 more
일단 <code 5>와 같이 application.yml에 'alternative' profile을 추가해 H2 Database를 memory DB로 사용하게 설정을 수정했다.
# Database
spring:
profiles: default
datasource:
hikari:
jdbc-url: jdbc:h2:~/browndwarfboard
username: sa
password:
jpa:
database-platform: org.hibernate.dialect.H2Dialect
database: H2
generate-ddl: true
show-sql: true
hibernate:
ddl-auto: create-drop
h2:
console:
enabled: true
---
spring:
profiles: alternative
datasource:
hikari:
jdbc-url: jdbc:h2:mem:browndwarfboard
username: sa
password:
jpa:
database-platform: org.hibernate.dialect.H2Dialect
database: H2
generate-ddl: true
show-sql: true
hibernate:
ddl-auto: create-drop
h2:
console:
enabled: true
그리고 다시 build한 후 새로 만들어진 war 파일을 적용하기 위해 <pic 6>과 같이 Elastic Beanstalk > {생성한 Application} > 애플리케이션 버젼으로 진입한다.
상단의 '업로드' 버튼을 클릭하면 신규 버전 업로드를 위한<pic 7>과 같은 팝업이 보여진다. 버전 레이블은 각 버전을 구분할 수 있게 명명하면 된다. (이 예제에서는 'boardservice-source-modify'로 수정) 버전 레이블 명명과 war 파일 upload 후에 하단의 '업로드' 버튼을 클릭했다.
정상적으로 버전 업로드 작업이 끝나면 <pic 8>과 같은 결과를 확인할 수 있다. 아직은 신규 버전만 생성한 상태이며, 여기서 새로 추가한 'boardservice-source-modify' 버전을 선택한 후 '작업' > '배포'를 선택하면 <pic 9>와 같이 신규 버전 배포 여부를 결정하는 팝업이 보여진다.
새로 Upload한 버전을 기존에 만들어진 'Boardservice-env' 환경에 배포하게 설정한 후 '배포' 버튼을 클릭하면 배포가 이루어진다.
수 분이 지나면 <pic 10>과 같이 'boardservice-source-modify' 버전이 'Boardservice-env' 환경에 배포 되었고, 동시에 기존 버전은 Deprecated 된 것을 알 수 있다. Deprecated 된 버전을 선택한 후 '작업' > '삭제' 을 선택하면 <pic 11>과 같이 삭제하는 화면으로 진입하게 된다.
배포가 성공적으로 끝났더라도 새로 추가한 Profile을 적용하기 위해 환경 변수를 추가해야 한다. 환경 변수를 추가하기 위해 Elastic Beanstalk > 환경 > {생성된 환경} > 구성을 선택한 후에 오른쪽 패널에서 '소프트웨어' 항목을 편집했다. (<pic 12> 참조)
'소프트웨어' 편집으로 진입한 후 <pic 13>과 같이 하단에 위치한 환경 속성 항목들 중에 spring.profiles.active 환경 변수를 추가한다. (Remark. Java 플랫폼 에서는 환경 변수 명을 대문자로 변경하고, '.'을 '_'로 변경해야 하지만 Tomcat 플랫폼해서는 변경 없이 사용해야 한다.) spring.profiles.active의 값은 위에서 추가한 profile인 'alternative'로 했다.
그리고 화면 최하단에 '설정'버튼을 클릭하면 EC2 Instance가 Replace되는 수 분이 지나고, <pic 3>과 같이 Application Launching됐다는 Message가 나타난다. 그리고 다시 새로 배포한 애플리케이션에 접속해 보면 새로 추가한 Root Page가 <pic 14>과 같이 보여진다.
추가
이제 만들어진 EC2 Instance에 진입(이전 포스팅 참조)해 최초에 발생했던 오류의 원인을 확인해보자. Permission Error가 발생했던 /usr/share/tomcat8 directroy로 접근해서 권한을 확인해 보면 'root' user만 write 권한을 가진 것을 확인할 수 있다.
[ec2-user@ip-172-31-49-11 ~]$ cd /usr/share/tomcat8/
[ec2-user@ip-172-31-49-11 tomcat8]$ ls -la
total 12
drwxr-xr-x 3 root tomcat 4096 6월 5 06:31 .
drwxr-xr-x 86 root root 4096 6월 5 06:31 ..
drwxr-xr-x 2 root root 4096 6월 5 06:31 bin
lrwxrwxrwx 1 root tomcat 12 6월 5 06:31 conf -> /etc/tomcat8
lrwxrwxrwx 1 root tomcat 23 6월 5 06:31 lib -> /usr/share/java/tomcat8
lrwxrwxrwx 1 root tomcat 16 6월 5 06:31 logs -> /var/log/tomcat8
lrwxrwxrwx 1 root tomcat 23 6월 5 06:31 temp -> /var/cache/tomcat8/temp
lrwxrwxrwx 1 root tomcat 24 6월 5 06:31 webapps -> /var/lib/tomcat8/webapps
lrwxrwxrwx 1 root tomcat 23 6월 5 06:31 work -> /var/cache/tomcat8/work
설정 변경을 위해 쓰기 권한이 있는 Directory를 점검해 보았다. 아래와 같이 /var/tmp 디렉토리가 write 권한에 제약이 없는 것을 확인하고 application.yml 파일에서 default Profile의 <code 6>과 같이 수정했다.
[ec2-user@ip-172-31-49-11 tomcat8]$ ls -la /var
...
drwxrwxrwt 2 root root 4096 6월 8 03:15 tmp
...
# Database
spring:
profiles: default
datasource:
hikari:
jdbc-url: jdbc:h2:/var/tmp/browndwarfboard
...
---
...
Debug section과 동일한 과정을 거쳐 새로운 Application을 배포하고 spring.profiles.active에 'default'를 적용하면 <pic 14>와 동일한 화면으로 접속할 수 있다. 그리고 다시 Elastic Beanstalk에 의해 생성된 EC2 instance로 들어가 /var/tmp 디렉토리를 확인해 보면 아래와 같이 H2 Database의 Data File이 생성된 것을 확인할 수 있다.
...
[ec2-user@ip-172-31-4-127 ~]$ sudo ls /var/tmp
browndwarfboard.mv.db
...