この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
先日の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
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ネットコム株式会社 佐々木さんのご担当です。楽しみですね!