メインコンテンツに移動
デベロッパーのためのクラウド活用方法

ソフトウェアの「手の内化」を推し進めるサステナブルな学習環境を内製化してみた

2021-05-02 | Author : 濵﨑 智宏 (株式会社デンソー)

はじめに

こんにちは、株式会社デンソーの濵﨑です。

私はこれまで AWS が主催する、AWS GameDay や AWS Jam に参加し、研鑽を積んできました。初めて、参加したのは 2019 年 6 月に AWS Loft Tokyo で開催された AWS GameDay です。結果はさて置き。AWS Loft Tokyo から新幹線で名古屋に戻り、里帰り中の妻から陣痛が来たと連絡を受け、翌朝、始発で山口に帰りましたが、出産に間に合わなかったことは今でも鮮明に覚えています。その後、2020 年にロシアで開催された技能五輪国際大会にて、クラウドコンピューティング職種の日本代表選手として参加し、銅メダルを獲得することができました。

このような経験を社内の他のメンバーにも共有したいとの思いから、社内で有志を集め、DX 人材強化策の一環として、社内コンテストを企画し、AWS Jam を参考にしたコンテストシステムを開発し、2022 年 2 月 19 日に開催された社内イベントで社員 108 名に提供しました。この記事では、このコンテストシステムについて紹介します。


X ポスト » | Facebook シェア » | はてブ »

builders.flash メールメンバー登録

builders.flash メールメンバー登録で、毎月の最新アップデート情報とともに、AWS を無料でお試しいただけるクレジットコードを受け取ることができます。

今すぐ登録 »

「手の内化」とは

「手の内化」という言葉をご存じでしょうか ? トヨタグループで使われている言葉です。

「先ずは自らやってみて、原理原則を理解すること、そして現場で改善を繰り返し、競争力を高めること」と紹介されています。私は「手の内化」はトヨタグループの基本姿勢であると理解し、ソフトウェアにおいても、他社に任せっきりになることなく、手の内化を進めるべきと考え、コンテストシステムの内製化に挑戦しました。

Two direction signs against the sky, one pointing right labeled 'IN-HOUSE' and one pointing left labeled 'OUTSOURCE', illustrating a choice between in-house and outsourcing options.

コンテストシステム「Minoru」

このシステムは「Minoru」と名付けました。
その由来は初めて自分で作ったシステムであり、今後も育てていく、という思いから息子の「実」の名前を取りました。また、「Minoru」を使って学習した人の努力が「実る」とも掛けています

「Minoru」の今後の展望としては、利用者の方が任意のタイミングで自分用の環境を構築し、公開されている課題にチャレンジすることができ、また、利用者自身でも課題を開発し、それを共有できるようにすることで、自然とバリエーションが増えることを期待しています。これによって「サステナブルな」学習環境を構築していくことができると考えています。

AWS GameDay / AWS Jam とは

皆さん、AWS GameDay や AWS Jam には参加したことがありますか ? それぞれ、AWS のイベントなどで開催されるコンテキスト形式の学習コンテンツです。コロナ禍の現在、オンラインでの開催になっていますが、私が参加した当時は、AWS Loft などに集まって、初対面の方数人とチームを組んで参加する形でした。リアルタイムに更新されるスコアを見ながら、協力してプレイすることは、エキサイティングで楽しかったです。それぞれ下記のようなコンテンツになっています。私は、AWS GameDay や AWS Jam は AWS やシステム運用のスキルを深めるために非常に有効な学習コンテンツだと思います。しかし、これらは常時開催されているものではなく、経験できるチャンスは年に数回しかありません。この点についても内製化に踏み切る動機になりました。

AWS GameDay

与えられたシステムと手順書を元に実際のサービスを AWS 上で運用します。途中で様々な障害などが発生し、チームでそれらに対処し、システムを安定的に高いパフォーマンスで運用できると高得点が取れるようになっています。お題に沿って自ら考えてシステムの構築、障害対応、パフォーマンス改善、自動化などをする必要があり、それらを通して、AWS のベストプラクティスを学ぶことができます。

AWS Jam

様々なユースケースをお題として、小さな課題が複数用意されています。課題を解決することで得点を得ることができ、その得点を競います。使うと獲得できる点数は減ってしまいますが、Hint も用意されています。普段触ることのない AWS サービスに触ったり新しいスキルを身に着けることができます。

1. アーキテクチャ概要

「Minoru」の構築に利用するオペレーション環境には AWS Cloud9 を利用しました。
「Minoru」の大半は、AWS Serverless Application Model (AWS SAM) を利用して構築することができます。Cloud9 にソースコードを展開後、 sam packagesam deploy を実行することで環境構築が完了するようにしています

次に AWS CLI を利用して Amazon S3 へ課題のドキュメントで参照する画像をアップロードし、AWS SDK for Python (Boto3) を利用して Amazon DynamoDB へ課題のドキュメントや得点などのデータを挿入します。

私は、シンプルな操作は AWS CLI を、複雑な操作は AWS SDK を利用するようにしています。 AWS CLI は、aws s3 help と打つことで手元でヘルプを見ることができ、さっと目的を達成したいときに向いています。しかし、複雑な処理を行いたい時や失敗する可能性もあるときには、AWS SDK を使うと便利です。AWS SDK は API 呼び出しのリトライ機構が組み込まれており、言語ごとの例外処理機構を使ったエラーハンドリングも実装しやすくなっています。

Architecture diagram illustrating the AWS Cloud Organizer environment, featuring components such as CloudFront, API Gateway, Lambda, EC2 Auto Scaling group, CloudFormation, DynamoDB, and S3, along with client interactions and resource setup workflows.

2. 使用している技術

  • 開発運用環境 : AWS Cloud9、シェルスクリプト、Python スクリプト、Docker、AWS Serverless Application Model (AWS SAM)
  • バックエンド
    • Webアプリケーション : Flask + Jinja2 + AWS SDK for Python (Boto3)
    • REST API : boto3 (Python)
  • フロントエンド : JavaScript、Bootstrap
  • フロントエンドの開発に経験がなくかなり苦戦し時間をかけてしまいましたが、最終的には、他の方に助けてもらいかっこよくしてもらいました。

    3. 工夫したポイント

    ここからは、「Minoru」の開発にあたり、工夫したポイントを 3 つ紹介します。

    1. 複数アカウントでのデプロイ管理

    2. 同一 AWS アカウント内に複数の課題を共存させ、権限により分離する

    3. クリーンアップ

    3-1. 複数アカウントでのデプロイ管理

    AWS Jam は様々な難易度設定がされた小さな課題が複数用意されています。「Minoru」でも同様の構成をとっています。各課題は AWS CloudFormation を利用して初期構成を行いました。

    今回は 40 の AWS アカウントにそれぞれ 20 個の課題を用意していたので合計 800 の CloudFormation スタックを作成する必要がありました。1 つのスタックから 800 個のスタックを順番に作っていると日が暮れてしまいます。そこで、Docker を利用した並列デプロイの仕組みを作成しました。1 つの課題用スタックをデプロイするコンテナはメモリーが 70 MB 程度必要でしたが、t3.2xlarge を利用すると 400 個以上のコンテナを同時に実行できるので 800 スタックをデプロイするのに 30 分もかかりませんでした。

    Architecture diagram illustrating the setup of AWS Cloud9 StackManager for a multi-region challenge, showing the orchestration of containerized resources across multiple AWS regions and environments using StackManager.py and credentials files.

    アーキテクチャの改善

    AWS Serverless Application Model (AWS SAM)と Docker を利用し、コンテナごとに AWS 認証情報を渡して AWS SAM CLI を実行することで、各課題に利用するソースの可搬性を保ちながら並列デプロイを実現することができました。 テスト時には、EC2 インスタンスの空きメモリーに関係なくコンテナを起動したため、Cloud9 の接続は切れ、コンテナは潰れ、大失敗しました。そこで、メモリーを監視しながらコンテナを管理するようにスクリプトを改善しました。一部抜粋して下記にサンプルコードを記載します。 また、今後は、AWS Step Functions と AWS Lambda を活用して改善するとスタック作成をより迅速にかつコストを削減できそうと考えています。

    bash
    cmd="""
    docker run -v `pwd`/Challenges:/Challenges \
        --env RootUserId={} \
        --env AWS_ACCESS_KEY_ID=`echo {} | cut -d , -f 3` \
        --env AWS_SECRET_ACCESS_KEY=`echo {} | cut -d , -f 4` \
        --env AWS_AccountId=`echo {} | cut -d , -f 5` \
        --env RandomString=`echo {} | cut -d , -f 6 | tr -d '\r'` \
        --env ChallengeTitle=`echo {} | cut -d , -f 1` \
        --env LabId=`echo {} | cut -d , -f 2` \
        --env AWS_DEFAULT_REGION=`echo {} | cut -d , -f 3 | tr -d '\r'` \
        createstack &
    """
    while True:
        proc = subprocess.Popen("free -m | grep Mem | awk '{print $4}'", stdout=subprocess.PIPE, shell=True)
        (out, err) = proc.communicate()
        if int(out) > 100:
            break
        else:
            time.sleep(5)
            print("Free Mem:", int(out), "MB")
    cmd=(cmd.format(
        RootUserId,
        CredentialsItems_list[CredentialsItemIndex_int]['Access key ID'],
        CredentialsItems_list[CredentialsItemIndex_int]['Secret access key'],
        CredentialsItems_list[CredentialsItemIndex_int]['AWS Account Id'],
        CredentialsItems_list[CredentialsItemIndex_int]['RandomString'],
        ChallengesItems_list[ChallengesItemIndex_int]['Title'],
        ChallengesItems_list[ChallengesItemIndex_int]['ChallengeId'],
        ChallengesItems_list[ChallengesItemIndex_int]['Region']
    ))
    os.system(cmd)

    3-2. AWSアカウント内に複数の課題を共存させ、権限により分離する

    AWS Jam のような AWS 上での課題を解決する問題を出すときには、問題ごとにクリーンな環境を用意したくなります。しかし、コンテスト参加者に対して、課題ごとに別の AWS アカウントを用意すると AWS アカウントの数が膨大になってしまいます。 そこで今回は、1 リージョンにつき 1 課題とし、1 つの AWS アカウントに複数の課題が共存できるようにしました。権限を制限する例として今回利用した課題から抜粋し例を示します。

    bash
     LabUserPermissionsBoundary:
        Type: AWS::IAM::Policy
        Properties: 
          PolicyName: LabUserPermissionsBoundary-000ff9fd-b3ddb-s
          Roles: 
            - !Ref LabUserRole
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              # グローバルサービス以外のStackを作成したリージョン以外のアクセスを禁止する
              - Effect: Deny
                NotAction: 
                  - a4b:*
                  - acm:*
                  - aws-marketplace-management:*
                  - aws-marketplace:*
                  - aws-portal:*
                  - budgets:*
                  - ce:*
                  - chime:*
                  - cloudfront:*
                  - config:*
                  - cur:*
                  - directconnect:*
                  - ec2:DescribeRegions
                  - ec2:DescribeTransitGateways
                  - ec2:DescribeVpnGateways
                  - fms:*
                  - globalaccelerator:*
                  - health:*
                  - iam:*
                  - importexport:*
                  - kms:*
                  - mobileanalytics:*
                  - networkmanager:*
                  - organizations:*
                  - pricing:*
                  - route53:*
                  - route53domains:*
                  - s3:GetAccountPublic
                  - s3:ListAllMyBuckets
                  - s3:PutAccountPublic
                  - shield:*
                  - sts:*
                  - support:*
                  - trustedadvisor:*
                  - waf-regional:*
                  - waf:*
                  - wafv2:*
                  - wellarchitected:*
                Resource: '*'
                Condition:
                  StringNotEquals:
                    'aws:RequestedRegion':
                      - !Sub '${AWS::Region}'
              # LabUserPermissionsBoundary に対するアクセスを禁止する
              - Effect: Deny
                Action:
                  - iam:RemoveUserFromGroup
                  - iam:DeleteGroup
                  - iam:UpdateGroup
                  - iam:DeletePolicy
                  - iam:DetachGroupPolicy
                  - iam:DeleteUserPermissionsBoundary
                  - iam:DeleteGroupPolicy
                Resource:
                  - !Sub 'arn:aws:iam::${AWS::AccountId}:policy/*LabUserPermissionsBoundary*'
      LabUserRole:
        Type: AWS::IAM::Role
        Properties:
          MaxSessionDuration: 43200
          AssumeRolePolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Principal:
                  AWS: !Sub 'arn:aws:iam::${RootUserId}:root'
                Action: sts:AssumeRole
          Path: /
          Policies:
            # 課題を進めるために必要な許可する
            - PolicyName: labuserpolicy
              PolicyDocument:
                Version: '2012-10-17'
                Statement:
                  - Effect: Allow
                    Action:
                      - s3:ListAllMyBuckets
                    Resource: '*'
                  - Effect: Allow
                    Action:
                      - s3:*
                    Resource:
                      - !Sub 'arn:aws:s3:::${ProductBucket}/*'
                      - !Sub 'arn:aws:s3:::${ProductBucket}'
            # 課題を壊すアクセスを禁止する
            - PolicyName: labbasepolicy
              PolicyDocument:
                Version: '2012-10-17'
                Statement:
                  - Effect: Deny
                    Action:
                      - lambda:*
                    Resource:
                      !GetAtt 'CopyFilesFunction.Arn'
                  - Effect: Deny
                    Action:
                      - logs:*
                    Resource:
                      - !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*:*'
                  # このStackに対するアクセスを禁止する
                  - Effect: Deny
                    Action:
                      - cloudformation:DescribeStackEvents
                      - cloudformation:GetTemplate
                      - cloudformation:DescribeStackResource
                      - cloudformation:ListStackResources
                    Resource:
                      - !Sub '${AWS::StackId}'
                  PolicySimulatorへのアクセス許可
                  - Effect: Allow
                    Action:
                      - iam:GetRole
                      - iam:GetRolePolicy
                      - iam:ListRolePolicies
                      - iam:ListAttachedRolePolicies
                    Resource: 
                      - !Sub 'arn:aws:iam::${AWS::AccountId}:role/*LabUserRole*'
                  - Effect: Allow
                    Action:
                      - iam:ListRoles
                      - iam:GetPolicy
                      - iam:GetPolicyVersion
                    Resource: '*'

    3-3. クリーンアップ

    コンテスト後に無駄な課金が発生しないためにも、クリーアップが必要でした。誰もが一度は、リソース全削除ボタンを切望したのではないでしょうか。しかし、現実にはそんな機能はありません。また、課題の初期構成を行った CloudFormation のスタックを削除するだけでは、参加者が競技中に追加で作成したリソースの削除はできませんし、S3 にファイルを追加した場合など、操作によってはスタックの削除自体が失敗します。そこで 2 Step に分けて削除する方法をとりました。

    • Step 1 : AWS CLI から AWS CloudFormation スタックを削除

    • Step 2 : aws-nuke の実行

    Step 1 を必要とした理由は、Step 2 だけを実行した場合に比べると削除所要時間の短縮と成功率の向上が図れると想定したためです。Step 1 は「3-1. 複数アカウントでのデプロイ管理」で実行した方法と同じ手法を用いています。ここからは、Step 2 の aws-nuke  についてご紹介します。

    3-3-1. aws-nuke について

    aws-nuke は、AWS アカウントのリソースを全て削除する OSS のツールです。 今回は、Docker イメージのビルド時に GitHub からダウンロードしてバイナリを使用しています。

    aws-nuke は非常に強力なツールですので試す際は気を付けてください。コンテナ作成に利用した、Dockerfile と実行スクリプト、aws-nuke の設定ファイルのサンプルを記載します。

    aws-nukeDockerfile

    Dockerfile

    bash
    FROM amazonlinux:2
    
    ENV LANG=en_US.SJIS
    ENV PYTHONIOENCODING=utf-8
    WORKDIR /
    
    # aws cli install
    RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" \
        && yum install -y unzip \
        && unzip awscliv2.zip \
        && ./aws/install \
        && amazon-linux-extras install python3.8 -y \
        && python3.8 -m pip install pillow==8.3.2 \
        && yum install -y less
        
    RUN yum install -y wget tar gzip expect
    
    # aws-nuke のダウンロード
    RUN wget https://github.com/rebuy-de/aws-nuke/releases/download/v2.16.0/aws-nuke-v2.16.0-linux-amd64.tar.gz \
        && tar -zxvf aws-nuke-v2.16.0-linux-amd64.tar.gz
    
    # nuke-config.yaml と実行スクリプトをコンテナに配置する
    COPY nuke-config.yaml /
    COPY Run-nuke.sh /
    
    RUN chmod +x Run-nuke.sh
    
    CMD ["/bin/sh", "Run-nuke.sh"]

    AllDelete.sh

    AWS セキュリティ認証情報を登録したファイル (conf.ini, Credentials.csv) を読み込み、空きメモリーを監視しながら aws-nuke イメージを実行する

    bash
    #!/bin/bash -ex
    INI_FILE=conf.ini
    INI_SECTION=Organizer_Account
    
    # ini parser
    eval `sed -e 's/[[:space:]]*\=[[:space:]]*/=/g' \
        -e 's/;.*$//' \
        -e 's/[[:space:]]*$//' \
        -e 's/^[[:space:]]*//' \
        -e "s/^\(.*\)=\([^\"']*\)$/\1=\"\2\"/" \
       < $INI_FILE \
        | sed -n -e "/^\[$INI_SECTION\]/,/^\s*\[/{/^[^;].*\=.*/p;}"`
        
    OrganizationAccountId=${AccountId}
    
    CREDENTIALS_FILE_NAME='Credentials.csv'
    WORK_CREDENTIALS_FILE_NAME='./w_'$CREDENTIALS_FILE_NAME
    cp $CREDENTIALS_FILE_NAME $WORK_CREDENTIALS_FILE_NAME
    HEADER=`head -n 1 $WORK_CREDENTIALS_FILE_NAME`
    sed -i -e '1d' $WORK_CREDENTIALS_FILE_NAME
    
    while read credential; do
        echo "AWS_AccountId:" `echo ${credential} | cut -d , -f 5`
        
        while true; do
            FreeMemMB=`free -m | grep Mem | awk '{print $4}'`
            if [ $FreeMemMB -gt 100 ]; then
                break
            else
                echo "FreeMemMB" $FreeMemMB
                sleep 5
            fi
        done
        
        docker run -v `pwd`/AllDelete:/AllDelete \
            --env RootUserId=${OrganizationAccountId} \
            --env AWS_ACCESS_KEY_ID=`echo ${credential} | cut -d , -f 3` \
            --env AWS_SECRET_ACCESS_KEY=`echo ${credential} | cut -d , -f 4` \
            --env AWS_AccountId=`echo ${credential} | cut -d , -f 5` \
            nuke &
    done < $WORK_CREDENTIALS_FILE_NAME

    nuke-config.yaml : aws-nuke の設定ファイルのテンプレート

    全てのリージョンを対象に、IAM 以外のリソースを削除する。

    bash
    regions:
    - us-west-1
    - us-west-2
    - us-east-2
    - ap-south-1
    - ap-northeast-1
    - ap-northeast-2
    - ap-northeast-3
    - ap-southeast-1
    - ap-southeast-2
    - ca-central-1
    - eu-central-1
    - eu-north-1
    - eu-west-1
    - eu-west-2
    - eu-west-3
    - sa-east-1
    - me-south-1
    - ap-southeast-3
    - af-south-1
    - ap-east-1
    - global
    
    account-blocklist:
    - "BlockID" # production
    
    resource-types:
      excludes:
      - IAMRole
      - IAMRolePolicy
      - IAMRolePolicyAttachment
      - IAMUser
      - IAMUserAccessKey
      - IAMUserGroupAttachment
      - IAMLoginProfile
      - IAMGroup
      - IAMGroupPolicyAttachment
     
    accounts:
      "TargetID": {} # aws-nuke-example

    Run-nuke.sh

    nuke-config.yaml を更新して対話型の aws-nuke を自動実行する。

    bash
    #!/bin/bash -eu
    
    mkdir -p AllDelete/${AWS_AccountId}
    
    # 運営用アカウントを削除しないよう、nuke-config.yaml にaccount-blocklist を設定する
    echo ${RootUserId}
    sed -i -e "s/BlockID/${RootUserId}/" nuke-config.yaml
    sed -i -e "s/TargetID/${AWS_AccountId}/" nuke-config.yaml
    
    aws iam create-account-alias --account-alias ${AWS_AccountId}-test
    
    script="""
        set timeout -1
        spawn bash -c \"./aws-nuke-v2.16.0-linux-amd64 -c ./nuke-config.yaml --access-key-id ${AWS_ACCESS_KEY_ID} --secret-access-key ${AWS_SECRET_ACCESS_KEY} --no-dry-run\"
        expect \"Do you want to continue? Enter account alias to continue.\"
        send -- \"${AWS_AccountId}-test\r\"
        expect \"Do you want to continue? Enter account alias to continue.\"
        send -- \"${AWS_AccountId}-test\r\"
        expect \"Nuke complete\"
        exit 0
    """
    
    expect -c "$script" 2>&1 | tee /AllDelete/${AWS_AccountId}-log.txt

    実行結果

    イベント実行後、コストが無事に 0 になっていることが確認できて一安心しました。
    ちなみにコスト取得 API を開発し、日ごとのコストを取得できるようにしていました。
    A screenshot of a sample AWS account cost JSON structure showing daily and monthly cost data for 2022 in USD. The image includes example account IDs and cost amounts for various dates.

    4. コストについて

    今回のイベントのコストについて、108 名で 3 時間半実施してどのくらいの費用が掛かったと思いますか ? ずばり、723.85 USD です。 1 チーム当たり 17.42 USD、初めてということで前日に準備しましたので、時間あたりに換算すると 0.67 USD / 時になりました。

    運営に利用した「Minoru」のコストは、26.906 USD でした。この金額をどう考えますか?弊社としては、コンテストの予算として、数百万規模を想定していましたので、破格でした。特に EC2 インスタンス以外サービスのコストの安さには驚きました。

    A Japanese table displaying an AWS service cost breakdown as of 2022/2/18, listing costs for various AWS services such as EC2, CloudWatch, API Gateway, DynamoDB, Lambda, X-Ray, and more, with a total cost at the bottom.

    5. パフォーマンスと監視について

    ここからは、サービスの提供にあたってユーザー体験に大きな影響を与えるパフォーマンスについて開発中に得た学びを 2 つご紹介したいと思います。

  • オブザーバビリティを活用したボトルネックの発見
  • Amazon API Gateway のレスポンスレイテンシーの改善について
  • 5-1. オブザーバビリティを活用したボトルネックの発見

    コンテスト本番中のエラーを把握するために Amazon CloudWatch と AWS X-Ray を使って、各サービスの連携を可視化してみたところ、アプリケーションの設計の見直しに繋がった気づきがありました。

    まず、図の Amazon CloudWatch のメトリクスから Lambda 関数 Properties-Function の実行時間が 2.5 秒を超えていて、他の関数より明らかに時間がかかっていることが分かりました。

    Line graph showing the average duration in milliseconds for multiple AWS Lambda functions over time, including Validatekey-Function, StartChallenge-Function, Properties-Function, Clue-Function, Load-Function, and DashBoard-Function.

    原因と対策

    そのため AWS X-Ray のトレースを確認してみたところ、AWS CloudFormation へのリクエストに時間が掛かっていることが分かりました。ソースコードを確認したところ、この AWS CloudFormation へのアクセスは AWS アカウント、リージョンを跨ぐもので、そのため、大きく遅延が発生しているものと推測しました。

    また AWS CloudFormation のスタックから取得する出力セクションのデータはリアルタイム性を必要としないため、データベースに保存するなどの改善策を考えることができました。

    A screenshot of an AWS X-Ray trace map interface in Japanese, displaying the flow and timing of a GET request across AWS API Gateway, Lambda, DynamoDB, and CloudFormation services. The interface includes response times, process steps, and status codes for each component in a distributed tracing view.

    5-2. Amazon API Gateway のレスポンスレイテンシーの改善について

    次に、AWS X-Ray のサービスマップとトレースビューを利用してのパフォーマンス改善について紹介します。

    こちらの図は Lambda 関数 Load-Function のサービスマップです。これをみると処理に 1 秒も掛かっていることが分かり、ユーザー体験に影響があったため、改善に取り組みました。

    A diagram showing the architecture and performance metrics of an AWS Lambda function interacting with multiple Amazon DynamoDB tables. The visualization displays flow between Lambda functions, API Gateway, EC2, and DynamoDB tables, including average response times and request rates.

    案 1. Lambda 関数のメモリーをスケールアップする

    ログを確認すると下記のようにそもそもメモリを使いきれていなかったので効果が薄いのではないかと思いましたが、Lambda は割り当てるメモリーに比例して割り当てられる CPU も強化されます。 今回は、512 MB から 5120 MB にスケールアップして評価しました。しかし、メモリ割り当てを増やすとコストも上がってしまいます。この関数の場合は、処理時間 (Duration) が 1.0S から 737ms に改善されました。改善される処理時間と単位時間あたりのコスト増加は比較してコスト効率を検討する必要があります。

    bash
    REPORT RequestId: c518157a-0c9c-4f77-a087-f9f0593bbbea Duration: 913.43 ms Billed Duration: 914 ms Memory Size: 512 MB Max Memory Used: 71 MB
    
    XRAY TraceId: 1-61b2acad-5422ef6d7eed5deb52e3d981 SegmentId: 6ccf50315b9740a5 Sampled: true

    案 2. Amazon API Gateway を REST API から HTTP API へ変更する

    下記のブログでは「HTTP API はレイテンシーを最大 60 % 削減します。」とありますが、現在、HTTP API は X-Ray との連携がサポートされていないので今回は選択肢から外しました。

    高速、低コストで、より良いAPIの構築 – HTTP APIが利用可能(GA)になりました »

    案 3. リージョン選定の見直し

    開発中は us-east-1 米国東部 (バージニア北部) を利用していました。

    なぜ us-east-1 米国東部 (バージニア北部) を利用していたかというと、安い、早い、うまいが 3 拍子揃っているからです。

  • 安いは、t2.micro のコストを比較すると
    us-east-1 米国東部 (バージニア北部) : 0.0116 USD / 時間
    ap-northeast-1 アジアパシフィック (東京) : 0.0152 USD / 時間
  • 早いは、対応サービス数
    us-east-1 米国東部 (バージニア北部) : 190
    ap-northeast-1 アジアパシフィック (東京) : 168
  • うまいは、ちょっと無理やりですが、us-east-1 だと、IAM Polilcy の制限をリージョン指定でかけてもグローバルサービスに影響しない権限の設計がしやすいです。
  • しかし、今回のケースでは「早い」は、レスポンスのレイテンシーで考えるべきです。
    サービス設置先のリージョン選定のポイントとして物理的な距離は重要なポイントです。

    参考までに計測した東京リージョンとのレスポンスタイムの比較は、

  • ap-northeast-2 アジアパシフィック (ソウル) : 39.32 ms
  • us-east-1 米国東部 (バージニア北部) : 160.83 ms
  • でした。

    そして、東京リージョンに「Minoru」を移すと処理全体のレイテンシーが 613 ms にまで改善されました。そのため、今回はリージョンを東京リージョンに移動することで、レイテンシーを改善しました。

    効果としてはクライアントから見たレスポンスタイムが 47 % に改善されました。

    改善前後のレイテンシーの変化

    A screenshot of a network activity table showing two load events, their status codes, types (preflight and xhr), initiators, sizes, load times, and a waterfall chart. This image is related to insourcing a sustainable learning environment.

    改善前

    A screenshot showing a network activity log with status codes, request types, file names, transfer sizes, and response times, typically used for monitoring and analyzing web application performance.

    改善後

    まとめ

    今回、初めて設計から開発、検証、運用を行い、改めてこれまで学んできた知識が腹落ちしました。私の場合は、技能五輪の参加を目的として AWS の学習をはじめましたが、Web アプリケーションを自分自身で作り切るというのは貴重な経験と沢山の学びを得ることができ、これを作ること自体が「手の内化」につながる活動となったと考えています。まだまだ未熟ですが、これからも慢心せず、精進します。

    この記事が「新しい挑戦をしてみよう !」と思うきっかけになっていただければ幸いです。

    筆者プロフィール

    濵﨑 智宏
    株式会社デンソー コアスキル開発部 

    WorldSkills Kazan 2019 に出場後、後進育成をしています。有志活動として社内のクラウドエンジニア育成にも取り組んでします。

    A Japanese competition participant, wearing a blue jacket and cap with Japan flag emblems, works at a computer during an event. The individual is focused on the screen, sitting at a desk with a name placard and small models.