본문 바로가기

Public Cloud/AWS

Elastic Beanstalk - (3) RDS 포함한 환경 구성 및 .extension 폴더 활용

반응형

이전 포스팅에 이어 Spring Boot Application을 Elastic Beanstalk로 Application을 배포할 때 RDS와 연결하고 .extension 폴더를 통해 Application 배포 환경을 구성하는 예를 공유하려고 한다.

 

  1. Jar 파일을 이용한 배포 및 내부 구조 이해
  2. Tomcat + War 파일을 이용한 배포 및  내부 구조 이해
  3. .extension 폴더 및 각종 환경 설정을 더한 Spring Boot Application 배포 (by Jar 파일)

구현 시나리오

이 포스팅에서 구현하려는 구현 시나리오는 다음과 같다.

  1. Application 배포를 위해 생성되는 EC2 Instance에 User Group과 User 추가 (실제로 사용하지는 않는다.)

  2. Application 환경 구성 전에 S3에서 Data File(csv format) Download

  3. Application 초기 구동시 Download한 Data file을 Parsing해서 Data 초기화

.ebextension 폴더를 활용한 환경 구성

AWS Documentation에서는 '.extension' 폴더에 대해 아래와 같이설명한다

You can add AWS Elastic Beanstalk configuration files (.ebextensions) to your web application's source code to configure your environment and customize the AWS resources that it contains. Configuration files are YAML- or JSON-formatted documents with a .config file extension that you place in a folder named .ebextensions and deploy in your application source bundle. (웹 애플리케이션의 소스 코드에 AWS Elastic Beanstalk 구성 파일(.ebextensions)을 추가하여 환경을 구성하고 환경에 있는 AWS 리소스를 사용자 지정할 수 있습니다. 구성 파일은 .config 파일 확장명을 사용하는 YAML이나 JSON 형식 문서로, .ebextensions 폴더에 놓고 애플리케이션 소스 번들로 배포합니다.)

Java 플랫폼을 이용해 Application을 배포하려면 Jar 파일과 .extension folder를 <pic 1>처럼 같은 Path에 위치시킨 후 zip으로 묶어서 zip 파일을 upload한다. 

<pic 1> Java 플랫폼에서의 .ebextensioons 폴더의 위치의 예

 

Tomcat 플랫폼에서 war 파일을 사용할 때에는 <pic 2>와 같이 적당한 위치에 폴더를 생성하고(<pic 2>에서는 'extension' 폴더) 그 아래 config 파일을 작성한 후, build.gradle 파일에 <code 1>과 같이 bootWar task에 해당 Folder를 ./extension Path로 포함시키게 수정하면 된다. (Remark. Spring Boot 2부터는 gradle 실행 시 war task가 skip 되기 때문에 bootwar task에 이 내용을 정의하였다.)

 

<pic 2> Tomcat 플랫폼에서의 config 파일을 포함한 폴더 구조의 예

...
bootWar {
	from('src/main/resources/ebextensions') {
		into('.ebextensions')
	}
}
...
<code 1> 수정된 build.gradle 

 

.ebextension 폴더 아래에 확장자가 '.config'인 환경 설정 파일을 추가하면 되고, 이 포스팅에서는 아래 2개의 시나리오를 적영하였다.(참조문서 (여기서 좀 헷갈리는 사항이 있는데 많은 문서에서 Config file의 이름순으로 실행된다고 언급하는데, 해당 내용을 AWS document에서는 찾을 수 없었고, Linux 서버 환경 구성시에는 정해진 Key 순서에 따라 실행된다고 했다. 이 부분을 정확하게 확인하지 못했다.)

 

시나리오 1. 사용자 그룹 및 사용자 추가

.extension 아래에 <code 2>, <code 3>과 같은 2개의 Config 파일을 추가하였다. 각각의 환경 파일이 정상적으로 작동하게 되면 EC2 Instance에 'browndwarf' User Group이 생성되고, 'browndwarf' User 생성되면서 그 User group에 속하게 된다. 그리고 홈 디렉토리로 /home/browndwarf가 생성되게 된다.

groups:
  browndwarf:
    gid: "10000"
<code 2> 01-group.config

 

users:
  browndwarf:
    groups:
      - browndwarf
    uid: "50"
    homeDir: "/home/browndwarf"
<code 3>02-user.config

 

아래와 같이 jar 파일과 2개의 Config 파일을 포함한 '.ebextensions' 폴더를 zip 파일로 압축한다.

# 디렉토리 구조 및 포함된 파일 확인
[dohoon@browndwarf01 target]$ ls -la
합계 44996
drwxrwxr-x. 3 dohoon dohoon       64  7월  3 17:13 .
drwxrwxr-x. 9 dohoon dohoon     4096  6월 26 13:33 ..
drwxrwxr-x. 2 dohoon dohoon       73  7월  3 17:13 .ebextensions
-rw-rw-r--. 1 dohoon dohoon 46070757  7월  3 17:14 boardservice-0.1-SNAPSHOT.jar

[dohoon@browndwarf01 target]$ find .
.
./.ebextensions
./.ebextensions/01-group.config
./.ebextensions/02-user.config
./boardservice-0.1-SNAPSHOT.jar

# app.zip 생성
[dohoon@browndwarf01 target]$ zip -r app.zip .
  adding: .ebextensions/ (stored 0%)
  adding: .ebextensions/01-group.config (deflated 8%)
  adding: .ebextensions/02-user.config (deflated 43%)
  adding: boardservice-0.1-SNAPSHOT.jar (deflated 10%)
  
# 최종 결과
[dohoon@browndwarf01 target]$ ls -la
합계 85612
drwxrwxr-x. 3 dohoon dohoon       79  7월  3 17:29 .
drwxrwxr-x. 9 dohoon dohoon     4096  6월 26 13:33 ..
drwxrwxr-x. 2 dohoon dohoon       73  7월  3 17:13 .ebextensions
-rw-rw-r--. 1 dohoon dohoon 41589465  7월  3 17:29 app.zip
-rw-rw-r--. 1 dohoon dohoon 46070757  7월  3 17:14 boardservice-0.1-SNAPSHOT.jar

 

위에서 만든 app.zip 파일을 Elastic Beanstalk로 Application 코드로 Upload 하고, Application이 정상적으로 배포된 것을 확인한 후에 Elastic Beanstalk에 의해 생성된 EC2 Instance에 ssh로 접속(이전포스팅 참조)해 결과를 확인해보면, 아래와 같이 User가 생성되지 않은 것으로 확인된다. 

# EC2 Instance에 SSH 접속
[dohoon@browndwarf01 Downloads]$ ssh -i ./browndwarf-eb-key.pem ec2-user@54.180.3.243
The authenticity of host '54.180.3.243 (54.180.3.243)' can't be established.
ECDSA key fingerprint is SHA256:41sm7a5x0ahAxZXvnmORtQrUIv8Jwe5MnvmcgM1UBbA.
ECDSA key fingerprint is MD5:1c:19:5b:b3:d3:aa:71:60:8a:fe:b2:1e:91:1c:9d:b8.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '54.180.3.243' (ECDSA) to the list of known hosts.
 _____ _           _   _      ____                       _        _ _
| ____| | __ _ ___| |_(_) ___| __ )  ___  __ _ _ __  ___| |_ __ _| | | __
|  _| | |/ _` / __| __| |/ __|  _ \ / _ \/ _` | '_ \/ __| __/ _` | | |/ /
| |___| | (_| \__ \ |_| | (__| |_) |  __/ (_| | | | \__ \ || (_| | |   <
|_____|_|\__,_|___/\__|_|\___|____/ \___|\__,_|_| |_|___/\__\__,_|_|_|\_\
                                       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-50-103 ~]$ ls /var/log/
amazon            cfn-init.log    cloud-init-output.log  eb-activity.log       eb-commandprocessor.log  maillog   rotated  wtmp
boot.log          cfn-wire.log    cron                   eb-cfn-init-call.log  healthd                  messages  secure   xray
cfn-init-cmd.log  cloud-init.log  dmesg                  eb-cfn-init.log       lastlog                  nginx     spooler  yum.log

# Application Log 확인
[ec2-user@ip-172-31-50-103 ~]$ cat /var/log/eb-activity.log 
...
[2020-06-28T04:43:48.982Z] INFO  [3134]  - [Application deployment boardservice-source@2/StartupStage0/EbExtensionPreBuild/Infra-EmbeddedPreBuild] : Starting activity...
[2020-06-28T04:43:48.985Z] INFO  [3134]  - [Application deployment boardservice-source@2/StartupStage0/EbExtensionPreBuild/Infra-EmbeddedPreBuild/prebuild_0_BoardService] : Starting activity...
[2020-06-28T04:43:48.985Z] INFO  [3134]  - [Application deployment boardservice-source@2/StartupStage0/EbExtensionPreBuild/Infra-EmbeddedPreBuild/prebuild_0_BoardService] : Completed activity.
[2020-06-28T04:43:48.989Z] INFO  [3134]  - [Application deployment boardservice-source@2/StartupStage0/EbExtensionPreBuild/Infra-EmbeddedPreBuild/prebuild_1_BoardService] : Starting activity...
[2020-06-28T04:43:48.989Z] INFO  [3134]  - [Application deployment boardservice-source@2/StartupStage0/EbExtensionPreBuild/Infra-EmbeddedPreBuild/prebuild_1_BoardService] : Completed activity.
[2020-06-28T04:43:48.989Z] INFO  [3134]  - [Application deployment boardservice-source@2/StartupStage0/EbExtensionPreBuild/Infra-EmbeddedPreBuild] : Activity execution failed, because: Failed to add user browndwarf (ElasticBeanstalk::ExternalInvocationError)


[2020-06-28T04:43:48.989Z] INFO  [3134]  - [Application deployment boardservice-source@2/StartupStage0/EbExtensionPreBuild/Infra-EmbeddedPreBuild] : Activity failed.
[2020-06-28T04:43:49.012Z] INFO  [3134]  - [Application deployment boardservice-source@2/StartupStage0/EbExtensionPreBuild] : Activity failed.
[2020-06-28T04:43:49.012Z] INFO  [3134]  - [Application deployment boardservice-source@2/StartupStage0] : Activity failed.
[2020-06-28T04:43:49.012Z] INFO  [3134]  - [Application deployment boardservice-source@2] : Completed activity. Result:
  Application deployment - Command CMD-SelfStartup failed

그렇지만 User Group은 정상적으로 잘 생성되었다.

[ec2-user@ip-172-31-50-103 ~]$ sudo cat /etc/group
root:x:0:
...
browndwarf:x:10000:

이 문제를 해결하기 위해 AWS Documnet를 확인해 보고 여러 방법을 찾아봤으나, 동일 사례에 대한 질문은 있지만 명확한 답은 못 찾았다. 대신 <code 4>와 같이 직접 Linux Shell Command를 수행하는 방법이 대안으로 공유되고 있었다. (혹시 아시는 분은 댓글로 알려주시면 감사하겠습니다.)

#users:
#  browndwarf:
#    groups:
#      - browndwarf
#    uid: "50"
#    homeDir: "/home/browndwarf"
commands:
  command 02_add_user:
    command: useradd -g browndwarf -d /home/browndwarf -u 10010 browndwarf 
    ignoreErrors: true
<code 4> 수정된 02-user.config

 

시나리오 2 : S3에서 File 가져오기 

S3에서 File을 가져오는 Code는 AWS Document에 있는 내용을 그대로 복사해서 사용했다. (<code 5> 참조)

Resources:
  AWSEBAutoScalingGroup:
    Metadata:
      AWS::CloudFormation::Authentication:
        S3Auth:
          type: "s3"
          buckets: ["browndwarfbucket"]
          roleName: 
            "Fn::GetOptionSetting":
              Namespace: "aws:asg:launchconfiguration"
              OptionName: "IamInstanceProfile" 
              DefaultValue: "aws-elasticbeanstalk-ec2-role"
files:
  "/var/tmp/initdata.csv" :
    mode: "000755"
    owner: browndwarf
    group: browndwarf
    authentication: "S3Auth"
    source: https://browndwarfbucket.s3.ap-northeast-2.amazonaws.com/initdata.csv
<code 5> 03-file.config

 

위에서 Download하려는 'initdata.csv' 파일은 아래와 같다.

1,John,23,Male
2,Jane,22,Female

 

<code 5>의 내용을 해석하면 files.source에 S3에 위치한 File을 명시하고, 이를 /var/tmp/initdata.csv 라는 이름으로 Download하게 되는데, 이 때 'S3Auth'라고 정의한 권한 정보로 S3에 Access하게 된다.

 

Elastik Beanstalk는 상위 개념으로 Template 기반의 배포 Tool인 CloudFormation이 사용되는데, 'S3Auth'라는 권한을 설정하는 방식이 CloudFormation에서 정의한 Authentification Template 양식이다. 아직 이 부분을 자세히 알지는 못하지만 내용상으로는 S3의 'browndwarfbucket'이라는 Bucket에서 Object를 Access할 수 있는 권한을 'aws-elasticbeanstalk-ec2-role'에 추가하는 것으로 유추된다.

 

최종 결과

위의 <code 3>, <code 4>, <code 5>에서 명시한 config 파일을 모두 포함해서 zip 파일을 만든 후에 배포 code로 올린 최종 결과는 아래와 같다. User Group과 User 모두 정상적으로 만들어 졌고, initdata.csv 파일도 /var/tmp에 S3에서 Download 되어 있다.

# EC2 Instance에 SSH 접속
[dohoon@browndwarf01 Downloads]$ ssh -i ./browndwarf-eb-key.pem ec2-user@52.78.125.156
The authenticity of host '52.78.125.156 (52.78.125.156)' can't be established.
ECDSA key fingerprint is SHA256:dn/80fLInOIYLqQMzN8sONPrj8meH8xR8hbvJsEwG8g.
ECDSA key fingerprint is MD5:9a:a0:96:aa:b5:c1:0d:00:58:eb:4a:51:7d:92:93:d0.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '52.78.125.156' (ECDSA) to the list of known hosts.
 _____ _           _   _      ____                       _        _ _
| ____| | __ _ ___| |_(_) ___| __ )  ___  __ _ _ __  ___| |_ __ _| | | __
|  _| | |/ _` / __| __| |/ __|  _ \ / _ \/ _` | '_ \/ __| __/ _` | | |/ /
| |___| | (_| \__ \ |_| | (__| |_) |  __/ (_| | | | \__ \ || (_| | |   <
|_____|_|\__,_|___/\__|_|\___|____/ \___|\__,_|_| |_|___/\__\__,_|_|_|\_\
                                       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

# 추가된 Group과 User 확인
[ec2-user@ip-172-31-1-201 ~]$ cat /etc/group
root:x:0:
...
browndwarf:x:10000:

[ec2-user@ip-172-31-1-201 ~]$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
...
browndwarf:x:10010:10000::/home/browndwarf:/bin/bash

[ec2-user@ip-172-31-1-201 ~]$ ls -la /home
total 24
drwxr-xr-x  6 root       root       4096  6월 22 23:46 .
drwxr-xr-x 25 root       root       4096  6월 22 23:45 ..
drwx------  2 browndwarf browndwarf 4096  6월 22 23:46 browndwarf
drwx------  3 ec2-user   ec2-user   4096  6월 22 23:45 ec2-user
drwx------  2 healthd    healthd    4096  6월 22 23:45 healthd
drwx------  2 webapp     webapp     4096  6월  5 11:36 webapp

# Download된 File 확인
[ec2-user@ip-172-31-1-201 ~]$ ls -la /var/tmp
total 12
drwxrwxrwt  2 root       root       4096  6월 22 23:46 .
drwxr-xr-x 21 root       root       4096  6월 22 23:46 ..
-rwxr-xr-x  1 browndwarf browndwarf   32  6월 22 23:46 initdata.csv

RDS 연결

이번에는 AWS RDS를 연결해보자. Elastic Beanstalk를 통해 Application을 구성할 때 RDS를 포함시킬 수 있는데 이 때 만들어진 RDS에서 5개의 Property가 정의된다. (참고

  • RDS_HOSTNAME : RDS Instance의 Endpoint URL

  • RDS_PORT : RDS Instance의 기본 Port (ex. Postgresql의 경우 5432)

  • RDS_DB_NAME : RDS Instance 생성 시 만들어지는 Database Instance

  • RDS_USERNAME : RDS Instance의 User Account

  • RDS_PASSWORD : 생성된 User의 Password

application-eb.yml 

기존 Application이 Elastic Beanstalk에 의해 만들어지는 RDS Instance를 이용하기 위해 'eb'라는 이름의 application profile을 추가했다. application.yml 파일(or application.properties 파일)에서는 위에서 생성된 Property 이름들 중에서 대문자는 소문자로, '_'(언더스코어)는 '.'(Dot)로 변경해서 사용하면 된다. 이렇게 RDS 접속 정보를 추가한 application-eb.yml 파일은 <code 6>과 같다. (Remark. RDS Instance는 PostgreSQL로 사용하기 때문에 data-platform과 'database' 값은 여기에 맞게 설정했다) 

# Database
spring:
  profiles: eb
  datasource:
    url: jdbc:postgresql://${rds.hostname}:${rds.port}/${rds.db.name}
    username: ${rds.username}
    password: ${rds.password}
    hikari:
      connection-timeout: 30000
      maximum-pool-size: 10
#JPA
  jpa:
    database-platform: org.hibernate.dialect.PostgreSQL95Dialect
    database: POSTGRESQL
    show-sql: true
    generate-ddl: true
    hibernate:
      ddl-auto: create-drop
    properties:
      hibernate:
        #format_sql: true        
        jdbc:
          lob:
            non_contextual: true

server:
  port: 5000

root:
  path: /var/tmp
<code 6> RDS Instance를 사용하는 application-eb.yml

 

그리고 Application이 시작할 때 S3에서 가져온 CSV format의 파일을 Parsing해서 Writer를 DB에 등록하는 기능을 추가하기 위해 <code 7~10>을 추가하였다.

 

새로 추가하는 Writer Entity는 <code 7>과 같이 정의했고, 이 Entity를 저장하기 위한 Repository interface는 <Code 8> 과 같다.

@Getter @Setter
@Entity
@NoArgsConstructor
public class Writer {

	@Id
	private long	id;
	
	String name;
	
	int	age;
	
	String sex;
	
	@UpdateTimestamp
	@Column(nullable=false)
	private	Timestamp	updateTime;
	
	@CreationTimestamp
	@Column(nullable=false, updatable=false)
	private	Timestamp	createdTime;
	
	@Builder
	public	Writer(long id, String name, int age, String sex) {
		this.id = id;
		this.name = name;
		this.age = age;
		this.sex = sex;
	}
	
}
<code 7> Writer entity 
public interface WriterRepository extends JpaRepository<Writer, Long> {}
<code 8> Writer Repository

 

Business Layer에서는 입력값으로 받은 WriterParam을 Writer Entiry로 변환해 저장하는 <Code 9>를 추가하였다. (WriteParam은 <Code 10> 참고)

@Service
public class WriterService {

	@Autowired
	WriterRepository	writerRepository;
	
	public long	registerWriter(WriterParam param) {
		
		Writer writer = Writer.builder()
							.name(param.getName())
							.sex(param.getSex())
							.age(param.getAge())
							.build();
		
		writer = writerRepository.saveAndFlush(writer);
		
		return writer.getId();
	}
	
}
<code 9> Business Layer(Service Layer)
@Getter @Setter
@NoArgsConstructor
public class WriterParam {
	
	long id;

	String name;
	
	int	age;
	
	String sex;
	
}
<code 10> WrierParam

 

그리고 <code 11>과 같이 Application 초기화가 끝나면 ApplicationReadyEvent를 받아 csv 파일을 Parsing해 DB에 저장하는 EventHandler Code를 추가하였다

@Slf4j
@Component
public class StartUpListener implements ApplicationListener<ApplicationReadyEvent> {

	@Autowired 	private WriterRepository writerRepository;

	@Value("${root.path:'/'}")
	String	rootPath;
	
	@Override
	public void onApplicationEvent(ApplicationReadyEvent event) {

		try {
		    CsvSchema csvSchema = CsvSchema.builder().addColumn("id").addColumn("name").addColumn("age").addColumn("sex").build();
		    CsvMapper mapper = new CsvMapper();
		    File file = new File(new StringBuilder(rootPath).append("/initdata.csv").toString());
		    MappingIterator<WriterParam> readValues = mapper.readerFor(WriterParam.class).with(csvSchema).readValues(file);
		    
		    List<WriterParam> writerList = readValues.readAll();
		    writerList.forEach(writerParam->{
				Writer writer = Writer.builder()
						.id(writerParam.getId())
						.name(writerParam.getName())
						.sex(writerParam.getSex())
						.age(writerParam.getAge())
						.build();		   
				
				writerRepository.saveAndFlush(writer);
				
				log.info(">>> Register wrtier : {}", writerParam.getName());
		    });
		    
		    log.info("Complete to register writers : {} writers", writerList.size());
		} catch (Exception e) {
			e.printStackTrace();
		    log.error("Fail to initialize. ({}) ", e.getMessage());
		}
		
	}

}
<code 11> Application 시작 시 CSV file을 Parsing해 Writer로 등록하는 Event Handler Code

 

배포

이전 2개의 포스팅에서 추가 구성 없이 Application을 생성한 것과 달리, 이 포스팅에서는 생성 시에 추가 구성을 했다. 추가 구성 화면에는 소프트웨어, 인스턴스, 용량 등 12개의 항목을 추가적으로 설정(총 2020년 6월 기준)할 수 있는데 이 포스팅에서는 소프트웨어, 보안, 네트워크, 데이터베이스 등 4가지 항목을 추자적으로 구성했다.

 

Step 1. 소프트웨어

 

앞에서 추가한 'eb' Profile을 사용하기 위해 <pic 3>과 같이 '환경 속성'에서 spring.profiles.active 값을 설정했다. 이전 포스팅에서도 언급했듯이, Java Platform에서 Spring Property를 사용하려면 소문자를 대문자로 변경하고, '.'(Dot)를 '_'(언더스코어)로 변경해야한다. 그래서 spring.profiles.active는 SPRING_PROFILES_ACTIVE로 값을 정의한다. (<pic 3> 참고)

<pic 3> 소프트웨어 항목의 환경 속성 설정

 

Step 2. 보안 

 

보안 항목에서는 ec2 instance에 ssh로 접속할 수 있게 키페어를 설정했다. (<pic 4> 참조)

<pic 4> 보안 설정 

 

Step 3. 데이터베이스 설정

 

Application과 연동하는 RDS Instance를 아래와 같이 설정했다. (<pic 5> 참조)

  • Database Engine : postgres
  • Database Engine Version : 11.6
  • 인스턴스 클래스 : db.t2.micro
  • Storage(저장공간) : 5G
  • 보전 (Retention) : 삭제
  • 가용성(Availability) : 낮음 (Single AZ에 보관)

혹시 발생할 비용 문제로 인해 최소한의 Spec으로 설정했고, 그런 이유로 Snapshot 생성과 Multi AZ 설정을 하지 않았다.

<pic 5> RDS 설정

 

Step 4 네트워크 설정

 

EC2 Instance와 RDS Instance가 생성될 Network을 설정하는 부분이다. Single EC2 Instance으로 Application을 배포할 때에는 네트워크 설정이 필요 없었는데, RDS Instance로 인해 네트워트 설정이 필수 이다. 이미 만들어 두었던 각 AZ에 존재하는 Subnet 중 하나를 선택해서 네트웍이 구성될 수 있게 했다. (<pic 6> 참고)

 

<pic 6> 네트워크 설정

 

Application 생성 결과 Review 및 Debug

 

정상적으로 Application 생성이 되었다는 화면을 확인한 후에 EC2 Instance들을 확인해 보았다. 먼저 'EC2' > 'Dashboard'로 가면 <pic 7>과 같이 Elastic Beanstalk를 통해 생성된 EC2 Instance를 확인할 수 있다.

 

<pic 7> 생성된 EC2 Instance

 

그리고 'RDS' > '데이터베이스'로 집입했을 때 아래와 같은 RDS Instance를 확인할 수 있었다. Instance의 이름은 자동으로 만들어지고, Endpoint는 IP Address 대신 자동으로 생성된 Instance 명을 기반으로 URL이 만들어져 있다.

 

<pic 8> 생성된 RDS Instance

 

RDS Instance는 일반적으로 시스템 내부에서만 사용되기 때문에 외부에서 접속할 수 없게 설정되는데, 상단 오른쪽에 '수정' 버튼을 클릭해 RDS Instance 설정을 수정하는 메뉴로 진입하면, 항목 중간에 <pic 9>와 같이 '네트워크 및 보안' Section에서 '퍼블릭 액세스 가능성' 여부를 설정하는 check box가 있다. <pic 9>와 같이 퍼블릭 액세스 가능하게 설정한다.

 

<pic 9> RDS Instance의 Public Access 허용

 

그리고 RDS Instance의 보안 그룹을 수정해야 한다. <pic 8>에서 VPC 보안그룹에 진입하면 <pic 10>과 같은 RDS Instance의 보안 설정으로 진입한다. 화면 하단에서  인바운드 규칙 Tab을 선택하고 '인바운드 규칙 편집' 버튼을 클릭하면 규칙을 설정하는 <pic 11>으로 진입한다.

 

<pic 10> RDS Instance의 보안그룹 

 

인바운드 규칙을 설정하는 <pic 11>과 같은 화면 중 소스 컬럼에  '내 IP'를 선택하면 사용자의 현재 머신이 가지는 Public IP가 자동으로 설정된다. 이 인바운드 규칙을 추가한 후 '규칙 저장' 버튼을 클릭하면 해당 머신에서는 DB Client를 통해 RDS Instance로 접근이 가능해진다. 

 

<pic 11> RDS Instance 보안그룹 설정에서 인바운드 규칙 수정

결과

위와 같이 추가 구성을 끝내고 Application 생성이 완료됐다는 화면을 확인한 후, 최초 요구사항이 알맞게 적용됐는 지 확인해 보았다.

 

Log

Log 파일을 확인해보면 CSV File에서 가져온 2명의 Writer 정보가 DB에 저장되는 것으로 보인다.

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.6.RELEASE)

2020-06-23 22:40:54.950  INFO 3271 --- [           main] c.b.b.BoardserviceApplication            : Starting BoardserviceApplication on ip-172-31-39-86 with PID 3271 (/var/app/current/application.jar started by webapp in /var/app/current)
2020-06-23 22:40:54.964  INFO 3271 --- [           main] c.b.b.BoardserviceApplication            : The following profiles are active: eb
2020-06-23 22:40:57.329  INFO 3271 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2020-06-23 22:40:57.560  INFO 3271 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 204ms. Found 2 JPA repository interfaces.
2020-06-23 22:40:59.431  INFO 3271 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 5000 (http)
2020-06-23 22:40:59.471  INFO 3271 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-06-23 22:40:59.471  INFO 3271 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.33]
2020-06-23 22:40:59.642  INFO 3271 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-06-23 22:40:59.647  INFO 3271 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 4530 ms
2020-06-23 22:41:00.494  INFO 3271 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2020-06-23 22:41:00.775  INFO 3271 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 5.4.12.Final
2020-06-23 22:41:01.109  INFO 3271 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
2020-06-23 22:41:01.288  INFO 3271 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2020-06-23 22:41:01.944  INFO 3271 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2020-06-23 22:41:02.020  INFO 3271 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.PostgreSQL95Dialect
Hibernate: drop table if exists post cascade
2020-06-23 22:41:04.255  WARN 3271 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Warning Code: 0, SQLState: 00000
2020-06-23 22:41:04.262  WARN 3271 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : table "post" does not exist, skipping
Hibernate: drop table if exists writer cascade
2020-06-23 22:41:04.267  WARN 3271 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Warning Code: 0, SQLState: 00000
2020-06-23 22:41:04.267  WARN 3271 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : table "writer" does not exist, skipping
Hibernate: drop sequence if exists hibernate_sequence
2020-06-23 22:41:04.268  WARN 3271 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Warning Code: 0, SQLState: 00000
2020-06-23 22:41:04.269  WARN 3271 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : sequence "hibernate_sequence" does not exist, skipping
Hibernate: create sequence hibernate_sequence start 1 increment 1
Hibernate: create table post (id int8 not null, contents varchar(255), created_time timestamp not null, title varchar(255), update_time timestamp not null, writer varchar(255), primary key (id))
Hibernate: create table writer (id int8 not null, age int4 not null, created_time timestamp not null, name varchar(255), sex varchar(255), update_time timestamp not null, primary key (id))
2020-06-23 22:41:04.308  INFO 3271 --- [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2020-06-23 22:41:04.319  INFO 3271 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2020-06-23 22:41:05.596  WARN 3271 --- [           main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2020-06-23 22:41:06.069  INFO 3271 --- [           main] pertySourcedRequestMappingHandlerMapping : Mapped URL path [/v2/api-docs] onto method [springfox.documentation.swagger2.web.Swagger2Controller#getDocumentation(String, HttpServletRequest)]
2020-06-23 22:41:06.351  INFO 3271 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-06-23 22:41:06.900  INFO 3271 --- [           main] d.s.w.p.DocumentationPluginsBootstrapper : Context refreshed
2020-06-23 22:41:06.954  INFO 3271 --- [           main] d.s.w.p.DocumentationPluginsBootstrapper : Found 1 custom documentation plugin(s)
2020-06-23 22:41:07.057  INFO 3271 --- [           main] s.d.s.w.s.ApiListingReferenceScanner     : Scanning for api listing references
2020-06-23 22:41:07.492  INFO 3271 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 5000 (http) with context path ''
2020-06-23 22:41:07.499  INFO 3271 --- [           main] c.b.b.BoardserviceApplication            : Started BoardserviceApplication in 14.305 seconds (JVM running for 16.283)
Hibernate: select writer0_.id as id1_1_0_, writer0_.age as age2_1_0_, writer0_.created_time as created_3_1_0_, writer0_.name as name4_1_0_, writer0_.sex as sex5_1_0_, writer0_.update_time as update_t6_1_0_ from writer writer0_ where writer0_.id=?
Hibernate: insert into writer (age, created_time, name, sex, update_time, id) values (?, ?, ?, ?, ?, ?)
2020-06-23 22:41:07.776  INFO 3271 --- [           main] c.b.b.eventlistener.StartUpListener      : >>> Register wrtier : John
Hibernate: select writer0_.id as id1_1_0_, writer0_.age as age2_1_0_, writer0_.created_time as created_3_1_0_, writer0_.name as name4_1_0_, writer0_.sex as sex5_1_0_, writer0_.update_time as update_t6_1_0_ from writer writer0_ where writer0_.id=?
Hibernate: insert into writer (age, created_time, name, sex, update_time, id) values (?, ?, ?, ?, ?, ?)
2020-06-23 22:41:07.786  INFO 3271 --- [           main] c.b.b.eventlistener.StartUpListener      : >>> Register wrtier : Jane
2020-06-23 22:41:07.786  INFO 3271 --- [           main] c.b.b.eventlistener.StartUpListener      : Complete to register writers : 2 writers
2020-06-23 22:43:23.221  INFO 3271 --- [nio-5000-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-06-23 22:43:23.221  INFO 3271 --- [nio-5000-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2020-06-23 22:43:23.251  INFO 3271 --- [nio-5000-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 29 ms

 

DB

Database에서 더 정확하게 확인하기 위해 psql로 위에서 확인했던Endpoind Url로 연결했다

psql -h aa5qyuu8bhfdph.cnyrgflh0tpc.ap-northeast-2.rds.amazonaws.com -U browndwarf -d postgres
browndwarf 사용자의 암호: *****
psql(11.7, 11.6 서버)
SSL 연결정보 (프로토콜: TLSv1.2, 암호화기법: ECDHE-RSA-AES256-GCM-SHA384, 비트: 256, 압축: off)
도움말을 보려면 "help"를 입력하십시오.

postgres=>

Database List를 조회하면 'ebdb'라는 이름을 가진 Elastic Beanstalk에 의해 만들어진 것으로 추정되는 Database Instance를 확인할 수 있고, 내부에 포함된 Table들을 조회하면, Entity로 정의했던 'Post' table과 'Writer' table이 조회된다.

# 현재 Database List 확인
postgres=> \l
                                    데이터베이스 목록
   이름    |   소유주   | 인코딩 |   Collate   |    Ctype    |        액세스 권한        
-----------+------------+--------+-------------+-------------+---------------------------
 ebdb      | browndwarf | UTF8   | en_US.UTF-8 | en_US.UTF-8 | 
 postgres  | browndwarf | UTF8   | en_US.UTF-8 | en_US.UTF-8 | 
 rdsadmin  | rdsadmin   | UTF8   | en_US.UTF-8 | en_US.UTF-8 | rdsadmin=CTc/rdsadmin
 template0 | rdsadmin   | UTF8   | en_US.UTF-8 | en_US.UTF-8 | =c/rdsadmin              +
           |            |        |             |             | rdsadmin=CTc/rdsadmin
 template1 | browndwarf | UTF8   | en_US.UTF-8 | en_US.UTF-8 | =c/browndwarf            +
           |            |        |             |             | browndwarf=CTc/browndwarf
(5개 행)

# 'dbdb' Database로 접근  
postgres=> \c ebdb browndwarf
psql(11.7, 11.6 서버)
SSL 연결정보 (프로토콜: TLSv1.2, 암호화기법: ECDHE-RSA-AES256-GCM-SHA384, 비트: 256, 압축: off)
접속정보: 데이터베이스="ebdb", 사용자="browndwarf".

# Table 조회
ebdb=> \dt
        릴레이션(relation) 목록
 스키마 |  이름  |  종류  |   소유주   
--------+--------+--------+------------
 public | post   | 테이블 | browndwarf
 public | writer | 테이블 | browndwarf
(2개 행)

그리고 initdata.csv 에 있던 2 명의 Writer들도 Application 초기화 과정중에 DB에 등록되었다는 점을 확인할 수 있다.

ebdb=> SELECT * FROM writer;
 id | age |      created_time       | name |  sex   |       update_time       
----+-----+-------------------------+------+--------+-------------------------
  1 |  23 | 2020-06-23 22:41:07.756 | John | Male   | 2020-06-23 22:41:07.756
  2 |  22 | 2020-06-23 22:41:07.784 | Jane | Female | 2020-06-23 22:41:07.784
(2개 행)

 

API 동작 확인

Controller에 포함된 API를 통해 입력하는 게시물 Data도 DB에 정상적으로 입력되는 지 curl command를 통해 확인해 보았다. 아래와 같이 DB에 정상적으로 값이 들어가는 것을 알 수 있다.

# 'curl' command로 게시물 등록
[dohoon@browndwarf01 ~]$ curl -X POST 'http://boardservice-env.eba-7m8phywp.ap-northeast-2.elasticbeanstalk.com/board?writer=Jane' \
> -H 'accept: text/plain' 
> -H 'Content-Type: application/json' 
> -d '{ "contents": "Hi~~ Everyboday!", "title": "Welcome"}'
SUCCESS TO REGISER NEW POST

...
# 등록된 게시물 Data 조회
ebdb=> SELECT * FROM post;
 id |     contents     |      created_time       |  title  |       update_time       | writer 
----+------------------+-------------------------+---------+-------------------------+--------
  2 | Hi~~ Everyboday! | 2020-06-23 22:58:00.662 | Welcome | 2020-06-23 22:58:00.662 | Jane
(1개 행)

 

Reference