200ミリ秒の検索APIをMicrosoft Azureでデザインする

200ミリ秒の検索APIとは

ここでは、httpクライアントからリクエストを受けてから全文検索を含む何らかの検索処理を行い、httpクライアントにレスポンスを返すまでの処理を、おおむね200ミリ秒(200ms、0.2秒)前後の時間でこなすWeb APIを指します。

検索という機能を考えたとき、200msという数字はまずまず軽快な応答速度ではないかと私は考えます。

今回はAzureの各種サービスを使って検索APIを作った時のノウハウを共有します。このAPIはデータサイズによって多少のばらつきはあるものの、ほぼ200ms前後の応答速度を実現することができております。

以下は、実際の処理履歴となります。おおむね200ms前後で処理が完了し、応答していることが分かると思います。

ログ

Microsoft Azureで作る

Microsoft Azure(以下Azure)で検索APIを作るにあたり、以下のようなシステムデザインを行いました。

システムデザイン

ある程度Azureに詳しい方であれば、上記の図を見ただけで概ねの構成がご理解いただけると思います。

Azure CDN

いわゆるCDN(Contents Delivery Network)サービスです。今年から動的コンテンツの高速化にも利用できるようになりました。

今回のケースでも「動的コンテンツの高速化」がその利用目的となります。

Azure Functions

実際にhttpリクエストを受け付け、httpレスポンスを返すためのアプリケーション・ロジックをデプロイし、運用するためのFaaSとなります。最近v2がリリースされましたが、私のケースではv1を利用しました。

C#やF#、PHPなどの言語に対応していますが、今回はNode.jsをつかって作ってみました。実際の実装・はまりどころについては前回のエントリにまとめてありますので、そちらも併せてご参照ください。

Azure Search

Apache LuceneクエリやODataフィルタを利用可能な検索エンジンサービスです。

全文検索や緯度経度をもとにした距離検索、ファセットなどにも対応しているため、複雑な検索機能を実装するにはほぼ必須となります。

また、Azure CosmosDBやAzure Table Storageなどから定期的(最短で5分おき)にデータを取り込むIndexerという仕組みがついてきますので、検索結果にリアルタイム性を求めない限り、データのインポートをする仕組みを自分で作る必要がありません。

Azure CosmosDB (または Azure Table Storage)

どちらもスキーマレスなデータストアとして利用できるサービスです。

今回はAzure CosmosDBを利用し、Azure Search向けの一次データをストックしておくデータストアとします。

まとめ

200ms前後の応答速度の検索APIをAzureで実現するシステムデザインを紹介しました。

「えっ、実際の作り方は書かないの?」という声が聞こえてきそうですが、その辺は公式ドキュメントや有志のブログに書いてあることしかやっていません。

強いてあげるとすれば、前回のエントリで書いたようなはまりどころがあるくらいですので、そちらを見ていただきたいと思います。

参考にしたドキュメント・ブログ

webアプリを提供する上でやめた7つのもの

よもやま話など

ytnobodyです。実に3か月ぶりの更新です。ご無沙汰しております。

最近はPerlやサーバサイド、クラウドなどについて、個人的にこれといって真新しいとかすごい、と感じたものがあまりなく、そのため更新が滞っておりました。

また、私の主業務におけるiOSアプリ開発の割合が非常に高くなっており、そちら側のキャッチアップにエネルギーを注ぎまくっている状況でして、これがまた楽しくて楽しくて。

・・・というような事情で、なかなかこちらにはアウトプットできていませんでした。

今日はそんな状況を顧みて、「じゃあ自分がいまどういう哲学に基づいて設計・開発などを行っているかをひけらかしてみたらどうだろうか?」と思い立ち、キーボードをたたいている次第です。

もはや当たり前、と思われる方もおられるかもしれません。まぁしかし、個人のブログですし、お目こぼしくださると幸甚です。

本題

さて、webアプリを公開するにあたって、いくつか「最低限これは必要だよね」というものがあると思います。私の場合、7年ほど前であれば以下のものを挙げたことでしょう。

  • ドメイン名
  • ネームサーバ(とりあえず外部のやつでもよい)
  • webサーバ(もしくはVM)
  • ネットワークおよび構成機器(ルータだとかスイッチだとか)
  • OS(UbuntuとかCentOSとか)
  • httpd(ApacheとかNginxとか)
  • プログラミング言語の動作環境(perlだとかphpだとかrubyだとか)
  • デプロイ手段(capistranoとかansibleとかftpとかscpとか)

ここでは「最低限」なので、DBは割愛しました。しかし、こうして見てみると結構多いですね。

では現在はどうかというと、以下のものを挙げます。

  • ドメイン名
  • DNS as a Service(Amazon Route53だとかGoogle Cloud DNSだとかAzure DNSだとか)
  • Platform as a Service(Amazon Elastic BeanstalkだとかGoogle Apps EngineだとかAzure Web Appsだとか)
  • git

だいぶ少なくなりました。減ったものについて着眼していきます。

ネームサーバ

DNS as a Serviceに取って代わられました。

DNS as a Serviceを利用するにも相変わらずネームサーバの知識は必要なのですが、物理的にサーバを用意したりする必要がなく、クラウドプラットフォームへのログインが可能な状況であれば、いつでもリソースレコードの修正や追加が可能となりました。

webサーバ

Platform as a Serviceに取って代わられました。

これにより、VMや物理サーバの運用をする必要がなくなりました。概念的にはサーバがなくなったことになります。

ネットワークおよび構成機器

概念的にサーバがなくなってしまったことによって、ネットワークとその構成機器もまた概念的に不要となりました。

従来これらが賄っていたことはPlatform as a Serviceの一機能として提供されているか、あるいはLoad Balancer as aa Serviceによって代替できるため、必要があればLoad Balancer as a Serviceを追加する程度です。

OS

概念的にサーバがなくなっていますので、OSも概念的に不要となりました。

これはPlatform as a ServiceによってOSが自動的に提供され、かつ更新が行われるようになったためです。

httpd

これもPlatform as a Serviceによって不要となりました。

ApacheやNginxなどの複雑なconfigを書く必要がなくなり、PaaS側に組み込まれた機能によって代替できるためです。

プログラミング言語の動作環境

Platform as a Serviceに組み込まれているもので十分なので、自分で用意することはやめました。

最近はPHP7に寄せておりますが、JavaやC#、pythonなどもよさそうだと感じます。

デプロイ手段

デプロイ手段を自前で用意するのもやめました。

Platform as a Serviceがgitに対応しており、tagを切ることでデプロイが行われるように設定しました。

やめたことで得られたもの

OSやその下位レイヤであるVM/サーバの管理から解放された

PaaSを使う主目的の一つとしてあげられるメリットですが、その大きさは想定以上でした。

思えば、私が過去にやってきた業務の半分近くが、VM/サーバの管理やOSの管理だったという時期があったくらいですから、従来の半分強のエネルギーで業務が回るようになったと言えます。

ネットワークの管理から解放された

OSやVM/サーバもそうなんですけど、一からネットワークを構築して運用することは、それだけで一つの仕事として成り立つくらいに高度かつエネルギーや時間を使う行為です。

また、ネットワークで問題が発生した時には、そのトラブルシュートに必要な知識は専門性が高く、エンジニアのジョブセキュリティが高まってしまいます。そのため、ひとたび問題が発生すると、解決までは帰宅できないという事態が発生します。

OS、VM、ネットワークの管理から解放されるということはつまり、「エンジニアでありながらも、いつも定時で帰宅しプライベートを満喫できる」という状況が、確固たるものになるということではないでしょうか。

httpdの設定から解放された

httpdの設定は一箇所間違うと、サイト全体に致命的な影響を及ぼすことがあります。ネットワークの問題にも性質が似ているのですが、基本的には「解決しないと帰れない」を生み出す可能性を内包しております。

また、OS同様にセキュリティ上の問題が発見された場合、その対応をするのはhttpdを管理している人間が担当することになります。

これらの管理コストをPaaSに代替させることで、利用料金という形であらかじめ解決しておくことができるのは、不確定要素を予定調和化する上で良い判断だったと考えます。

プログラム動作環境・デプロイ手段の整備から解放された

プログラム動作環境の構築やデプロイをAnsibleなどの構成管理ツールで対応することは、もう3年以上前には行われていたことですが、playbookの作成と管理に心理的コストがかかることをずっと気にしていました。

ところがPaaSを使うことで、プログラム動作環境は毎回お仕着せのものが降って来ますし、デプロイについてはgitレポジトリの特定ブランチにpushするか、tagを切ることで実施されるようになりました。

結果、playbookの管理にかかる心理コストがなくなり、「デプロイをするぞ」という意識から、「pushするぞ」「tag切るぞ」という意識へと移ろい、「デプロイをする」という作業自体がなくなりました。

負荷をあまり気にしなくなった

完全に気にしなくなる状況には至っていないのですが、それでもVMを自分で管理するのに比べて、オートスケール機能が設定されたPaaSの場合は負荷に対する心理的コストが圧倒的に低いと感じます。

手が空いた分iOSアプリの開発ができるようになった

もともと私はサーバサイドアプリ(要はwebアプリ)がメインで、インフラを少々嗜む程度のスキルセットでしたが、新しいスキルを習得する余力ができたのは、明らかに従来の業務遂行に必要な時間やエネルギーが半減したからだと考えます。

得られたものから考える

ここまで書いてきたことは、唯一の判断基準「手間・管理の削減」を突き詰めた結果です。

httpdの項目でも書いた「不確定要素を予定調和化する」ということは、手間・管理を削減するうえでキーとなる考え方ではないでしょうか。

歴史を見ると、「速く快適に移動する方法」として馬や馬車、人力車などが広く利用されていた時代がありました。ところが時代が進むにつれ、馬も馬車も人力車も利用シーンが激減するのですが、その一番の理由は「自動車の普及」でした。

「webアプリを公開する方法」という本質だけを見た時に、歴史や得られたものから考えると、何がより進化したものなのかを思わずにはいられないものです。そして、進化したものはいつも「斬新」で「ミニマリズム」であると、私は考えます。

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さんから改めてお電話があり、関連しているかもしれない異変があることを説明してくれました。

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

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

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

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

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

まとめ

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

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

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

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

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

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

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

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

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

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

{
    "queues": {
        "batchSize": 3
    }
}

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

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

まとめ

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

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

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という物がよくわからないという方などに向けた内容となる予定です。