AzureのSupportが大変素晴らしいという話

山の日(8/11)、私がひっそりと進めている南関競馬予想「南関テイオー」が朝の予想バッチをサボタージュしてしまいました。南関テイオーの開発秘話についてはこちらに書いてありますので、興味のある方は是非読んでみてください。なお、南関テイオーは事業化できるように鋭意調整中です。

南関テイオーの大まかな構成

南関テイオーは、Azure(従量課金プラン)上に予想エンジンを構築してあり、予想結果と当選情報をBloggerに垂れ流すようにしてあります。最近まで一部、機械学習を用いた学習モデルを適用している箇所がありましたが、試行錯誤の末、一般的な数式に置き換えました(その方が回収率が高い)。

南関テイオーの大まかなシステム構成

forkに失敗する?

山の日に起こった現象は、「forkに失敗するせいで関数が起動できない」というものでした。以下、実際にAzure Portalで確認した内容です。

Azure Portalで確認

念のためAzureSupportに聞いてみる

あまりにもよくわからない現象でしたので、@AzureSupportに質問することにしました。

その後Direct Messageで詳細を聞かれましたが、Support Requestを送るように指示されました。

We recommend filing a support case via “Resource health” from the Azure Portal. After selecting the resource you are having issues with, navigate to “Resource health” by clicking the link under the “Support + Troubleshooting” section of the left blade. Select the option for “Contact Support” and follow the “New support request” workflow to submit your case. If you do not have a Support Plan enabled, be sure to select “Resource health” as the Support Plan.

オペレーション方法も教えてくれて、めっちゃ親切です。

で、この指示に従ってSupport Requestを送りました(念のため英語で・・・本当に拙い英語で・・・)。

すると10分後・・・

電話がかかってきた!

電話の相手は日本マイクロソフトのサポート担当の女性の方でした。英語ではなく日本語で対応してくれたことをありがたく思っていたところ、サポート曰く、

「事業影響度をAに設定したのはなぜですか?」

とのこと。事業影響度というのは、Support Requestのパラメータの一つであり、A,B,Cの3段階で1つ選択して設定することができるものなのですが、それぞれ以下のような定義となっています。

A. Critical impact - Significant loss or degradation of services (重大な影響 - サービスの大幅な損失または劣化)

B. Moderate impact - Moderate loss or degradation of services (中程度の影響 - 中程度の損失またはサービスの低下)

C. Minimal impact - Minimal loss or degradation of services (最小限の影響 - サービスの最小限の損失または劣化)

南関テイオーは当時も現在もサービスとして有償提供しているものではありません。しかし冒頭にも書いた通り、事業化を視野に入れていることも事実です。そこで私は先ほどの質問に対し、

「現時点では個人で稼働させているものであり、サービスインしているわけではないのですが、将来的にサービスインさせた時に、現在のこの状況はお客さんからクレームが来ることになりますので、Aとしました。」

と答えたところ、

「わかりました、それでは事業影響度をAとして対応を進めさせていただきます。後ほどエンジニアから連絡します。」

とのお返事をいただきました。すごい。ちゃんと話すことは重要ですね。

サポート部門のエンジニアさんからの連絡

数分後、サポートエンジニアのXさん(こちらは男性)からお電話がありました(祝日にもかかわらず対応ありがとうございました)。どこかで聞いたようなお名前の方でしたが、それはさておき、まずは調査してくださるということでした。

さらに数分後、Xさんから改めてお電話があり、関連しているかもしれない異変があることを説明してくれました。

1
2
3
4
5
6
7
Xさん「当該時間帯に、デプロイ先のインスタンスでメモリリソースの枯渇があったようです。現時点では解決しているので、関数を再実行できるようでしたらお試しいただけますでしょうか?」

私「ありがとうございます。試してみます。(インスタンスのメモリリソース枯渇って、PaaS利用者としては気にしたくないことだよなぁ・・・)」

Xさん「それから、現在Consumption Planで稼働しているようですが、関数のメモリ使用量が多いようですので、App Service Planでの稼働もご検討してみてください。」

私「わかりました、ありがとうございます。」

若干の違和感を感じながらも、ひとまず関数の再実行を試みたところ、正常に稼働しました。Azure Functionsでforkできない時には、メモリ不足を疑うと良いようですね。

まとめ

どうしてもわからない障害にぶち当たった時でも、24/365で対応してくれるAzureのサポート体制に大変感動しましたし、そして本当に助かりました。

Azure Web Appsのデプロイオプションと.sshの関係性

今日色々とAzure Web Appsで設定をしていて、デプロイオプション周りでハマりました。とはいえ、わかってしまえばどうと言うことのないことなので、簡単に説明していきます。

Azure Web Appsでデプロイオプションを変更すると、~/.sshが初期化される

見た通りのことなのですが、例えばn0bisuke氏が書いたこのエントリを参考にデプロイ設定をしたとします(なお、リンク先は本当に参考になるエントリです)。

で、一通り設定が終わった後に、Azure Portalから「デプロイオプション」> 「切断」を実行すると、 ~/.ssh の中身が空っぽになります。

この挙動に気がつかないと、改めて「デプロイオプション」を再設定しても「なぜかgit cloneに失敗する」という状態に陥ることになります。

まとめ

デプロイオプションを変更したら、 ~/.ssh の中身を確認しましょう。

Azure FunctionsでqueueTriggerのスロットリングをする

Azure Functionsを使った以下のようなサイト巡回の仕組みについて、是非とも気をつけたい箇所がありましたので、共有です。

このような特定のサイトを巡回する仕組みを作るとき、巡回先のサイトに対して秒間10リクエストくらいの比較的高密度なリクエストを送ることは、普通に考えると相手先のシステムにとって迷惑であろうと想像がつくことでしょう。

このような場合、リクエスト密度を軽減するために、並列リクエスト数の制限を行ったり、リクエストとリクエストの間にクールダウンを設けたりすることがあります。

クールダウンについては適宜sleepを入れるなどの対応をすることで、一応相手先に対するリクエスト密度の軽減が見込めることでしょう。しかし、並列リクエスト数の制限を行わないことには、ひっきりなしにリクエストを投げることになりかねません。

Queue Triggerにおけるジョブの並列処理数調整

今回例に挙げた構成の場合、巡回対象URLが多くなってくるとQueue Storageに一気にジョブが登録され、それらが片っ端から一気にQueue Triggerによって処理されてしまうことが問題となります。

上記の図にもある通り、Azure FunctionsにおけるQueue Triggerの並列処理数は、デフォルトでは16となっています。

この並列処理数を下げるためには、プロジェクト直下に以下のような内容でhost.jsonを作成し、デプロイする必要があります。

1
2
3
4
5
{
"queues": {
"batchSize": 3
}
}

このqueues.batchSizeこそが、Queue Triggerの並列処理数設定となっています。

このように、相手先のシステムに過剰な負担を与えないようにすることができます。

まとめ

web巡回をAzure Functionsでシステム化しようとしている方は、是非ともQueue Triggerの並列処理数にも気をつけてください。

なお、host.jsonに関わる情報はgithubのazure-webjobs-sdk-scriptプロジェクトしか情報源を見つけることができませんでしたが、host.jsonをしっかり確認することで、Azure Functionsのチューニングが可能とも言えますので、要チェックです。

Kichijoji.pm #11 でLTしました

Kichijoji.pm #11 に参加し、「あれっもしかしてPHPっていいんじゃないの?」というタイトルでLTしてきました。

This is an embedded Microsoft Office presentation, powered by Office Online.

内容としてはYAPC::Fukuoka 2017で話した内容の超絶短縮版なのですが、若干の内容追加も行なっております。

個人的に興味あった話

Songmuさんのbusyboxを参考にしたバイナリサイズダイエットの話ってのがなかなか面白くて、go generateからのperlってのがとにかく格好良さありましたね。一つの言語に縛られずに手間を無くしていく様子はさすがというところです。

macopyさんkuiperbeltの話なんかは、だいぶ前に構想を聞いた時と比較してどんどんdocs周りが洗練されてきていると感じました。リアルタイムなやり取りが必要なケースではちょっと試してみたいかも。

sakuraさんの検索の話、独特のリズム感と検索エンジンに出てくる広告の話への転換があり、トークの間「次は何がくる?」という期待の連続で非常に楽しませていただきました。

Azure Functions CosmosDB input bindingsを使う上でのコツ - 余剰演算子編

先日あたりからAzure FunctionsのCosmosDB input bindingsを使っていて、いくつかはまりどころのような挙動に出くわしましたので、その説明と対処法を紹介していきます。

sqlQueryで剰余演算子(Modulation Symbol)がそのまま使えない

例えば以下のようなQueryをCosmosDBに投げるとします。

1
SELECT c.id FROM c WHERE c.level % 20

このクエリを直接クエリエクスプローラで実行する場合、期待する結果が得られます。

ところがこのクエリをAzure FunctionsのCosmosDB input bindingsにsqlQueryとして指定した場合、以下のようなエラーがでてしまい、期待した動作をしません。

1
2017-07-05T16:45:43.113 Exception while executing function: Functions.MyFunction. Microsoft.Azure.WebJobs.Host: The '%' at position 34 does not have a closing '%'.

理由

実はsqlQueryの仕様として、%というのはアプリケーション設定の値を参照するために使う記号でして、%myappSetting%のような感じで使うものだったんですね。

そのため、単独で%が登場すること自体があってはならない、と、そういうことが理由となっています。

対処

対処法の一つとして、アプリケーション設定値に%を設定して、それを使えばよい、というものがあります。

アプリケーション設定から値を設定する

上記画像のように、例えばmodulationsymbolというアプリケーション設定値を定義しておき、sqlQuery側では以下のように%%modulationsymbol%に置き換えることで、期待通りの結果を得ることができます。

1
SELECT c.id FROM c WHERE c.level %modulationsymbol% 20

あまりカッコよくはないですが、致し方がない感じはしますね。

元ネタ:https://stackoverflow.com/questions/44903632/sqlquery-of-documentdb-input-bindings-with-modulation-symbol-makes-functions-fa

Azure Functions CosmosDB input bindingsを使う上でのコツ - バインド変数編

さて、今度はバインド変数に関する躓きを共有します。

クエリエクスプローラとFunctions経由との間でクエリの結果が異なる???

例えば以下のようなクエリをCosmosDBに投げるとします。

1
SELECT c.id FROM c WHERE CEILING(c.age / 10) * 10 = 30

クエリエクスプローラでは以下のような結果が得られました。

1
2
3
4
5
6
7
8
9
10
11
[
{
"id": "user-123"
},
{
"id": "user-124"
},
{
"id": "user-133"
}
]

今度はクエリの30に当たる箇所を{generation}として、sqlQueryに設定します。

1
SELECT c.id FROM c WHERE CEILING(c.age / 10) * 10 = {generation}

こんな感じですね。そしてFunctions側でQueue Storageトリガーを使って以下のようなジョブを受け取ります。

1
{"generation": 30}

この時、Functionsで受け取れるCosmosDB inout bindingsの結果は以下のようなものとなります。

1
[]

すっからかんですね。。。一体どういうことでしょう。

理由

初めて知った時には非常に驚きましたが、どうやら各bindingsにわたってくる値は文字列としてキャストされてしまうっぽいということです。

本当は以下のように展開してほしいのですが。。。

1
SELECT c.id FROM c WHERE CEILING(c.age / 10) * 10 = 30

こんな感じになってしまうということです。

1
SELECT c.id FROM c WHERE CEILING(c.age / 10) * 10 = "30"

これは厄介ですね。。。。

対処

これを回避するには、クエリの方で強制的に数値に置き換えてやればよいのですが、CosmosDBのビルトイン関数には数値へのキャストをしてくれるものがありません。

そこで登場するのが**ユーザ定義関数(User Defined Function/UDF)**です。

CosmosDBでは、javascriptを使ってユーザが関数を定義することができるのです。

CosmosDBのブレードにあるスクリプトエクスプローラというメニューから、UDFの定義ができます。

UDF開発画面

上記画像のような関数を定義してやり、sqlQuery側を以下のように変更すると、無事に期待した結果が得られます。

1
SELECT c.id FROM c WHERE CEILING(c.age / 10) * 10 = udf.ConvertToNumber({generation})

このほか、時刻にまつわる関数などもCosmosDBにはビルトインでは存在しませんが、このようなUDFを作って対応することが可能です。

CosmosDBのUDFは非常に強力な機能ですので、是非とも有効に活用していきたいものですね。

元ネタ:https://stackoverflow.com/questions/44916811/difference-among-the-azure-cosmosdb-query-explorers-results-and-the-azure-funct/44918767#44918767

YAPC::Fukuoka 2017 HAKATAでトークしました

YAPC::Fukuoka 2017 HAKATAにてトークしてきました。

PaaSをつかって怠惰・短気・傲慢にシステムを作るためのマインドセットについての話です。当日は徳丸さんの裏番組ということもあり、人が来てくれるのかと心配しておりましたが、思いの外結構人がいらっしゃったので、話した甲斐があったと思っております。

これを機に「Azureってすごいじゃん!」とか「Azure PaaSで作ってみようかな」と思ってもらえれば本望です。

スタッフの皆様、会場提供してくださったLINE様、その他YAPC::Fukuoka成功に向けて尽力された皆様に、改めて御礼申し上げます。ありがとうございました。

YAPC::Fukuoka 2017 Hakataにて登壇することが決まりました

7/1(土)に開催されるYAPC::Fukuoka 2017 Hakataへの登壇が決まりました。

発表タイトルはBe PaaS Monger - クラウドエンジニアの三大美徳、またはIaaSを使わない3つの理由となります。

上記のセッションでは、PaaSを使ったシステム設計・構築におけるマインドセットを、プログラマの三大美徳になぞらえて解説します。

クラウドの運用に人手がかかっていてお悩みの方、PaaSという物がよくわからないという方などに向けた内容となる予定です。

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句は現時点ではサポートしていません(かなりアツい要望は多数あるようです。私も欲しい)。

Otogiri-0.18 - A lightweight medicine for using database - metacpan.org

Otogiri-0.18 - A lightweight medicine for using database - metacpan.org

Today, I released Otogiri-0.18 on CPAN.

In new version, suppress to call ping() when last_insert_id() is called. It affects to fast_insert() because it calls last_insert_id() internally.

https://github.com/ytnobody/Otogiri/pull/16

Then renewed settings about travis-ci, and increased test coverage.

https://github.com/ytnobody/Otogiri/pull/17

Thank you, @tsucchi!