[AWS] 마이크로서비스

Numberbeen·2023년 2월 22일
0

AWS

목록 보기
11/13
post-thumbnail

프로젝트3 를 시작하고 벌써 최종장 까지 왔다....

솔직히 지금도 내 스스로도 수박 겉핥기 라고 생각하면서 불안감을 가지는 와중에
3번째 프로젝트 까지 버텨냈고 하고 있다는 것만으로도 ... 후

하지만 다 배운 것들이라고 하니 정리한 내용들을 이용하고 팀원들의 시너지를 기대하며 시작한다.

<자동 재고 확보 시스템> MSA


구성 요소

  1. Sales API
  2. Factory API
  3. 프론트엔드(웹사이트) : cURL / postman / k6 등을 통한 API 호출로만 구현
    Sales API를 통해 백엔드에 요청
  4. 백엔드(서버) : 구매 시 창고에서 재고 확인 후 재고 감소 로직 구현
    재고가 부족할 경우 Factory API를 통해 재고 확보 요청
    데이터베이스(창고) : RDS에 mysql db 구성
    요청에 따른 재고 상태 변경

시작


Step 1 Lambda 서버에 DB 연결

이 스텝을 보고 이제는 조금 AWS 에 대해 친해진걸까? 예전에는 설명을 봐도 이해가 안되는 것들이 미약하게 알아볼 수 있게 된거 같다.

먼저 예제 파일을 깃허브 리파지토리에서 Sales API 폴더를 확인하자.

우선 제공받은 데이터베이스를 로컬에서 접속하도록 해야된다.
mysql 명령어로 먼저 접속해본다. 만d약 mysql 설치가 안되있다면 설치하고 진행하자.

 mysql -h 호스트네임 -u 유저네임 -p 다비명 
 
 입력 후 패스워드 입력하면 접속이 된다.
 


로컬에서 접속 후 전달받은 init.sql 파일을 순차적으로 실행해준다.

마지막 부분에 factroy_id 와 ad_id 를 따로 적어놓고 마지막 insert 문에 자신의 id 를 입력해야한다.

show tables; 명령어로 잘 생성됬는지 확인

로컬에서 테스트를 진행하기 위해 .env 파일을 생성한 후 전달받은 디비 정보를 입력해주자. 보안적인 부분때문에 형식으로 대체한다.

HOSTNAME=호스트네임
USERNAME=유저네임
PASSWORD=패스워드
DATABASE=디비명

자 그러면 이제 테스트를 해봐야한다. 우선 다른 터미널창을 열어서 sales-api 폴더로 이동 후 npm start 명령어로 실행시키자.

curl -X POST localhost:8080/checkout

왜 curl 명령어에 마지막에 checkout 이 붙는지는 handler.js 파일을 조사하면 금방 찾을 수 있을 거다.


정상적으로 수행되고 있다. 계속 curl 명령어를 보내게 되면?

이런식으로 구매 실패에대한 메시지가 나올것이다.

첫번째 트러블 curl... 젭알... 대문자...

curl 명령어와는 안친한거 같다... 정말 바보같이 curl -X 후 POST 명령어를 대문자로 안하고 소문자로 해서 ... 이건 트러블이라 하기에도 송구스럽다....

Step 2 "재고없음" 메시지 전달

우리 조는 처음에 SNS, SQS 기능을 Serverless.yml 파일을 이용해 구현해보기로 방향을 잡고 진행하였으나, 되는지 안되는지의 확신이 없었고 시간도 촉박해서 우선 스텝을 끝내고 추후 시간에 serverless.yml 파일로 도전하기로 하고 콘솔로 생성 및 연결해주는 방향으로 노선을 변경하였다.

로컬에서 테스트 하기전 SNS 와 SQS 를 생성하여 연결 시켜줘야 한다.

연결시켜보자.

먼저 SNS 콘솔로 이동해서 주제를 생성해주자. 나는 표준 유형의 이름은 project3SNS 로 생성해주었다.

그런 후 sqs 를 만들어준다.

주의할 점 SNS 유형을 표준으로 생성했기에 SQS 도 표준으로 생성해줘여 한다.
생성 후 sqs 에서 처음 생성한 sns 를 구독시켜준다. sqs 와 sns의 연결은 끝났다.

여기서 우린 sales-api 에 checkout 으로 포스트가 명령이 오면 sns 를 통해 sqs 로 가게 하고싶다. 그렇타면 sales-api 에 sns 를 연결시켜줘야 하는데? 이것을 어떻게 하냐?

바로!! .env 파일에 환경변수 Topic_ARN 을 입력해줘야 한다.

TOPIC_ARN=SNS ARN

추가해주자.

이제 미리 제공된 code snippet 부터 어디다 넣어야할지 고민해보자.

성공하면 재고가 있다는 뜻이니 sqs 로 보낼 필요가 없겠다 생각하여.

handler.js 파일에 .json....구매완료...} else { 다음에 붙여넣기 해버렸다.

그리고 로컬에서 테스트를 위해 curl -X POST 명령어를 실행하니...

두번째 트러블

스크린샷 2023-02-20 오후 4 34 12
MessageAttributeFactoryId 오류가 떳다.

흠 파파고 해석을 돌려보니 MessageAttributeFactoryId 값이 비어있으면 안된다는 뜻이었다. 뭐지... 코드 그대로 붙여넣으면 안되는건가...?

MessageAttributeFactoryId 값이 잘못됬다는 걸 확인하였으니 값이 제대로 들어오는지
console.log(" MAFID 값 : ", req.body.MessageAttributeFactoryId) 를 입력하여 값을 확인해보았다.

undefined 값이 출력 되었다... 왜지?

    MessageAttributeFactoryId: {
      StringValue: req.body.MessageAttributeFactoryId,
      DataType: "String",

코드를 확인해보니 응답받는 body 값에서 FactoryId 를 받게 되어있다. 포스트맨 으로 FactoryId 값을 입력하여 POST 하면 정상적으로 재고없다는 메세지가 출력 되었다. 그치만 우리는 그냥 POST 했을때 바디에 FactoryId 값을 입력하는게 아닌 DB 에서 값을 조회해서 되게끔 하고 싶었다.

먼저 console.log(product) 를 해보았다.

BIN_TO_UUID(factory_id) 값이 필요해서 로그를 확인하고 아래와 같이 변경해서 포스트 해보았다.

    MessageAttributeFactoryId: {
      StringValue: product.BIN_TO_UUID(factory_id)
      DataType: "String",

안된다... 저게 필요한데...

위와 같은 상황에서 계속 구글링하며 이것 저것 시도해보던중 광훈님께서 공유해주신 방법으로 해결했다.

database.js 파일에 const getProduct 구문에서 BIN_TO_UUID(factory_id) 뒤에 as factory_id (별칭지정?) 를 입력해주었다.

    MessageAttributeFactoryId: {
      StringValue: product.factory_id
      DataType: "String",

그런 후 hander.js 파일에서 오류가 출력되던 부분을 위와 같이 바꿔주고 포스트 명령을 날렸다.

성공



이제 배포에 성공하면 lambda 함수가 생성되어 있을 것이다. 테스트를 위해 curl 명령어를 이용해 테스트를 하는데...?

세번째 트러블

curl -X POST api엔드포인트/checkout

흠... 역시 뭐 내컴에서만 되고 딴컴에서 안되는게 맞는말이다... 로컬에서 됬는데 왜지... lambda 에서의 로그는 모니터링 탭에 view CloudWatch logs 로 확인 가능하다. 가보자9

Authorization Error 가 떳다. 익숙하다. 내가 생각한건 권한 문제라고 생각했다.
에러메시지를 파파고 돌린다. ❤️파파고

역시나 권한 문제가 맞다. SNS 에 읽기 권한이 필요한다. 없어서 생기는 문제였다. 권한을 추가하러 가자.

역활 이름을 눌러 해당 권한으로 이동하자.
인라인 정책 생성으로 들어가고 sns 에 대한 모든 권한을 줘도 되지만... 보안상 최소 권한만 주는게 좋으니 publish 권한만 주고 리소스도 특정 리소스만 접근하게 아까 위에서 생성한 sns arn 을 복사해서 붙여넣자.

그런 후 이름을 설정한 후 저장을 누르면 위와 같이 생성된다.

그럼 다시 curl 해보자.

네번째 트러블

이건.. 캡쳐화면이 없다... 말로 설명할 예정이다.

curl 을 했을 때 또 server error 가 떳다 로그를 확인해보니 DB에 접근을 못한 에러였다... 왜지? 로컬에선 됬는데...?

깊은 생각에 빠지게 된 찰나 .env 파일은... 배포할때 배포되나? 나의 디비 정보는 다 거기있는데... ? 그래서 이렇게 저렇게 해보던중 귀인이 나타났다. 내 추측이 맞다며 .env 파일은 aws 로 베포할때 배포가 안된다고 하시고 해결책도 제시해주셨다.

다른 방법

serverless.yml 파일에 아래와 같이 추가해준 후 배포해도 된다고 한다.

functions:
  api:
    handler: handler.handler
     enviroment:
       HOSTNAME: 호스트명
       USERNAME: 유저명
       PASSWORD: 패스워드
       DATABASE: 디비명
       TOPIC_ARN: SNS ARN

먼저 해당 폴더에서 npm i -D serverless-dotenv-plugin 설치 하고 serverless.yml 파일에 plugins 를 위와 같이 추가한 후 배포했더니 .env 파일을 읽으면서 정상적으로 되었다.

Step 3 메세지를 Factory API 전송하는 Lambda, DLQ 추가

현재 우리는 SNS 와 SQS 까지 생성하여 연결까지 해놓은 상태이다.

dlq 추가는 콘솔에서 sqs 를 하나 더 만드는데 뒤에 dlq 를 붙여서 만들고 원래 만들었던 sqs 편집하여 배달 못한 편지 대기열 사항을 선택 좀전에 만든 dlq 로 끝나는 sqs arn 을 선택해주었더니 정상적으로 추가 되었다.

스크린샷 2023-02-20 오후 5 02 35

이제 메시지를 Factory API 로 전송하는 Lambda 구성해보자.

sls 명령어를 이용해서 설치하자.

AWS - Node.js - SQS worker 을 선택 이름은 stock-lambda 로 하고 지금 배포는 안할거니 n 을 선택하자.

선택한 후 index.js 파일을 열어 우리가 필요한 코드만 남겨놓고 정리하자. 현재 sqs 와 producer 에 해당하는 람다는 이미 생성했기 때문에 consumer 만 놔두고 정리하자.

그리고 serverless.yml 파일도 설정하자.

service: stock-lambda
frameworkVersion: '3'

provider:
  name: aws
  runtime: nodejs14.x
  region: ap-northeast-2

functions:
  producer:
    handler: index.consumer
    events:
      - sqs: sqs arn 주소

plugins:
  - serverless-lift

제일 중요한 부분은 functions - producer - event - sqs 부분에 위에서 생성한 sqs arn 주소를 넣어주자.

consumer function 함수가 완성면 트리거 조건으로 이벤트 sqs 메시지를 폴링하겠다는 뜻

sls deploy 배포해보자.


현재 sqs에 사용가능한 메시지가 4개 있다. 위의 stock-lambda 가 정상적으로 배포하게되면 저 메시지 4개를 가져갈 것이다.


배포가 되었으니 콘솔에서 확인해보자.

정상적으로 생성된 모습

sqs 에 사용가능한 메시지도 0으로 되어있다. 그런 즉 메시지가 전달 됬다는 거다. log 를 한번 확인해보자.

해당 lambda 확인 해보니 잘 들어가있다... 다행이다 후

Step 4 API 문서를 참고하여 데이터베이스의 재고를 증가시키는 Lambda 함수

API 문서를 보고 데이터 베이스를 증가시키는 함수(lambda)를 생성해야 한다.

stock-lambda 폴더의 index.js 를 수정해서 factoy api 문서 형식에 맞게 데이터를 가공해서 보내줘야 한다.

record.body 를 활용하여 만들었는데... 이걸 어떻게 저기다 넣을까? 제일 오래걸린 부분이었다...

그래서 producer의 log 를 확인해서 보게되면 MessageAttributeFactoryId, MessageAttributeProductId 를 볼 수 있다. 아 저걸 body 값으로 받고 내가 출력할 수 있게 설정해주면 되겠구나?

그럼 나머지 값들은 없는데 뭐지... 우선 MessageGroupId 내용은 문서를 보니 stock-arrival-group 를 넣으라고 했는데 내가 만들걸 티내고 싶어서 PROJECT3-B 이렇게 입력했다. 그리고 마지막 CallbackUrl 주소는 무엇을 넣어야되는지 몰라서... 고민하던 중 다시 한번 또 귀인이 나타났다.

리파지토리 clone 할때 같이 들어있던 stock-increase-lambda 를 배포하여 그 api게이트웨이 주소를 넣어야 한다고 한다. 바로 배포하자. 코드는 아무것도 수정 안하고 serverless.yml 생성 해서 배포했다.

배포가 완료되어 increase 어쩌고 생성됬다.

그럼 여기 게이트웨이 주소를 넣자.

  const payload = {
    "MessageGroupId" : "PROJECT3-B",
    "MessageAttributeProductId" : body.MessageAttributeProductId.Value
    "MessageAttributeProductCnt" : 3,
    "MessageAttributeFactoryId" : body.MessageAttributeFactoryId.Value
    "MessageAttributeRequester" : "현수빈",
    "CallbackUrl" : "stock-increase-lambda api 게이트웨이 주소"
   }

callbackurl 뒤에 product/donut 가 붙는 이유는? stock-increase-lambda 에 handler.js 확인하면 금방 찾을 수 있다.

저장하고 배포하자.


다시 curl 명령어를 입력한다.

구매 실패 메시지는 정상적으로 떳다 시작이 좋다.

이제 나의 예상경로는 -> sns 에서 sqs 로 가서 stock-lambda 를 거쳐 stock-increase-lambda 로 간 다음 디비에 재고를 추가하게된다.

이제 로그를 확인해보자.

다섯번째 트러블

앗차... 삭제하고 다시하느라 axios 를 설치 안하고 했다.
npm install axios 명령으로 설치하고 재배포하자.

여섯번째 트러블

배포 후 curl -X sales-api 게이트웨이 엔드포인트/checkout
치니 axios 에러는 안뜨지만... 타입 에러가 떳다.

Value 값이 언디파인드 란다. 뭐지... body 값을 console.log 로 확인해보았다.
나는 잘 준거 같은데... 보던중 들여쓰기가 눈에 보였다... 앗 설마...
나는 위에서

"MessageAttributeProductId" : body.MessageAttributeProductId.Value

이렇게 입력했다... 저 들여쓰기를 토대로 수정해보았다.

  "MessageAttributeProductId" : body.MessageAttributes.MessageAttributeProductId.Value

이렇게 수정하고 그 밑에 MessageAttributeFactoryId 도 수정해줬다. 다시 배포해주자

자 이제 컬을 날려보자. 두구두구두구두구두구두구

console.log 로 paylod 값을 찍었을 때 잘 나온다.. 좋다~

이게 현재 재고가 없기 때문에 stock-increase-lambda 로 전송되어야 한다.
바로 밑에 로그를 보니 정상적으로 간걸 확인했다.

마지막으로 stock-increase-lambda log 를 확인해보자. 들어오는 걸 확인했다. curl 을 계속 날려보자.

재고가 없으면 구매실패가 뜨고 잠시 있다가 3개가 또 추가된다.

성공

이로써 마무리하려고 하는.... 이 아니라 이게 되네? 왜지...

왜 되는거지?

분명 전날에 프로젝트 팀원들 끼리 안되서 9시까지 붙잡고 있다가 겨우 해결한건데... ㅜㅜ 당연히 안될줄 ...

입곱번째 트러블

우선 프로젝트는 성공이다.

다만 우리가 겪었던 트러블에 대해 적어보도록 한다.

로그와 기록은 없지만 내 기억을 토대로 적어보려고 한다.

우선 curl 명령을 날리게되면 정상적으로 stock-lambda 까지 도달하고 거기서 stock-increase-lambda 까지 가는걸 확인 했으나 로그에 아무 내용도 찍히지 않는 상황이 발생했다.

나는 stock-lambda 에 index.js 파일을 잘못 수정했나 하여 이거만 붙잡고 있었는데 귀인이 등장.

stock-increase-lambda handler.js 에 console.log(incremental) 값을 입력하여 확인하니 원래는 우리가 cnt 값을 입력한 3이 들어가야 하는데 undifiend 가 출력되는게 아닌가...?

이거구나... 바로 handler.js 파일을 수정했다.

위의 기존의 내용을 아래와 같이 수정했다.

결과는 성공.

완성 다이어그램 및 피드백

태초의 다이어그램


프로젝트를 시작하기전 요구사항만 확인하여 구성한 태초의 다이어그램이다

프로젝트 후 다이어그램

피드백

대체적으로 이동경로는 비슷하나 sns 서비스와 lambda 함수를 하나 더 추가한 모습

좌측 상단의 도메인은 구매요청 서비스
우측 하단의 도메인은 재고요청 서비스
중앙하단의 도메인은 재고생산 서비스

SQS 로 바로 연결안하고 SNS 를 연결하는 이유는?
SNS 의 확장성 때문.
추후에 SNS 를 이용 SQS, Lambda, HTTP, SMS, 이메일, 모바일 애플리케이션 엔드포인트 에 연결할 수 있는 확장성 때문에 사용

Factory API 의 관리는 누가해야하나?

이 부분에서는 정답은 없다. 하지만 개인적인 생각으로는 구매요청을 관리하는 팀에서 해야한다고 생각한다.
Factory 에서 지정한 형식으로 받아서 재고 생산을

MSA 장점

  1. 배포
  • 서비스별 개별 배포가 가능하다(배포시 전체 서비스의 중단이 없다.)
  • 특정 서비스의 요구사항만을 반영하여, 빠르게 배포 가능하다
  1. 확장
  • 특정 서비스에 대한 확장성(scale-out)이 유리하다
  • 클라우드 기반 서비스 사용에 적합하다
  1. 장애
  • 일부 장애가 전체 서비스로 확장될 가능성이 적다.
  • 부분적으로 발생하는 장애에 대한 격리가 수월하다.
  1. 그 외
  • 새로운 기술을 적용하기 유연합니다.(전체 서비스가 아닌 특정 서비스만 별도의 기술 또는 언어로 구현 가능)
  • 각각의 서비스에 대한 구조 파악 및 분석이 모놀리식 구조에 비해 쉽습니다.

추가 시나리오.

피드백

우선 발표할때 도메인 별로 설명하는게 듣는 사람으로써 이해하기가 쉽다.

sns 에서 SQS 로 보내고 Email-lambda 로 갈 필요 없이 바로 SES 서비스로 연결해도 상관없다.
그럼 왜 factory 에도 SES 서비스를 사용하지 않는걸까?

이메일의 경우 바로바로 전송이 가능하지만 Factory 의 경우 현실에서는 물건을 제조하고 만드는데 시간이 소요되기 때문에 비동기식 전송해야 하므로...? 💡 이건 다시 물어볼 예정...

Lambda 의 수평확장은 serverless 라 자동적으로 확장된다.
lambda 의 과금 요소는 요청수 ,작동시간, 메모리 을 기반으로 과금이 책정된다.

피드백

이 시나리오에 대해서는 2가지 안을 제시했다. 크루님의 설명을 듣고 난 b 안이 더 효율적이라고 생각했다.

a 안은 sales api 에서 sns 를 통해 customer sqs 로 이동하고 거기서 또 구매갯수가 100 개인지 아닌지 확인하는 lambda 를 붙인 후 mysql 로 전송하는 다이어그램이지만 이것도 가능은 하다. 하지만 쓸데없는 비용이 들어가고 있는거 같다는 말을 듣고 공감하였다.

sns 를 거치게 되면 sns 비용이 부과되니 같은 일을 두번한다는 생각이 들었다.

b 안 sns 거치지 않고 sqs 를 통해 lambda 함수로 전달되는 모습이다.

내생각 sales API 에서 100개 이상 구매한 고객은 lambda 를 거치지 않고 바로 고객정보 ec2 의 db 로 전달할 수 있지 않을까 생각해봤다.

테라폼으로 구성 해야되는데... 시작부터 막막하다... 귀인을 구합니다...

profile
내기 이해한 것을 보관하는 곳

0개의 댓글