Azure Functionsのパフォーマンスに関する実験

昨日、ほんの出来心でMojoアプリをAzure Functionsで動かせたらどうだろうなどと考えてしまい、2時間弱くらいでMojo::Server::AzureFunctionsというものを作りました。

Mojo::Server::AzureFunctionsを作って使うまで

もともとAzure FunctionsのHTTP TriggerとHTTP output bindingをハンドリングするためのアダプタとして作ろうとあらかじめ決めており、Functions自体はリクエストごとにプロセスを起動する動作モデルであるため、これはCGIと同様であるとみなすことができました。

従って、既存のMojo::Server::CGIを参考に、環境変数の解析と入出力の調整だけでどうにかMojoアプリをAzure Functionsで動作させることに成功しました。

動作確認したソースコードはこちらのレポジトリに上げてあります。

苦しかった点

Mojoでは $c->render(json => {...}); とすることでJSONによるレスポンスを行うことができます。

ところが、Mojo::ServerMojo::Messageではrenderメソッドの第1引数に渡された値を一切検出できません。

そのため、レスポンスのContent-TypeやContentそのものを検証して、ContentがJSON Stringであるかどうかを検証する必要があり、そうしなければAzure FunctionsのHTTP output bindingでは単なる文字列として判別されてしまい、<string>{"message": "hello"}</string> という感じの残念なレスポンスを返してしまうことになるのです。

これを回避するコードがココでして、Contentが{で始まっていれば多分JSONだろうとみなし、evalで囲った中でdecode_jsonをかけるという非常に野蛮な方法で対応したことがお分かり頂けると思います。

実際に動作させてみた

さて、Azure Functionsにデプロイして実際に動作させてみた結果をお見せします。

Azure Functionsの大変便利な機能の一つとして、関数ごとにレスポンスにかかった時間のモニターをすることができる、その名もずばり「モニター」という機能があります。今回はそこで記録された動作速度を見ていただきたいと思います。

一番右側の丸括弧で囲われた値がレスポンスまでにかかった時間です。平均およそ23秒ほどでしょうか。これは遅いですね!

なぜ遅いか?

実は遅い理由は非常に単純でして、Mojoliciousをuseするコストがあまりにも高いことが挙げられます。これはMojoliciousが悪いわけでもありませんし、もちろんAzure Functionsが悪いわけでもありません。

どういうことかというと、そもそもAzure Functionsには非常にシンプルなものがデプロイされることが期待されています。Webアプリケーションフレームワークのような複雑なコードは毎回ロードするだけでもコストがかかりますので、当然のようにレスポンスまで時間がかかるのです。

証拠

例えば関数内のrun.shの内容が以下のように非常に単純なものだったとします。

perl -MJSON::PP -le 'print encode_json({message => "Hello, World!"})' > $res

その場合は、以下のようなレスポンス速度を実現することができるのです。

概ね0.5〜2秒程度でレスポンスを返すことができています。必要十分な速度と言えますね。

まとめ

Azure FunctionsでMojoアプリを動作可能にするMojo::Server::AzureFunctionsを作って、レスポンス速度を確かめてみました。

その結果わかったのは、Azure Functionsには複雑なコードをデプロイすべきではなく、徹底的にシンプルなコードをデプロイすることでその真価を発揮することができる、ということです。その先にはほぼ無限のスケーラビリティとメンテナンスフリーにほど近いシステムの実現があると私は思います。

Sisimai ♥ Azure Functions

先日のHokkaido.pmの帰りに時間が余ってしまったので、新千歳空港のロビーでダラダラしながら、SisimaiをAzure Functionsで動作させてみました。

Azure FunctionsでPerlが動く

Azure Functions上では、制約がありながらもPerlが稼働します。これは既にHokkaido.pmで発表した通りです。

Sisimaiとは

Sisimai

Sisimaiとは、バウンスメールの解析を行い、その情報を取得したりJSONに変換するためのライブラリです。
Perl版とRuby版が二条項BSDライセンスにて提供されており、GitHub上で開発されています。

本エントリではこれ以降、単にSisimaiと表記する場合は、特筆がない限りPerl版のp5-Sisimaiを指すものとします。

小さく堅牢なSisimaiに注目

ちょうどHokkaido.pmにSisimai作者のazumakuniyukiさんがいましたので、Sisimaiの依存モジュールについて聞いてみました。

わいとん:Sisimaiって依存モジュールどのくらいあるんです?

azumakuniyukiさん:確かJSONとClass::Accessor::Liteくらいだったんちゃうかなぁ・・・「小さく堅牢に」っちゅうのを心がけてるんで、そのくらいやったと思います。

実際metacpanのdependencyを見ても非常に依存が少なく、またXSを利用したモジュールに依存していません。Azure Functionsで稼働させる対象としては適任であると考えられます。

実際に作った

実は去年の夏にSisimaiに関連したエントリを公開しております。

当時はSisimaiが稼働するDockerコンテナを作りましたが、今年はSisimaiが稼働するサーバレスシステムを作りました。

Azure Portalにアクセスできる状態なら、Deploy to Azureボタンを押すだけでデプロイできるようにしてあります。

デプロイが終わったら、適宜バウンスメールが含まれたMailbox形式のファイルを以下のようにPOSTしてみてください。

ENDPOINT_URL="https://yourfuncname.azurewebsites.net/api/sisimai?code=..."
curl -X POST 
     -H "Content-Type: text/plain" 
     --data-binary @path/to/mailbox 
     $ENDPOINT_URL

以下が動作イメージです。

image

作る上でキモとなった点

Sisimaiをどうやってインストールするか

まずローカル環境でcpanfileを作って以下のような内容とします。

requires 'Sisimai';

あとはcpm installするとlocal以下にSisimaiとその依存モジュールが入ってきます。

これだけです。

それらをどうやってアップロードするのか

基本的にはGitHubにあがっていれば、Functionsにそのまま落としてくることが可能ですので、ひとまずGitHubにおきました。

Azure Functions の継続的なデプロイというエントリにそのやり方が書いてありますので、開発中はこの方法でデプロイ済みのAzure Functionsにファイルをアップロードしていきました。

ARM Template

これが一番難しかったのですが、以下のドキュメントを読みながら地道に作っていきました。

まとめ

小さく堅牢に作られたSisimaiをAzure Functionsにデプロイする方法を自動化し、数クリックでデプロイできるようにしました。またそのデプロイボタンを一般公開しました。

エラーメールの解析をサービスとして展開できるので、ぜひお試しいただけると幸いです。

Hokkaido.pm 13でサーバレスアーキテクチャの話をしてきた

昨日札幌で開催されたHokkaido.pm 13に参加し、Azure Functionsを使ったサーバレスアーキテクチャについて発表してきました。

大まかに以下の項目について話しました。

  • サーバレスアーキテクチャの概要
  • 歴史と現状
  • Azure Functionsを使う理由
  • Azure FunctionsとPerl
  • 実例とデモ

持参した回線の調子がよろしくなく、20分ほどの時間オーバーという大失態をしでかしてしまいましたが、温かく見守ってくださった皆様には、この場を借りて改めて御礼申し上げます。ありがとうございました。

落穂拾い

発表では説明しきれなかったことをざっくりとここでフォローアップしていきたいと思います。

Function Appへの関数のデプロイ

基本的にはAzure App Serviceのデプロイ方法に沿ってデプロイします。
Azure App Service へのアプリのデプロイという公式のエントリがありますので、詳しくはそちらを参照していただきたいと思います。
なお、ざっくりではありますが、以下のデプロイ方法が利用可能です。

  • FTP
  • FTPS
  • git(ローカルgitという方法で、App Service側にremote reposが作られる方法)
  • github
  • OneDrive
  • Dropbox
  • BitBucket
  • 外部gitレポジトリ(公開レポジトリにpushするとデプロイされる)

テスト手法について

基本的にFunctionsの環境を模倣するものはFunctions以外にありませんので、Azure上にテスト用のFunctionsを起こしておくのが良いと思います。
Functionsのダッシュボードにはテスト用のリクエスト発行をする仕組みが備わっていますので、そちらを使うのがスムーズでしょう。

CIについて

継続テストをもしやるのであれば、上述したテスト手法をガンガン定期的に実施する以外の方法はないと思います。
当然データストアなどへの副作用も発生しますので、その辺を考えて継続テストを行わないといけないです。
そんなわけで、今の時点ではあまり現実的な解は見つけられていませんが、どなたか知見のある方のご意見をいただけると幸甚です。

最後に

私個人としてはPerl5に対して、クラウド向けのグルー言語として、今後もまだまだ利用価値があると考えています。特にPure Perlで記述されたライブラリやロジックは、サーバレスな環境において、大変に便利な存在となり得ると思います。

今回そのことを、北海道という場所で語れたことを非常に嬉しく思っております。主催の永谷氏と参加メンバーの皆さん、そして会場についてのサポートをしてくださったJPAの皆様、本当にありがとうございました。