AWS Application Composerのチュートリアルをやってみた #reinvent
先日のre:InventでプレビューリリースされましたAWS Application Composerがどんなものか触ってみたく、公式ガイド記載のチュートリアルをやってみたのでご紹介します。
なお、本記事は「Japan AWS Ambassador Advent Calendar 2022」4日目の記事としてエントリーしています。APN Ambassadorって何?と言う方は「APN Ambassadorsってなんだ?2021年度版」をご参照ください。
AWS Application Composer
AWS Application Composerは、複数のAWSサービスからサーバーレスアプリケーションを構築するために使用できるビジュアルデザイナーです。Application Composerのインタラクティブビルダーを使用して、キャンバス上でAWSリソースを選択、接続、および定義することによって、アプリケーションアーキテクチャを設計することができます。
また同時に、Application ComposerはAWSのベストプラクティスに従って、AWS CloudFormationとAWS Serverless Application Model (AWS SAM)のテンプレートを自動的に作成します。
さっそくやってみよう
AWS Application Composerは現時点ではプレビュー版です。記事中で紹介される内容は一般公開(GA)時には変更される可能性がある点にご留意してお読みください。
Application Composerは嬉しいことに東京リージョンで今すぐプレビュー利用できますので、今回は東京リージョンでチュートリアルを実施します。
AWSコンソールから「Application Composer」の管理コンソールを開きます。Homeページにある「Open demo」を開きます。
「Create a demo project」のポップアップが表示され接続モードの選択が必要になりますので「Connected」を選択し、「Select folder」からテンプレートファイル等の保管場所として同期するローカルフォルダーを指定し「Create」します。
Application Composerでは以下、2つの接続モードで作業することができます。
接続モード | 説明 |
---|---|
接続モード(Connected) | 設計中に自動的にテンプレートファイルとプロジェクトフォルダをローカルに同期して保存します |
非接続モード(Unconnected) | テンプレートファイルを手動でインポートおよびエクスポートします |
現時点では「Edge」「Chrome」は接続モードがサポートされているようです。非対応のブラウザを利用している場合、以下のように「Connected」はグレーアウトされており、ブラウザ非対応であるとのメッセージが表示されます。
デモプロジェクトを作成した直後のローカルフォルダーは以下のとおりでした。
$ tree application-composer-demo/ application-composer-demo/ ├── README.md ├── samconfig.toml ├── src │ ├── CreateItem │ │ ├── index.js │ │ └── package.json │ ├── DeleteItem │ │ ├── index.js │ │ └── package.json │ ├── GetItem │ │ ├── index.js │ │ └── package.json │ ├── ListItems │ │ ├── index.js │ │ └── package.json │ └── UpdateItem │ ├── index.js │ └── package.json └── template.yaml
チュートリアル用に提供されているプロジェクトが展開されます。Application Composerの基本的な操作方法については右上に表示されている「Take a quick tour of composer」メッセージ内の「Start」をクリックすると5ページ程度のツアーを通じてざっくりと理解することができます。
チュートリアル1: Application Composerデモプロジェクトの読み込みと変更
先の手順でデモアプリケーションとして、以下を含む基本的なCRUD(Create/Read/Update/Delete)サーバーレスアプリケーションのテンプレートが展開されています。
- 5つのルートを持つAmazon API Gatewayリソース
- 5つのLambda関数
- Amazon DynamoDBテーブル
画面上部の「Canvas」「Template」で画面表示を切替えることができるようですね。試しに「Template」に切り替えてみると以下のような画面になりました。
ビューを「Canvas」に戻し、DynamoDBテーブルにLambda関数を追加しアプリケーションアーキテクチャを拡張してみます。左ペインのメニューを「Resources」に切り替え、「Lambda Function」をドラッグ&ドロップでCanvas内に配置します。
そしてDynamoDBテーブル「items」と配置した「Function」の端子を接続します。画面上部の「Arrange」をクリックすると配置をキレイに整理してくれるようですね。
追加したリソースカード「Function」をダブルクリックすると、リソースのプロパティパネルが表示されますので必要な値を設定します。
設定を保存するには画面右上の「Menu」から「Save change」をクリックします。接続モードを利用している場合は自動的にローカルに保存されています。
チュートリアル1は以上です。
チュートリアル2: Application Composer を使用して最初のアプリケーションを構築する
次のチュートリアル2では、データベースでユーザーを管理するCRUDサーバーレスアプリケーションを作成します。作成するAPIは以下のように設定します。
Method | Path | Function Name |
---|---|---|
GET | /items | getItems |
GET | /items/{id} | getItem |
PUT | /items/{id} | updateItem |
POST | /item | addItem |
DELETE | /items/{id} | deleteItem |
AWSコンソールから「Application Composer」の管理コンソールを開きます。Homeページにある「Create project」を開きます。新規のプロジェクトを作成しますので「New blank project」を選択、今回も接続モードを利用することにします。
空のプロジェクトを作成しましたのでローカルフォルダにあるのはtemplate.yaml
のみでした。
$ tree application-composer-tutorial2/ application-composer-tutorial2/ └── template.yaml
このステップでは、Amazon API Gatewayリソースと5つのLambda関数を含むアプリケーションアーキテクチャの設計を開始します。
左ペインのリソースパレットからAPI Gatewayを1つ、Lambda関数を5つ配置します。
次にAPIルートを設定していきます。API Gatewayリソースカードをダブルクリックし、リソースプロパティを表示します。次に1つ目のルートを「Method:GET
」、「Path:/items
」に設定します。
次に「Add route」をクリックして2つ目以降のルートを設定します。追加する設定は先の表を参照しながら残り4つを設定し、保存します。
次にLambda関数の設定をします。Lambda関数「Function」リソースカードをダブルクリックし、リソースプロパティを表示します。次に「Logical ID:getitems
」を入力し保存。
同様に先の表を参照しながら残り4つのLambda関数にも「Logical ID」を設定します。
表を参照しながら各APIメソッドとパスの端子とLambda関数の端子を結びます。図を整理するために「Arrange」をクリックします。
Shiftを押しながらLambda関数をクリックすると複数選択することができます。この状態から「Group」をクリックするとグループ設定することが出来ます。
「Group」のリソースプロパティを表示し、グループ名をAPIに変更し、保存します。
左ペインのリソースパレットからDynamoDBテーブルをドラック&ドロップします。各Lambda関数の端子とDynamoDBテーブルを接続します。
テンプレートビューに切り替えてみると以下のように、およそ220行程度のテンプレートが自動生成されていることがわかります。
template.yaml
Transform: AWS::Serverless-2016-10-31 Resources: Api: Type: AWS::Serverless::Api Properties: Name: !Sub - ${ResourceName} From Stack ${AWS::StackName} - ResourceName: Api StageName: Prod DefinitionBody: openapi: '3.0' info: {} paths: /items: get: x-amazon-apigateway-integration: httpMethod: POST type: aws_proxy uri: !Sub arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${getitems.Arn}/invocations responses: {} /items/{id}: get: x-amazon-apigateway-integration: httpMethod: POST type: aws_proxy uri: !Sub arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${getitem.Arn}/invocations responses: {} put: x-amazon-apigateway-integration: httpMethod: POST type: aws_proxy uri: !Sub arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${updateitem.Arn}/invocations responses: {} /item: post: x-amazon-apigateway-integration: httpMethod: POST type: aws_proxy uri: !Sub arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${additem.Arn}/invocations responses: {} /itesms/{id}: delete: x-amazon-apigateway-integration: httpMethod: POST type: aws_proxy uri: !Sub arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${deleteitem.Arn}/invocations responses: {} EndpointConfiguration: REGIONAL TracingEnabled: true getitems: Type: AWS::Serverless::Function Properties: Description: !Sub - Stack ${AWS::StackName} Function ${ResourceName} - ResourceName: getitems CodeUri: src/Function Handler: index.handler Runtime: nodejs18.x MemorySize: 3008 Timeout: 30 Tracing: Active Events: ApiGETitems: Type: Api Properties: Path: /items Method: GET RestApiId: !Ref Api Environment: Variables: TABLE_NAME: !Ref Table TABLE_ARN: !GetAtt Table.Arn Policies: - DynamoDBCrudPolicy: TableName: !Ref Table getitemsLogGroup: Type: AWS::Logs::LogGroup DeletionPolicy: Retain Properties: LogGroupName: !Sub /aws/lambda/${getitems} getitem: Type: AWS::Serverless::Function Properties: Description: !Sub - Stack ${AWS::StackName} Function ${ResourceName} - ResourceName: getitem CodeUri: src/Function2 Handler: index.handler Runtime: nodejs18.x MemorySize: 3008 Timeout: 30 Tracing: Active Events: ApiGETitemsid: Type: Api Properties: Path: /items/{id} Method: GET RestApiId: !Ref Api Environment: Variables: TABLE_NAME: !Ref Table TABLE_ARN: !GetAtt Table.Arn Policies: - DynamoDBCrudPolicy: TableName: !Ref Table getitemLogGroup: Type: AWS::Logs::LogGroup DeletionPolicy: Retain Properties: LogGroupName: !Sub /aws/lambda/${getitem} updateitem: Type: AWS::Serverless::Function Properties: Description: !Sub - Stack ${AWS::StackName} Function ${ResourceName} - ResourceName: updateitem CodeUri: src/Function3 Handler: index.handler Runtime: nodejs18.x MemorySize: 3008 Timeout: 30 Tracing: Active Events: ApiPUTitemsid: Type: Api Properties: Path: /items/{id} Method: PUT RestApiId: !Ref Api Environment: Variables: TABLE_NAME: !Ref Table TABLE_ARN: !GetAtt Table.Arn Policies: - DynamoDBCrudPolicy: TableName: !Ref Table updateitemLogGroup: Type: AWS::Logs::LogGroup DeletionPolicy: Retain Properties: LogGroupName: !Sub /aws/lambda/${updateitem} additem: Type: AWS::Serverless::Function Properties: Description: !Sub - Stack ${AWS::StackName} Function ${ResourceName} - ResourceName: additem CodeUri: src/Function4 Handler: index.handler Runtime: nodejs18.x MemorySize: 3008 Timeout: 30 Tracing: Active Events: ApiPOSTitem: Type: Api Properties: Path: /item Method: POST RestApiId: !Ref Api Environment: Variables: TABLE_NAME: !Ref Table TABLE_ARN: !GetAtt Table.Arn Policies: - DynamoDBCrudPolicy: TableName: !Ref Table additemLogGroup: Type: AWS::Logs::LogGroup DeletionPolicy: Retain Properties: LogGroupName: !Sub /aws/lambda/${additem} deleteitem: Type: AWS::Serverless::Function Properties: Description: !Sub - Stack ${AWS::StackName} Function ${ResourceName} - ResourceName: deleteitem CodeUri: src/Function5 Handler: index.handler Runtime: nodejs18.x MemorySize: 3008 Timeout: 30 Tracing: Active Events: ApiDELETEitesmsid: Type: Api Properties: Path: /itesms/{id} Method: DELETE RestApiId: !Ref Api Environment: Variables: TABLE_NAME: !Ref Table TABLE_ARN: !GetAtt Table.Arn Policies: - DynamoDBCrudPolicy: TableName: !Ref Table deleteitemLogGroup: Type: AWS::Logs::LogGroup DeletionPolicy: Retain Properties: LogGroupName: !Sub /aws/lambda/${deleteitem} Table: Type: AWS::DynamoDB::Table Properties: AttributeDefinitions: - AttributeName: id AttributeType: S BillingMode: PAY_PER_REQUEST KeySchema: - AttributeName: id KeyType: HASH StreamSpecification: StreamViewType: NEW_AND_OLD_IMAGES Metadata: AWS::Composer::Groups: Group: Label: API Members: - getitems - getitem - updateitem - additem - deleteitem
Lambda関数を配置するとローカルフォルダには/src
フォルダや仮のindex.js
などが自動生成されていましたので、これらを使ってとりあえずえいやーでデプロイしてしまうことが出来そうですね!
tree application-composer-tutorial2/ application-composer-tutorial2/ ├── .aws-composer │ └── 20221203T212813399 │ └── template.yaml ├── src │ ├── Function │ │ ├── index.js │ │ └── package.json │ ├── Function2 │ │ ├── index.js │ │ └── package.json │ ├── Function3 │ │ ├── index.js │ │ └── package.json │ ├── Function4 │ │ ├── index.js │ │ └── package.json │ └── Function5 │ ├── index.js │ └── package.json └── template.yaml
チュートリアル2は以上です。
さいごに
まだプレビュー版ということもあり各リソースプロパティで設定できる項目はそれほど多くないのですが、ドラッグ&ドロップだけで各AWSサービス間の連携ができ、IaC用のテンプレートまで自動生成されるというユーザー体験は非常にワクワクを感じるものでした。GAに向けてさらなる対応リソースの追加、設定項目の追加に期待したいですね。
さて、「Japan AWS Ambassador Advent Calendar 2022」5日目はというと、AWS認定資格試験テキストをはじめとする書籍でも皆さんお世話になっているであろう、NRIネットコム株式会社 佐々木さんのご担当です。楽しみですね!