반응형

개요

 
이번 포스팅은 Choreography 기반의 Saga 패턴을 실습해 보기 위해 구성되었으며 Spring Boot와 AWS의 MSK(Managed Streaming for apache Kafka) 기반으로 구성되었습니다.
 

 

아키텍처

 
빠른 예제와 비용 최적화를 위해 1개의 EC2 인스턴스에서 2개의 서비스(Spring Boot 기반)를 서로 다른 포트로 실행시켜 두대의 서버에서 운영하는 방식으로 예제를 구성했습니다. Amazon MSK(Managed Streaming service for apache Kafka)는 2대의 브로커를 구성할 예정입니다. 
 

 

유즈케이스 다이어그램

 

두대로 구성되는 스프링 부트 서비스는 Customer와 Order로 구성되어 있습니다.
 
- 커스터머는 등록시 이름과 CreditLimit을 설정합니다.
- 커스터머는 오더가 발생시 본인의 CreditLimit 안에서만 주문을 할 수 있습니다.
- 주문이 발생되면 주문 금액만큼 커스터머의 CreditLimit이 차감됩니다.
 
- 오더는 커스터머 ID와 Order금액을 입력을 합니다.
- 주문시 오더 테이블에 주문 상태는 Pending 상태입니다.
- 주문 정보가 커스터머 서비스에 전달되어 해당 오더 금액 만큼 Credit이 차감됩니다. 이때 초과하면 에러가 발생합니다.
- 커스터머는 Credit 차감 결과를 다시 오더에 전달합니다. 
- 오더는 커스터머로부터 받은 차감 결과에 따라 정상이면 오더에 대해 Approve를 비정상(CreditLimit초가) 이면 오더에 대해 Reject를 합니다.
 
주) 이 예제에서는 2022년 6월 이후 AWS에 적용된 새로운 UI 환경 기준으로 설명합니다.

 

 

네트워크 구성

빠른 실습과 이해를 위해 별도의 VPC를 생성해 실습을 합니다.

VPC 생성

VPC > Your VPCs > Create VPC

 
Name tag auto-generation을 변경하고 나머지는 기본값을 그대로 사용합니.(e.g. event-driven-sample) 이 경우 2개의 AZ를 사용하며 Public Subnet 1, Private Subnet 1가 각각의 AZ에 생성됩니다.

생성 완료

 

MSK 구성

다음으로 MSK를 설치하도록 합니다.

VPC Security Group 생성

설치에 앞서 MSK 접속을 위한 보안 그룹을 먼저 생성해 줍니다.
 
VPC > Security Groups > Create security group
Basic details > Security group name : msk-security-group
Basic details > Description : msk security group
Basic details > VPC > event-driven-sample-vpc
Inbound rules > Type : Custom TCP, Protocol : TCP, Prot range : 2181, Source: Anywhere IPv4
Inbound rules > Type : Custom TCP, Protocol : TCP, Prot range : 9092, Source: Anywhere IPv4

보안그룹 생성이 완료되었습니다.


MSK Cluster 생성

aws console에서 Amazon MSK를 찾아 MSK 화면으로 이동합니다.

 
다음의 순서대로 설정합니다.
Amazon MSK > Clusters > Create cluster
Create method > luster creation method : Custom create
Cluster name > Cluster name: eda-msk
Apache Kafka Version > Apache Kafka version > 2.6.2
Brokers> Broker type: kafka.t3.small
Brokers> Number of zones : 2 <-- Kafka 기본 클러스터 시작은 3개이나 우리는 예제를 위해서 2개만 생성
Brokers> Number of brokers per zon : 1
나머지는 기본 값 선택 후 Next

 
설정시 VPC와 보안 그룹을 잘 보고 설정하도록 합니다.
 

CloudWatch Log Group 생성

MKS의 로그를 CloudWatch에서 모니터링하기 위해 CloudWatch에 Log Group을 생성합니.

 

Networking

Networking> VPC : event-driven-sample-vpc
First zone > Zone : ap-northeast-2a
First zone > Subnet : <-- ap-northeast-2a에서 만든 private subnet
Second zone > Zone : ap-northeast-2b
Second zone > Subnet : <-- ap-northeast-2b에서 만든 private subnet
Security groups in Amazon EC2 > Browse> msk-security-group 선택 <-- 앞서 생성
 

Security

다른 항목은 모두 기본으로 두고 Encryption영역의 Between clients and brokers 영역에 Plaintext만 체크합니다.
 

Monitoring and tags

Broker log delivery: Deliver to Amazon CloudWatch Logs
Log group> Browse > MSKLogGroup 선택 <-- 앞서 생성
제일 아래 Cluster tags - optional 에 Key : Name을 Value-optional에 MSK-EDA를 입력합니다.
나머지는 기본값으로 두고 Next

 
지금까지 입력한 값 리뷰 후 Create Cluster

 

생성시 최소 30분에서 60분 이상 소요됩니다.

 

 
 

App개발 테스트를 위한 Bastion 서버 (EC2) 설정

실제 개발이나 운영환경이라면 컨테이너화되어 EC2나 EKS등에 배포되겠지만 우리는 빠른 데모를 위해 Public Subnet에 있는 EC2 1개에 Public IP를 부여하고 Order/Custmer 서비스를 각각 다른 포트(8082/8081)로 기동 시킨 후 테스트를 할 예정입니다.
새로운 EC2를 Public Subnet에 다음과 같이 생성하도록 합니다.
 
EC2 > Instances > Launch an instance
Name and tags> Mame: Bastion-Svr
Application and OS Images(Amazon Machine Image) > Amazon Linux
Application and OS Images(Amazon Machine Image) > Amazon Machine Image(AMI) > Amazon Linux 2 AMI(HVM) - Kernel 5.10, SSD Volume Type
Instance type> Instance type: t2.micro

 
Key pair(login)> Key pair name -required : ----> 'Create new key pair'를 통해서 새로 만들 도록 합니다.
원격 접속을 위한 Key Pair를 아래와 같이 생성해 PC에 다운로드합니다.(unix/linux는 pem파일 형식, windows는 ppk 파일 형식)

 
Network settings> Edit > VPC - required: event-driven-sample-vpc
Network settings> Subnet > event-driven-sample-subnet-public1-ap-northeast-2a
Network settings> Auto-assign public IP > Enable

 

Network settings> Firewall(security groups) > Create security group
Network settings> Security group name - required : bastion-security-group
Network settings> Description - required : bastion-security-group
Network settings> Inbound security groups rules> Type: ssh, Protocol: TCP, Port range:22, Source type: Anywhere, Source: 0.0.0.0/0
Network settings> Inbound security groups rules> Add security group rule> Type: Custom TCP, Protocol: TCP, Port range:8081, Source type: Anywhere, Source: 0.0.0.0/0
Network settings> Inbound security groups rules> Add security group rule> Type: Custom TCP, Protocol: TCP, Port range:8082, Source type: Anywhere, Source: 0.0.0.0/0

 

Advanced details

*모두 기본값으로 두고 아래의 쉘 스크립트 내용을 복사해 Advanced details의 User data에 복사해 둘 것
Advanced details> User data >
#!/bin/bash 
#yum update
sudo yum update -y
sudo yum install java-11-amazon-corretto.x86_64 << EOF
y
EOF
#kafka library download
wget https://archive.apache.org/dist/kafka/2.6.2/kafka_2.12-2.6.2.tgz -P /home/ec2-user;
#extract archive file
tar -xzf /home/ec2-user/kafka_2.12-2.6.2.tgz -C /home/ec2-user/
#Install git in your EC2 instance
sudo yum install git -y;
#git repository clone
git clone https://github.com/sharplee7/event-driven-architecture-example.git /home/ec2-user/event-driven-architecture-example
모두 완료후 Launch instance 클릭

대쉬보드에서 방금 만든 EC2 Instance가 Pending > Running 에서 Initializaing에서 2/2 checks passed가 될때까지 대기합니다.

 
 

Bastion 서버 접속

EC2 관리 콘솔의 좌측 메뉴에서 Instances를 찾아 방금 생성한 Bastion-Svr을 선택하고 상단의 Connect 버튼을 클릭합니다.
EC2 Instance Connect 탭을 선택 한 후 SSH client 접속 코맨드를 복사해서 클라이언트 PC의 key pair 파일이 있는 디렉토리에서 실행해 EC2인스턴스에 접속합니. (이때 key pair 파일은 chmod 400 key-pair명 으로 권한을 변경합니다)
 
 

Kafka Topic 생성

Bastion 서버로 접속을 했다면 EC2 Instance 생성시 User Data로 미리 다운로드 받은 kafka 라이브러리로 이동한다.
cd /home/ec2-user/kafka_2.12-2.6.2
 
앞서 만들어 두었던 MSK(Managed Streaming for Apache Kafka)의 zookeeper endpoint를 확인하기 위해 Amazon MSK > Clusters > eda-msk를 클릭합니다.

 
eda-msk의 관리 화면의 우측 상단에 있는 "View client information" 버튼을 클릭합니다.

 
View client information 화면에서 Apache Zookeeper connection 항목의 Plaintext 중 첫번째 주소를 복사해 둡니.(zookeeper는 3개의 cluster로 구성되어 있어서 3개의 endpoint 주소가 있지만 여기서는 한개만 복사하도록 합니다)

예제에 필요한 토픽을 생성하기

아래의 명령구문에서 --zookeeper 다음의 어드레스는  바로 위에서 복사한 Apache ZooKeeper Connection의 Plaintext 주소루 변경합니다.
토픽은 customerTopic과 orderTopic 2개를 만듭니다.
 
bin/kafka-topics.sh --create --zookeeper [YOUR_ZOOKEEPER_ADDRESS] --replication-factor 2 --partitions 1 --topic customerTopic
bin/kafka-topics.sh --create --zookeeper [YOUR_ZOOKEEPER_ADDRESS] --replication-factor 2 --partitions 1 --topic orderTopic
 

생성된 토픽 확인

msk 화면에서 이번엔 Bootstrap servers의 접속 url을 복사해 옵니다.(복수대가 있더라도 하나만 복사하면 됩니다)
아래의 명령어를 통해 생성된 토픽을 확인합니다. --bootstrap-server 다음의 url은 본인이 만든 MSK의 Bootstrap server 주소로 변경합니다.(Zookeeper 주소와 혼동하면 안된다.)
bin/kafka-topics.sh --bootstrap-server [YOUR_MSK_BOOTSTRAP_SERVER_ADDRESS] --list

 

 

프로그램 변경

cd $HOME/event-driven-architecture-example/customer-service/src/main/resources 로 옮겨 customer-service 프로그램의 application.yml 파일을 vi등의 에디터로 열어 제일 아래에 있는 kafka bootstrap url을 본인 msk의 bootstrap url로 변경합니다.

 
cd $HOME/event-driven-architecture-example/order-service/src/main/resources 로 옮겨 order-service 프로그램의 application.yml 파일을 vi등의 에디터로 열어 제일 아래에 있는 kafka bootstrap url을 본인 msk의 bootstrap url로 변경합니다.
(이미지는 동일하므로 생략)

 

 

컴파일 및 실행

customer-service 컴파일 및 실행

cd $HOME/event-driven-architecture-example/customer-service 로 옮겨 다음의 명령어 실행
./gradlew build

 
아래 캡쳐와 같이 BUILD SUCCESSFUL이 떨어졌는지 확인합니다.
정상적으로 컴파일 되었다면 /home/ec2-user/event-driven-architecture-example/customer-service/build/libs 디렉토리로 이동해 프로그램을 실행해 보도록 합니다.
java -jar customer-service-0.0.1-SNAPSHOT.jar
프로그램이 정상 실행 되었습니다.
이제 웹브라우저를 통해 접속해 봅니다. (이때 IP는 앞서 만든 EC2의 Public IP)를 입력한다.
http://your-ip:8081/customer-order/customer-service/swagger-ui/

POST를 클릭해서 사용자 이름과 CreditLimit을 입력한다.
Try It의 결과에 리턴되어온 Customer ID 값을 확인한다.
 

order-service 컴파일 및 실행

cd /home/ec2-user/event-driven-architecture-example/order-service 로 이동합니합니다.
Order Service를 컴파일합니다.
./gradlew build
 

 

cd /home/ec2-user/event-driven-architecture-example/order-service/build/libs 로 이동해서 프로그램 실행합니다.

java -jar order-service-0.0.1-SNAPSHOT.jar​
 
 
 
Write a caption
ㅎㄷㅅㅅㅇ
브라우저를 통해 웹 접속 해봅니다.(이때 IP는 앞서 만든 EC2의 Public IP)를 입력한다.
 
http://yourip:8082/customer-order/order-service/swagger-ui
 
 
 
Write a caption
 
POST 를 클릭해서 Order를 주문합니다.
이때 CustomerID는 앞서 생성한 CUSTOMER ID를 입력합니다.
초기 테스트에서는 Customer가 가진 CreditLimit 안에서 주문을 해보고 해당 주문의 결과를 아래 GET 메소드를 통해 확인해 봅니다.
(Order가 정상적으로 진행되면 order id가 리턴된다. 이 id 값을 get 메소드에 넣으면 된다.) --> 정상 주문 되었다면(creditlimit 안에서 주문이 이루어 졌다면) 주문의 상태가 approved로 되어 있는 것을 확인 할 수 있습니다. 만약 CreditLimit 보다 더 많은 금액을 주문했다면 주문 상태는 Reject로 되어 있음을 확인 할 수 있습니다. <-- Choreography 방식으로 Order Service에 Compensation이 발생했습니다.
 
 
 
 
 
이번 예제에서 사용한 프로그램은 아래의 git에서 확인 할 수 있습니다.

https://github.com/sharplee7/event-driven-architecture-example

 

GitHub - sharplee7/event-driven-architecture-example

Contribute to sharplee7/event-driven-architecture-example development by creating an account on GitHub.

github.com

 

이번 포스팅은 여기 까지~

 
 
반응형

+ Recent posts