본문 바로가기

Public Cloud/AWS

Elastic Beanstalk - (1) Java Jar 파일을 통한 Web Application Deploy

반응형

Spring Boot로 개발한 Application을 AWS의 Elastic Beanstalk으로 배포하는 내용을 아래의 3개의 포스팅에 걸쳐 정리해 보았습니다.

  1. Jar 파일을 이용한 배포 및 내부 구조 이해

  2. Tomcat + War 파일을 이용한 배포 및  내부 구조 이해

  3. RDS 포함한 환경 구성 및 .extension 폴더 활용

Sample Application

여기서 Sample로 사용한 Application은 제가 Demo용으로 사용하는 게시판 Web Application이고, 개발 환경 및 주요 code들은 아래와 같습니다. 

  • Spring Boot 2.2.6

  • Open JDK 1.8 (1.8.0_222)

  • Gradle 5.6.2

  • 주요 Dependency : Swagger / H2 Database (File Write Mode)

build.gradle

Spring Boot Project 생성시 Spring  Web, JPA dependency, H2 databse를 Dependency 항목으로 추가하고 Swaager UI를 위한 spring-fox와 Json parameter 처리를 위한 Jackson library도 추가했습니다.

plugins {
	id 'org.springframework.boot' version '2.2.6.RELEASE'
	id 'io.spring.dependency-management' version '1.0.9.RELEASE'
	id 'java'
}

group = 'com.browndwarf'
version = '0.1-SNAPSHOT'
sourceCompatibility = '1.8'

...

dependencies {

	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-web'

	implementation 'io.springfox:springfox-swagger2:2.9.2'
	implementation 'io.springfox:springfox-swagger-ui:2.9.2'

	implementation 'com.fasterxml.jackson.core:jackson-core:2.10.3'

	compileOnly 'org.projectlombok:lombok'
	
	runtimeOnly 'com.h2database:h2'

	annotationProcessor 'org.projectlombok:lombok'

	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
}

 

application.yml

application.yml에서는 h2 database 설정, jpa 설정, server port에 관련된 설정을 아래와 같이 했습니다.

# 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

#Connection
server:
  port: 5000

설정 사항 중 2가지만 살펴 보자면, 

  1. spring.datasource.hikari.jdbc-url : jdbc:h2:~/browndwarfboard ; DB data를 Memory 보관 대신 File에 보관하는 것으로 설정.(File write 위치 확인 用)

  2. server.port : 5000 ; port를 5000으로 설정. 이는 Elastic Beanstalk를 통해 Java Application을 Web Application으로 배포하는 과정 中 NGINX가 Resverse Proxy 역할로 자동으로 실행되는데, <pic 1>과 같이 NGINX는 80 port로 받은 외부의 Request들을 Java Application의 5000 port로 Forwarding 해주기 때문에 이를 수신하기 위해 5000 port를 통해 사용하는 것으로 설정해야 한다. (참고)

<pic 1> Elastic Beanstalk에서 NGINX를 이용한 Reverse Proxy 구성

Controller

게시판 기능을 위해 제공하는 Rest API들은 아래의 Code로 구성되었다.

...

@RestController
@RequestMapping(value="/board")
public class BoardController {

	...
	
	@ApiOperation(tags={"Board"}, value="Register new post")
	@PostMapping(produces="text/plain")
	@CrossOrigin
	public ResponseEntity<String>	registerPost(@RequestParam String writer,
		@RequestBody PostReqParam reqParam) {
		
		...
	}
	
	@ApiOperation(tags={"Board"}, value="Update the exist post")
	@PutMapping(path="/{id}", produces="text/plain")
	@CrossOrigin
	public ResponseEntity<String>	updatePost(@PathVariable("id") String id,
		@RequestParam String writer,
		@RequestBody PostReqParam reqParam) {
		
		...
	}
	
	@ApiOperation(tags={"Board"}, value="Delete a post")
	@DeleteMapping(path="/{id}", produces="text/plain")
	public ResponseEntity<String>	deletePost(@PathVariable("id") String id,
		...
	}
	
	@ApiOperation(tags={"Board"}, value="Retrieve a post")
	@GetMapping(path="/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
	public ResponseEntity<PostResParam>	retriveSinglePost(@PathVariable("id") String id) {

		...
	}
	
	@ApiOperation(tags={"Board"}, value="Retrieve a post")
	@GetMapping(path="/", produces = MediaType.APPLICATION_JSON_VALUE)
	public ResponseEntity<List<PostResParam>>	retriveAllPost() {

		...
	}
}

배포

Step 1. Application Compile.

제일 먼저 아래와 같이 gradle build를 통해 jar 파일을 생성했다.

[browndwarf@localhost boardservice]$ ./gradlew clean build

> Task :test
..
BUILD SUCCESSFUL in 8s
6 actionable tasks: 5 executed, 1 up-to-date
...

[browndwarf@localhost boardservice]$ ls target/
boardservice-0.1-SNAPSHOT.jar

 

Step 2. Elastic Beanstalk 진입

AWS에 Login한 후 Elastic Beanstalk로 가면 화면 우측에 <pic 2>와 같이 'Create Application'이라는 버튼이 있고, 이 버튼을 Click하면 Web Application 생성하는 화면으로 진입하게 된다. (예전 학습 영상들을 보면 '환경' menu로 진입해서 웹서버 환경 외에 다른 구성도 할 수 있었던 것으로 보이는데 2020년 6월 현재 기준으로는 Web Application 생성으로만 진입이 가능한 것으로 보인다.)

 

<pic 2> Elastic Beanstalk 생성 버튼

 

Step 3. 환경 설정

배포할 Web Application의 실행 환경을 설정하는 단계이다. 아래와 같이 설정하였다. (<pic 3>, <pic 4>, <pic 5>참조)

  • 애플리케이션 이름 : BoardService

  • (Optional) Tag 

    • STAGE : TEST 

  • 플랫폼

    • 플랫폼 : Java

    • 플랫폼 브랜치 : Java 8 running on 64 bit Amazon Linux

    • 플랫폼 버전 : 2.10.8

  • 소스코드 오리진

    • 로컬 파일 업로드 : jar 파일

<pic 3> 웹서버 환경 설정 - 1

 

<pic 4> 웹서버 환경 설정 - 2

 

<pic 5> 웹서버 환경 설정 - 3

그리고 '애플리케이션 생성' 버튼을 클릭('추가 옵션 구성'은 제외)하면 아래와 같이 애플리케이션 구성 Log가 보여진 후 <pic 6>와 같이 애플리케이션 생성 결과를 보여준다.

12: 30오후	Environment health has transitioned from Pending to Ok. Initialization completed 26 seconds ago and took 2 minutes.
12: 30오후	Successfully launched environment: Boardservice-test
12: 30오후	Application available at Boardservice-test.eba-jrrhuepk.ap-northeast-2.elasticbeanstalk.com.
12: 29오후	Added instance [i-0ec3c4a737bfc117a] to your environment.
12: 29오후	Waiting for EC2 instances to launch. This may take a few minutes.
12: 28오후	Created EIP: 15.165.27.63
12: 28오후	Environment health has transitioned to Pending. Initialization in progress (running for 15 seconds). There are no instances.
12: 28오후	Created security group named:awseb-e-fypqpfpqej-stack-AWSEBSecurityGroup-90IJY509CSJ5
12: 28오후	Using elasticbeanstalk-ap-northeast-2-812577045739 as Amazon S3 storage bucket for environment data.
12: 28오후	createEnvironment is starting.

 

<pic 6> Elastic Beanstalk 생성 결과 화면

서비스 확인

사용했던 예제에 Root Page가 설정되지 않아 <pic 6>에서 나온 Endpoint URL로 접속했을 때는 <pic 7>과 같이 404 오류가 발생하게 된다. 

 

<pic 7> Root page 부재로 인한 오류

 

Root page대신 swagger-ui.html로 접속하면<pic 7>와 같이 Elastic Beanstalk에 의해 만들어진 Web Application이 배포되었음을 확인할 수 있다.

<pic 7> 배포한 Application의 Swagger Document 화면

그리고 아래와 같이 curl command을 통해 동작이 정상적으로 이루어지고 있음도 확인할 수 있다.

# 게시물 등록
[browndwarf@browndwarf02 ~]$ curl -X POST 'http://boardservice-env.eba-m2qhkaw4.ap-northeast-2.elasticbeanstalk.com/board?writer=browndwarf' \
> -H 'accept: text/plain' \
> -H 'Content-Type: application/json' \
> -d '{ "contents": "Hello. World!", "title":"WELCOME"}'
SUCCESS TO REGISER NEW POST

# 등록된 게시물 확인
[browndwarf@browndwarf02 ~]$ curl -X GET -H 'accept: application/json'  'http://boardservice-env.eba-m2qhkaw4.ap-northeast-2.elasticbeanstalk.com/board/'
[{"id":1,"title":"WELCOME","writer":"browndwarf","contents":"Hello. World!","updateTime":"2020-06-26 06:46:15","createdTime":"2020-06-26 06:46:15"}]

배포 구성 Deep Dive

위에서 Web Application이 정상적으로 동작하는 것은 확인하였다. 그렇다면 Elastic Beanstalk는 어떤 식으로 환경을 자동으로 구성해 주는 지 확인해 보자. Elastic Beanstalk를 통해 Application배포가 이루어지면, Application을 실행할 VM으로 EC2 Instance를 생성하게 된다. EC2 Dashboard에서 생성된 EC2 Instance를 <pic 8>과 같이 확인할 수 있다. 

<pic 8> Elastic neanstalk가 생성한 EC2 Instance

 

하지만 해당 EC2 Instance로 기존에 만들어진 키페어를 사용해 ssh 접속을 시도하면 아래와 같이 Permission Denied가 발생하면서 접속할 수 없다.

[browndwarf@browndwarf01 Downloads]$ ssh -i ./aws-browndwarf-keypair.pem ec2-user@ec2-52-78-41-104.ap-northeast-2.compute.amazonaws.com
The authenticity of host 'ec2-52-78-41-104.ap-northeast-2.compute.amazonaws.com (52.78.41.104)' can't be established.
ECDSA key fingerprint is SHA256:q0EpVIG/WylmOnG1hAmaBLaeEA41e7KqXqO4wkOM3fE.
ECDSA key fingerprint is MD5:39:33:ef:c3:98:0a:83:4c:ca:d1:18:6f:fd:37:14:46.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'ec2-52-78-41-104.ap-northeast-2.compute.amazonaws.com,52.78.41.104' (ECDSA) to the list of known hosts.
Permission denied (publickey).

 

이는 기존에 만들어진 키페어가 Elastic Beanstalk에 의해 만들어진 EC2 Instance에 적용되지 않아서 생기는 문제이다. 이를 해결하기 위해 EC2 > 키페어로 진입해서 신규 키페어를 생성해 적용해 보았다.(<pic 9 참조) 물론 기존에 존재하는 키페어를 사용해도 된다.

<pic 9> EC2 키페어 설정 화면

 

<pic 9>에서 '키페어 생성' 버튼을 클릭한 후, 신규 키페어로 'browndwarf-eb-key'를 생성했다. 키페어가 정상적으로 생성되면 <pic 10>과 같이 새로 추가된 키페어를 확인할 수 있다.

<pic 10> 새로 생성된 EC2 키페어

 

다시 Elastic Beanstalk 메뉴로 돌아와 <pic 11>와 같이 Elastic Beanstalk > 환경 > {생성된 환경} > 구성으로 진입하면 오른쪽에 표기된 환경 구성 요소 중에 '보안' 항목을 확인할 수 있다. '편집' 버튼을 클릭해서 진입해보자.

<pic 11> Elastic Beanstalk 환경 구성 수정

보안 구성 항목들 중에 <pic 12>과 같이 가상 머신 권한을 설정하는 항목을 확인할 수 있을 것이다. 여기에 위에서 생성한 키페어를 선택한다. 

 

<pic 12> Elastic Beanstalk에서 생성되는 EC2 Instance의 키페어 설정

 

<pic 12>에서 '적용' 버튼을 클릭하면 <pic 13>과 같이 EC2 Instance가 교체된다는 경고 메시지가 나오는데 여기서는 별 상관없기 때문에 '확인' 버튼을 클릭해서 넘어갔다. 기존 EC2 Instance가 삭제되고 새로운 EC2 Instance가 만들어 지기까지 수 분이 소요된 후 <pic 4>와 같이 정상적으로 만들어졌다는 결과가 나왔다.

<pic 12> EC2 Instance가 교체된다는 경고 Message

 

위에서 생성한 'browndwarf-eb-key' 키 페어를 다운 받은 후, 이 키 페어를 이용해서 ssh 연결을 시도하면 아래와 같이 Elastic Beanstalk에 의해 만들어진 EC2 Instance에 접속된 것을 확인할 수 있다. (Remark. 키 파일의 permission 수정 필요 )

...
[browndwarf@browndwarf01 Downloads]$ sudo chmod 400 browndwarf-eb-key.pem 
...
[browndwarf@browndwarf01 Downloads]$ ssh -i ./browndwarf-eb-key.pem ec2-user@ec2-52-78-41-104.ap-northeast-2.compute.amazonaws.com
Last login: Fri Jun  5 04:00:56 2020 from 121.136.189.225
 _____ _           _   _      ____                       _        _ _
| ____| | __ _ ___| |_(_) ___| __ )  ___  __ _ _ __  ___| |_ __ _| | | __
|  _| | |/ _` / __| __| |/ __|  _ \ / _ \/ _` | '_ \/ __| __/ _` | | |/ /
| |___| | (_| \__ \ |_| | (__| |_) |  __/ (_| | | | \__ \ || (_| | |   <
|_____|_|\__,_|___/\__|_|\___|____/ \___|\__,_|_| |_|___/\__\__,_|_|_|\_\
                                       Amazon Linux AMI

This EC2 instance is managed by AWS Elastic Beanstalk. Changes made via SSH 
WILL BE LOST if the instance is replaced by auto-scaling. For more information 
on customizing your Elastic Beanstalk environment, see our documentation here: 
http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html
[ec2-user@ip-172-31-11-28 ~]$

 

아래와 같이 'top' command를 통해 Process를 확인하고, 그 중에서  'java' command로 실행할 항목을 검색해 보았다. Upload한 jar 파일이 'application.jar'라는 이름으로 변경되어 있고, 이를 'java -jar' command로 실행한 것으로 추정할 수 있다.

[ec2-user@ip-172-31-11-28 /]$ top

top - 04:31:09 up 32 min,  1 user,  load average: 0.00, 0.02, 0.00
Tasks:  83 total,   1 running,  58 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.7%us,  0.0%sy,  0.0%ni, 99.0%id,  0.3%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   1009148k total,   757412k used,   251736k free,    44824k buffers
Swap:        0k total,        0k used,        0k free,   338712k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                            
 3142 webapp    20   0 2185m 194m  15m S  0.7 19.7   0:17.88 java  
 ...
 
 
 [ec2-user@ip-172-31-11-28 /]$ ps 3142
  PID TTY      STAT   TIME COMMAND
 3142 ?        Sl     0:17 java -jar application.jar

 

위에서 command를 실행한 user가 'webapp' 이라, 'webapp' user의 home directory를 검사해보니 H2 DB의 파일이 여기에 생성된 것으로 확인됐다. 즉 Application의 Root Path는 '/home/webapp'으로 설정되어 있다.

# 'webapp' User의 Home Directory 조회
[ec2-user@ip-172-31-11-28 /]$ sudo ls -la home/webapp/
total 44
drwx------ 2 webapp webapp  4096  6월  5 03:58 .
drwxr-xr-x 5 root   root    4096  6월  5 03:58 ..
-rw-r--r-- 1 webapp webapp    18  8월 30  2017 .bash_logout
-rw-r--r-- 1 webapp webapp   193  8월 30  2017 .bash_profile
-rw-r--r-- 1 webapp webapp   124  8월 30  2017 .bashrc
-rw-r--r-- 1 webapp webapp 24576  6월  5 04:28 browndwarfboard.mv.db

 

upload한 jar 파일의 위치를 확인해 보니 아래와 같이 '/var/app/Current' Path에 위치해 있다.

[ec2-user@ip-172-31-11-28 /]$ sudo find -name 'application.jar'
./var/app/current/application.jar

Reference