LambdaのデプロイをシェルスクリプトからServerless Frameworkに切り替えた時に苦労した点
はじめに
こんにちは植木和樹@上越妙高オフィスです。今月に入ってLambdaファンクションのデプロイをお手製シェルスクリプトからServerless Frameworkに切り替えました。本日はServerless Frameworkを使った時に躓いた点や移行時のコツなどを書き残しておきたいと思います。
環境
- Python 2.7.13
- Serverless Framework 1.24.0
- pyenv
- virtualenv
- fish (bashやzshでも良い)
pyenvでPythonのバージョンを切り替えるようにして、virtualenvでプロジェクト単位のライブラリをインストールしています。
$ pyenv install 2.7.13 $ omf install pyenv $ cd PATH/TO/PROJECTDIR $ pyenv local 2.7.13 $ pip install virtualenv $ virtualenv venv $ source venv/bin/activate.fish
Serverless Frameworkに移行した動機
Lambdaの利用は2016年の春頃から開始していました。当初はAPIを呼び出してデータを移行するだけの比較的単純な処理でした。
ただ1年ほど利用したところ下記のような課題があり、解決手段として専用のデプロイツールを採用することにしました。
- DynamoDBが依存リソースとなりコードで管理したくなった。(S3バケットへのコンテンツアップロードをトリガーとしたLambdaの呼び出しが複数回発生するため重複回避処理を実装)
- CloudWatch Eventsなどの設定もコードで管理したい。
- 開発環境と本番環境を切り替えるため、環境変数などをちゃんと管理したくなった。
- 複数のプロジェクトでLambdaを使っており、標準的なデプロイ手順を整備したかった。(本番環境への誤ったデプロイをしづらくする)
Serverless Framework オススメプラグイン
Serverless Frameworkはプラグインを利用して、各種機能を追加することができます。 「なにかいいプラグインないかなー」と探したいときは
$ sls plugin search --query aws
のように適当なキーワードで検索すると、プラグイン名と簡単な説明文を出力してくれます。
下記のようなコマンドでインストールすると、プラグインをダウンロードしつつ package.json と serverless.yml も自動的に書き換えてくれます。
$ sls plugin install -n serverless-pseudo-parameters
もっと詳しい使い方を知りたければ、プラグイン名をGoogle等で検索します。npmのページや先人のブログが見つかるため参考にします。
serverless-python-requirements
sls deployでパッケージを作成する際に、requirements.txt で指定したライブラリを一緒にzipにまとめてくれます。便利。
これまではこんな感じのシェルスクリプトを書いてzipファイルを作ってました。
mkdir -p package cp -pr *.py *.conf venv/lib/python2.7/site-packages/* package (cd package && zip -r ../package.zip .)
serverless-pseudo-parameters
serverless.ymlに文字列をいれておくと、デプロイ時にデプロイ先のAWSリージョンやAWSアカウント名に置換してくれます。 開発環境と本番環境で作成するS3バケット名などを変更したい時に使います。
resources: Resources: MyBucket: Type: AWS::S3::Bucket Properties: BucketName: mybucket-#{AWS::Region}-#{AWS::AccountId}
移行時の悩み
既存リソースをどう管理するか/リソースに何を含めるか
すでに存在するS3バケットや、DynamoDBテーブルを改めてServerless Frameworkで管理するにあたり「どこまでを serverless.yml に含めるか?」で悩みました。
結論として、CloudFormationと同じく永続的に必要なリソースはスタックに含めない が正しいと思います。 別の言い方をすると、sls remove した時一緒に削除されて欲しくないリソースは含めないです。
CloudFormationのお作法と同様に、EIPやRDSなど一度作ったら消されてほしくないものはLambda関数と同じスタックに入れないのが良いでしょう。
今回移行時についていうと、重複処理制御に利用しているDynamoDBテーブルや、処理負荷調整のためのSQSは「すべて消してまた作り直しても問題ない」ためスタックに含めました。 ただデータ連携(ファイルアップロード)で利用しているS3バケットは「消されるのはちょっと嫌」だったのでスタックに含めないことにしました。
関連する話題
- Skip resources if already exist · Issue #3183 · serverless/serverless
- SC5/serverless-plugin-additional-stacks: Additional Stacks Plugin for Serverless 1.x
リソースには dash, underscore は使わない
Serverless Frameworkでは論理名に dash(-)、underscore(_) を使うと、関連するリソースの論理名がとても長くなります。 論理名は スネークケース でなく キャメルケース で書くようにしましょう。
たとえばLambda関数に "my_function" という名前をつけ、CloudWatch Eventsで定期イベントと紐付けるとします。 するとCloudWatch Eventsに作成されるイベント名は MyUnderscorefunctionEventsRuleSchedule1 という名前になります。長い。
serverless.yml
functions: my_funtion: handler: my_funtion.lambda_handler events: - schedule: rate(1 hour)
.serverless/cloudformation-template-update-stack.json (抜粋)
"MyUnderscorefunctionEventsRuleSchedule1": { "Type": "AWS::Events::Rule", : :
ステージの切り替え
Serverless Frameworkでは sls deploy 時にステージ名を指定することができます。 開発時は sls deploy、 本番時は sls deploy -s prod のようにすることで誤って本番環境にリリースしにくくしています。
serverless.yml には下記の設定をいれておくと、ステージ毎にプロファイルやCloudWatch Eventsの有効/無効が設定できます。
provider: name: aws runtime: python2.7 stage: ${opt:stage, self:custom.defaultStage} profile: ${self:custom.profiles.${self:provider.stage}} region: ${opt:region, self:custom.defaultRegion} functions: MyFunction: handler: my_function.lambda_handler description: Hello World!! events: - schedule: rate: rate(1 hour) enabled: ${self:custom.enableTrigger.${self:provider.stage}} description: 'Enqueue jobs per 1 hour.' custom: defaultStage: dev defaultRegion: ap-northeast-1 profiles: dev: project-dev prod: project-prd enableTrigger: dev: false prod: true
もうちょっとがんばって、CodeCommitのprodブランチにpush → テスト → 成功したらデプロイ、まで持っていきたい所存。
AWSのプロファイルはどこに書く?
上記で紹介したステージ毎にAWSのプロファイルを切り替える方法ですが、1点注意点があります。 assumeRoleの設定は ~/.aws/config(CLI 設定ファイル) でなく ~/.aws/credentials(AWS 認証情報ファイル) に書きましょう。AWS SDK for JavaScriptではCLI 設定ファイルは読み込みません。
設定の書き方は config も credentials も同じですが、INIファイルのセクションの書き方が微妙に異なるのでご注意ください。
~/.aws/config
[profile project-prd] role_arn = arn:aws:iam::123456789012:role/my-role source_profile = default region = ap-northeast-1
~/.aws/credentials
[project-prd] (← profile は不要なので注意!) role_arn = arn:aws:iam::123456789012:role/my-role source_profile = default region = ap-northeast-1
まとめ
冒頭に書いたように、当初慣れないうちはなにしているか分かりやすい自作シェルスクリプトでやっていました。これはLambdaを利用するにあたって、Lambda自体の知識に加えてServerless Framework(とCloudFormation)の知識も習得しなければならず敷居が高いと感じたためです。
しかし開発・本番環境が整い、依存するリソースも増えてきた時には、それらをまとめてコードで管理できるデプロイツールを採用するのが良いと感じました。
デプロイツールは Serverless Framework 以外にも各種あります。
- awslabs/serverless-application-model: AWS Serverless Application Model (AWS SAM) prescribes rules for expressing Serverless applications on AWS.
- marcy-terui/lamvery: User-friendly deployment and management tool for AWS Lambda function
- apex/apex: Build, deploy, and manage AWS Lambda functions with ease (with Go support!).
どれも使い込んだわけではないので、こっちのデプロイツールもいいよ!という方がいれば教えていただけるとうれしいです。(lamveryもシンプルで良かったです)
あとこのServerlessプラグインもオススメだよ! というのがあればぜひ教えてください。