2023/10/28現在、Node.jsのLTSは20.9.0になった。昔の記事でも見てくれている方がいましたので、最新の環境で動作するか確認し、記事を更新しました。
はじめに
これはAteam Brides Inc. Advent Calendar 2021(現:エイチームライフデザイン)の二日目の記事です。
さて、今回は業務でServerless Framework
を使ったので基本的な使い方を紹介したいと思います
Serverless Frameworkってどうやって動いてるの?
全体のイメージを持って取り掛かると理解が早いと思い、概要図を描きました
開発者は1〜3をするだけで、必要なAWSのリソースを作成してくれます補足
概要図では最終的にLambdaとAPI Gatewayのリソースを作成していますが、API Gatewayの部分はLambdaが動くトリガーだったり使用するリソースになるので、作りたいものによって変わります。今回は、実際に作ってみる雛形に合わせたリソースを概要図に描いています。
今までAWSコンソールでポチポチしてリソース作成したり、バラバラだったLambda等の運用方法が統一され開発速度が上がることが Serverless Framework を導入するメリットかと思います。
開発環境を準備する
それでは、早速、動かしてみましょう。私の実行環境は以下の通りです
OS: macOS Ventura v13.5.2
node: v18.16.0
1. Serverless Framework をインストール
まずは公式ドキュメントに従って、自分のマシンにServerless Framework
をインストールしましょう
# インストール
$ npm install -g serverless
# バージョン確認
serverless --version
> Framework Core: 3.36.0
> Plugin: 7.1.0
> SDK: 4.4.0
2. Serverless Framework が使う IAMユーザーを作る
概要図にも書いた通り、Serverless Framework
はデプロイコマンド一つでAWSの各リソースを作成してくれます。その際に、リソースを作成する権限をもったIAMユーザーが必要になります
それでは早速、AWSコンソールからIAMユーザーを作成してみましょう。
AWSコンソールから IAMユーザーを追加
- ユーザー名:
serverless-servicename-agent
- AWS アクセスの種類を選択: アクセスキー - プログラムによるアクセス のみ
- 既存のポリシーから直接アタッチ
- ポリシーの作成
- JSON
- 公式ドキュメントで紹介されている設定からコピペします(最終的には必要な権限のみ残して運用してください)
- JSON
- ポリシー名:
serverless-servicename-policy
- ポリシーの作成
- ポリシーを作成後、リロードして作成したポリシーにチェックをつけてユーザを作成
- ユーザーが作成されたら、ローカルコードから使用するためのアクセスキーを作成します。
作成したら、「アクセスキー ID」と「シークレットアクセスキー」をメモしておいてください(後で使います)
3. 作成した IAMユーザーのクレデンシャル情報を自分のマシンに設定する
手順2で作成した IAMユーザーを使って自分のマシンからデプロイするためには、クレデンシャル情報(「アクセスキーID」と「シークレットアクセスキー」)を認証情報ファイルに記載する必要があります
下記のコマンドの「アクセスキーID」と「シークレットアクセスキー」の箇所を先程メモした値に書き換えて実行してください。
profile
オプションを指定することで名前付きプロファイルにしています。(今回は分かりやすいようにIAMユーザー名に合わせています。)
$ serverless config credentials --provider aws --key アクセスキーID --secret シークレットアクセスキー --profile serverless-servicename-agent
# 確認(コマンドqで終了)
less ~/.aws/credentials
# profileオプションに指定したプロファイル名のクレデンシャル情報が追加されていることを確認
> [default]
> aws_access_key_id=アクセスキーID
> aws_secret_access_key=シークレットアクセスキー
>
> [serverless-servicename-agent]
> aws_access_key_id=アクセスキーID
> aws_secret_access_key=シークレットアクセスキー
プロジェクトを作成
開発環境が準備できたら、峠は超えました。
下記のコマンドを実行してプロジェクトを作成しましょう
$ mkdir my-special-service
$ cd my-special-service
$ serverless create --template aws-nodejs-typescript --name my-special-service
$ npm install
-
name
オプションで指定したサービスを作成します -
サービス名にスネークケースは使わないほうがよいでしょう
- 余談:私はサービス名を`my_special_service`のようにスネークケースで設定したのですが、デプロイでS3バケットが自動作成される際に、S3の命名規則(`小文字、数字、ドット (.)、およびハイフン (-) のみ`)に違反しておりエラーになってしまいました。もちろん、S3バケット名は任意の名称を設定できるので回避策もありますが、最初は公式ドキュメント通りがオススメです。
知っておきたいserverless.tsファイル
このファイルがServerless Framework
のconfigファイルであり、とて〜も重要なファイルになります。
このファイルを自由に扱えたらServerless Framework
中級者なのかなと思います(私は初心者)。
ざっくりと設定項目を洗ってみたいと思います
key | 内容 | 使い所 |
---|---|---|
provider | どこのクラウドサービスをどんな設定で動かすか(AWS以外もGCPとか対応してる) | slsコマンドのデフォルト値とか設定しておくとオプションで指定しなくてもよくなる |
functions | lambdaファンクションとそのトリガーの設定 | |
layers | lambdaファンクションが使うlayerの設定 | node_moduleや共通処理を切り出してくれる |
resources | lambdaファンクションが使うリソースを事前に定義 | 定義したリソースはデプロイ時に自動作成してくれる |
plugins | 機能拡張できるプラグインを設定します | 有名なのだとserverless-offlineプラグインを入れてローカルで動かしたり |
サービスをデプロイする
概要図の3.デプロイコマンドを実行
に該当します。
ここで紹介するsls deploy
というコマンドはこれからいっぱい使うことになります。(sls
はservelessの
略)
しかし、まだ下記のコマンドは実行しないでください
$ sls deploy --aws-profile serverless-servicename-agent --region ap-northeast-1 --stage dev --verbose
-
aws-profile
オプションの値は先に設定した名前付きプロファイルの名前を指定します -
aws-profile
やregion
、stage
オプションはserverless.ts
に設定しておけばCLIオプションで指定する必要がなくなります(後述します)
serverless.ts の設定を調整
先述のとおり、デプロイのたびにCLIオプションを書くのは面倒ですね。
そこで、CLIオプションの値をserverless.ts
に設定することで、毎回書く手間を省いてくれます
...
provider: {
name: 'aws',
runtime: 'nodejs14.x',
+ profile: 'serverless-servicename-agent',
+ region: 'ap-northeast-1',
+ stage: "${opt:stage, 'dev'}",
apiGateway: {
minimumCompressionSize: 1024,
shouldStartNameWithService: true,
},
environment: {
AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1',
NODE_OPTIONS: '--enable-source-maps --stack-trace-limit=1000',
},
lambdaHashingVersion: '20201221',
},
...
-
stage
に指定した${opt:stage, 'dev'}
ですが、これは${可変のソース, デフォルト値}
の形になっています。opt
(オプション)以外にもenv
(環境変数)など色んなソースを参照できます。
書いてみて分かると思うのですが、typescriptの型によってインテリセンスが効くので書きやすいですし、他にも用意されている設定を知ることもできて面白いです。
さて、これで先程のデプロイコマンドはだいぶスッキリ書くことができます。デプロイしてみましょう
$ sls deploy --verbose
-
--verbose
オプションは任意です。つけておくとServerless Framework
がどうやって動いているか理解しやすくなるので最初はオススメです - もし、デプロイする前に構文を検証したい場合は
deploy
の部分をpackage
に変更して実行してください
エラーがでるかも?(cloudformation:DeleteChangeSet権限がない)
最新バージョンでは下記のエラーが発生するかもしれません。
User: arn:aws:iam::99999999999:user/serverless-servicename-agent is not authorized to perform: cloudformation:DeleteChangeSet on resource: arn:aws:cloudformation:ap-northeast-1:99999999999:stack/my-special-service-dev/db85a570-7572-11re-9dc9-06df524be86t because no identity-based policy allows the cloudformation:DeleteChangeSet action
serverless-servicename-agent
ユーザーにcloudformation:DeleteChangeSet
権限が許可れれていないというエラーなので、下記のように更新してください。
{
"Statement": [
{
"Action": [
"apigateway:*",
+ "cloudformation:DeleteChangeSet",
"cloudformation:CancelUpdateStack",
"cloudformation:ContinueUpdateRollback",
"cloudformation:CreateChangeSet",
"cloudformation:CreateStack",
"cloudformation:CreateUploadBucket",
"cloudformation:DeleteStack",
"cloudformation:Describe*",
"cloudformation:EstimateTemplateCost",
"cloudformation:ExecuteChangeSet",
"cloudformation:Get*",
"cloudformation:List*",
"cloudformation:UpdateStack",
"cloudformation:UpdateTerminationProtection",
"cloudformation:ValidateTemplate",
"dynamodb:CreateTable",
"dynamodb:DeleteTable",
"dynamodb:DescribeTable",
"dynamodb:DescribeTimeToLive",
"dynamodb:UpdateTimeToLive",
"ec2:AttachInternetGateway",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:CreateInternetGateway",
"ec2:CreateNetworkAcl",
"ec2:CreateNetworkAclEntry",
"ec2:CreateRouteTable",
"ec2:CreateSecurityGroup",
"ec2:CreateSubnet",
"ec2:CreateTags",
"ec2:CreateVpc",
"ec2:DeleteInternetGateway",
"ec2:DeleteNetworkAcl",
"ec2:DeleteNetworkAclEntry",
"ec2:DeleteRouteTable",
"ec2:DeleteSecurityGroup",
"ec2:DeleteSubnet",
"ec2:DeleteVpc",
"ec2:Describe*",
"ec2:DetachInternetGateway",
"ec2:ModifyVpcAttribute",
"events:DeleteRule",
"events:DescribeRule",
"events:ListRuleNamesByTarget",
"events:ListRules",
"events:ListTargetsByRule",
"events:PutRule",
"events:PutTargets",
"events:RemoveTargets",
"iam:AttachRolePolicy",
"iam:CreateRole",
"iam:DeleteRole",
"iam:DeleteRolePolicy",
"iam:DetachRolePolicy",
"iam:GetRole",
"iam:PassRole",
"iam:PutRolePolicy",
"iot:CreateTopicRule",
"iot:DeleteTopicRule",
"iot:DisableTopicRule",
"iot:EnableTopicRule",
"iot:ReplaceTopicRule",
"kinesis:CreateStream",
"kinesis:DeleteStream",
"kinesis:DescribeStream",
"lambda:*",
"logs:CreateLogGroup",
"logs:DeleteLogGroup",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
"logs:FilterLogEvents",
"logs:GetLogEvents",
"logs:PutSubscriptionFilter",
"s3:CreateBucket",
"s3:DeleteBucket",
"s3:DeleteBucketPolicy",
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:GetObject",
"s3:GetObjectVersion",
"s3:ListAllMyBuckets",
"s3:ListBucket",
"s3:PutBucketNotification",
"s3:PutBucketPolicy",
"s3:PutBucketTagging",
"s3:PutBucketWebsite",
"s3:PutEncryptionConfiguration",
"s3:PutObject",
"sns:CreateTopic",
"sns:DeleteTopic",
"sns:GetSubscriptionAttributes",
"sns:GetTopicAttributes",
"sns:ListSubscriptions",
"sns:ListSubscriptionsByTopic",
"sns:ListTopics",
"sns:SetSubscriptionAttributes",
"sns:SetTopicAttributes",
"sns:Subscribe",
"sns:Unsubscribe",
"states:CreateStateMachine",
"states:DeleteStateMachine"
],
"Effect": "Allow",
"Resource": "*"
}
],
"Version": "2012-10-17"
}
デプロイの実行結果
改めて概要図を見ると、--verbose
オプションで出力したログの流れと合っていることが確認できるかと思います。
- ローカルディレクトリを見るとデプロイ用に
.serverless
が作成されていることが分かります - 実際にAWSコンソールを見ると、各リソースが作成されています(Lambda関数が見つからない場合は東京リージョンになっていないかもしれません)
ローカルから実行してログを見る
デプロイしたLambda関数をローカルから実行してみましょう。
テンプレートにあるhello関数(`src/functions/hello/handler.ts`)を見ると、引数に`event`を受け取り、`event.body.name`を出力していることが分かります。
const hello: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (event) => {
return formatJSONResponse({
message: `Hello ${event.body.name}, welcome to the exciting Serverless world!`,
event,
});
}
このevent
に同階層にあるmock.json
を渡したい
{
"headers": {
"Content-Type": "application/json"
},
"body": "{\"name\": \"Frederic\"}"
}
このevent
は、--path
or-p
オプションでjsonファイルを指定することで渡すことができます。コマンドは以下の通りです。
$ sls invoke -f hello -p src/functions/hello/mock.json
> Running "serverless" from node_modules
> {
> "statusCode": 200,
> "body": "{\"message\":\"Hello Frederic, welcome to the exciting Serverless world!\",\"event\":{\"headers\":{\"Content-Type\":\"application/json\"},\"body\":{\"name\":\"Frederic\"},\"rawBody\":\"{\\\"name\\\": \\\"Frederic\\\"}\"}}"
> }
$ sls logs -f hello
> Running "serverless" from node_modules
> INIT_START Runtime Version: nodejs:14.v39 Runtime Version ARN: arn:aws:lambda:ap-northeast-1::runtime:78812e04a9860255e6713cb1e4f6e6092ec4c3cc2ad1a87256041a81269654fd
> START
> END Duration: 4.86 ms (init: 189.27 ms) Memory Used: 58 MB
- Lambdaの実行ログがローカルからも確認できました
サービスを削除する
お金かかるしね、消しときましょう
$ sls remove --verbose
おわりに
初めて業務でServerless Framework
を触りましたが、公式ドキュメントが丁寧に書いてあるので理解しやすかったです。サーバーに負荷の高い画像処理なんかを任せちゃえるし、今後も使っていきたいなと思いました
Ateam Brides Inc. Advent Calendar 2021の3日目は、
@Shuni がお送りします!!どんなネタを用意してくるのか楽しみです!!
参考