はじめに
みなさん。IoTやっていて、こんなこと思ったことありませんか?
「Lambdaでデータを整形して、DynamoDBに保存するなんて大層なことせんでもいいから、デバイスから送ったテキストデータを任意の名前でS3に保存できるだけで十分なのにな〜」
私はあります。(超個人的意見)
なんでそんなこと思ったのか?と言いますと、お客様からこんな要望が上がって来ました。
「デバイスとAWSの疎通をとりあえず、なる早で確認したい。ついでにテキストデータをどこかに保存して欲しい。」
こんなときにサクッとできないかな〜と思って、ServerlessFramework使って実装しました。
別にIoT以外でも利用できる場面はあると思います。
主な用途としては、 APIGatewayにファイル(JSONなどのテキストファイル)をPOSTしてS3にPUTする となります。
アーキテクチャ
フローとしては
1. Deviceから APIGatewayのEP/send/{fileName}
にPOSTする
2. APIGatewayからLambdaをキックする
3. LambdaからPOSTのBodyをS3にkeyを{fileName}としてPUTする
みたいな感じです。
ソースコード
ServerlessFrameworkのインストールやプロジェクトの作成はこの辺りの記事を参考にしてください。
参考記事はこちら
serverless.yml
はこんな感じ
service: apigateway-sample
# 基本設定
custom:
defaultStage: dev
defaultRegion: ap-northeast-1
# LambdaのruntimeやiamRoleなどの設定
# APIKeyを用意して、APIGatewayに適用します
# iamRole
# S3 BucketName:serverless-apigateway-sample-bucketに対してGetObject/PutObjectを許可
provider:
name: aws
runtime: nodejs6.10
stage: ${opt:stage, self:custom.defaultStage}
region: ${opt:region, self:custom.defaultRegion}
apiKeys:
- ${self:custom.defaultStage}-send-apiKey
usegePlan:
quota:
limit: 5000
offset: 2
period: MONTH
throttle:
burstLimit: 200
rateLimit: 100
iamRoleStatements:
- Effect: "Allow"
Resource: "arn:aws:s3:::serverless-apigateway-sample-bucket"
Action:
- "s3:GetObject"
- "s3:PutObject"
# Lambdaのhandlerやevent(トリガー)の設定
# APIGatewayにAPIKeyを適用しないなら、private: falseに変更
functions:
putS3FromApiGateway:
handler: index.handler
description: iot-serverless-sample LambdaFunction from APIGateway
events:
- http:
path: send/{fileName}
method: post
cors: true
private: true
# その他各種サービスリソースの設定
# 今回はS3のBucketを作成
# BucketNameは適宜書き換えてください
resources:
Resources:
S3Bucket:
Type: "AWS::S3::Bucket"
Properties:
BucketName: BucketName
BucketPolicy:
Type: "AWS::S3::BucketPolicy"
Properties:
PolicyDocument:
Id: "serverless-sample-bucket-policy"
Version: "2012-10-17"
Statement:
- Sid: "AllowPutObjects"
Effect: "Allow"
Principal: "*"
Action:
- "s3:GetObject"
- "s3:PutObject"
Resource: "arn:aws:s3:::serverless-apigateway-sample-bucket/*"
Bucket: "serverless-apigateway-sample-bucket"
Lambdaのソースコードはこんな感じ
"use strict";
const AWS = require("aws-sdk");
const s3 = new AWS.S3();
const config = require("../config");
exports.handler = (event, context, callback) => {
// POSTリクエストのBody(JSON)を取得
const reqBody = JSON.parse(event["body"]);
// S3にPUTする際のKey名(ファイル名)を取得
const fileName = event["pathParameters"]["fileName"];
// S3にPUTするパラメータを形成
// serverless.ymlで指定したBucketNameに書き換えてください
const param = {
Bucket: BucketName,
Key: fileName,
Body: JSON.stringify(reqBody)
};
// S3にPutObject
return s3.putObject(param).promise()
.then(() => {
console.log("All Done");
callback(null, {
statusCode: 200,
headers:{ "Access-Control-Allow-Origin" : "*" },
body: JSON.stringify({message: "SUCCEEDED!", res: ""})
});
}).catch((err) => {
console.error(err);
callback(null, {
statusCode: 400,
headers:{ "Access-Control-Allow-Origin" : "*" },
body: JSON.stringify({message: "ERROR!", error: err})
});
});
};
Deploy実行
作成した serverless.yml
があるディレクトリで下記コマンドを実行します。
$ sls deploy
・
・
・
・
Service Information
service: apigateway-sample
stage: dev
region: ap-northeast-1
stack: apigateway-sample-dev
api keys:
dev-send-apiKey: APIKey //APIKeyの文字列が表示されます
endpoints:
POST - https://xxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/send/{fileName}
functions:
putS3FromApiGateway: apigateway-sample-dev-putS3FromApiGateway
成功すれば上記のようなメッセージが表示され、APIGatewayのエンドポイントやAPIKey、Lambda名などが表示されます。
実際にJSONをPOSTしてみる
ローカルのPCからJSONファイルをPOSTしてみようと思います
JSONファイルの中身はこんな感じ
{
"deviceID": "sample0002",
"record_time": "2018-03-13T00:00:00.000+0900",
"val_1": "testtesttest",
"val_2": "1234567890",
"val_3": "q2w3e4r5t6y7u8i"
}
CURLでPOSTします。
sample.json
を sample_post.json
としてS3にPUTすることを想定しています。
※APIKeyを適用していないなら -H "X-API-KEY: APIKey"
を削除してください。
$ curl -X POST --data @sample.json \
-H "Content-Type: application/json" \
-H "X-API-KEY: APIKey" \
https://xxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/send/sample_post.json
{"message":"SUCCEEDED!","res":""}
成功すれば、上記のようにメッセージが返ってきて、S3に保存されていると思います。
さいごに
AWS非推奨ではありますが、APIKeyを利用して認証を行なっています。
本来であればAWS認証情報(Sig4)を生成してそれを元に認証を行うのですが、デバイスの中には認証情報を生成できるだけのリソースがないという場合が多々あります。
その場合にこのAPIKeyを利用してAWS側へリクエストを送信するという方法を取ることになります。
もう一度言っておきますが この方法は非推奨ですのでご注意ください
んじゃどうしたらいいの?というあなたに SORACOM です。
SORACOMのサービスを利用すれば、このような問題は一気に解決されます。
SORACOM側にAWSの認証情報を持っておくことができるので、デバイスには一切AWSの認証情報を持つ必要が無くなります。
デバイスからSORACOMへリクエストを送信して、SORACOM側であらかじめ設定しておいた認証情報を元にAWS認証情報を生成し、AWSにリクエストを送信します。
認証情報を変えたければコンソールから変えれば良いだけです。
うん、めっちゃ楽。
なんかSORACOMの宣伝みたいになってしまいましたが、IoTデバイスからAWS等のクラウドにリクエストを送信する際には便利なツールですので、ぜひお使いください。
ではまた!