Serverless.inc社より、Serverless Componentsという新しいOSSプロダクトが公開されました。
これがどういったものかというと、Serverless Frameworkは基本的にfunctionとeventは簡単に管理できますが、それ以外のDynamoDBやS3といった必要なCloud上のリソースを定義するのはまだまだ非常に大変です。それはResourcesセクションに生のCloudFormationを定義しないといけなからというのが大きな理由です。
Serverless Componentsはそれを解決します。
componentsという単位でCloud上のリソースを簡単に定義して再利用可能なものにします。これはAWSだけ対応しているのではなく、その他のCloudやNetlifyやGitHubといったSaaSサービスもcomponentという単位で定義出来ることがポイントです。
これは、サーバーレスアプリケーションがこれからはAWS上のものだけではなく、様々なSaaSやFunctionを組み合わせて定義することが必要になり、それは非常に複雑なものになることに対する解決策になるという意味です。
使い方
Serverless ComponentsはCLIベースのアプリケーションとして公開されています。
以下がサンプルアプリケーションの設定例です。serverless.ymlファイルにcomponentsというセクションを定義してその下にcomponentをそれぞれ設定していきます。現状対応しているcomponentsはcomponents registryで定義されているものがすべてになります
type: retail-app
version: 0.0.1
components:
webFrontend:
type: static-website
inputs:
name: retail-frontend
contentPath: ${self.path}/frontend
templateValues:
apiUrl: ${productsApi.url}
contentIndex: index.html
contentError: error.html
hostingRegion: us-east-1
hostingDomain: retail-${self.serviceId}.example.com
aliasDomain: www.retail-${self.serviceId}.example.com
createProduct:
type: aws-lambda
inputs:
memory: 512
timeout: 10
handler: products.create
root: ${self.path}/code
env:
productTableName: products-${self.serviceId}
getProduct:
type: aws-lambda
inputs:
memory: 512
timeout: 10
handler: products.get
root: ${self.path}/code
env:
productTableName: products-${self.serviceId}
listProducts:
type: aws-lambda
inputs:
memory: 512
timeout: 10
handler: products.list
root: ${self.path}/code
env:
productTableName: products-${self.serviceId}
productsApi:
type: rest-api
inputs:
gateway: aws-apigateway
routes:
/products: # routes begin with a slash
post: # HTTP method names are used to attach handlers
function: ${createProduct}
cors: true
# sub-routes can be declared hierarchically
/{id}: # path parameters use curly braces
get:
function: ${getProduct}
cors: true # CORS can be allowed with this flag
# multi-segment routes can be declared all at once
/catalog/{...categories}: # catch-all path parameters use ellipses
get:
function: ${listProducts}
cors: true
productsDb:
type: aws-dynamodb
inputs:
region: us-east-1
tables:
- name: products-${self.serviceId}
hashKey: id
indexes:
- name: ProductIdIndex
type: global
hashKey: id
schema:
id: number
name: string
description: string
price: number
options:
timestamps: true
そして、以下のコマンドで操作を行います
$ components deploy # 定義したcomponentsのデプロイ
$ components info # デプロイ済みのcomponents情報の確認
$ components remove # デプロイ済みのcomponentsの削除
サンプルアプリケーション
まずはをサンプルアプリケーション立ち上げてcomponentsを体験してみましょう。
以下のコマンドでインストールしましょう。
$ npm install --global serverless-components
そして環境変数にAWSのアクセスキーを設定してください
export AWS_ACCESS_KEY_ID=my_access_key_id
export AWS_SECRET_ACCESS_KEY=my_secret_access_key
componentsのサンプルアプリケーションをgit cloneします
$ git clone https://github.com/serverless/components.git
$ cd components/examples/retail-app
最後にデプロイします
$ components deploy
Creating Bucket: 'retail-gebw40fi7i.example.com'
Creating Bucket: 'www.retail-gebw40fi7i.example.com'
Creating Role: func-gebw40fi7i-3naoitg1-execution-role
Creating Role: func-gebw40fi7i-d2vuqltm-execution-role
Creating Role: func-gebw40fi7i-rw1u3wpq-execution-role
Created table: 'products-gebw40fi7i'
Seeding 3 items into table products-gebw40fi7i.
Creating Lambda: func-gebw40fi7i-d2vuqltm
Creating Lambda: func-gebw40fi7i-3naoitg1
Creating Lambda: func-gebw40fi7i-rw1u3wpq
Item inserted to table: 'products-gebw40fi7i'
{"id":22,"name":"Model A+","description":"A precision-milled, highly durable enhancement of the Model A for performance applications.","price":8.99,"createdAt":"2018-05-03T10:12:58.142Z"}
Item inserted to table: 'products-gebw40fi7i'
{"id":21,"name":"Model B","description":"A cost-reduced version of our classic offering, providing the highest value.","price":4.99,"createdAt":"2018-05-03T10:12:58.126Z"}
Item inserted to table: 'products-gebw40fi7i'
{"id":20,"name":"Model A","description":"Our standard, highly reliable part.","price":6.99,"createdAt":"2018-05-03T10:12:58.158Z"}
Setting policy for bucket: 'retail-gebw40fi7i.example.com'
Setting website configuration for Bucket: 'retail-gebw40fi7i.example.com'
Creating Role: api-gebw40fi7i-plfr8qus-iam-role-gebw40fi7i-plfr8qus
Setting redirection for Bucket: 'www.retail-gebw40fi7i.example.com'
Set policy and CORS for bucket 'retail-gebw40fi7i.example.com'
Creating API Gateway: "api-gebw40fi7i-plfr8qus"
Creating Site: 'retail-frontend'
Syncing files from '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A' to bucket: 'retail-gebw40fi7i.example.com'
REST API resources:
POST - https://ob7r8ppmgk.execute-api.us-east-1.amazonaws.com/dev/products
GET - https://ob7r8ppmgk.execute-api.us-east-1.amazonaws.com/dev/products/{id}
GET - https://ob7r8ppmgk.execute-api.us-east-1.amazonaws.com/dev/catalog/{...categories}
Static Website resources:
http://retail-gebw40fi7i.example.com.s3-website-us-east-1.amazonaws.com
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/apple-icon-60x60.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/android-icon-96x96.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/apple-icon-180x180.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/android-icon-48x48.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/android-icon-72x72.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/apple-icon.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/android-icon-144x144.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/apple-icon-114x114.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/apple-icon-precomposed.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/android-icon-36x36.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/apple-icon-152x152.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/favicon-16x16.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/apple-icon-76x76.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/android-icon-192x192.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/apple-icon-120x120.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/apple-icon-72x72.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/apple-icon-57x57.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/browserconfig.xml' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/apple-icon-144x144.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/favicon-32x32.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/favicon-96x96.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/ms-icon-144x144.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/error.html' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/favicon.ico' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/manifest.json' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/ms-icon-150x150.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/ms-icon-70x70.png' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/fonts/serverless-regular.woff2' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/index.html' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/fonts/serverless-regular.woff' ...
Uploading file: '/var/folders/w4/nnwlqs093jlcg6sn2t2t2_2r0000gn/T/tmp-44738fLC7PSPBze0A/assets/favicons/ms-icon-310x310.png' ...
Objects Found: 0 , Files Found: 31 , Files Deleted: 0
これでWebSiteやREST APIのエンドポイントにアクセスするとちゃんとデプロイされていることが確認されているはずです。
Serverless Frameworkとの統合
将来的にはServerless Frameworkとの統合も計画されています。そうすると以下のようにfunctionsとeventsとcomponentsが上手く定義できるようになるでしょう
service: my-service
provider:
name: aws
runtime: nodejs6.10
region: us-east-1
functions:
myFunction:
handler: handler.myFunction
events:
- http:
path: api/public
method: post
environment:
database: ${components.myDatabase}
components:
myDatabase:
type: aws-dynamodb
inputs:
region: us-east-1
tables:
- name: myTable
hashKey: id
schema:
id: number
foo: string
provider:
name: aws
runtime: nodejs6.10
region: us-east-1
functions:
...
components:
myDatabase:
type: aws-dynamodb
inputs:
...
myTwilio:
type: twilio-webhook
inputs:
...
myGoogleCloudVisionApi:
...
myAuth0:
...
特にFrameworkはAWS以外のproviderのfunctionとeventも定義できます。GCPやAzureのfunctionとeventを定義してそれをAWSやNetlifyといったコンポーネントとも上手くインテグレーションが出来る様になるということです。これはかなりサーバレスというもののやれることや可能性が現実的になるのではなるのでないでしょうか。