Azure Functions Cosmos DB Bindingsの新機能sqlQueryを試してみた

Cosmos DB Binding

Azure Functions Cosmos DB bindingsというドキュメントが公開されていることに最近気がついたのですが、その中でも個人的に以前から密かに待望していた機能であるsqlQueryについて、とうとう公式に明記されました。

大変便利と思われるこの機能を早速試してみましたので、本エントリで紹介していきます。

なお、本エントリの前提知識として、以下のエントリを読んでおくことをお勧めします。

Azure DocumentDB inputを設定する

今回私が作った関数のfunction.jsonですが、DocumentDB inputの設定は以下のようになっております。よく見ると、sqlQueryに見慣れたSQLが記述されていますね。

これを標準エディタで見てみますと、以下のようになります。

sqlQueryの仕様

テーブル名を必ず指定する必要がある(ただし何でもOK)

気をつけなければいけない点として、フィールド名を指定する場合はテーブル名を指定する必要があるということです。

function.jsonでコレクション名を指定してあるため、テーブル名そのものは何でもOKであり、特に何らかの影響を与えるわけではありません。このため、慣例的にテーブル名をcとすることが多いようです。そのため、フィールド名nameを指定するためにはc.nameとする必要があります。

クエリバインド変数の指定方法

httpバインディングで受け付ける関数の場合、sqlQueryに対して{name}などとすることでクエリバインド変数を利用することが可能です。

例えば SELECT c.id FROM c WHERE c.type={type}というsqlQueryを指定した場合、getパラメータにtype=imageのように指定することで、typeimageであるドキュメントを全て持ってくることができます。

今回使ったデータ

今回の検証のために、あらかじめ別の関数を使ってCosmos DBに以下のようなドキュメントデータをいくつか登録しておきました。

関数のコーディング

普段ですとPerlやBashでコードを書いたりするのですが、今回はPHP7での実装事例を紹介します。

中身はこれしかありませんが、一応おさらいも兼ねて解説していきます。

1行目ですが、このコードはphpで動作するコードですので、Functionと言えど <?phpで開始する必要があります。

2行目ではfunction.jsonで指定したsqlQueryの結果を$messageに格納する処理を行っています。

getenvの引数に指定する文字列は、function.jsonで指定したnameと同じものを指定します。これによりgetenvはCosmos DBから取得したJSONデータを内包した一時ファイルのパスを返します。あとはfile_get_contentsで中身を取り出し、json_decodestdClassに復元してやることで、phpで自在に操作可能となります。

3行目はレスポンス処理を行っています。getenvでレスポンス用の一時ファイルパスを取得し、そこにjson_encodeしたデータをfile_put_contentsで書き込んでやるだけです。

実際にアクセスしてみる

以下は、GETパラメータにname=tagoをつけてリクエストを投げてみた結果です。

任意の絞り込みを行った結果を取得できていることがわかると思います。

まとめ

今回の検証で、Azure Functions + Cosmos DBを使ったSQLライクな問い合わせができることがわかりました。

ちなみにCosmos DBに対するクエリとして、既に一部の集計関数がサポートされています。ただし、GROUP BY句は現時点ではサポートしていません(かなりアツい要望は多数あるようです。私も欲しい)。

Azure DocumentDB が Azure Cosmos DB に名称変更したようです

Azure Functionsと共に利用すると便利なフルマネージドNoSQLサービスである「Azure DocumentDB」が名称変更して「Azure Cosmon DB」になったようです。

以下、Azure Portal上の説明を引用します。

Azure Cosmos DB is a fully managed, globally-distributed, horizontally scalable in storage and throughput, multi-model database service backed up by comprehensive SLAs. Azure Cosmos DB is the next generation of Azure DocumentDB. Cosmos DB was built from the ground up with global distribution and horizontal scale at its core – it offers turn-key global distribution across any number of Azure regions by transparently scaling and replicating your data wherever your users are. You can elastically scale throughput and storage worldwide and pay only for the throughput and storage you need. Cosmos DB guarantees single-digit millisecond latencies at the 99th percentile anywhere in the world, offers multiple well-defined consistency models to fine-tune for performance and guaranteed high availability with multi-homing capabilities – all backed by industry leading service level agreements (SLAs).

Cosmos DB is truly schema-agnostic – it automatically indexes all the data without requiring you to deal with schema and index management. Cosmos DB is multi-model – it natively supports document, key-value, graph and columnar data models. With Cosmos DB, you can access your data using NoSQL APIs of your choice – DocumentDB SQL (document), MongoDB (document), Azure Table Storage (key-value), and Gremlin (graph), are all natively supported. Cosmos DB is a fully managed, enterprise ready and trustworthy service. All your data is fully and transparently encrypted and secure by default. Cosmos DB is ISO, FedRAMP, EU, HIPAA, and PCI compliant as well.

委細について、本日深夜(日本時間)に開催される Build 2017 での発表が気になるところです。

なお、ライブビューイングがストリーミング配信される様ですので、そちらも見ておくと良いと思われます。

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には複雑なコードをデプロイすべきではなく、徹底的にシンプルなコードをデプロイすることでその真価を発揮することができる、ということです。その先にはほぼ無限のスケーラビリティとメンテナンスフリーにほど近いシステムの実現があると私は思います。

Azure + Perl でサーバレスなジョブシステムを作る

Azure + Perl でサーバレスなジョブシステムを作る

※このエントリは Microsoft Azure Advent Calendar 2016 及び Perl Advent Calendar 2016 の10日目のエントリです。

どーも、わいとんです。

今日はタイトルの通り、Microsoft AzureとPerlでサーバレスなジョブシステムを作るまでの流れを紹介していきます。

Azure 及び Perl について

その前に、このエントリにはAzure及びPerl双方の大まかな知識があることと、手元の端末がMacOSXであることが前提になります。

念のため、それぞれざっくりした説明をしておきます(多分に私見が混じっておりますことをあらかじめご了承ください)。

Azure とは

Microsoft Azure(以下Azure)はMicrosoft社がサービス提供をしているクラウドサービスです。

仮想マシンやネットワークなどをはじめとしたIaaS、Web Appsを軸にしたPaaS、そしてMobile AppsやNotification HubsなどのSaaSが提供されており、近年Amazon Web ServicesやGoogle Cloud Platformと並んで、クラウド御三家 などと例えられたりします(主に僕が例えてます)。

Microsoftのサービスらしく、基盤技術としてはWindowsや.NET技術が中心となっていますが、同社は最近OSSへのコミットメントが大変著しく、AzureでもLinuxのサポートが行なわれているサービスが結構存在します。

なお当エントリは、既にMicrosoftアカウントをお持ちで、Azure Portalでのサービス操作が可能なことが前提となっています。また、各種費用については事前に料金計算ツールなどでご確認の上、ご自身の責任においてお試しください。

Perl とは

1987年にLarry Wall(ラリー・ウォール)によって公開されたスクリプト言語です。

今日LL(Lightweight Language)と呼ばれるプログラミング言語群の先駆けであり、過去にはCGIというWebアプリケーション提供手法と組み合わせたパラダイムで一世を風靡しましたが、ここ最近はPHPやRubyなどの登場により、シェアは漸減しているようです。

しかしながら、CPANを軸にしたエコシステムの存在によって拡張性が大変強力であり、かつ徹底した後方互換性の維持方針によって、古いソースコードでも安定稼働が期待できます。

近年はテストドリブンな開発手法や継続テストなどによるモダンかつ安定性を重視した開発手法が採用されることもあり、当エントリも「モダンな手法としてのサーバレスアーキテクチャ」をPerlで実現する、というところを重視したつもりです。

Azure上に環境を作る

前置きが長くなりましたが、ここからはスナップショット画像を交えて、Azure上への環境構築の様子を解説していきます。

リソースグループを作る

Azure Portal上で新しくリソースグループを作ります。

※リソースグループとは: Azureにある様々なサービスを、用途ごとにまとめておく枠組みです。詳しくはこちらのエントリを参照してください。

画面左側にある水色の立方体アイコンがあるので、これをクリックすると、リソースグループ一覧が表示されます。そこに「追加」というボタンがあるので押すと、リソースグループ名の入力とロケーションの選択を促されます。

これらの入力を終えて作成ボタンを押して少し待つと、以下のようにリソースグループの概要が示されます。

リソースグループができた様子

これでリソースグループの完成です。

Event Hubsをデプロイする

リソースグループの概要画面で「追加」ボタンを押すと、デプロイしたいサービスを選んだり検索できる画面になります。ここでは「event hubs」と検索窓に入力してあげると、以下のようにEvent Hubsが登場しますので、これを選択します。

event hubsを検索した様子

すると、以下のように概要説明が出てきますので、作成ボタンを押します。

event hubsを検索した様子

作成ボタンを押すと、以下のように料金体系の選択や名称の設定など、5項目について入力を求められます。

event hubsをデプロイする様子

なお、今回僕は以下のように設定しました(なんかでかいかも)。

event hubsの設定例

デプロイには数分かかることもあるので、コーヒーでも淹れながらのんびり待っていると良いかと思います。そのうち以下のような通知がベルマークのところに出てくるので、待ちましょう。

デプロイ完了の通知

デプロイが完了してからリソースグループの更新を行うと、以下のようにEvent Hubsがデプロイされていることが確認できます。

event hubsがデプロイされた様子

Event HubsにHubを追加する

続いて、実際にペイロードの受容部となるHubを追加します。

先ほど作成したEvent Hubsを選択すると、以下のような画面になりますので、「+Event Hub」ボタンを押します。

event hubsにHubを追加する様子

すると、以下のように設定項目が登場しますので、NameとPartition Count(今回は8にしました)を適宜設定し、Createボタンを押します。

Hubの設定

少し待つと、Event Hub作成完了通知が以下のように出てきます。

Hubの作成完了通知

これでHubの追加が完了しました。

追加したHubにSAS鍵を追加する

実際にHubに対してペイロードを受容してもらうためには、あらかじめ定められた鍵を以ってペイロードの送信を行う必要があります。この鍵を使ったやりとりのことをSAS(Shared Access Signature)と呼びます。

鍵を作るには、先ほど追加したHubの概要を開きます。下記の例では 「Event Hubs」をメニューから選択し、一番下の方にちらっと見えている「advent」という名のHubを選択する感じになります。

Event HubsのHub一覧

すると以下のような画面が出てきますので、メニューから「Shared Access Policy」(鍵マークのやつ)を選択します。

Hub概要

最初は見ての通りShared Access Policyが何も存在しません。ポリシーを新しく作る必要があるので、「Add」ボタンを押します。

SASポリシーの確認

新規作成するポリシーの名称及び権限設定(Claim)を要求されます。

SASポリシーの設定

名前は任意の文字列、権限は「Manage」を設定して「Create」ボタンを押すと、ポリシーが作成されます。

SASポリシーの確認、再度。

中身を見てみると、以下のように権限設定の確認、それと鍵と接続文字列がそれぞれPrimary, Secondaryと並んでいます。

とりあえず後で使うので、接続文字列を(Primary/Secondaryどちらでもいいので)コピーして、どこかにメモしておいてください。

Function Appをデプロイする

Event Hubsの準備ができたところで、次にFunction Appsの準備を進めます。

改めてリソースグループの概要に戻り、「追加」ボタンを押して「function app」と検索窓に入力します。すると、Function App が検索結果の一番上に出てくるので、これを選びます。

function app の検索結果

以下のように解説が出てきますが、とりあえず「作成」ボタンを押します。

function app の説明

設定を要求するブレードが登場しますので、以下の例を参考に各々埋めて「作成」ボタンを押してください。

function appの設定

しばらく待っているとデプロイが完了しますので、リソースグループの概要で「更新」ボタンを押すと、以下のように稲妻アイコンのApp Serviceが登場しています。これがFunction Appです。また、ストレージアカウントも一緒に作られます。

Function app 作った後のリソースグループ

Function Appに関数を追加する

早速、出来上がったFunction Appを見てみると、以下のようにデカデカと関数への早道と書かれた仰々しい画面が登場しますが、これはどうでもいいです。

Function Appの概要

とりあえずデカイやつはどうでもよくて、左側のメニューにある「新しい関数」を選択します。すると以下のように「テンプレートの選択」が登場します。

ここでいろんなテンプレートがあって目移りするかもしれませんが、僕としては男は黙ってBashという信条がございます故、言語にBashを選択します。

言語選択

すると・・・

おお、神よ!Bashを救いたまえ!

悲しいかな、選べるものが1つしかなく、しかも「Azure Queue Storage」をトリガーとした関数テンプレートしかないのです。

しかし!ここで諦めてはいけない! まず、そのままこのテンプレートを選択します。

すると、選択したテンプレートの下に以下のようなフォーム群が登場します。とりあえず関数名を簡単なものにして、そのまま「作成」ボタンを押してください。

設定が登場する

これでとりあえず関数がデプロイされました。ただ、このままではEvent Hubsとの連携ができていませんので、この後直していきます。

追加した関数の入力元にEvent Hubsを指定する

さて、出来上がった関数は以下のようになっています。

出来上がった関数(bash)

なるほど。シンプルですね。

次に、左側のメニューから「統合」を選択します。

関数の統合設定

先ほどのテンプレート設定みたいな画面が出てきましたが、右上にある「詳細エディター」を選択してください。

詳細エディター

詳細エディターというのは、トリガーや入出力をJSON形式で自力で設定するためのツールなのですが、ここで以下のように修正し、保存します。

編集後

変更した箇所は "type" の設定値が "queueTrigger" だったのを "eventHubTrigger" に変えただけです。

この状態で「標準エディタ」を選択すると・・・

標準エディタで見てみた図

トリガーの部分が「Azure Queue Storage」から「Azure イベント ハブ」に変わっていますね!

Event Hub接続をFunctions Appに設定する

今度は「イベント ハブ接続」の設定を行うため、上気した画面の右下にある「新規」リンクを選択します。すると下記のような画面になります。

標準エディタで見てみた図

最初は「結果はありません」と書かれていますね。その上にある「接続文字列の追加」を押してください。

Service Bus 接続の図

接続名は任意の文字列隣ます。、接続文字列ですが、ここで先ほどコピーしておいたEvent Hubの接続文字列を貼り付けます。

ここまでやったら「OK」ボタンを押します。

イベントハブ接続が確立した図

ご覧の通り、イベントハブ接続の設定ができました。

クライアント側の実装を作る

さて、ここまででようやくAzure側の設定が完了しました。あとはクライアント側のコード実装を行い、ペイロードを投げ込むだけです。

ところで皆さん、PerlでEventHubsにペイロードを信するための方法をご存知でしょうか?

おそらくほとんどの方はご存知ないと思いますが、Net::Azure::EventHubsというモジュールがCPANにリリースされていて、これを使うと簡単にEventHubsにペイロードを送信することができます。なお、Net::Azure::EventHubsを作ったのは です。

Net::Azure::EventHubsを導入する

それでは cpanm あるいは cpm を使ってNet::Azure::EventHubsをインストールしてみましょう。

  • cpanmの場合

    $ sudo cpanm Net::Azure::EventHubs

  • cpmの場合

    $ sudo cpm install -g Net::Azure::EventHubs

これでインストールに成功すると思います。

スクリプトを書く

では実際にペイロードを送信するプログラムを書いてみます。

use strict;
use warnings;
use Net::Azure::EventHubs;

my $hub = Net::Azure::EventHubs->new(
    connection_string => 'Endpoint=sb://...',
);

my $req = $hub->message({
    name => 'ytnobody',
    age  => '36',
    fav  => [qw/Gyoza Curry Lucky-Pierrot/],
});
$req->do;

connection_string はご自身の環境に合わせて変更してください。

実際にペイロードを送信してみる

これをbashで実行すると、下記のgifのようになります。

実際にペイロードを送ってみる

ウル技(ウルテク)

さらにもう一歩Perlを推し進めたウル技を披露します。

Function AppでPerlを使ってペイロードを処理する

Azure側の関数のコードを以下のようにすることで、関数記述言語として、なんとPerl(v5.22.0)が利用可能です!!!!

まず run.sh は以下のように。

直接受ける側のbashコード

そして新しく worker.pl を作り、以下のようなコードにします。

worker.pl

非常に簡単なコードですね。これは run.sh で受けたペイロードをそのまま worker.pl に横流しして、そのままデバッグログに出力するだけの仕組みです。

これを動かすと、こうなります・・・!

worker.plをFunction Apps上で動かす

Azure Function AppはPerlが動くぞ!すごい!!

まとめ

  • AzureとPerlは協調できる!
  • Net::Azure::EventHubs を作ったぞ!
  • Azure Function App はPerlも使えるぞ!

最後に

このエントリは YAPC::Hokkaido 2016 SAPPORO参加しながら執筆しました。次回のYAPC::Japanは関西での開催らしいです。

Net::Azure::CognitiveServices::Face – A wrapper class for Face API of Azure Cognitive Services – metacpan.org

Net::Azure::CognitiveServices::Face – A wrapper class for Face API of Azure Cognitive Services – metacpan.org

Net::Azure::NotificationHubs – A Client Class for Azure Notification Hubs – metacpan.org

Net::Azure::NotificationHubs – A Client Class for Azure Notification Hubs – metacpan.org

Net::Azure::EventHubs – A Client Class for Azure Event Hubs – metacpan.org

Net::Azure::EventHubs – A Client Class for Azure Event Hubs – metacpan.org