Serverless Framework と TypeScript でサーバレス開発事始め

前回の記事から時間が空いてしまいましたが、ちょうど新しいプロダクトの開発を始めるところなので Serverless FrameworkTypeScript で開発する際の事始めについてまとめます。
例によって Hello World になりますが、こんな感じで作り始めをお伝えします。

シリーズの記事

環境
本記事の開発環境は以下となります。

  • Windows 10 64bit
  • Visual Studio Code
  • Node.js 9
  • TypeScript 2.7
  • Serverless Framework 1.26.0

主な実行環境について、今回は準備なので全部稼働まではいきませんが以下を想定しています。
※ Amazon API Gateway などは、Serverless Framework が自動設定するので省略、明示的に使うものとしてリストになります。

  • Amazon S3
  • AWS Lambda

想定アプリケーション

ウェブサイトで、アクセス時に JavaScript で Web API を呼び出し “Hello World” の挨拶を取得します。 まずは動作させるところを目標にシンプルな形で進めます。

ウェブサイトは Amazon S3 に HTML を配置し静的ウェブサイトのホスティングを使います。
本格稼働するには Amazon CloudFrontAmazon Route 53 などが必要となりますが、まずは稼働を目指し S3 だけで進めます。

Web API はサーバーレスで作りたいので AWS Lambda を使います。
インスタンスやコンテナーの管理が不要で、純粋にアプリケーションの開発やメンテに集中できるのサーバーレスにこだわりたいです。
こちらも本格稼働にあたっては Amazon API Gateway にカスタム・ドメインを割り当てたりしますが、まずは稼働を目指し自動割り当てで進めます。

開発環境の構築

コーディングにためのエディターを用意します。こちらは手慣れたもので大丈夫です。今回は Visual Studio Code を使いました。
こちら https://code.visualstudio.com から取得できます。

続いて Node.js をインストールします。こちら https://nodejs.org からダウンロードしてインストールします。今回は 9.5.0 Current をインストールしました。

プロジェクトの作成

Node.js がインストールできたら、プロジェクトのディレクトリを作成します。
今回は C:\Temp\hello-serverless をプロジェクトのディレクトリとしました。

続いて Serverless Framework を 導入します。今回は Visual Studio Code の ターミナルを使っていますが、コマンド プロンプト からも同様のコマンドになります。
今回はプロジェクトごとに Serverless Framework を 導入したいので、-g なしで npm install serverless としました。

1
2
3
4
C:\Temp\hello-serverless> npm install serverless
(省略)
+ serverless@1.26.0
added 302 packages in 44.148s

Serverless のモジュールが導入され、node_modulespackage-lock.json が作られています。

Serverless の AWS/TypeScript ボイラープレートからプロジェクトのひな形を作ります。
※ 今回はプロジェクト・ローカルにServerless Framework を導入しているため node_modules\.bin\serverless コマンドになります。node_modules\.bin にパスを通しておくか、めんどくさい場合は npm install -g serverless-g をつけてグローバルに入れてしまうのも手です。

1
2
3
4
C:\Temp\hello-serverless> node_modules\.bin\serverless create --template aws-nodejs-typescript
(省略)
Serverless: Successfully generated boilerplate for template: "aws-nodejs-typescript"
Serverless: NOTE: Please update the "service" property in serverless.yml with your service name

Node.js/npm プロジェクト関連のファイルが追加されます。

package.jsondevDependencies に Serverless Framework が入っていないので、もう一度 --save-dev を付けて npm install --save-dev serverless を実行します。
※ 最初から --save-dev しないのは package.json が作られているとボイラープレートが展開できないためです。

他のモジュールもインストールするため、npm install を実行します。

プロジェクトの設定

自動生成された設定が入っているので、プロジェクトに合わせた設定を行います。

package.json は、最小限 name description author あたりを変更しておきます。
また、作成しているのはアプリケーションなのでモジュール公開の防止のために "private": true を設定しておくとよいでしょう。private の詳細はこちら package.json | npm Documentation をご確認ください。

serverless.yml は、service: name: の値を変更しておきます。

tsconfig.jsonlibesnext を追加します。
2018年2月現在、これを追加しておかないとビルド時に参照しているモジュールで以下のようなエラーが発生するためです。

1
2
3
4
5
6
7
C:\Temp\hello-serverless> node_modules\.bin\serverless package

ERROR in C:\Temp\hello-serverless\node_modules\@types\graphql\subscription\subscribe.d.ts
(17,4): error TS2304: Cannot find name 'AsyncIterator'.

ERROR in C:\Temp\hello-serverless\node_modules\@types\graphql\subscription\subscribe.d.ts
(29,4): error TS2304: Cannot find name 'AsyncIterable'.

プロジェクトのビルド

serverless package コマンドでビルドします。

1
2
3
4
5
6
7
8
9
C:\Temp\hello-serverless> node_modules\.bin\serverless package
Serverless: Bundling with Webpack...
ts-loader: Using typescript@2.6.2 and C:\Temp\hello-serverless\tsconfig.json
Time: 2085ms
Asset Size Chunks Chunk Names
handler.js 2.94 kB 0 [emitted] handler
handler.js.map 3.18 kB 0 [emitted] handler
[0] ./handler.ts 350 bytes {0} [built]
Serverless: Packaging service...

TypeScript がコンパイルされ、JavaScript とソースマップが作られ、.serverless ディレクトリに AWS CloudFormation のテンプレートと Lambda のデプロイ Zip が作られます。

デプロイと稼働テスト

ビルドに成功したら serverless deploy コマンドで AWS 環境へデプロイします。
デプロイにあたっては AWS IAM ユーザー のアクセス・キーが必要となります。あらかじめ作成アクセス・キーを取得し、%USERPROFILE%\.aws\credentials (e.g. C:\Users\username\.aws\credentials) に記述しておきます。
IAM のアクセス権は PowerUserAccess では足りず IAM 周りの権限追加が必要です。まだ絞り込みきれておらず結局 AdministratorAccess を使ってしまっています。頑張らないと。。。

1
2
3
4
[hello]
region = us-west-2
aws_access_key_id = ZDEJREC4D1GM3DXXXX
aws_secret_access_key = ce4FH4Dcyh5DGh2dDGG4hDk6DYfhDAjrFhXXXX

デプロイを実行します。
serverless deploy --aws-profile hello--aws-profilecredentials[] で指定したプロファイル名を指定します。

1
2
3
4
5
6
C:\Temp\hello-serverless> node_modules\.bin\serverless deploy --aws-profile hello
(省略)
endpoints:
GET - https://ca3kg0hrXX.execute-api.us-east-1.amazonaws.com/dev/hello
functions:
hello: hello-serverless-dev-hello

無事にデプロイできると自動生成された API Gateway のエンドポイントが出力されます。

ブラウザでアクセスすると、handler.ts の実装の通り「Go Serverless webpack (TypeScript) v1.0! Your function executed successfully!」のメッセージと HTTP リクエストの内容が表示されます。

お掃除

serverless remove で AWS 環境をクリーンアップできます。

1
2
3
4
5
6
7
C:\Temp\hello-serverless> node_modules\.bin\serverless remove --aws-profile hello
Serverless: Getting all objects in S3 bucket...
Serverless: Removing objects in S3 bucket...
Serverless: Removing Stack...
Serverless: Checking Stack removal progress...
..........
Serverless: Stack removal finished...

ソースコード

今回作成した部分までのソースをGitHub へアップしました。
https://github.com/riotz-works/samples-hello-serverless/tree/0.0.1


初期設定に多少の作業がありましたが、Serverless Framework を使うことで簡単にデプロイとクリーンアップができました。

アプリケーションとしては Lambda でメッセージを返すレベルではありますが、インフラ面を考えると、これをしべて AWS マネジメントコンソールではかなりの手間がかかります。それを Serverless Framework が補ってくれるので、サーバレス開発のインフラ運用が削減できるメリットに加えて、インフラ構築も格段と楽になるのではないでしょうか。

また Try & Error を繰り返すうちに AWS 環境がついつい散かってしまい、よくわからないリソースを放置されるケースが出てきたりします。開発環境とはいえ Serverless Framework で環境を作るようにすると片付けも簡単ですね。

引き続き、このプロジェクトに設定やコードを加えてもう少し発展させていきます。

CSV ファイルのバッチ連携も AWS Lambda でサーバーレス

この記事は Serverless Advent Calendar 2017 の 7日目になります。

Serverlessconf Tokyo 2017 で発表させていただく機会をいただき、 Java チームが選択した TypeScript による AWS Lambda 開発 のタイトルでお話をさせていただきました。

今回はそのシステムの裏手側、CSV ファイルを連携するバッチをサーバーレスで実現したアーキテクチャについてご紹介します。

システム全体の概略図

ざっくりと下図のようなシステムで AWS 上に作られています。
スマートフォンなどから IoT 機器を操作するようなクラウド・サービスがあり、そのバックグラウンド・プラットフォームになります。認証や機器のデータ管理を行うシステムです。
このシステムは、図の Amazon API GatewayAWS Lambda が、ペアになっているアイコン毎が1つ1つのマイクロサービスとして作られています。つまり 10を超えるマイクロ・サービスの集合体になります!

今回の話は、この図の右上部分 “Traditional DC”、企業の伝統的なデータセンターからデータを受ける部分です。
この部分は伝統とシキタリにのっとり CSV ファイルを受け取り、処理する仕組みが必要となります。

※ CSV の C は Comma と信じてましたが、Character や Column 説 (e.g. Wikipedia) があり広義には Comma に限らず特定の文字でデータを区切るファイルらしい。どうでもいいけど。。。

なぜ AWS Lambda ?

このようなシステムを AWS で実装するには、AWS BatchAWS Glue などを使います。しかしながら、今回は Lambda で実装することにしました。

背景としては、以下のような点があります。

  • 少人数の DevOps チームであり、AWS のマネージド度合いが高いほど嬉しかった
  • データ量や処理量から想定してコスパが優れていた
  • 1回に連携するデータ量が Lambda の処理時間におさまる可能性が持てた

とくに2番目のコストについては、Glue に対して圧倒的によかったのが大きかったです。今回の処理は ETL に近いとはいえ、DWH 連携ではなく、DB のマスター・データ登録だったので Glue を活かしきれない点がありました。

なお Lambda の実行ランタイムは Java になります。
(Serverlessconf Tokyo 2017 の 発表資料 の通り、Java チームなので)

CSV ファイル連携のアーキテクチャ概要図


“Traditional DC” からは、Amazon S3 に CSV を置いてもらうことにします。FTP/SFTP とのリクエストがありましたが、サーバーレス厨とは言わずとも、ここは S3 へのアップロードへお願いしたいところ、何としてでも承諾いただきます。(いただきました)

S3 からは Amazon SNS へイベント通知をし、Lambda を起動します。その Lambda で CSV をパースして DynamoDB へデータを登録します。

複雑なデータの加工や外部の API/データを参照するような場合は、DynamoDB Streams から Lambda を呼び出して後続の処理として実装します。このようにすることで、データの登録に特化させて処理数を稼ぐことができ、また DynamoDB Streams の後続処理は、登録されたデータ1件ごとの処理になるので時間的な余裕が生まれます。

ここでのポイントは S3 ⇒ SNS からの Lambda は DynamoDB への登録だけに特化し、他の処理をしないことです。このようにすることで、なるべく CSV ファイルの対応に処理を振り分けるようにし、時間内で処理できる量を増やすようにしています。

処理能力とアーキテクチャの改良

このような形にすることで、CSV を Lambda で処理できるようになります。
このアーキテクチャの処理能力は、CSV ファイル 1行のデータ量や、カラム数によって変わってくる部分がありますが、今回のケースでは 1ファイルで約 800行のレコードが処理できました!
て、まったく処理できてません。。。

これでは役に立たないので対応が必要となります。
Lambda は設定するメモリの使用量によって処理速度が変わるという話もあり、最大に設定してみましたが設定による差はとくにありませんでした。ファイルを読み込みながらの順次処理なので、ここでの処理性能はあまり変わらないのでしょう。また DynamoDB へアクセスするオーバーヘッドも処理性能では大きく変わらないはずです。

では、どうするのか。


S3 に置かれたファイルを DynamoDB へ登録する前に、ファイル分割する処理をはさみます。
1回の Lambda で DynamoDB へ登録できるのは余裕を見て 500件までとして、CSV ファイルを 500行ごとのファイルに分割して S3 へ再アップロードします。その 500行ごとのファイルを Lambda で DynamoDB へ登録する形にしました。

これにより、30,000行まで処理できるようになり現実的なラインまで持っていけるようになりました。S3 からの Lambda に変わりはないので、サーバーレスの形態も維持できます。

まとめ

サーバーレスでシステムを作ろうとしても、往々にして発生してしまう既存システムとのバッチ連携。そのために FTP/SFTP やら、CSV パースするための処理やらとどうしてもサーバ・インスタンスが欲しくなってしまい、せっかくのサーバーレスがサーバー有り(サーバーレスの反対語って何だろう)になってしまいます。

Glue に見合う処理であればよいのですが、今回のように小規模なデータ登録となるとオーバースペック気味にもなります。そんな時には S3 からの Lambda で処理することで、サーバーレスでバッチ連携をするのも手ではないでしょうか。

ポイントとしては以下になります。

  • ファイルは S3 に置いてもらう
  • S3 ⇒ SNS から Lambda を起動し DynamoDB へデータ登録に特化する
  • 1回の Lambda で処理できるデータ量は少ない、必要に応じてファイル分割の Lambda をはさむ

Serverless Advent Calendar 2017 、楽しく、そして勉強になる記事がたくさんです。

昨日6日目は @RyoheiMorimotoさんの AWS LambdaをTest Runnerとして使ってみる でした。 ユーザ管理のシステムをサーバーレスで 34歳タローさんがハナコさんになれるかをテストしています。サーバーレス・テスト、おもしろいですね!

明日8日目は @narikeiさんです。何の記事か楽しみです!


では、ハッピー・サーバーレス・ライフ! ハッピー・ホリデー!!

Hello World !!

こんにちは、世界!
こんにちは、みなさん!!

わたしたちは Riotz.works です。

“a cheerful engineering team” を標榜する、わたしたちは
IT を中心に技術的なことやさまざまな挑戦が大好きで陽気で愉快なエンジニアの集団です。

これまで学んできたこと今挑戦していること、先々やってみたいことなど、をおもしろおかしく、まるでお祭り騒ぎのようにアウトプットしていきたい、そんな思いから『Articles | Riotz.works』の場を作りました。

またエンジニアにとっての最初の挨拶は、何といっても “Hello World !” ですね。
ということで最初のポストのタイトルも “Hello World !!” にしました。

この記念すべき Hello World はプログラム言語でいうと、どんな感じだろうかと「Hello worldプログラムの一覧 - Wikipedia」を眺めていたら、これかな!

1
import __hello__

Python のコードで、”プログラマーに必要な機能をすべて標準ライブラリーとして提供する「バッテリー同梱 (batteries included)」の思想を具現化するジョーク - Wikipedia」なのだとか。

さすがに「すべて」とまではいかないですが、エンジニアにとって必要なものがたくさんアウトプットされている場にしていきたいと考えています。

わたしたちのアウトプットがわずかでもお役に立てられれば、勉強させていただいているコミュニティへ少しでも還元できれば、そして新たな出会いと挑戦につながればと考えております。

どうぞ、よろしくお願いいたします!!

共有: