はじめに
12月も早いもので中盤を迎えましたね。個人的なお話になりますがこの日はお誕生日なのでとってもハッピーな気分でいます。この記事もそんな気持ちで書いています。
最近、ServerlessFrameworkとAWSを触っているのでそれに関して自分なりにまとめてみました。使用したサービスは、LambdaとS3、SQS、API Gatewayなのでそのことについて書いてあります。
今回使用したコードはここにあります。
また、本記事を作成するにあたり以下の記事を参考にしました。
とことんサーバーレス①:Serverless Framework入門編
Serverless FrameworkとS3で超簡単な投票システムを作った話
[Serverless FrameworkでLambdaをPython 3.6で使ってみた]
(https://dev.classmethod.jp/cloud/aws/serverless-framework-with-python-3-6/)
Serverless Frameworkの使い方まとめ
準備・環境
Node.jsとPythonの環境が必要です。あとは、AWSのアカウントとかも必要になります。
Node.jsはServerlessFramework、PythonはAWS CLIを利用するために必要です。
1. Serverlessのインストール
npmでインストールします。
$ npm install -g serverless
$ sls -v
Framework Core: 1.54.0
Plugin: 3.1.2
SDK: 2.1.2
Components Core: 1.1.1
Components CLI: 1.4.0
このように表示されればインストール完了です。
2. IAMユーザーの作成とAWS Credentialを設定する
ServerlessFrameworkでAWSを利用するためのIAMを作成する必要があります。
IAMユーザーの作成
IAMのダッシュボードでユーザーからユーザーの作成をクリックすることで作成できます。基本的には、プログラムによるアクセスにチェックを入れることと今回、作成するのに必要な権限を与えれば大丈夫です。また、AWS Credentialのキーはここでしか取得できないので注意します。
AWS Credentialの設定
AWS Credentialの設定を行うためには、AWS CLIをインストールする必要があります。以下のコマンドでインストールできます。
$ pip install aws-sdk
$ aws --version
aws-cli/1.16.276 Python/3.7.3 Darwin/18.7.0 botocore/1.13.12
このように表示されればインストールが完了しています。
以下のようにしてAWSCredentialの設定を行います。
$ aws configure
AWS Access Key ID [None]: 作成したIAMのアクセスキーID
AWS Secret Access Key [None]: 作成したIAMのシークレットアクセスキー
Default region name [None]: ap-northeast-1
Default output format [None]: ENTER
引用: とことんサーバーレス①:Serverless Framework入門編
これでServerlessFrameworkを利用する準備は完了です。
3. ServerlessFrameworkでHelloWorld
まずは、Serverlessのプロジェクトを作成していきます。
以下のコマンドを打つことで作成できます。
$ sls create -t aws-python3 -p ほげほげぷろじぇくと
以下のように表示されれば成功です。
Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "/..."
_______ __
| _ .-----.----.--.--.-----.----| .-----.-----.-----.
| |___| -__| _| | | -__| _| | -__|__ --|__ --|
|____ |_____|__| \___/|_____|__| |__|_____|_____|_____|
| | | The Serverless Application Framework
| | serverless.com, v1.54.0
-------'
Serverless: Successfully generated boilerplate for template: "aws-python3"
次に、プロジェクト名のディレクトリにあるhandler.py
を以下のようにします。
import json
def hello(event, context):
body = {
"message": "Hello, world",
}
response = {
"statusCode": 200,
"body": json.dumps(body)
}
return response
また、デプロイ先のリージョンを東京にします。
service: ほげほげ
provider:
name: aws
runtime: python3.7
region: ap-northeast-1 #東京リージョンにデプロイする
functions:
hello:
handler: handler.hello
そして、デプロイをします。
$ sls deploy
実行します。
$ sls invoke -f hello
以下のように表示されれば成功です。
{
"statusCode": 200,
"body": "{\"message\": \"Hello, world\"}"
}
また、AWSのLambdaのダッシュボードにデプロイした関数があるので、そこから実行してみても構いません。
4. API GateWayからLambdaを起動する
まず、先ほどデプロイしたhello関数をAPI Gatewayを利用して実行してみます。serverless.yml
を以下のように変更します。
service: ほげほげ
provider:
...
functions:
hello:
handler: handler.hello
# ここから追加
events:
- http:
path: hello
method: get
cors: true
引用: https://serverless.com/framework/docs/providers/aws/events/apigateway/
このようにしてからsls deploy
をすると以下のようにendpoints
がでます。
Service Information
...
endpoints:
GET - https://hogehoge.amazonaws.com/dev/hello
functions:
hello: hogehoge-dev-hello
layers:
None
このAPIをcurlコマンドなどで叩くと、HelloWorldが表示されます。
5. S3にファイルを保存してそれを読み取る
S3にファイルを保存する関数の作成
新たに、sthreeput.py
を作成し、以下のように記述します。
ここでは、100までのランダムな数字を10個記述してあるテキストファイルを日付と時刻の名前で保存する関数を作成します。
import boto3 #Python用のAWS SDK
import datetime
import random
s3 = boto3.resource('s3')
bucketName = "hogehogebucket" # 自分で決めたS3バケットの名前
def index(event, context):
dt = datetime.datetime.now()
text = ""
for i in range(10):
num = str(random.randint(0, 100))
text += num + " "
key = "{0:%Y-%m-%d-%H-%M-%S}.txt".format(dt) #保存するファイル名になる
obj = s3.Object(bucketName, key)
obj.put(Body=text.encode())
return {
"statusCode": 200,
"body": text
}
serverless.ymlの設定
ファイルを保存するようのS3バケットを作成します。また、S3にファイルを追加するための関数についても記述します。S3のバケット名は、すでにあるもの(自分以外のものでも)だと使えないので気をつけてください。
functions:
hello:
....
#関数名
sthree-put:
handler: sthreeput.index #sthreeput.pyのindex関数を起動する
resources:
Resources:
SthreeBucket: # S3のバケットを作成する
Type: AWS::S3::Bucket
Properties:
BucketName : "hogehogebucket" # S3のバケットの名前(すでにある名前はつけられない)
これでsls deploy
して、関数を実行してみましょう。うまくいっていれば、作成したバケットにファイルが保存されています。
S3にファイルが保存されたのをトリガーにしてファイルを見る
新たにsthreereceive.py
を以下のように作成します。
import boto3
s3 = boto3.client('s3')
def index(event, context):
bucket = event["Records"][0]["s3"]["bucket"]["name"] #バケットの名前
key = event["Records"][0]["s3"]["object"]["key"] #保存したファイルの名前
response = s3.get_object(Bucket=bucket, Key=key) #バケットの中にあるオブジェクトの取得
body = response['Body'].read().decode("utf-8") #ファイルの中の情報を取得してutf-8にデコードする
print(body)
return body
S3にフックされてLambda関数が起動すると1番目の引数eventに以下のようなJSONが渡されます。
{
"Records": [
{
"eventVersion": "2.0",
"eventSource": "aws:s3",
"awsRegion": "ap-northeast-1",
"eventTime": "1970-01-01T00:00:00.000Z",
"eventName": "ObjectCreated:Put",
"userIdentity": {
"principalId": "EXAMPLE"
},
"requestParameters": {
"sourceIPAddress": "127.0.0.1"
},
"responseElements": {
"x-amz-request-id": "EXAMPLE123456789",
"x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"
},
"s3": {
"s3SchemaVersion": "1.0",
"configurationId": "testConfigRule",
"bucket": {
"name": "example-bucket",
"ownerIdentity": {
"principalId": "EXAMPLE"
},
"arn": "arn:aws:s3:::example-bucket"
},
"object": {
"key": "test/key",
"size": 1024,
"eTag": "0123456789abcdef0123456789abcdef",
"sequencer": "0A1B2C3D4E5F678901"
}
}
}
]
}
この情報からS3バケットの名前とファイル名であるkeyを取得します。
serverless.ymlの設定
関数の設定とS3のイベントの設定を行います。
...
functions:
sthree-put:
handler: sthreeput.index #sthreeput.pyのindex関数を起動する
sthree-receive:
handler: sthreereceive.index
events:
- s3:
bucket: "sthreebucket-hogehoge" #バケットの名前
event: s3:ObjectCreated:* #S3に作られた時に起動
existing: true #S3のバケットを作成しているためこれをつける
最後のexisting: ture
がない場合、バケットが作成されるためすでに同じ名前のバケットがある場合デプロイすることができません。先ほど、バケットを作成したためこのオプションを設定します。serverless.ymlのResourcesの部分を消し、sls remove
をしてまっさらな状態にすれば、最後のオプションがなくてもデプロイできます。
さて、これでsls deploy
を行い、sthreeput関数を起動すれば、バケットにファイルが保存され、sthreereceive関数が起動していることがLambdaのダッシュボードのモニタリングから確認できます。
また、今回はS3に保存されたことをトリガーにして、ファイルの確認を行いましたが、バケットの名前と確認したいファイルのキーを指定してファイルの確認をすることもできます。
6. SQSにメッセージを送信してそのメッセージ受け取る
SQSにメッセージを送る関数の作成
新たにsendmessage.py
を以下のように記述します。
import boto3
import random
import json
sqs = boto3.resource('sqs')
queueName = "hogehoge-queue"
def index(event, context):
queue = sqs.get_queue_by_name(QueueName=queueName) # キューを取得
message = "" # 送信するメッセージ
for i in range(10):
num = str(random.randint(0, 100))
message += num + " "
print(message)
queue.send_message(MessageBody=json.dumps({ "message": message })) # メッセージの送信
return message
serverless.ymlの設定
メッセージを送信するためのキューを作成します。S3のバケットとは違いSQSのキューの名前は、他のアカウントの人と名前が同じでも大丈夫です。
provider:
...
iamRoleStatements:
- Effect: "Allow"
Action:
- "s3:PutObject"
- "s3:GetObject"
- "s3:ListBucket"
- "sqs:CreateQueue"
- "sqs:DeleteQueue"
- "sqs:SendMessage"
- "sqs:GetQueueUrl"
Resource: "*"
...
functions:
sthree-put:
...
sendmessage:
handler: sendmessage.index
resources:
Resources:
SthreeBucket:
...
hogehogeQueue:
Type: AWS::SQS::Queue # SQSのキュー作成
Properties:
QueueName: "hogehoge-queue"
これでsls deploy
を行い、sendmessage関数を起動すれば、作成したキューにメッセージが追加されているはずです。
SQSにメッセージを送るのトリガーにメッセージを受け取る
次に、先ほど送信したメッセージを受け取ってみましょう。receivemessage.py
を作成して以下のように記述します。
import json
def index(event, context):
body = json.loads(event["Records"][0]["body"])
message = body["message"]
print(message)
return message
SQSにフックされてLambda関数が起動すると1番目の引数eventに以下のようなJSONが渡されます。
{
"Records": [
{
"messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78",
"receiptHandle": "MessageReceiptHandle",
"body": "Hello from SQS!",
"attributes": {
"ApproximateReceiveCount": "1",
"SentTimestamp": "1523232000000",
"SenderId": "123456789012",
"ApproximateFirstReceiveTimestamp": "1523232000001"
},
"messageAttributes": {},
"md5OfBody": "7b270e59b47ff90a553787216d55d91d",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:ap-northeast-1:123456789012:MyQueue",
"awsRegion": "ap-northeast-1"
}
]
}
ここからbodyを取得することによって、送信したメッセージを取得できます。
serverless.ymlの設定
...
functions:
receivemessage:
handler: receivemessage.index
events:
- sqs:
# arn:aws:sqs:region:accountid:hogehoge-queue みたいになる
arn:
Fn::Join:
- ':'
- - arn
- aws
- sqs
- Ref: AWS::Region
- Ref: AWS::AccountId
- "hogehoge-queue"
これでsls deploy
をしてsendmessage関数を起動するとreceivemessage関数が起動します。receivemessage関数がエラーを起こしているとなんども起動されるので注意しましょう。この場合、キューのメッセージをクリアすることによって止めることができます。
おわりに
必要なければ削除しましょう。
以下のコマンドできれいさっぱりになります。
sls remove
バケットが上手く消えなかったりするのですがその時は、バケットを空にしたり、直接スタックを削除すると良いかもしれません。
AWSはいろんなサービスが他にもあるのでと触ってみようかなぁと思いました