本稿はServerless2 Advent Calendar 2018の14日目です。
作るもの
画像をAWS S3にアップロードすると、AWS Lambdaが自動的に実行され、AWS Rekognitionによって自動でラベル付けがされるというアプリケーションです。
このようなラーメンの画像をアップロードすると
[
{ "Name": "Noodle", "Confidence": 99.91551208496094 },
{ "Name": "Food", "Confidence": 99.91551208496094 },
// 中略
]
このようなJSONファイルが出力されます。「Noodle」「Food」といったラベル情報が付いていることがわかります。
構成
画像はCloudcraftで作成
この図のどこを触っているのかイメージしながら本稿を読み進めると、理解しやすいと思います。
対象
- TypeScriptに触れてみたい人
- AWSの下記サービスに触れてみたい人
- AWS Lambda: サーバレスアーキテクチャでコードを実行
- AWS S3: クラウドストレージ
- AWS Rekognition: AIによる画像認識
- これらを一括でデプロイするServerless Frameworkに触れてみたい人
必要なもの
- AWSアカウント
- AWS利用料金(たぶん5円ぐらい)
- パソコンとブラウザ
以下、2018年12月10日時点の情報です。
AWS Cloud9で開発環境を作る
参考: 初めてのサーバーレスアプリケーション開発 ~Serverless Framework を使ってAWSリソースをデプロイする~ | DevelopersIO
まずは開発環境を作っていきます。
Node.jsが実行できる環境が必要なのですが、ここでは開発環境構築にAWS Cloud9を使っていきます。AWS Cloud9を作るだけで、これから開発するために必要な準備がほぼ全て揃います。
AWS管理コンソールを開き、
- 画面右上のドロップダウンから「アジアパシフィック(シンガポール)」を選択
- 画面左上の「サービス」から「Cloud9」を選択
- 「Create environment」
- Nameに適当な名前、たとえば「my-serverless-dev」等を入力して「Next step」
- 下記を設定して「Next step」※いずれもデフォルト設定のはずです
- Environment type: 「Create a new instance for environment (EC2)」
- 今回の開発環境のために、あたらしくサーバをひとつ立てるという設定です
- Instance type: 「t2.micro (1 GiB RAM + 1 vCPU)」
- サーバのスペックです。高性能になるほど高額になるので、ここでは最低スペックにしています
- 動作が重かったり「out of memory」のようなエラーが散見される場合は、スペックを上げましょう。
- Cost-saving setting: 「After 30 minutes(default)」
- 操作せずに30分経ったら自動でサーバを休止状態にして費用を節約します
- Environment type: 「Create a new instance for environment (EC2)」
- 設定を確認したら「Create Environment」
すこし時間かかりますので、その間にAWS Cloud9の特徴を説明します。
AWS Cloud9は、作成すると自動的に仮想マシン(AWS EC2)が立ち上がり、仮想マシンの中のファイルを編集し、コードを実行できるようになります。AWS Cloud9自体は無料ですが、この仮想マシンに利用料金がかかります。
AWS Cloud9には下記の特徴があります。
- 数ステップで簡単に開発環境が構築できる
- ブラウザ上でIDEが開き、サーバ上のファイルを編集できる
- AWSの各種サービスとの連携が容易
もちろん、開発に慣れている方はご自身のマシンのローカル環境下に開発環境を構築したほうがより生産性高く開発を進めることができます。その場合はNode.js等の必要なソフトウェアのインストールが必要になることと、AWSアカウントの接続情報のセットアップ(後述)が必要な事に注意しましょう。
Serverless Frameworkのセットアップ
Cloud9が起動したら、画面下部の ec2-user:~/environment $
と表示されているbashターミナル部分を使って操作していきます。
画像はAWS Cloud9 の IDE チュートリアル - AWS Cloud9より引用
下記コマンドを順番に実行していきます。
$ npm install --global serverless # Serverlessのインストール
$ serverless create --template aws-nodejs-typescript --path my-serverless-sample # Serverlessのプロジェクト作成
$ cd my-serverless-sample # 作成したプロジェクトに移動
$ npm install --save-dev # 必要なnpmパッケージのインストール
serverless create
コマンドを実行すると、必要なパッケージ情報が書いてあるpackage.json
や、実行するプログラム本体となるhandler.js
等が自動で作成されます。今回は--template aws-nodejs-typescript
と指定しているので、プログラムはhandler.ts
となりますし、TypeScriptでの実行やコンパイルに必要な設定ファイルやパッケージ情報が追記されます。
とりあえずデプロイしてみます。
$ serverless deploy
このコマンドを実行すると、必要なファイルがwebpackでコンパイルされ、AWS上にデプロイされます。今回であれば、handler.ts
に記述したプログラムがAWS Lambdaにデプロイされます。Cloud9の認証情報を使うので、AWSへの接続情報などを特に指定することなく、デプロイすることができます。
デプロイされたプログラムを手動実行するには、下記コマンドを実行します。
$ serverless invoke --function hello
下記のレスポンスが返ってきたら成功です。
{
"statusCode": 200,
"body": "{\"message\":\"Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!\",\"input\":{}}"
}
参考: git管理する場合
プロジェクトをgit管理する場合、下記コマンドを実行しておきます。
$ git init
$ curl https://raw.githubusercontent.com/github/gitignore/master/Node.gitignore > .gitignore # お好みで
$ echo 'package-lock.json binary' >> .gitattributes # お好みで
$ git remote set-url origin https://github.com/USERNAME/REPOSITORY.git # GitHubに上げるなら
必要に応じて所々git diff
や git commit
しながら進めると、より理解しやすいと思います。
参考: Serverlessのテンプレートを使わない場合
TypeScriptのテンプレートを使わない場合や、既存のJavaScriptで書かれたプロジェクトをTypeScriptに書き換える場合、下記コマンドで必要なパッケージや設定ファイルをセットしていく事ができます。
$ npm install --save-dev typescript webpack serverless-webpack ts-loader @types/aws-lambda @types/aws-sdk @types/node
$ npx tsc --init
webpackの設定ファイルも自力で記述する必要があります。
const path = require('path');
module.exports = {
entry: entry: './handler.ts',,
mode: 'production',
resolve: {
extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'],
},
output: {
libraryTarget: 'commonjs',
path: path.join(__dirname, '.webpack'),
filename: '[name].js',
},
target: 'node',
module: {
rules: [
{ test: /\.tsx?$/, loader: 'ts-loader' },
],
},
};
また、もし「JavaScriptは書いたことがあるもののTypeScriptでの開発に慣れていない」という人は、tsconfigにstrict: false
の設定を入れると楽になると思います。
/* Strict Type-Checking Options */
- "strict": true, /* Enable all strict type-checking options. */
+ "strict": false, /* Enable all strict type-checking options. */
画像のラベル付けをするプログラムを書く
※以下、「S3のバケット名」となる<myserverless20181214>
の部分は、ご自身のハンドルネームなど適当な名前に書き換えて下さい。重複しない名前をつける必要があるためです。
まず、今回必要になるAWSサービスをデプロイするための情報を記述します。デフォルトの状態でAWS Lambdaはすでにデプロイできているので、AWS S3とAWS Rekognitionの情報を記述していきます。
service:
name: my-serverless-sample
# Add the serverless-webpack plugin
plugins:
- serverless-webpack
provider:
name: aws
runtime: nodejs8.10
region: us-east-1
iamRoleStatements:
- Effect: "Allow"
Resource: "arn:aws:s3:::*"
Action:
- "s3:*"
- Effect: "Allow"
Resource: "*"
Action:
- "rekognition:*"
functions:
hello:
handler: handler.hello
events:
- s3:
bucket: <myserverless20181214>-input-images # 書き換えて下さい
event: s3:ObjectCreated:*
resources:
Resources:
NewResource:
Type: AWS::S3::Bucket
Properties:
BucketName: <myserverless20181214>-output-tags # 書き換えて下さい
provider
に、AWS S3とAWS Rekognitionへのアクセス許可設定を、functions
に、入力用S3オブジェクトが作成されたら実行する設定を、resources
に、出力用S3バケットを作る設定を記述します。
TypeScriptでAWS S3とAWS Rekognitionにアクセスするために、aws-sdk
をインストールする下記コマンドを実行します。
$ npm install --save aws-sdk
TypeScriptのプログラムを記述します。S3に配置されたファイルのバケット名とキー名を取得して、Rekognitionに渡して、JSONにしてS3に書き込む、というプログラムを書いていきます。
import * as AWS from 'aws-sdk';
import * as AWSLambda from 'aws-lambda';
const rekognition = new AWS.Rekognition();
const s3 = new AWS.S3();
export const hello = function(event: AWSLambda.S3Event, context: AWSLambda.Context) {
// AWS S3にアップロードされた画像の情報を取得
const bucket = event.Records[0].s3.bucket.name;
const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+g/, " "));
const paramsToRekognition: AWS.Rekognition.DetectLabelsRequest = {
Image: {
S3Object: {
Bucket: bucket,
Name: key
}
},
MaxLabels: 10,
MinConfidence: 1
}
// AWS Rekognitionに渡す
rekognition.detectLabels(paramsToRekognition, function (err, data) {
const paramsToS3: AWS.S3.PutObjectRequest = {
Body: JSON.stringify(data.Labels, null, 2),
Bucket: '<myserverless20181214>-output-tags',
Key: `${key}.json`
};
// AWS S3に出力する
s3.putObject(paramsToS3, function (err, data) {
console.log("成功!");
});
});
}
上記プログラムをコピペしてもいいですが、たとえばconst paramsToRekognition: AWS.Rekognition.DetectLabelsRequest = { ...
等は、実際に手で書いてみるとエディタの補完が効いて簡単に記述できるのが実感できると思います。
: AWS.Rekognition.DetectLabelsRequest
の部分はTypeScriptの型情報であり、大雑把に言うと「この変数にはこんなデータが入りますよ」という情報が定義されています。AWSとの連携は独自のメソッドやパラメータが多くて覚えづらいですが、TypeScriptの型情報があれば覚える必要はありません。エディタはその情報に沿って、補完を出してくれます。例えば「大文字か小文字かわかりづらい」ような時も、補完があれば間違うことはありません。
デプロイ
完成したら、先程と同じく下記コマンドでデプロイします。
$ serverless deploy
最初にデプロイした物は上書きされて新しい物だけになるので、何回デプロイしても大丈夫です。
使ってみる
画像をアップロードしてみます。AWSのタブをもうひとつ開き
- 画面左上「サービス」から「S3」
-
<myserverless20181214>-input-image
というバケットを選択 - 画面上に適当な画像をドラッグアンドドロップ
- 「アップロード」を選択
- 「Amazon S3」に戻り、
<myserverless20181214>-output-tags
というバケットを選択 - JSONファイルがあるはずなので、開く
JSONファイルが作成され、ラベル情報が付いていれば成功です。
完成品はこちらにも置いてあります。
https://github.com/s2terminal/serverless-aws-sample
うまくいかないばあい
AWS CloudWatchを使って、デバッグログを確認します。
- 画面左上「サービス」から「Lambda」
- 「my-serverless-sample」を選択
- 「モニタリング」
- 「CloudWatchのログを表示」
- 時間帯が最も近いログを選択
プログラム中でconsole.log()
を使った場合なども、ここに出力されます。
成功!
が表示されていれば成功しているはずです。
片付け
S3にアップロードしたファイルは、手動で削除しておきます。
下記のコマンドで、serverless deploy
した物は削除できます。
$ serverless remove
S3内のファイルを削除していないと、このコマンドでS3バケットを削除できないのでご注意下さい。
最後に、Cloud9の開発環境も、削除しておきます。
- 画面左上「サービス」から「Cloud9」
- my-serverless-dev(付けた名前)を選択し「Delete」
参考: AWSをはじめて使う人向け
AWSのアカウントを作成したばかりであれば12ヶ月間は無料利用枠があるため、今回紹介した利用範囲内の小規模・低スペック用途であれば、ほぼ無料で利用することができます。
画面右上の「自分のアカウント名」→「請求ダッシュボード」から、実際の利用料金がいくらなのか確認できます。
また、万が一「Cloud9環境を立ち上げっぱなしにしてしまった」などで意図しない請求が発生しないよう、課金金額が一定額を超えたらアラートが飛ぶように設定することもできます。
参考: アラートと通知で請求額をモニタリング - AWS 請求情報とコスト管理
今回つかったサービスの料金
- AWS Cloud9
- Cloud9自体は無料
- EC2: 1時間あたり0.0116ドル
- AWS Cloud9 の料金 – アマゾン ウェブ サービス
- AWS S3
- 1GBあたり約0.023ドル
- 料金 - Amazon S3(クラウドストレージ)|AWS
- AWS Lambda
- RAM1024MBで、1回1,000msecかかるとして、実行1,000回あたり0.01667ドル
- 料金 - AWS Lambda(サーバーレスでコードを実行)|AWS
- AWS Rekognition
- 画像10枚あたり0.01ドル
- 【Amazon Rekognition】料金(人工知能・機械学習の画像認識・画像解析) | AWS
参考: もっと情報が欲しい人向け
以下をやってみましょう。
- たとえばServerlessを活用して、AWSのS3やRekognition以外の他サービス(API Gateway、Dynamo DB等)と連携してみたり
- たとえばTypeScriptではない言語をLambdaで動かしてみたり
- たとえばTypeScript+webpackを活用して、WebフロントエンドやCLIアプリケーションを作ってみたり
- たとえばMicrosoft AzureやGoogle等の他のAIサービスと連携してみたり
参考: ローカルで開発する人向け
今回つかったCloud9環境にインストールされていたNode.jsのバージョンはv6.15です。
$ node --version
v6.15.0
これよりも新しいバージョンであれば基本的には問題なく動作すると思います。
AWS IAMの設定
AWS Cloud9では必要ありませんでしたが、Serverless DeployするためのIAMアカウントを作る必要があります。
https://serverless.com/framework/docs/providers/aws/guide/credentials/ を参考に
- AWS consoleを開く
- 「IAM」→「ユーザー」→「ユーザーを追加」
- ユーザー名を、例えば「serverless-sample-user」等で設定
- アクセスの種類「プログラムによるアクセス」
- 「既存のポリシーを直接アタッチ」→「AdministratorAccess」
- 下記を実行
$ export AWS_ACCESS_KEY_ID=XXXX # 画面に表示されたアクセスキー
$ export AWS_SECRET_ACCESS_KEY=XXXX # 画面に表示されたシークレットアクセスキー
これで$ serverless deploy
が実行可能になります。
ご自身でアクセスキーを管理する場合は、AWSが開発しているgit-secretsを使うのがおすすめです。誤ってcommitにシークレットアクセスキーを含めてしまう、といった事故を予防できます。
まとめ
Serverless Frameworkを使うことで、簡単にサーバレスアプリケーションを開発することができました。またTypeScriptを使うことでAWSと連携するプログラムが記述しやすくなる事もわかりました。
ぜひServerlessやTypeScriptを活用してみて下さい。