🗽

【re:Invent 2022】分離されたマイクロサービスのワークショップに参加してきました

2022/11/30に公開

AWS re:Invent 2022で行われた、「Decoupled microservices」のワークショップレポートです
この記事は、要点・見どころ・ポイントについてまとめます

所要時間10分程度

ワークショップ概要

Title

Decoupled microservices

分離されたマイクロサービス

Overview

Companies may deal with integration challenges in many areas and on many layers, and all integration approaches come with their individual trade-offs. However, loosely coupled integration can not only help with designing independent systems that can be developed and operated individually but can also increase availability and reliability of the overall systems landscape. In this workshop, implement fundamental integration patterns in a serverless microservices scenario on AWS. The focus is on standard integration patterns for loose coupling and asynchronous communication and how simply they can be implemented with cloud-native services on AWS. You must bring your laptop to participate.
企業は多くの分野、多くのレイヤーで統合の課題に取り組む可能性があり、すべての統合アプローチには個々のトレードオフが伴います。しかし、疎結合の統合は、個別に開発・運用できる独立したシステムの設計に役立つだけでなく、システム全体のランドスケープにおける可用性と信頼性を向上させることが可能です。このワークショップでは、AWS上のサーバーレスマイクロサービスシナリオで基本的な統合パターンを実装します。疎結合と非同期通信のための標準的な統合パターンに焦点を当て、それらがいかにシンプルにAWS上のクラウドネイティブサービスで実装できるかを説明します。参加にはノートPCの持参が必要です。

Services

Amazon EventBridge, Amazon Simple Notification Service (SNS), Amazon Simple Queue Service (SQS)

Session type

Workshop

Speakers

  • Mithun Mallick, Principal Specialist SA, Integration Services, AWS
  • Dirk Fröhner, Principal Solutions Architect, Amazon Web Services

Report

2時間内での作業となりました

会場の雰囲気

ワークショップのためPC持参で、もくもく会のような雰囲気でした
参加者数は50名程度で満席になりました

そもそもマイクロサービスとは?

簡単に説明すると小さな独立した複数のサービスで、ソフトウェアを構成するアプローチ手法です
開発期間を短縮できたり、スケーリング、耐障害性、他サービスへの影響を与えないなど色々メリットあります

マイクロサービスをもっと知りたい方はこちら↓
https://aws.amazon.com/jp/microservices/

ワークショップスタート

今回ネタとなるお題は..

今からユニコーン位置情報のアプリをマイクロサービスで実現させっぞ!

AWSではよくお題でユニコーンのキャラクターが出題・登場します

https://youtu.be/UJxog8pDydU

要件

各ユニコーン(エンドユーザーで捉えてもらえばよい)には、位置情報やバイタルサインを報告するセンサーがついてます
あなたはWild Rydesアプリを提供してる架空の会社を運営していて、いろんなユニコーン提供者が、Wild Rydesと提携するためにプラットフォームが用意されてます
Wild Rydesアプリを利用する顧客は、モバイルアプリからの乗車リクエストを送信できます

という 仮想の設定内容 でワークショップを進めていきます
ここはそういうお題なんだってことで把握だけしておきましょう

そしてここから、非同期メッセージングを使用して、マイクロサービス化されたサーバーレスアプリケーションを構築します

自分の冒険を選ぼう


出題された4つのラボから、どれかを選んで実践することになりました
今回は、 「ラボ2: Topic-queue chaining & load balancing(トピックキューチェーンと負荷分散)」 を選択しました

そして、ユニコーンの乗車完了通知は↓の構成になります

上記のようなマイクロサービスアーキテクチャで、「トピックキューチェーンと負荷分散」のラボを実践していきます

大まかな流れとしては

  1. Wild Rydesアプリのユニコーンからリクエスト
  2. API Gateway + Compute(乗り物プロセッサー)の乗車サービスを起動
  3. 以降、各コンポーネントサービスへ水平で起動させる仕組みを構成
    • 顧客通知サービスへ
    • 顧客会計サービスへ
    • 顧客忠実サービスへ
    • データレイク統合サービスへ
  4. レスポンス

ここから作業が始まります

ワークショップ前提条件

AWSアカウント用意

予めre:Invent運営側で、今回のワークショップのためにAWSアカウントが用意されたのでそちらを利用しました
AWSアカウントがない場合は作成しましょう(無料利用枠あり)

https://aws.amazon.com/jp/register-flow/

IDE Cloud9の用意

こちらも同様にre:Invent運営側で、CloudFormationで作成されたCloud9のスタックが用意されていましたので、Cloud9にログインします
クラウドの統合開発環境(IDE)Cloud9で開発環境を用意するための設定ファイルを準備しました

Cloud9に既に用意されていたソースコードは↓から確認できます
https://github.com/aws-samples/asynchronous-messaging-workshop/tree/master/code/lab-1

【実践!!】Topic-queue chaining & load balancing Architecture

リソースの作成と設定

AWSマネジメントコンソールから作成するケースも記載されてましたが、手っ取り早くAWS SAM CLIを利用してデプロイするやり方にしました

AWS SAMは、YAML構文でサーバーレスアプリケーションを構築することができるフレームワークです
https://aws.amazon.com/jp/serverless/sam/

Cloud9起動後に事前にSAM CLIも利用できるようになっていたのでそのまま作業に取り掛かります

初期設定

統合を含むユニコーン管理サービスの初期設定を構築します

ここで用意するのはAPIGateway, サービスごとのLambda、DynamoDBです

sam cliを利用してビルドを行い、アプリケーションをデプロイします

$ sam build
$ export AWS_REGION=$(aws --profile default configure get region)
$ sam deploy \
    --stack-name wild-rydes-async-msg-2 \
    --capabilities CAPABILITY_IAM \
    --region $AWS_REGION \
    --guided

sam deploy時に確認項目が入りますがSubmitRideCompletionFunctionの承認定義の質問事項をyesで回答して、それ以外はデフォルトでEnterを押して進みます

CloudFormationでスタックが作成され、CREATE_COMPLETE後にリソースが作られたかどうか確認します

Amazon SNSトピックを作成

ここから乗車サービスのLambdaからSNSトピックをPublishするため、Amazon SNSトピックを作成します

Cloud9上で用意されているラボごとのディレクトリに用意されているtemplate.yamlにSNSリソースを追加していきます

template.yaml
  RideCompletionTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: RideCompletionTopic

追加したリソースをビルド後、デプロイします

$ sam build
$ sam deploy

これでSNSが作成されます

顧客通知サービスのサブスクリプションを作成

ここでは顧客通知サービスで利用するAmazon SNSのサブスクリプション経由で起動させるAmazon SQSのキューを作成していきます

template.yamlに追加するリソース、ポリシーを追加します

template.yaml
  # SQSを標準キューとして定義
  CustomerNotificationServiceQueue:
    Type: AWS::SQS::Queue
  # SNSにSQSに対してPublishするポリシーを定義
  CustomerNotificationServiceQueuePolicy:
      Type: AWS::SQS::QueuePolicy
      Properties:
        Queues:
          - !Ref CustomerNotificationServiceQueue
        PolicyDocument:
          Statement:
            Effect: Allow
            Principal: '*'
            Action: sqs:SendMessage
            Resource: '*'
            Condition:
              ArnEquals:
                aws:SourceArn: !Ref RideCompletionTopic
  # SNSのサブスクリプションフィルターにSQSのArnを定義
  CustomerNotificationServiceQueueToRidesTopicSubscription:
      Type: AWS::SNS::Subscription
      Properties:
        Endpoint: !GetAtt CustomerNotificationServiceQueue.Arn
        Protocol: sqs
        RawMessageDelivery: true
        TopicArn: !Ref RideCompletionTopic
  # 顧客通知サービスのLanbdaに対してSQSからの起動とアクセス権限のポリシーも定義
  CustomerNotificationService:
      Type: AWS::Serverless::Function
      Properties:
        CodeUri: generic-backend-microservice/
        Handler: app.lambda_handler
        ReservedConcurrentExecutions: 5
        Environment:
          Variables:
            SERVICE_NAME: CustomerNotificationService
        Policies:
          - SQSPollerPolicy:
              QueueName: !Ref CustomerNotificationServiceQueue
        Events:
          CustomerNotificationServiceJobQueue:
            Type: SQS
            Properties:
              Queue: !GetAtt CustomerNotificationServiceQueue.Arn
              BatchSize: 1

追加したリソースをビルド後、デプロイします

$ sam build
$ sam deploy

新しいAmazon SQSキューとAmazon SNSサブスクリプションをデプロイされたかを確認します

顧客会計サービスのサブスクリプションを作成

次に顧客会計サービスのAWSリソースも同様に作成していきます

template.yaml
  # SQSを標準キューとして定義
  CustomerAccountingServiceQueue:
    Type: AWS::SQS::Queue
  # SNSにSQSに対してPublishするポリシーを定義
  CustomerAccountingServiceQueuePolicy:
      Type: AWS::SQS::QueuePolicy
      Properties:
        Queues:
          - !Ref CustomerAccountingServiceQueue
        PolicyDocument:
          Statement:
            Effect: Allow
            Principal: '*'
            Action: sqs:SendMessage
            Resource: '*'
            Condition:
              ArnEquals:
                aws:SourceArn: !Ref RideCompletionTopic
  # SNSのサブスクリプションフィルターにSQSのArnを定義
  CustomerAccountingServiceQueueToRidesTopicSubscription:
      Type: AWS::SNS::Subscription
      Properties:
        Endpoint: !GetAtt CustomerAccountingServiceQueue.Arn
        Protocol: sqs
        RawMessageDelivery: true
        TopicArn: !Ref RideCompletionTopic
  # 顧客会計サービスのLanbdaに対してSQSからの起動とアクセス権限のポリシーも定義
  CustomerAccountingService:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: generic-backend-microservice/
      Handler: app.lambda_handler
      ReservedConcurrentExecutions: 5
      Environment:
        Variables:
          SERVICE_NAME: CustomerAccountingService
      Policies:
        - SQSPollerPolicy:
            QueueName: !Ref CustomerAccountingServiceQueue
    Events:
       CustomerAccountingServiceJobQueue:
         Type: SQS
         Properties:
           Queue: !GetAtt CustomerAccountingServiceQueue.Arn
           BatchSize: 1

追加したリソースをビルド後、デプロイします

$ sam build
$ sam deploy

新しいAmazon SQSキューとAmazon SNSサブスクリプションをデプロイされたかを確認します

並外れな乗り物サービスのサブスクリプションを作成

こちらも上記2点のサービスと同じ流れでAWSリソースを作成していきます

template.yaml
  # SQSを標準キューとして定義
  ExtraordinaryRidesServiceQueue:
    Type: AWS::SQS::Queue
  # SNSにSQSに対してPublishするポリシーを定義
  ExtraordinaryRidesServiceQueuePolicy:
      Type: AWS::SQS::QueuePolicy
      Properties:
        Queues:
          - !Ref ExtraordinaryRidesServiceQueue
        PolicyDocument:
          Statement:
            Effect: Allow
            Principal: '*'
            Action: sqs:SendMessage
            Resource: '*'
            Condition:
              ArnEquals:
                aws:SourceArn: !Ref RideCompletionTopic
  # SNSのサブスクリプションフィルターにSQSのArnを定義
  ExtraordinaryRidesServiceQueueToRidesTopicSubscription:
      Type: AWS::SNS::Subscription
      Properties:
        Endpoint: !GetAtt ExtraordinaryRidesServiceQueue.Arn
        Protocol: sqs
        RawMessageDelivery: true
        TopicArn: !Ref RideCompletionTopic
        FilterPolicy: { "fare": [{"numeric": [">=", 50]}], "distance": [{"numeric": [">=", 20]}] }
  # 並外れな乗り物サービスのLanbdaに対してSQSからの起動とアクセス権限のポリシーも定義
  ExtraordinaryRidesService:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: generic-backend-microservice/
      Handler: app.lambda_handler
      ReservedConcurrentExecutions: 5
      Environment:
        Variables:
          SERVICE_NAME: ExtraordinaryRidesService
      Policies:
        - SQSPollerPolicy:
            QueueName: !Ref ExtraordinaryRidesServiceQueue
      Events:
        ExtraordinaryRidesServiceJobQueue:
          Type: SQS
          Properties:
            Queue: !GetAtt ExtraordinaryRidesServiceQueue.Arn
            BatchSize: 1

追加したリソースをビルド後、デプロイします

$ sam build
$ sam deploy

新しいAmazon SQSキューとAmazon SNSサブスクリプションをデプロイされたかを確認します

ユニコーン管理サービスを更新

ここまで作業が完了していると現状構成は下記のようになっています

ここからLambda関数にAmazon SNSの公開メッセージ許可を付与するポリシーを追加します

template.ymal
Resources:
  SubmitRideCompletionFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: unicorn-management-service/
      Handler: app.lambda_handler
      Environment:
        Variables:
          TABLE_NAME: !Ref RidesTable
          # SNSのトピックArnを追加
          TOPIC_ARN: !Ref RideCompletionTopic
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref RidesTable
        # SNSをPublishするアクセス許可設定されてるポリシーを追加
        - SNSPublishMessagePolicy:
            TopicName: !GetAtt RideCompletionTopic.TopicName
      Events:
        WildRydes:
          Type: Api
          Properties:
            Path: /submit-ride-completion
            Method: post

続いて、Lambdaハンドラーapp.pyにSNSトピックをPublishするように、処理を変更します

app.py
config = Config(connect_timeout=5, read_timeout=5, retries={'max_attempts': 1})
dynamodb = boto3.client('dynamodb', config=config)
# Lambda内からSNS操作を行うため、追加
sns = boto3.client('sns', config=config)
app.py
   response = dynamodb.put_item(
        TableName=TABLE_NAME,
        Item={
            'id': {'S': id},
            'from': {'S': request['from']},
            'to': {'S': request['to']},
            'duration': {'N': str(request['duration'])},
            'distance': {'N': str(request['distance'])},
            'customer': {'S': request['customer']},
            'fare': {'N': str(request['fare'])}
        }
    )
     # Lambda内からSNS操作を行うため、追加
   response = sns.publish(
       TopicArn=TOPIC_ARN,
       Message=json.dumps(request),
       MessageAttributes = {
           'fare': {
               'DataType': 'Number',
               'StringValue': str(request['fare'])
           },
           'distance': {
               'DataType': 'Number',
               'StringValue': str(request['distance'])
           }
       }
   )

追加/変更したリソースをビルド後、デプロイします

$ sam build
$ sam deploy

これでアプリケーションの構築は一通り終わりました
つぎに確認に進みます

テストする

Amazon SNSトピックがすべてのメッセージをすべてのサブスクライバーにPublishしていることを検証していきます
サブスクライバーもメッセージの処理に失敗する可能性があるので、Amazon SNSがメッセージを再配信していることも検証します

curlコマンドを使って擬似的にユニコーンからアクセスさせます
並外れな乗り物サービスのフィルターをテストするときは、リクエストペイロードを変更して5回以上APIを実行してください

curl -XPOST -i -H "Content-Type\:application/json" -d '{ "from": "Berlin", "to": "Frankfurt", "duration": 420, "distance": 600, "customer": "cmr", "fare": 256.50 }' $ENDPOINT

結果として処理に失敗された場合に、Amazon SNSからのメッセージの再配信をしていることで負荷分散

テストして成功するか確認しましょう! 上手くいけば起動されます
今回のワークショップはここまでです

説明に含めてませんが、ラボ1,3,4もやってみたい方はこちらから実践可能です
https://catalog.us-east-1.prod.workshops.aws/workshops/e8738cf6-6eb0-4d1d-9e98-ae240d229535/en-US

あとは作成したリソースを使わない場合、料金発生するので消しましょう

まとめ

AWS SAMを利用した構築だとマネジメントコンソールからAWSリソースを作るより、手っ取り早く構築ができるので便利だなと再確認できたのは事実
ただし、理解ができてないレベルであればAWS SAMを使うより、まずはマネジメントコンソールからAWSリソースを作成して知識を身に着けていくことがいいでしょう
時間があれば、全てのラボをやってみると分離されたマイクロサービスの理解がより深まるのではと考えています。

以上

Discussion