2016年後半以降のサーバレスの歴史

丹羽一智氏:次、2016年11月にAzure FunctionsがGAしました。

次に先ほど出てきたAWS SAMというものがリリースされました。AWS SAMとは、Cloud FormationというAWSのリソースの、YAMLやJSONで「こんな感じのEC2のインスタンスを作る」と定義しておくことで、AWSにそのYAMLなどをアップロードするとそのリソースを作ってくれるというものです。

インフラ管理のInfrastructure as CodeをAWSでやるためのものがCloud Formationで、それのプリプロセッサとして動いて、Lambdaがアプリケーションフレームワークとして使用できるのがAWS SAMです。それがリリースされたのが2016年11月ですね。

2016年12月にはStep Functionsというのがリリースされました。

これがLambdaでステートフルな処理を実行するための仕組みになっていて。さっきステートフルな仕組みは作りづらいという話をしましたが、ここですでにたぶんAmazonは気づいていて仕掛けていたんでしょうね。

ファンクションを数珠つなぎにして「この処理が失敗したらこっちに流れる」というのを事前に定義しておくと、それに従ってファンクションを生み出していってくれる。「このファンクションが終わったら、このファンクションを実行する」「このファンクションが失敗したらこっちを呼び出す。成功したらこっちを呼び出す」という感じで分岐させたり、「ここのファンクションとここのファンクションとここのファンクションが終わったら合流して次になにかをする」というのをStep Functionsで定義しておくと、それがワークフローとして実行されるというサービスです。

次に、2016年12月にGS2のサービスを開始しました。この段階ではおそらく世界初でフルサーバレスでサーバのシステムを作ったサービスなのではないかと思っています。

次に、2016年12月にBluemix OpenWhiskがGAしました。すごく慎重でしたよね。AWSなんて出した瞬間に「これはGAだ」というように決めてしまったんですが、すごくじっくりとβ期間などを設けてGAに持ってきました。

次に、Azure Functionsがデバッグ実行に対応しました。Visual Studio 2015やVisual Studio Code、あるいはCLIによるデバッグ実行に対応した。

これがサーバレス……「Function as a Serviceって開発するときデバッグとか大変なんじゃない?」というような話があるんですが、そのあたりについて、プラットフォーマー側もまったく気にしていなかったわけではなくて、ちゃんと手を打ってきているという話ですね。しかも、わりとAmazonが先行して動いているなか、Azureが先行してFunctionsにデバッギングができたりしているということです。

次は、LambdaがC#の.NET Core 1.0をサポート。ちょっと前に.NET CoreをLinux向けにリリースしますと言って、けっこうすぐにこのサポートが発表されたという感じです。

次に、2017年3月にGoogle Cloud Functionsがβリリースになります。αのときはエントリーして「あなたは当選しました」となったら使えるという感じだったのが、パブリックでいつでも誰でもβに参加できる状態になりました。

X-Rayとはなにか?

2017年4月にX-Rayと言われるサービスがAWSで提供されました。これはLambdaが動作する状況を視覚化するというものです。

左側が、「どのファンクションからどのファンクションが呼び出されました」「どのファンクションがHTTP通信をしてどこかに通信しました」というようなものがノード間を矢印でつなぐという感じで表現されていたり、そいつを実行するのにかかった時間がああいうロングテール的なグラフで見れたり。

あとは右側のものが、各ファンクションが処理でどれぐらい時間を使いましたかというものが見える状態になったと。でも、コード側に数行コードを書かないとこれは出てこない感じではありますが、こういうのができるようになりました。

これによってパフォーマンス・チューニングしたり、エラーが発生したときにノードが赤くなっていたりするので、「そのエラーが発生したときはどんな感じだっけ」というのがわかるようになり、調査がしやすくなったと。

Bluemix OpenWhiskがIBM Cloud Functionという名前に変わりました。

理由としてIBMは、Bluemix OpenWhiskは実装がオープンソースになっていて、それがApache OpenWhiskという名前で公開されていたんですが、それが利用者に混同されると。

「同じOpenWhiskの話をしてたときに、オープンソースのほうのOpenWhiskの話をしているのか、サービスとしてのOpenWhiskの話をしているのかがよくわからない感じになっていたのでリブランドしました」という話をしていて、IBM Cloud Functionsという名前に変わっています。

このとき同時にPHPのサポートが発表されています。PHPを作成しているFunction as a Serviceはけっこう少ないです。

突然出てきたんですが、ChaliceというのがGAしました。

これはAWSが提供しているアプリケーションフレームワークで、「さっきSAMの話がありましたよ?」と思うんですが、ちょっと思想が違っています。

SAMはCloud Formationのプリプロセッサとして動いて、アウトプットはCloud Formationのコード。あくまでInfrastructure as Code的な設定ファイルが出てくるんですが、これはがっつりPythonのアプリケーションフレームワークになっています。

だから、このパスを書いてこの関数を定義しておくと、API GatewayやLambdaがババっと設定されて、「/」というところにアクセスすると{’hello’ : ’world’}というJSONが返ってくるというようなアプリケーションフレームワークをAWSが出しています。

次、2017年10月にAzure FunctionsがJavaに対応するというPreviewを出しています。

真のサーバレスアーキテクトとはなにか?

2017年に11月にServerlessConf Tokyoの第2回目が開催されました。この時もGS2は引き続き登壇したので、その話をしたいと思います。

この時は「真のサーバレスアーキテクトとサーバレス時代のゲーム開発・運用」という仰々しい名前で発表をしました。

この時の登壇資料からよさそうなものだけエッセンスを抽出したいと思います。

いきなり、「真のサーバレスアーキテクトとはなにか?」という話をしたんですね。サーバレス時代の設計者に求められる技能というのは、適切な箇所で適切なフルマネージドサービスを適用できるスキルだと考えています。

それをできるようになるにはなにが必要かというと、各フルマネージドサービスの性能・特性、そのマネージドサービスがいくらかかるのか、利用料金はどういう感じか、というのをちゃんと把握した上で、今こういうユースケースであればこのフルマネージドサービスを使うのが適切だという判断をすることが求められると思っています。

このCloud Dataflowでサーバレスでストリーム分析をしようというようなことを言っていますが、データが1秒に1個も流れてこないような状態でやると、Cloud Dataflowはインスタンスが裏であって、料金も仮想サーバに対して課金が発生するんですね。

1秒に1個もデータが流れてこないところでクラスタを組むには(仮想サーバが)最低3台必要なんですが、仮想サーバ3台が常に立っているなんてすごくバカらしいんですよね。

「DynamoDBを使えば我が社もサクセス!」と書いていて。1パーセントへのデータが9割あるという特性があるというのは、DynamoDBは特定のデータに集中してアクセスが発生するのがすごく苦手なんですね。

なんでかというと、考えればわかります。DynamoDBの主キーはハッシュキーと呼ばれていて、そのハッシュ値をもとにクラスタのどこにデータがあるのかを特定してデータを取りにいく仕組みになっていて、特定のハッシュキーに対してアクセス数が集中するということは、特定のクラスタにアクセスが集中するということなんですね。

なので、向いていない。そういうことを理解していないままで、やりたかっただけのサーバレスアーキテクトになると痛い目を見ますよ、という話をしています。

サーバレスアーキテクトの最前線に立つために

次に、今私が話したように、サーバレスアーキテクトとして最前線でやっていこうと思ったときにどういう知識が必要なんだという例を、ここからいくつか並べています。

これがコールドスタート対策と言われるもので。先ほどAWS Lambdaで、JavaだったらJVMが起動するのに時間がかかったり、ENIを割り当ててVPCの中で動かそうと思うとそのNICの割り当てに時間がかかってしまうというようなことを言っていたと思いますが、そういう時間がかかるという現象に対してコールドスタートという名前がついています。

なんでコールドスタートと言っているかというと、もう1個ホットスタートというのがあります。AWS Lambdaは毎回新しくコンテナを起動してそこの上でファンクションを動かすわけではなくて、ある程度使い回してくれるんですね。そのほうがAWSとしてもたぶん合理的だからそうやっているんでしょう。

なので、1回目のアクセスをした直後、2回目のアクセスは、前回のコンテナが捨てられていなければ使い回されると。そのときは、先ほどのJVMの起動やNICの割り当てのようなことをしないので、レイテンシが軽減されるという特性があって、その状態をホットスタートといいます。

このコールドスタートでいかにレイテンシを小さくするかという工夫に関する話です。GS2について、最初はJavaを使って作ろうかなと思ったけれどPythonにしたというところで、Pythonにすることでコールドスタート時間を短縮していますというのが1つ目。

2つ目が、ある種邪道なんですが、1つのLambdaファンクションに全ロジックが入っています。

じゃあどうやって処理を振り分けるのかというと、API Gatewayの各エンドポイントに、どのエンドポイントでファンクションを受け付けたかという情報をLambdaに渡すように、引数を追加するリクエストのトランスフォーム処理が入っているんですね。

どのエンドポイントからLambdaが呼び出されたかというのがわかる状態にしておいて。それによってLambdaは分岐をするわけですね。「どのエンドポイントに対するアクセスなんだから、この処理を実行しろ」という分岐をしていると。

なんでそんなことをするとうれしいかというと、エンドポイントが例えば20、30ぐらいあってサービスが動いているとしたときに、そのエンドポイントの中で人気があるエンドポイントと人気がないエンドポイントというのが絶対出てくるはずです。

そうすると人気があるエンドポイントはすごくホットスタートの比率が高いけれど、人気がないエンドポイントはだいたいコールドスタートみたいな状態になってしまうんですね。

それを避けるために、Lambdaファンクションの中に1つ全部ロジックを入れて、すべてのエンドポイントが1つのLambdaファンクションを生み出すようにすることによって、人気がないエンドポイントであってもコールドスタートが起きにくくするというようなことをやっていたりします。

ログ収集で知っておくべきこと

次にログ収集についてです。Function as a Serviceは工夫しないときれいにログが残らないんですね。

CloudWatch Logsというサービスがあって、そこにLambdaの実行ログが出てきます。そのCloudWatch LogsにはLambdaが実行されたコンテナごとにログが分かれていて、「同じ特定の時間のログが見たい」「10時10分のログが見たい」と思っても、その瞬間コンテナが5つ動いていたら5個ログがあって、しかも目的のログがどのコンテナで動いていたかなんてわからないので、つらい、ということが起こるんですね。

なので、ここでは「Lambdaファンクションの先頭で」と言っていてますが、GS2では「ファンクションの最後で」受け取ったイベントの内容をKinesis Firehoseに書き出しています。

なんで最後なのかというと、レスポンスの内容も書き出しているからですね。Pythonのデコレータと言われる前後に処理を挟めるような仕組みがあって、それを使って処理をしていくと。

次に、Amazon S3のPUTイベントで、そのFirehoseから書き出されたログをフックして、そのログを仕分けてBigQueryに書き出すということをしています。その中でエラーログがあればSlackに通知するというようなことをやっています。最終的にファイルとしてのログがS3に残って、検索用のログとしてBigQueryにデータが残るという状態にしているというわけですね。

今だったらAthenaとかでもいいかなという気がするんですが、この当時はAthenaがなかったので、とくに困っていないし、BigQueryの調子がいいので今もBigQueryに(データが残る)という状態です。ただ、このログの取り回しは現在では若干変わっていて、今年のServerlessConfではそのあたりの話をしようと思っています。

次にプッシュ通知の実現。これはゲームに特化した内容ではあるんですが、TCPコネクションをクライアントとサーバで常に張って、サーバサイドでなにかがあったらクライアント側にデータを投げつけてプッシュ通知するというのがプッシュ通知なわけですよね。

これをバカ正直に設計すると仮想サーバが無限に並ぶと。同時接続10万ユーザーを支えられる通知サービスを作ろうというようなことをいうと、その人たちがぶら下がれるだけの仮想サーバが必要という話になってくるわけですね。

でも、GS2はサーバレスでやりたいと思っているのでそんなことはできなくて。じゃあどうしようと考えたときに、さっきのAWS IoTというのが出てくるわけですね。MQTTプロトコルがしゃべれてWebSocketで通信したりMQTTプロトコルで通信したりというのができるので。しかもAmazonは10億のデバイスをつないでも大丈夫だといっているので、さすがに10億台実際に繋いでみるワケにもいかないですし、大丈夫なんだろうと信じ込んで使うわけです。

なので、それによって、例えば、マッチメイキングで対戦相手が見つかったらサーバからクライアントに通知をしたり、チャットでギルド内のメンバーが新しく発言をしたらプッシュ通知をして「今新しいメッセージが投稿されたから取りに来いよ」というようなことを、AWS IoTを使って実現していると。

さらに、GS2では、通知対象がオフラインのときにはモバイルプッシュ通知に転送する仕組みも実装しています。なので、ギルドチャットで発言したんだけれど、「この人、オフラインになっている」と気づいたらモバイルプッシュにする、というようなことをできるようにしています。

トランザクションとアクセス権限管理

次にトランザクションですね。データベースで処理を書いていると、どうしてもトランザクションが必要なときがあるんですが、DynamoDBにはトランザクションがないんですね。

Amazonは「トランザクションが欲しいんだったら、全部追記にして、その履歴を使って処理をすればいいのでは?」「うそ、マジ?」というようなことを言うんですが(笑)、私はそんなことはしたくないので、素直にDynamoDBからさよならしてCloud Datastoreにいったという話です。

魔法石を扱うサービスを提供しているわけで、魔法石は当然「レシートと残高」というような話になってくるわけなんですよね。そうすると、トランザクションがなかったら残高とレシートがずれるかもしれない。そんなのありえないわけなんですよね。なので、この時はGoogleが提供しているCloud Datastoreを使うようにしたと。

このCloud Datastoreはパフォーマンスを引き出すテクニックというのがDynamoDBとはかなり違ってまったく別なアーキテクチャなので、Cloud Datastoreを使うんだったらCloud Datastoreのノウハウは正しく学んで使うようにしないといけないという話ですね。

次はアクセス権限管理についてです。GS2ではAWSのIAMを模したポリシーを提供しています。AWSを使ったことがある方だとIAMでJSONを書いたこともあると思いますが、あのJSONとほぼ同じ。

だから、アクションの部分にGS2のサービスが並んでいるような感じのポリシーを書いておくということになっているのですが、そのポリシーの検証をする処理をすべてのファンクションの先頭でやるわけではなくて、その処理の先頭でポリシー検証用のLambdaファンクションというのを呼び出して、そのLambdaファンクションがOKかNGかというのを返すので、それを見て処理を続行するかを決めるということをしています。

なんでそんなことをしているのかというと、このポリシードキュメントのキャッシュヒット率を高めるためにやっています。というのは、すべてのファンクションの先頭でポリシーの判定をしてしまうと、当然、そのポリシードキュメントを取ってこないと判定できないわけなんですね。ポリシードキュメントをいろんなファンクションが取りにくるということは……。

その話をする前に、ポリシードキュメントを1回取ってきたら、それをローカルのメモリに残すんですね。2回目以降はローカルのメモリ上にポリシーがあればそれを使う。あるいはある程度キャッシュ期間をもってそれを破棄するというようなことをやっていると。

でも、ファンクションがいっぱいあるとそのキャッシュヒット率が落ちてしまうので、Lambdaファンクションとして独立させて、ポリシー検証をするときにはそのファンクションを呼び出すということによって、いろんなファンクションがポリシー検証をするんだけれど、キャッシュはそのポリシー検証用のLambdaファンクションが全部握っているので、その検証にかかる時間を最適化できると。

マネジメントコンソールをどうするか?

次に、GS2のマネジメントコンソールについてです。GS2もAWSと同じで管理画面があって、「どの人が魔法石をいくら持っている」「この人に魔法石を付与する」ということも管理画面でできるんですが、それも本当はサーバレスで実現したかったんですね。

でも、マネジメントコンソールは管理用途なので、どうしてもプライベートAPIみたいなものを作らざるをえなかったと。

例えばGS2のアカウント作成のように、あまり表に出したくなかったり、とりあえず管理用途でのみ使いたいAPIみたいなものが当然あるので「プライベートAPIを用意して」というようなことがあって。そうすると、HTMLとJavaScriptでAjaxを使ってAPI Gatewayを叩いて、それを使ってなにか画面を差し替えていい感じにすればサーバレスにもなるんです。

でも、デベロッパー開発者ツールなどを使うとその通信が丸見えになって。どんなエンドポイントがあるのかというように、AWSと同じでGS2にもルートアカウントに相当するものがあって……IAMのクレデンシャルがなくても、そのアカウント情報からクレデンシャルを導き出してAPIを叩くというような仕組みなんですが。

そういうものが露出してしまって、それが漏れてしまうと他人になりすませる可能性があるというリスクなどを考えると、ちょっと難しいということがあって。この管理画面だけローンチしてからしばらくEC2で運用していました。

でも、App Engine には Standard 以外に Flexibleというのが途中から出てきて。Flexibleはわりと変化があったのに対し、Standardはずっとメンテナンスされていなくて、もう終わったんじゃないかと思っていたんですが、去年突然動き始めて。Java8に対応したと。

StandardとFlexibleでなにが違うのかというと、Flexibleは要は「仮想サーバを使ってApp Engineを動かせるようにした」という思想のもので、PaaS的な思想はそのままなんですが、料金の課金はIaaS的なんですね。

App Engine Standardは、「インスタンスが何台ある」というのが一応見れるものの、CPU時間による課金で、基本的にはGoogleにおまかせして運用できるというものです。

これがJava8に対応したことで、「これはいいものが出てきた」と思って、マネジメントコンソールまでJavaで書いていたので、それを乗せ替えて、Google App Engine StandardのJava8対応がGAした当日にマネジメントコンソールもリリースしました。ということで、マネジメントコンソールもサーバレス。インフラ面はなにも考えなくてよい状態になりました。

プラットフォーマーと仲良くするべし

こういうサーバレスでやっているとプラットフォーマーと仲良くするのはすごく重要になります。なんでかというと、ベンダーロックインはもう避けられないからです。仮想サーバであればほかの事業者に移したりできたわけなんですよね。

今はコンテナも、GoogleはKubanetesはロックインされない。開発者フレンドリーだというベンダートークをしているわけですね。

一方で、このFunction as a Serviceのような、なにも考えなくてもスケールするし、キャパシティ管理もいらないというような世界を目指して、DynamoDBの特性に合わせてアプリケーションを書いたりすると、DynamoDB以外では動かないようなコードができあがるし、Cloud Datastoreに特化したコードになっていればCloud Datastore以外で動かないものになるわけなんですが、そういうフルマネージドサービスにロックインされるのは避けられないと思うんですね。

なので、そういうふうになったときに怖いのは、やっぱり本当は障害が起こらないはずですが、仮に起こったとき。例えば、半年ぐらい前に、ネットワーク設定をミスしてGoogle Cloud Platformがかなり大規模な障害をおこしたというのがあって。プラットフォーマーとは、そういうときに状況を確認したりできる関係になっておきましょうということですね。

そういう状況ではだいたい「status.〜」というようなページがあるんですが、反映されないので。ああいうのがちゃんと即座に反映されているサービスはあんまり見たことがなくて。人にちゃんと聞くのが一番間違いないので、そういう関係を作っておくといい、ということですね。

次に可視化ですね。インフラ運用がいらないといっても、サービスの状況の可視化はサーバレスの時代にも必要です。だいたいのマネージドサービスはアクセス数といった指標がAPIで取れるようになっているので、そういうのを簡単に見れるようにしておきましょうというところですね。

なので、Mackerelとかだと「CPU使用率がいくつで、メモリの今の使用率がこんなもんで」……というようなものではなくて、もっとレイヤーとしては上の、サービスのrequest/secがどうなっているといったレベルの可視化をしていくことになります。

サーバレスの今

ServerlessConfの話は終わって、Cloud9というのがAWSから提供しているブラウザ上で使えるIDEで、2017年12月にこのCloud9がLambdaの開発に使えるようになりました。

ここ、わかりますか? このへんが存在するファンクションなどが出てきて、IDEっぽいものからそれを実行できるようになったりしています。

次に2018年2月、LambdaがGoとC#の.NET Core 2.0をサポートしました。

2018年6月にGoogle Cloud Functionsが東京リージョンで使えるようになりました。ごく最近ですね。

2018年6月にAWSのイベントソースにSQSが使えるようになりました。

SQSというのはキューのサービスで、リトライしないといけない処理が後続にあるときは、このSQSにいったんジョブとして積んで、そのジョブが次のLambdaに伝搬されます。そのSQSは、キューから取り出したあと、その処理が終わったよというのを通知してあげなかったら、まだ同じデータが出てくるという特性、仕様になっていて。

なので、Lambdaで後続の処理をして、例えばそのLambdaが突然動かなくなったり、そこで使っているデータベースにアクセス失敗したりといったように、なにかがあったときに、SQSに処理が終わったという通知をしなかったら、またしばらくしてSQSから同じデータが出てきて、別のクラスタでLambdaが実行されて、そのLambdaがリトライできるという感じの使い方ができるようになりました。

次に、2018年7月にGoogleのCloud FunctionsがGAしました。これがGAしたのは先月なので、ずっとSLAがない状態でした。このときにPython3.6が対応言語に追加されました。

さらに2018年7月、Serverless containersというサービスが発表されました。Cloud Functions が Dockerイメージによるアプリケーションデプロイに対応しましたと。

なので、DockerイメージをGoogleに預けておくと、エンドポイントにアクセスがあったらそのDockerイメージをロードして、コンテナを起動してアクセスを流してくれるというような仕組みになります。なので、Dockerイメージを預ければプラットフォームの対応言語とか関係の無い世界を作ろう、というのがServerless containersです。

さらに今後見えている予定として、9月にServerlessConf Tokyo 2018があります。

私も登壇をして、今年は、先ほどもちょっとお話ししたように、運用周りをどうしていくかという話を中心にやろうと思っています。

11月にラスベガスでre:Inventの発表で、AWS主催のカンファレンスがあって。ここで毎年サーバレス関係のアップデートが複数あるので、今年もなにかあるのではないかと思っています。

なので、これでひと通り話しました。歴史を学んだことで、どういうことがサーバレスでできるのかというのはなんとなくわかる状態になったのではないかと思っています。あとは手を動かしてやってみる段階になったと思うので、ぜひ実践していただければと思います。ご清聴ありがとうございました。

(会場拍手)