serverless-frameworkの使い方
概要
この記事を読むと、こんなことがわかります
・serverless-frameworkを使って、
・任意のAWSアカウントに対して、
・定時実行される、
・configファイルが外出しされた 、
・外部ライブラリも搭載したLambdaを実装+デプロイする、
・Infra as a Codeを実践できるようになる。
具体的には、定時刻にSlackへコメントを流すbotを作りながら、serverless-frameworkの基本的な使い方をみていきます。トリガー設定、外部ファイル参照、外部ライブラリの搭載などが含まれるため、ご自身の状況に合わせて適宜改造しちゃってください。
最終的なソースはこちら
【GitHub】serverless-framework-call-slack
インストール+環境構築
前提
* Mac OS X Catalina
* 使用言語:python
* node.jsはインストール済(v4以上を使用),
* pythonはインストール済(3系使用)
* AWSアカウントは作成済
* SlackのIncomming-WebhookのURLを取得済み
なお、SlackのURLの取り方は解説記事がたくさんあるので他を参照のこと。
例:【Qiita】slackのIncoming webhookが新しくなっていたのでまとめてみた
serverless-frameworkをインストールする
下記コマンドで一発インストール完了。次のコマンドで正常にインストールされたかを確認する。バージョンが表示されればオッケー。なお、自動でエイリアスが張ってあるので、以下のコマンドは全て「serverless」を「sls」に読み替えても成立する。
# インストール
$ npm install -g serverless
# 確認
$ serverless --version
1.28.0
# あるいはこんな表示になるかも?とかくバージョン情報がでればOK
$ serverless --version
Framework Core: 1.74.1
Plugin: 3.6.15
SDK: 2.3.1
Components: 2.31.11
# これでも同じ結果が得られる
$ sls --version
1.28.0
プロバイダーアカウントのセットアップ
serverless-frameworkはAWS内の1ユーザーとしてAWSアカウントへアクセスすることになる。ゆえに新規にユーザー発行が必要になる。要は以下3つのことをやればよいので、コンソール画面からでも可能。
・新規IAMユーザーを作成する
・アクセスキーとシークレットアクセスキーを発行する
・作成したユーザーにAdministratorAccessのポリシーを付与する
※もちろんXXXX....とYYYY....の部分は各環境にて異なる文字列がでているので、適宜読み替える。
# serverless-framework用専用ユーザー作成
$ aws iam create-user --user-name serverless-framework
$ aws iam add-user-to-group --user-name serverless-framework --group-name admin
$ aws iam create-access-key --user-name serverless-framework
{
"AccessKey": {
"UserName": "serverless-framework",
"AccessKeyId": "XXXXXXXXXXXXXXXXXXXX",
"Status": "Active",
"SecretAccessKey": "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY",
"CreateDate": "2020-07-04T04:10:00Z"
}
}
$ vim ~/.aws/credentials
# 下記を追記
[serverless-framework]
aws_access_key_id=XXXXXXXXXXXXXXXXXXXX
aws_secret_access_key=YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
【参考資料】
・【AWS公式文書】IAM ユーザーおよびグループの作成
・【AWS公式文書】IAM ユーザーのアクセスキーを作成する
※(必須ではない処理)
筆者は~/.aws/credentialsの[default]の値をわざと削除している。
AWSアカウントの指し先を指定をせずに、あるいは指定を忘れて意図しない場所に意図しない処理をかけてしまうことを防ぐため。
はじめてのデプロイ
下記コマンドでフォーマットを作成する。
するとこのような階層でディレクトリとファイルが作成される。
$ serverless create --template aws-python3 --name call-slack --path call-slack
$ tree ./call-slack/
call-slack
├── .gitignore
├── handler.py
└── serverless.yml
簡単にコマンドの説明を付記する。
--template
AWS Lambda用に各言語に合わせたテンプレートを用意してくれる。
詳細は公式ドキュメントへ。代表的なものはこちら。
aws-nodejs
aws-nodejs-typescript
aws-alexa-typescript
aws-python
aws-python3
aws-ruby
aws-java-maven
aws-java-gradle
aws-scala-sbt
aws-csharp
aws-go
aws-go-dep
aws-go-mod
--name
プロジェクトの名前をつける。
call-slack/serverless.ymlの冒頭の名前にくる。
なお都合上他のリソースと同名をつけるとあとでデプロイが失敗するため注意。
--path
トップのディレクトリの名前を指定する。
さて、この時点ですでにデプロイはできるが、指し先だけ設定してあげる。
serverless.ymlを開き、デフォルトででてくる余計なコメントを削除し、下記の部分に`profile: serverless-framework`を追記
service: call-slack
provider:
name: aws
runtime: python3.8
profile: serverless-framework # <-ここを追記
functions:
hello:
handler: handler.hello
その後、serverless.ymlがある階層で以下のコマンドを打つ。
するとバージニアリージョン(us-east-1)に「call-slack-dev-hello」という名前のLambdaができているはず。
なお、削除するときにはremoveと打てば完了。(次節から東京リージョンにLambdaを作成していくため、ここでは削除しておく)
# デプロイ
$ serverless deploy
# 削除
$ serverless remove -v
キックされるトリガー部分の作り方
CloudWatch のEventBridgeを使って、定時刻にLambdaを叩くトリガー部分を作成する。
(トリガーついでにリージョンを東京に修正しておく)
serverless.ymlの下記コメント欄以下を追記し、`$ serverless deploy`をする。
コンソール画面で確認すると、トリガー部分が追加されているはずである。
※なお、このcronはUTC時刻であることに注意
service: call-slack
provider:
name: aws
runtime: python3.8
profile: serverless-framework
region: ap-northeast-1 # <- ここも追記
functions:
hello:
handler: handler.hello
# ↓<===== ここから追記 =======>↓
events:
- eventBridge:
schedule: cron(0 9 * * ? *)
configファイルを外部ファイルとして読み込む方法
キー情報など、リポジトリに直接組み込みたくないファイルもある場合は、外部ファイルを読み出す設定を行う。
手始めにcronで動かす時刻を外出ししてみよう。
まず外部ファイルとしてconfig.ymlという名前のファイルをcall-slackディレクトリ内に作成し、下記の内容を書き込む。
schedule: cron(0 9 * * ? *)
次にトリガー部分を修正する。serverless.yml下記コメントの部分を修正し、`$ serverless deploy`でデプロイする
なお、file()のカッコ内は相対パスで指定することができる。
service: call-slack
provider:
name: aws
runtime: python3.8
profile: serverless-framework
region: ap-northeast-1
functions:
hello:
handler: handler.hello
events:
- eventBridge:
schedule: ${file(./config.yml):schedule} # <= ここを修正
外部ライブラリを積み込む方法
slackに通知を送る部分を作成する。
今回はrequestsライブラリをpipで取得してLambdaに搭載する。
ここではserverless-frameworkのプラグインであるserverless-python-requirementsを利用する。
※なお、これはpipで呼び出したライブラリをDockerを使ってAmazon Linux内でビルドしてくれる優れモノではあるが、個人開発のOSSである。
まずcall-slackディレクトリ直下で以下のコマンドを叩くと、必要なライブラリが取得できる。
次に、requirements.txtを作り、必要なライブラリを記入する。
# プラグイン追加
$ npm install --save serverless-python-requirements
# 取得したいPythonライブラリを記入
$ vim requirements.txt
# 以下を記入
requests
次にconfigファイルにURLを追記する。
Your-slack-incomming-webhookの部分は各人のSlackのWebHookに読み替えること。
schedule: cron(0 9 * * ? *)
Slack-WebHook: Your-slack-incomming-webhook
そしてhandler.pyを以下のように書き換える。(Slackに通知する最低限の実装)
url='Your-slack-incomming-webhook'は以下略
import json
import requests
def post_message(url, message):
"""Post a message to slack using a webhook url."""
data = {'text': message}
response = requests.post(url, data=json.dumps(data), headers={'Content-Type': 'application/json'})
return response.status_code
def hello(event, context):
return post_message(
url='Your-slack-incomming-webhook',
message='This is test message from AWS Lambda and serverless-framework'
)
ここで`$ serverless deploy`しテストすれば、Slackへ通知が飛ぶはず。こんな感じで(サムネはきっと違うけど)
キー情報をssmに外出し
ssmパラメータを使えば、パラメータを安全に保持することができる。
こちらに情報を外出しし、適宜アクセスできるようにする。
serverless-frameworkには、CloudFormationのリソース定義がほぼそのまま書くことができる。
ついでssmパラメータを読み取る権限も付与する。
service: call-slack
provider:
name: aws
runtime: python3.8
profile: ume-serverless-framework
#######################################
# ↓ここから追記↓ 権限付与
#######################################
region: ap-northeast-1
iamRoleStatements:
- Effect: "Allow"
Action:
- "ssm:Describe*"
- "ssm:Get*"
- "ssm:List*"
Resource:
- "*"
#######################################
# ↑ここまで↑
#######################################
functions:
hello:
handler: handler.hello
events:
- eventBridge:
schedule: ${file(./config.yml):schedule} # <= ここを修正
plugins:
- serverless-python-requirements
custom:
pythonRequirements:
dockerizePip: true
#######################################
# ↓ここから追記↓ ssmパラメータ作成
#######################################
resources:
Resources:
ssmParam:
Type: "AWS::SSM::Parameter"
Properties:
Name: Slack-WebHook
Type: String
Value: ${file(./keys.yml):Slack-WebHook}
また、Lambda本体を以下のように書き換える。
import json
import requests
import boto3
ssm = boto3.client("ssm", region_name="ap-northeast-1")
def get_ssm_params(ssm_parameter_key):
param_dict = ssm.get_parameter(Name=ssm_parameter_key)
return param_dict.get('Parameter').get('Value')
def post_message(url, message):
"""Post a message to slack using a webhook url."""
data = {'text': message}
response = requests.post(url, data=json.dumps(data), headers={'Content-Type': 'application/json'})
return response.status_code
def hello(event, context):
return post_message(
url=get_ssm_params('Slack-WebHook'),
message='This is test message from AWS Lambda and serverless-framework'
)
```
ここで`$ serverless deploy`しテストすれば、先ほどと同じくSlackへ通知が飛ぶはず。
最後に
Infra as a Code万歳!
参考文献
・Serverless Frameworkの使い方まとめ
・Serverless Frameworkのプラグインを利用した外部モジュールの管理