요구사항과 시나리오

시나리오

<도넛-스테이츠>는 온라인으로 도넛을 판매합니다. 웹사이트를 통해서 주문 버튼을 누르는 것으로 구매(Sales API)가 가능합니다. 창고에 재고가 있다면 재고가 감소하고 구매가 완료됩니다. 유튜브스타 hoyong.LEE가 도넛-스테이츠의 도넛이 맛있다고 영상을 올렸습니다. 그를 따르는 데브옵스 수강생들이 몰려듭니다. 주문이 급등합니다. 창고에 재고가 없기 때문에 구매가 불가능한 경우가 발생합니다. 창고의 도넛의 재고가 다 떨어지면 제조 공장에 알려서 다시 창고를 채우는 시스템을 구축해야 합니다. 제조 공장인 <팩토리-스테이츠>에 주문을 요청(Leagcy Factory API)할 수 있습니다. 주문이 요청되면 일정 시간이 지난 후 창고에 재고가 증가합니다.

상황

비효율적인 레거시 시스템 때문에 고객의 불만사항이 접수되고 있습니다. 제품별 재고부족 요청이 빈번하게 발생되고 있지만 전달 과정에서 지연과 누락 등 문제 상황이 발생하고 있습니다. 안정적으로 요청이 전달 될 수 있도록 시스템을 개선해야합니다. 비정상적으로 처리된 요청의 경우 운영팀에 상황을 알려야합니다.



요구사항 1 : 재고부족으로 인한 구매실패에 대한 조치

  1. Sales API 를 통해 요청을 받은 서버가 데이터베이스에서 재고 상황을 확인합니다.
  2. 재고가 있다면 감소시키고 응답으로 판매완료 내용을 전달합니다.
  3. 재고가 없는 경우 공장에 주문을 진행합니다
  4. 재고가 없다는 내용을 담은 메세지 페이로드와 함께 SNS topic이 생성됩니다.
  5. 메시지가 SQS로 구현된 stock_queue에 수신됩니다.

 

요구사항 2 : 메시지 누락 상황에 대한 조치

  1. 빈번한 요청으로 메시지 누락이 발생합니다.
  2. SQS에서 처리완료되지 않은 메시지들을 체계적으로 관리할 dead_letter_queue를 생성해야합니다.
  3. stock_queuedead_letter_queue를 연결합니다.

 

요구사항 3 : Legacy 시스템(Factory → Warehouse) 성능문제에 대한 조치

  1. 안정적으로 이벤트가 전달 될 수 있는 시스템을 구축해야합니다.
  2. stock_queue 의 메시지를 소비하는 stock_lambda에 의해 Factory API가 호출됩니다.
  3. Factory의 생산 완료 메시지를 수신한 stock_inc_lambdaDB에 상품 재고를 증가시킵니다.

Day1 : 리소스 다이어그램

작성한 리소스 다이어그램

먼저 USER가 sales api로 도넛 구매요청을 한다.

 만약, 도넛의 남은 재고가 없다면, SNS를 통해서 SQS로 "재고가 없어용 채워야해용" 메시지를 전송하고 SQS에 새 메시지가 들어오는 것을 트리거로 작동하는 Lambda함수를 통해 도넛공장 api로 도넛을 더 구워달라는 요청을 보낸다.

 그리고 도넛공장 api에서 제빵 완료라는 응답을 받은 Lambda는 DB의 남은 도넛의 재고를 증가시키는 매커니즘이다.

 


 

Day2 : 구현

 

 만들어둔 sales API와 재고 DB를 환경변수로 연결 정보를 설정해서 서로 연결해 DB연결이 잘 되는지 확인한다. 그렇게 문제가 없다고 생각해서 SALES API를 Lambda로 배포하고 SNS, SQS를 만들어준다. 그리고 재고부족요청을 POSTMAN으로 보냈는데 SNS로 메시지가 넘어가지 않는다. 그래서 CloudWatch 로그를 뒤적여보니 SNS 권한이 없단다. 그래서 먼저 SNSFullAccess 권한을 Lambda에 부여해주고 테스트를 한다음, 더 작은 권한인 SNS publish 권한을 부여했다. 

 

 그리고 SQS를 트리거로 하는 stock-lambda 람다가 SQS에 들어온 메시지를 가져가서 소비하는지 확인해봤다. 그런데 이번에도 권한이 없다는 메시지를 받아서 해당 Lambda함수의 역할에 SQS메시지 실행권한을 넣어줬더니 아주 잘 작동하는 것을 확인할 수 있었다.

 

 마지막으로 콘솔에서 했던 SNS, SQS 작업들을 serverless에서도 할 수 있도록 serverless.yaml 파일을 작성해줬다. 

service: sales-api
frameworkVersion: '3'

provider:
  name: aws
  runtime: nodejs14.x
  region: ap-northeast-2 #리전 설정.(default: us-east-1)
  iam: #iam으로 부여할 권한을 설정해준다.
    role:
      statements:
        - Effect: 'Allow'
          Action:
            - 'SNS:Publish' 
          Resource: !Ref stockTopic
  timeout: 15

constructs:
  stockQueue:
    type: queue
    worker:
      handler: stock.handler #핸들러 매핑시켜주기

functions:
  api:
    handler: api.handler
    events:
      - httpApi: '*'


resources:
  Resources:
    stockTopic: #SNS 토픽 설정
      Type: AWS::SNS::Topic
      Properties:
        TopicName: stock_empty
    stockSub: #SNS 토픽 구독 설정
      Type: AWS::SNS::Subscription
      Properties:
        TopicArn: !Ref stockTopic
        Endpoint: ${construct:stockQueue.queueArn}#큐의 ARN
        Protocol: sqs
        RawMessageDelivery: true

    stockQueuePolicy: #SQS에 접근할 수 있도록 권한을 부여한다.
      Type: AWS::SQS::QueuePolicy
      Properties:
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                Service: sns.amazonaws.com
              Action: 'sqs:SendMessage'
              Resource: ${construct:stockQueue.queueArn}
              Condition:
                ArnEquals:
                  aws:SourceArn: !Ref stockTopic
        Queues:
          - ${construct:stockQueue.queueUrl} #해당 큐의 URL

plugins:
  - serverless-lift

 


Day3 : 구현 및 추가 요구사항에 대한 다이어그램 작성

받아온 Stock empty 함수에서 Factory API에 원할하게 도넛생산요청을 보내고 Factory API의 응답에 따라서 stock increase function이 재고DB에 재고를 추가한다. 아래는 간단하게 작성해본 stock empty 함수 배포용 serverless.yaml이다. js코드만 잘 작성해주니 오류없이 편안하게 작동한다 ㅎㅎㅎ

service: stock-lambda
frameworkVersion: '3'

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

functions:
  consumer:
    handler: index.consumer
    events:
      - sqs: arn:aws:sqs:ap-northeast-2:비밀이에요 ㅎㅎ:stock_empty

plugins:
  - serverless-lift

추가 요구사항

  • 추가 시나리오에 대한 아키텍처를 구성해야 합니다.
    • 실제 구현하지 않아도 아키텍처상에 리소스 추가 배치
  • 확장된 아키텍처를 가지고 팀의 의견을 제시할 수 있어야 합니다.

a. 광고 중단 요청 진행 시나리오

재고가 없는 상황에서도 광고가 계속 진행되고 있습니다. 광고 비용 절감과 고객불만을 낮추기 위한 조치가 필요합니다. 메시지가 유실되는 상황을 막기 위해 내구성을 갖춘 시스템이 필요합니다.

  • 요구사항
    • 재고를 채우기 위한 과정이 진행될 때 광고 담당자에게 광고 중단 요청 내용을 담은 이메일이 전송되어야 합니다.
    • 메시지에 대한 내구성을 강화하기 위해 메시지 Queue가 사용되어야 합니다.
    • AWS SES 서비스를 이용해서 이메일을 전송해야 합니다.

b. VIP 고객관리 프로세스 추가 시나리오

모니터링 결과 대량 주문을 하는 일부 고객들이 확인되었습니다. 대량 구매 고객들의 사용자 정보를 식별할 수 있어야 합니다. 고객정보는 별도의 서버(EC2)와 데이터베이스(RDS)에서 관리되고 있습니다. 데이터베이스 기록과 외부 마케팅 시스템으로의 연결과정의 오류를 대비하기 위한 내구성 갖춘 시스템이 필요합니다.

  • 요구사항
    • 100개 이상 구매가 발생 시 해당 유저의 타입이 normal에서 Vip로 변경되어야 합니다.
    • 메시지에 대한 내구성을 강화하기 위해 메시지 Queue가 사용되어야 합니다.
    • 고객관리는 별도의 데이터베이스(RDS)로 관리되고 있기 때문에 해당 데이터베이스에 접근해서 정보를 수정해야 합니다.

추가 요구사항 반영한 다이어그램

추가 시나리오  a. 광고중단

재고주문알림의 메시지에 광고주에 대한 정보도 담아서 SQS에 메시지를 전달하면 해당 메시지를 받은  Stock Lambda가 메시지를 처리해 SES서비스를 통해 광고주에게로 광고중단메일을 송신한다.

추가 시나리오  b. 고객관리

먼저 100개 이상 주문하는 고객이 생기면 sales api에서 sns를 통해 VIP 고객관리 태그에 속한 SQS로 메시지를 보낸다. SQS를 트리거로 하는 Lambda가 해당 메시지를 받아서 EC2로 넘기게 된다. 그렇게 되면 EC2는 해당 메시지를 처리해 RDS에 담겨있는 고객 정보를 수정한다.

여기서 어째서 컴퓨팅 리소스를 EC2와 람다 2개를 같이 붙여서 사용하는지에 대한 의문이 들 수 있다. SQS에서 EC2가 메시지를 곧바로 받아오기 위해선 SQS를 풀링하는 코드를 실시간으로 실행해야하는데, Lambda의 트리거 기능을 사용하면 EC2에서 계속 풀링할 필요가 없어지기 때문에 EC2의 리소스를 절약하기 위해서 Lambda를 사용했다.

 

 


마치며..

이번 프로젝트는 내가 어떤 작업을 해야할지 명확하게 파악할 수 있도록 가이드라인을 만들어서 진행과정 중에 혼선이 적었다. 역시 프로젝트 초기 단계에서 방향성을 명확하게 하는 것이 아주 중요하다는 것을 다시한번 마음에 세길 수 있었고, IaC작업은 여전히 신기하고 흥미롭다 ㅎㅎㅎ. 아직은 리소스 하나를 작성하는데에도 많은 문서를 뒤적여야하는 응애이지만, 잘 쓸 수만 있다면 손쉽게 배포를 자동화시킬 수 있는 해피한 엔지니어가 될 수 있을 거라 믿는다.

+ Recent posts