LambdaのデプロイをシェルスクリプトからServerless Frameworkに切り替えた時に苦労した点

2017.11.22

はじめに

こんにちは植木和樹@上越妙高オフィスです。今月に入って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バケットは「消されるのはちょっと嫌」だったのでスタックに含めないことにしました。

関連する話題

リソースには 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 以外にも各種あります。

どれも使い込んだわけではないので、こっちのデプロイツールもいいよ!という方がいれば教えていただけるとうれしいです。(lamveryもシンプルで良かったです)

あとこのServerlessプラグインもオススメだよ! というのがあればぜひ教えてください。