Serverless Framework を利用するプロジェクトにアサインされました。
今まで全く触ったことがなかったのですが、いきなり「serverless.yaml
書いて」と言われたので急遽勉強するための環境を整えます。
インストール
node, npmバージョン確認
$ node -v v18.12.1 $ npm -v 8.19.2
インストール
$ npm install -g serverless
serverless バージョン確認
$ serverless -v Framework Core: 3.27.0 Plugin: 6.2.3 SDK: 4.3.2
プロジェクト作成
どうやら serverless
でインタラクティブにプロジェクトが初期化できるようなのですが、選べるテンプレートが少なく、Lambda(Python) がなかったので、create
コマンドで作成するようにします。
プロジェクト初期化
$ serverless create \ --template aws-python3 \ # 上記ページの "Available Templates" から選択 --path tutorial \ # コマンド実行ディレクトリに作成するプロジェクトディレクトリ名を指定 --name tutorial # serverless.yml に記載されるプロジェクト名を指定 # GitHub等からテンプレートを持ってくる場合には `--template-url` を指定します。
AWS 権限設定
serverless
でAWS APIを実行するための権限設定を行います。手順は以下の通り。
- IAMユーザ作成
- AdministratorAccessポリシーを付与・・・※補足1参照
- アクセスキーを作成
次に aws configure
をします。serverless config credentials
でラップされているようなのでこちらを使います。
$ serverless config credentials \ --provider aws \ --profile talkeyboid-serverless-user \ --key XXXXXXXXXXXXXX \ --secret XXXXXXXXXXXXXXXXXXX
プロファイル環境変数指定
$ export AWS_PROFILE=talkeyboid-serverless-user
補足1
※ドキュメントチュートリアルでは「AdministratorAccess を付与」と書いてありますが、怖すぎます。一応注意書きはある模様。
Note that the above steps grant Serverless Framework administrative access to your account. While this makes things simple when starting out, we recommend that you create and use more fine-grained permissions once you determine the scope of your serverless applications and move them into production.
権限を絞って与えるには以下が参考になりそうです。皆さん苦労されている様子。
ポリシージェネレータのリポジトリもありました。これですべてができるとは思いませんが、かなりの時短になりそうな予感。
補足2
以下のように serverless.yml
を記載することで、--stage
オプションに応じて、利用するプロファイルを変えることができるようです。便利。
service: new-service provider: name: aws runtime: nodejs14.x profile: ${self:custom.profiles.${sls:stage}} custom: profiles: dev: devProfile prod: prodProfile
また、定義した各リソースを CloudFormation 側でデプロイするときに利用するロール(サービスロール)を指定できるようでした。
provider: iam: deploymentRole: arn:aws:iam::123456789012:role/deploy-role
ここら辺は問題もある模様(カスタムリソースが勝手に作られそれにサービスロールが当たってしまう)。余裕があればここら辺もキャッチアップしたいですね。特に本番では気になる。
プロジェクト確認
ここで一度プロジェクトの内容を確認します。serverless create
で作成したプロジェクトはこんな感じ。
$ tree . └── tutorial ├── handler.py └── serverless.yml 1 directory, 2 files
handler.py
は Lambda デフォルトとほぼ同じコード、関数名が hello
になってますが、yaml側でハンドラー定義もされてるんでしょうか。
import json def hello(event, context): body = { "message": "Go Serverless v1.0! Your function executed successfully!", "input": event } response = { "statusCode": 200, "body": json.dumps(body) } return response # Use this code if you don't use the http event with the LAMBDA-PROXY # integration """ return { "message": "Go Serverless v1.0! Your function executed successfully!", "event": event } """
serverless.yml
はこんな感じ。やたらと親切にコメントでいろいろと書いてくれています。必要なところだけコメント外して書き換えるだけでいろいろとできそうですね。
# Welcome to Serverless! # # This file is the main config file for your service. # It's very minimal at this point and uses default values. # You can always add more config options for more control. # We've included some commented out config examples here. # Just uncomment any of them to get that config option. # # For full config options, check the docs: # docs.serverless.com # # Happy Coding! service: tutorial # app and org for use with dashboard.serverless.com #app: your-app-name #org: your-org-name # You can pin your service to only deploy with a specific Serverless version # Check out our docs for more details frameworkVersion: '3' provider: name: aws runtime: python3.8 # you can overwrite defaults here # stage: dev # region: us-east-1 # you can add statements to the Lambda function's IAM Role here # iam: # role: # statements: # - Effect: "Allow" # Action: # - "s3:ListBucket" # Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ] } # - Effect: "Allow" # Action: # - "s3:PutObject" # Resource: # Fn::Join: # - "" # - - "arn:aws:s3:::" # - "Ref" : "ServerlessDeploymentBucket" # - "/*" # you can define service wide environment variables here # environment: # variable1: value1 # you can add packaging information here #package: # patterns: # - '!exclude-me.py' # - '!exclude-me-dir/**' # - include-me.py # - include-me-dir/** functions: hello: handler: handler.hello # The following are a few example events you can configure # NOTE: Please make sure to change your handler code to work with those events # Check the event documentation for details # events: # - httpApi: # path: /users/create # method: get # - websocket: $connect # - s3: ${env:BUCKET} # - schedule: rate(10 minutes) # - sns: greeter-topic # - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000 # - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx # - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx # - iot: # sql: "SELECT * FROM 'some_topic'" # - cloudwatchEvent: # event: # source: # - "aws.ec2" # detail-type: # - "EC2 Instance State-change Notification" # detail: # state: # - pending # - cloudwatchLog: '/aws/lambda/hello' # - cognitoUserPool: # pool: MyUserPool # trigger: PreSignUp # - alb: # listenerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:listener/app/my-load-balancer/50dc6c495c0c9188/ # priority: 1 # conditions: # host: example.com # path: /hello # Define function environment variables here # environment: # variable2: value2 # you can add CloudFormation resource templates here #resources: # Resources: # NewResource: # Type: AWS::S3::Bucket # Properties: # BucketName: my-new-bucket # Outputs: # NewOutput: # Description: "Description for the output" # Value: "Some output value"
コメント外すとたったこれだけです。予想通りハンドラ定義ありましたね。ソースを変更するときはここを書き換えればよさそうです。
service: tutorial frameworkVersion: '3' provider: name: aws runtime: python3.8 functions: hello: handler: handler.hello
デプロイ
デプロイ
$ serverless deploy
CloudFormationスタックが作成されたことを確認。どうやらデフォルトで dev
がつくようですね。custom.profiles
を指定すれば任意に設定できるのでしょうかね(知らんけど)。
Lambda, ロググループ, IAMロール, S3バケット が作成されているようです。serverless.yaml
では Lambda しか定義していなかったですが良しなに作ってくれました。ただ、ここでリージョンが us-east-1
になっていることに気づきました。IAM権限設定の際に serverless config
をしましたが、ここでおそらくリージョンを設定しないといけなかった気がします。もしくはテンプレート内にリージョン書けばできるかな?
とりあえず、各リソースを確認していきます。
Lambda
ソースが反映されていることがわかります。
設定はマネジメントコンソールで作成するときとかなり違いますね。デフォルトだとタイムアウトが長く、メモリも大きいです。
ロール
ポリシーは以下が当たってました。Lambdaの一番シンプルな組み込みロールと同じ感じですね。
tutorial-dev-us-east-1-lambdaRole
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "logs:CreateLogStream", "logs:CreateLogGroup" ], "Resource": [ "arn:aws:logs:us-east-1:XXXXXXXXXXXX:log-group:/aws/lambda/tutorial-dev*:*" ], "Effect": "Allow" }, { "Action": [ "logs:PutLogEvents" ], "Resource": [ "arn:aws:logs:us-east-1:XXXXXXXXXXXX:log-group:/aws/lambda/tutorial-dev*:*:*" ], "Effect": "Allow" } ] }
S3
バケットには以下のファイルが格納されていました。
ファイル | 中身 |
---|---|
compiled-cloudformation-template.json | CloudFormationに変換したテンプレート |
serverless-state.json | serverless.yml で定義していない値をデフォルト値で埋めたテンプレート |
tutorial.zip | デプロイ資材(ここでは handler.py ) |
これらは serverless deploy
時にコンパイルされた .serverless
ディレクトリ内のファイルと "ほぼ" 同じもののようです。
$ ls .serverless/ cloudformation-template-create-stack.json serverless-state.json cloudformation-template-update-stack.json tutorial.zip
どのような対応か、わかりやすく説明してくださってるページがありました。
どうやらこんな感じの流れなようです。
serverless.yml
を解析- 埋まっていない値をデフォルト値で補完し
serverless-state.json
を出力 - 定義ファイルやソースを格納するS3バケットを作成するためのテンプレートを
cloudformation-template-create-stack.json
として出力 - 実際に作成するテンプレートを
cloudformation-template-update-stack.json
として出力。これはS3バケット上のcompiled-cloudformation-template.json
と同じ。※同じものがアップロードされるという意味なのかは不明
お掃除
全削除。CloudFormationスタックが削除されます。
$ serverless remove
serverless
実行用に作成したアクセスキーを無効化しておきます。
感想
全くの0からなんとなくイメージはつかめました。
serverless.yml
の書き方、IAM権限の絞り込み周りを勉強していきます。