본문 바로가기

Public Cloud/AWS

AWS 자습 노트 - 5. RDS 를 이용하는 웹어플리케이션 구성

반응형

이번 Posting에서는 "Spring Boot + JPA/Hibernate + Liquibase Project 구성"에서 정리했던 내용을 바탕으로 AWS RDS Service를 이용하는 하는 Spring Boot Project를 구성해본 내용을 정리해 보았다.

 

Project 수정 및 결과

이전 Post에서 만들었던 Project에서 application.yml 파일의 DB Connection URL 부분만 수정하면 된다. 이 부분에 "AWS 활용하기 - 4. RDS Instance 생성 및 확인"에서 만들었던 RDS의 Endpoint를 입력하면 된다.  (아래 참고)

 

application.yml

...
#Database
spring:
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://browndwarfpostgresex.xxxx.ap-northeast-2.rds.amazonaws.com:5432/browndwarf
    username: browndwarf
    password: xxxxxxxxxx
...

위와 같이 수정한 후에 실행하게 되면 Liquibase에 의해 아래와 같이 Database 내에 DB Instance들이 만들어 진것을 확인할 수 있다.

...
// RDS에 만들어진 DB Instance에 접근

[browndwarf@localhost ~]# psql -h browndwarfpostgresex.xxxxxx.ap-northeast-2.rds.amazonaws.com -U browndwarf
Password for user browndwarf: 
psql (11.4, server 9.6.12)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.
...

// Apllication 실행 전

browndwarf=> \dt
                  List of relations
 Schema |         Name          | Type  |   Owner    
--------+-----------------------+-------+------------
(0 rows)

// Application 실행 후
...
browndwarf=> \dt
                  List of relations
 Schema |         Name          | Type  |   Owner    
--------+-----------------------+-------+------------
 public | databasechangelog     | table | browndwarf
 public | databasechangeloglock | table | browndwarf
 public | job                   | table | browndwarf
(3 rows)

browndwarf=> \ds
              List of relations
 Schema |    Name    |   Type   |   Owner    
--------+------------+----------+------------
 public | job_id_seq | sequence | browndwarf
(1 row)

...

 

Rest API 및 연관된 DTO class

이 Application에서는 다음 3개의 Rest API을 제공하고 있다.

  • POST {server url}:{server port}/register ; 해야할 업무를 등록
    • parameter : Body에 JobDTO type 객체 사용. 등록할 업무 정보를 포함하고 있다. 
    • return value : long type의 ID.
  • PUT {server url}:{server port}/update?id={JOB ID} ; 이미 등록된 업무를 수정
    • parameter 
      • JOB ID ; 수정할 Job의 Id value.
      • Body ; JobDTO type 객체. 수정해야할 업무 정보를 포함하고 있다.
    • return value : Job ID (long type)
  • GET {server url}:{server port}/{id} ; 등록된 업무를 조회
    • parameter ; Path variable로 Job의 ID 값을 받는다.
    • return value ; JobDTO type의 Job 정보.

 

JobDTO class

...
@NoArgsConstructor
@setter @getter
public class JobDTO {

	private String 	jobName;
    
    private String	description;
    
    private	String	dueDate;

	private	String	customer;
}
...

 

실행

curl 명령어를 통해 각 API를 호출해 보았다. POST와 PUT command에 대해 정상적으로 long type의 Job ID를 response 해준다는 점과, GET command로 등록된 Job information을 return 해주는 점을 확인할 수 있다.

...
// Job 등록
[browndwarf@localhost ~]# curl -X POST "http://16.8.35.227:8080/register" -H  "accept: */*" -H  "Content-Type: application/json" -d "{  \"customer\": \"Amazon\",  \"description\": \"Develop UI for Amazon\",  \"dueDate\": \"20190613\",  \"jobName\": \"Customize UI\"}"
1
...
// 등록된 Job 수정. dueDate와 description 정보 수정
[browndwarf@localhost ~]# curl -X PUT "http://16.8.35.227:8080/update?id=1" -H  "accept: */*" -H  "Content-Type: application/json" -d "{  \"customer\": \"Amazon\",  \"description\": \"Review UI for Amazon\",  \"dueDate\": \"20190617\",  \"jobName\": \"Customize UI\"}"
1
...
// 등록된 Job 조회
[browndwarf@localhost ~]# curl -X GET "http://16.8.35.227:8080/1" -H  "accept: */*"
{"jobName":"Customize UI","description":"Review UI for Amazon","dueDate":"20190617","customer":"Amazon"}
...

위의 결과가 DB에도 잘 반영되어 있는 지 확인해 보자. 아래와 같이 Job ID로 1을 가지는 Record하나를 확인할 수 있고 dueDate와 description field가 PUT command에 의해 수정된 값으로 입력된 점도 확인할 수 있다.

...
browndwarf=> SELECT * FROM job;
 id |     name     | customer |     description      | jobstatus | duedate  |       createdtime       |       updatedtime       
----+--------------+----------+----------------------+-----------+----------+-------------------------+-------------------------
  1 | Customize UI | Amazon   | Review UI for Amazon |           | 20190617 | 2019-06-08 14:15:11.065 | 2019-06-08 14:28:04.389
(1 row)

browndwarf=> 
...

Test를 위해 여기까지는 Application은 On-Promise 환경에 있고 DB는 AWS Cloud 상에서 동작하는 Hybrid 구조로 실행했다. 하지만, 실제 WEB Service를 Cloud에서 구현하게 된다면 보안상의 이유로 외부에서 바로 DB로 접속하는 경로를 막고, Application을 AWS Cloud상에서 동작하게 한 후에 이 Application에서만 DB를 접속할 수 있게 변경해야 한다. 즉, 위의 예제 Project를 AWS EC2에서 실행하고 이 EC2에서만 DB를 접속할 수 있게 해야 한다.

 

EC2 Instance에서 RDS 접속 확인

과거 EC2 Instance를 생성했을 때와 유사한 절차에 따라 EC2 Instance를 만들었다. 다만, AMI를 선택할 때 Repository 설정이 잘되어 있다는 Comment에 끌려서 AWS Linux AMI를 선택했다.

Pic 1. AMI 선택 화면

EC2 Instance를 Launching할 때, 아래와 같이 보안그룹 구성시 8080 port만 public으로 Open해야 한다는 점만 주의하면 된다. (예제 Application이 8080 port를 사용하기 때문)

Pic 2. 보안 그룹 구성 화면

Instance가 만들어 진 후 SSH로 접속해서 yum update를 실행하고, RDS에 접속가능한 Postgresql Client Tool인 psql도 설치했다. (아래와 같이 psql의 버젼은 9.2.24였다.)

...
// Local Machine에서 EC2 Instance 접속
[browndwarf@localhost ~]$ ssh -i ./Downloads/aws-browndwarf-keypair.pem ec2-user@ec2-54-180-94-149.ap-northeast-2.compute.amazonaws.com
Last login: Tue Jul 16 23:32:59 2019 from 121.xxx.xxx.225

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-ami/2018.03-release-notes/
3 package(s) needed for security, out of 5 available
Run "sudo yum update" to apply all updates.

// yum update 실행
[ec2-user@ip-172-31-19-175 ~]$ sudo yum update
Loaded plugins: priorities, update-motd, upgrade-helper
amzn-main                                                            | 2.1 kB  00:00:00     
amzn-updates                                                         | 2.5 kB  00:00:00

...

Updated:
  kernel-tools.x86_64 0:4.14.128-87.105.amzn1   python27.x86_64 0:2.7.16-1.127.amzn1       
  python27-devel.x86_64 0:2.7.16-1.127.amzn1    python27-libs.x86_64 0:2.7.16-1.127.amzn1  

Complete!

// postgresql client program 설치
[ec2-user@ip-172-31-19-175 ~]$ sudo yum install postgresql
Loaded plugins: priorities, update-motd, upgrade-helper
Resolving Dependencies
--> Running transaction check
---> Package postgresql92.x86_64 0:9.2.24-2.66.amzn1 will be installed
--> Processing Dependency: postgresql92-libs(x86-64) = 9.2.24-2.66.amzn1 for package: postgresql92-9.2.24-2.66.amzn1.x86_64
--> Processing Dependency: libpq.so.5()(64bit) for package: postgresql92-9.2.24-2.66.amzn1.x86_64
--> Running transaction check

...


Installed:
  postgresql92.x86_64 0:9.2.24-2.66.amzn1                                                   

Dependency Installed:
  postgresql92-libs.x86_64 0:9.2.24-2.66.amzn1                                              

Complete!
[ec2-user@ip-172-31-19-175 ~]$ psql -V
psql (PostgreSQL) 9.2.24
...

(*) RDS Instance를 생성할 때 Public으로 생성했었고, 개인 PC에서도 접속이 잘되었기 때문에 새로 만들어진 EC2 Instance에서도 접속에 문제 없다고 생각했는데, 막상 psql을 이용해 접속하려고 하자 Connection이 이루어지지 않고 아래와 같이 Time out이 발생하였다.

...
[ec2-user@ip-172-31-7-71 ~]$ psql -h browndwarfpostgresex.xxxxxx.ap-northeast-2.rds.amazonaws.com -U browndwarf
psql: could not connect to server: Connection timed out
	Is the server running on host "browndwarfpostgresex.xxxxxx.ap-northeast-2.rds.amazonaws.com" (172.31.5.69) and accepting
	TCP/IP connections on port 5432?
...

이 접속 문제는 RDS를 Public으로 생성해도, 생성 당시에 AWS Console Management를 접속했던 기기가 사용하는 Public IP만을 허용하기 때문인 것이 원인이다. 아래 사진에 있는 RDS의 인바운드 규칙 중에 위에 Row가 바로 RDS 생성시에 만들어진 규칙이다. CIDR Block 규칙이 121.x.x.225에서 접근하는 Traffic만 허용하기 떄문에 필자가 사용하던 PC에서는 Public으로 접근이 가능했었는데, 필자가 생성한 EC2는 전혀 다른 IP 대역이기 떄문에 접근이 허락되지 않았던 것이었다.

 

이 문제를 해결하기 위해 RDS Instance의 보안 그룹에서 인바운드 규칙 편집을 수정했다. 아래 사진과 같이 해당 메뉴를 오픈한 후에 접근하고자 하는 Instance가 사용하는 IP가 포함되게 Allow 규칙을 만들어 추가하면 바로 적용이 된다. 아래 예에서는 172.31.x.x 의 IP를 가진 객체의 접근은 모두 허용하게 설정하였다. 

Pic 3. 보안 그룹 수정

그 후에 EC2 Instance에서 DB 접속을 다시 시도해 보면 정상적으로 접속되는 것을 확인할 수 있다.

...
[ec2-user@ip-172-31-7-71 ~]$ psql -h browndwarfpostgresex.xxxxxx.ap-northeast-2.rds.amazonaws.com -U browndwarf
Password for user browndwarf: 
psql (9.2.24, server 9.6.12)
WARNING: psql version 9.2, server version 9.6.
         Some psql features might not work.
SSL connection (cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256)
Type "help" for help.

browndwarf=> 
...

접속한 DB에 들어가서 내용을 확인해보면 이전에 실행한 결과들이 그대로 남아있는 것을 확인할 수 있다.

...
//
// DB Instance 확인
//
browndwarf=> \list
                                     List of databases
    Name    |   Owner    | Encoding |   Collate   |    Ctype    |     Access privileges     
------------+------------+----------+-------------+-------------+---------------------------
 browndwarf | 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 rows)

//
// browndwarf DB에 browndwarf 로 접근
//
browndwarf=> \c browndwarf browndwarf
psql (9.2.24, server 9.6.12)
WARNING: psql version 9.2, server version 9.6.
         Some psql features might not work.
SSL connection (cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256)
You are now connected to database "browndwarf" as user "browndwarf".

//
// browndwarf DB의 Table 조회
//
browndwarf=> \dt
                  List of relations
 Schema |         Name          | Type  |   Owner    
--------+-----------------------+-------+------------
 public | databasechangelog     | table | browndwarf
 public | databasechangeloglock | table | browndwarf
 public | job                   | table | browndwarf
(3 rows)

//
// job table 내용 조회
//
browndwarf=> SELECT * FROM job;
 id |     name     | customer |     description      | jobstatus | duedate  |       createdtime       |       updatedtime       
----+--------------+----------+----------------------+-----------+----------+-------------------------+-------------------------
  1 | Customize UI | Amazon   | Review UI for Amazon |           | 20190617 | 2019-07-16 14:15:11.065 | 2019-07-16 14:28:04.389
(1 row)

browndwarf=> 
...

 

EC2 Instance에서 RDS를 활용한 Application 실행

이제 EC2에서 jar 파일을 실행한 후에 외부에서 Rest API를 호출해 보도록 하겠다. 먼저 실행해야할 jar 파일을 sftp를 이용해서 upload 했다.

...
[browndwarf@localhost libs]$ ls
jobmanager-0.0.1-SNAPSHOT.jar

//
// Local 머신에서 EC2 Instance에 SFTP로 접속 후 jar File upload
//
[browndwarf@localhost libs]$ sftp -i ~/Downloads/aws-browndwarf-keypair.pem ec2-user@ec2-13-125-107-246.ap-northeast-2.compute.amazonaws.com
Connected to ec2-13-125-107-246.ap-northeast-2.compute.amazonaws.com.
sftp> put jobmanager-0.0.1-SNAPSHOT.jar 
Uploading jobmanager-0.0.1-SNAPSHOT.jar to /home/ec2-user/jobmanager-0.0.1-SNAPSHOT.jar
jobmanager-0.0.1-SNAPSHOT.jar                                                                                                        100%   41MB   9.7MB/s   00:04    
sftp> ls
jobmanager-0.0.1-SNAPSHOT.jar   
sftp> exit

//
// EC2 Instance에 접근 후 SFTP upload 결과물 확인
//
[browndwarf@localhost libs]$ ssh -i ~/Downloads/aws-browndwarf-keypair.pem ec2-user@ec2-13-125-107-246.ap-northeast-2.compute.amazonaws.com
Last login: Fri Jul 19 01:08:47 2019 from 121.xxx.xxx.225

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-ami/2018.03-release-notes/
[ec2-user@ip-172-31-7-71 ~]$ ls
jobmanager-0.0.1-SNAPSHOT.jar
...

 

그 후에 jar를 실행하려고 보니 EC2 Instance를 만들때 사용한 Amazon Linux AMI에 설치된 Java 버젼이 1.7이라 1.8 환경에서 만들어진 필자의 Application은 동작시키기 위해 1.7을 제거하고 1.8을 새로 설치하였다. 그 후에 jar 파일을 실행시키니 정상적으로 동작했다.

다시 필자의 Local 머신으로 이동해서 curl을 이용해 EC2 Instance에서 실행하는 Application의 Rest API를 호출해 보았다. 새로운 job data를 등록하였고, 등록된 job을 ID로 확인하였다.  

위에서 호출한 API를 실행하는 내용은 EC2 Instance에서 생성되는 Application의 Log를 통해 정상적으로 동작됨을 확인할 수 있다.

...
// 새로운 job이 insert 되고 있다
[01:35:52.447][INFO ][nio-8080-exec-2 / JobController                                               ] Start 'registerJob()'
Hibernate: 
    insert 
    into
        job
        (createdTime, customer, description, dueDate, jobStatus, name, updatedTime) 
    values
        (?, ?, ?, ?, ?, ?, ?)
Hibernate: 
    select
        currval('job_id_seq')
[01:35:52.472][INFO ][nio-8080-exec-2 / JobController                                               ] Complete 'registerJob()'

// id로 저장되어 있는 job을 조회하고 있다.
[01:37:30.301][INFO ][nio-8080-exec-4 / JobController                                               ] Start 'retrieveJobInfo()'
Hibernate: 
    select
        jobentity0_.id as id1_0_0_,
        jobentity0_.createdTime as createdT2_0_0_,
        jobentity0_.customer as customer3_0_0_,
        jobentity0_.description as descript4_0_0_,
        jobentity0_.dueDate as dueDate5_0_0_,
        jobentity0_.jobStatus as jobStatu6_0_0_,
        jobentity0_.name as name7_0_0_,
        jobentity0_.updatedTime as updatedT8_0_0_ 
    from
        job jobentity0_ 
    where
        jobentity0_.id=?
...

DB에서 실행한 결과를 확인해보면 최종에 2개의 Record가 있는 것을 확인할 수 있고 'Customize UI'와 'Explorer Moon' 이라는 Job이 등록되어 있음을 확인할 수 있다.

...
[ec2-user@ip-172-31-7-71 ~]$ psql -h browndwarfpostgresex.xxxxxx.ap-northeast-2.rds.amazonaws.com -U browndwarf
Password for user browndwarf: 
psql (9.2.24, server 9.6.12)
WARNING: psql version 9.2, server version 9.6.
         Some psql features might not work.
SSL connection (cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256)
Type "help" for help.

browndwarf=> \c browndwarf browndwarf
psql (9.2.24, server 9.6.12)
WARNING: psql version 9.2, server version 9.6.
         Some psql features might not work.
SSL connection (cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256)
You are now connected to database "browndwarf" as user "browndwarf".

browndwarf=> SELECT * FROM job;
 id |     name      | customer |     description      | jobstatus | duedate  |       createdtime       |       updatedtime       
----+---------------+----------+----------------------+-----------+----------+-------------------------+-------------------------
  1 | Customize UI  | Amazon   | Review UI for Amazon |           | 20190617 | 2019-07-16 14:15:11.065 | 2019-07-16 14:28:04.389
  2 | Explorer Moon | Solar    | Develop UI for Solar |           | 20190615 | 2019-07-19 01:35:52.455 | 2019-07-19 01:35:52.455
(2 rows)

browndwarf=> 
...