Azure

Azure サービス一言感想

Microsoft Azure をがっつり使ったサービスを作ったので、使った・試したサービスについて振り返りと一言感想を述べていきます。


アプリケーション編


App Service

Web アプリケーション用 PaaS。 Azure 版 Heroku といった感じだが、基本的に常時起動している。 ファイルサーバーがあり、 CLI もあるので提供されてない Perl や Go, Tomcat 以外の Java アプリも動かせるので、 PaaS の割には相当自由度が高く、実質的には特定のディレクトリのみ触れる IaaS に近い。

一方で .NET のアプリケーション( C#, .NET Core 他) であれば、 Visual Studio からボタン一つでデプロイできるので、開発効率は非常に良い。基盤の準備作業があまり要らない。

スケールアップ・スケールアウトどちらも可能なので、Web アプリケーションであればよほどの大規模でなければこれで十分なものが作れる。

認証、ジョブ、監視、異常診断、コンソール、Git デプロイなど周辺機能も充実していて、他のクラウドサービスにはここまで充実した PaaS は無いと思う。

最近は Linux 版、 Docker 版も出てきているが Windows 版ほど機能がそろってないので、今後に期待。


Azure Functions

Azure 版 Lambda だが結構中身は違い App Service の延長として作られている。

そのためいわゆるサーバレス形式の従量課金方式と、常時起動しているインスタンス上で動く App Service プランの2つの動作形態がある。

前者はコールドスタートでありスケールアップもするが初回起動は遅い(場合によるが10-20秒)、後者は即時起動するが課金単位は時間となるしスケールは自分で組む。

非同期処理であれば前者、HTTP処理で素早い反応がいるものは後者という風に使い分けることができる。

App Serviceプランは他のアプリケーションとも同居してリソースを共有できるので、App Serviceが動いているインスタンスで Functionも動かすという事も可能。

今回のシステムではオンラインで一時データだけストレージに書き込んで後続の処理を非同期処理で Function でやるとか、システム間のデータ連携におけるデータ変換するなどの活躍を見せた。

ただ、 Function から Function をさらに呼ぶみたいな ピタゴラスイッチ的な連携を行っている場所がいくつか出てきたので、いずれはちゃんとしたデータ連携の仕組みを組む必要があるのかもしれない。

プログラム方法では入出力バインドというフレームワーク的な便利機能があるのが他の FaaS サービスとの大きな違いになる。設定ファイルで関数の引数や戻り値に対応しているサービスのオブジェクトを設定できるので、例えばキュー起動ならキューの中身の値を引数に入れたり、戻り値に適当な文字列を返すとそれが自動的に Blob ストレージに入るなど設定次第で色々な入出力を自在に変えれる。

もちろん対応してないサービスへの入出力なら自分で実装がいる。

言語は色々あるが今回は直しやすさを考慮して JavaScript にした。 C# だとデプロイが面倒だし、 csx は簡単だったが DLL を使用する場合に Azure Functions ランタイムの DLL と競合したりと複雑な処理をする分には面倒なことが多かった。 Functions 2.0 ならちょっとはましになっただろうか。


ストレージ編


Storage サービス

ストレージアカウント何やねんと言うところから始まった。中に4種類のサービスがある。

開発時では、 Storage Explorer があって便利。


Blob Storage

Azure 版 S3。HTTP ホスティングやログの保管に活躍。 Azure の各サービスも暗黙的に使っていることが多い。 Blob の更新をしたら、HTTP 側にもほぼ即時で更新されるので整合性を考えなくてよかった。

CDN を通さないでそのまま使っているケースも多いと聞く。

HTTP ホスティングで困ったのは、index.html と 404 ページの設定ができないこと。

だったのだけど、プレビュー版で最近その機能が追加されたので S3 に追いつきそう。


File Storage

NTFS。 Azure 版 EFS。 EFS が日本リージョン対応するずっと前から、こちらはあったよ。

マウントする場合は普通に使えるが、権限設定が細かくできないので通常のファイルサーバーとして使うには少し困るだろう。File Sync などで ファイルサーバーのバックアップとして使うならいいのかな。

システム内部でちょっとした連携に使う分には十分。

REST API はファイル一覧の取得で、更新日時などの詳細情報が取れなくて困った。


Queue Storage

Azure 版 SQS。可もなく不可もなく。

単純なキューサービスならこれで十分。


Table Storage

Dynamo 感溢れる何か。使ってない。


Azure SQL Database

SQL Server ですね。30分単位の自動バックアップとか地味に便利でうっかり DB 吹き飛ばしたや移行のときに役立った。

スケールが簡単にできるのはクラウドならでは。

課金単位の DTU の概念が結局今もわからないが、開発中に明朗会計の vCore プランが出てきたのでみんなそう思ってんだなと。

遅いクエリの自動解析や、インデックス候補の提案など便利機能が非常に多い。やらなかったけどチューニングの自動化もできる。


Cosmos DB

読み書きが早いドキュメントデータベース。使い勝手は mongo DB に近い。マルチリージョンがウリになっているがそこはまだやってないのでコメントできない。

RDBMS と違ってクエリが遅くて困るという事態には全く遭遇しなかった。

基本 JSON でスキーマが要らないので、ちょくちょく変わる要件に応じてデータモデルを微妙に変えていくような今回の案件だと色々楽だった。だからといって何も設計が要らないわけではなくちゃんとモデリングはした。

詳細情報のような1件の非正規化したデータをキーで引くような部分で使用していた。

設計の上で一番大事なのはパーティションキーの設定になる。これの設定を間違えると変更はできないので、ここだけは注意深く設計する必要がある。

また基本的にトランザクションはないという前提で設計をすること。ストアドプロシージャという仕組みでトランザクションは一応できるのだが、この仕組みは楽観排他で片方が失敗したらリトライするというような実装を高度化したもので行われている。そのため更新頻度が高いデータにたいするストアドプロシージャはリトライの嵐になりがちで異常に遅くなったりしたので、Azure Functionsで非同期処理化したりしていた。

また最終的には 課金単位の RU にどれだけ金をぶち込めるかというところに落ちつく。

RU を増やせば増やすだけスケールするし、 RU の増減は一瞬でできる。

そのためスパイク時には RU を増やすのか、あるいは色々な策を講じて RU 消費を均すかといった設計をシステムの特性に応じて考える必要がある。

RU を均すには Azure Search を使ったり、Redis でキャッシュしたり、非同期処理化したりと色々ある。

今回は Azure Search を併用することで RUを大量消費する クロスパーティションクエリを一切使わないようにした。

非常に便利なサービスだと感じたが、これだけで全てのユースケースを賄えるというものでもないというのが感想。


Azure Search

位置情報検索、ファセット、全文検索などができる検索サービス。 Elasticsearch のようだが、こちらはスキーマ定義がガチガチだ。

Cosmos DB は縦方向のデータ取得に強いが、パーティションをまたぐ検索だと RU 消費が激しいので、横断検索を補う目的で導入。

インデックスのスキーマ定義が厳密で後から変更できなかったり、構造化した JSON 定義ができないなど、そこそこ複雑なデータ定義をするには工夫がいる。

Azure Functions からデータ投入をする方針にしたのだが、こいつは結構同時接続数の上限が低い。

ドキュメントに記載はないので体感だが、 S1レベルだと 20位の同時接続に耐えられなかったりする。

一般向けのシステムで使うのなら、S2レベルで複数レプリケーションは必須に思える。

そのため、 Azure Functions でインデックス更新を行う場合、スパイクがあると簡単に接続数の上限に達したりしていたので 後述の Event Hubs などを使って同時アクセス数を均しつつバッチ更新をするような工夫をしていた。詳しくは ここ

あと、Azure Portal が貧弱でデータ投入ができなかったり、検索が面倒だったりしたのでそのためのクライアントアプリを作ったりした https://github.com/kencharos/AzureSearchViewer


Redis Cache

PaaS 版の Redis。 Cosmos DB から頻繁に読まれるデータをキャッシュして応答性と RU 消費を抑えた。

上級のプランだと、キャッシュデータの永続化やクラスタが組めるらしいがまだそこまで必要としてない。

Redis Cache が悪いわけではないが、 クライアントである StackExchange.Redis が結構使いにくく苦労した。

.NET Core MVC との相性なのかもしれないが、たまに Redis コマンドのパイプラインが複数のHTTPリクエストのコマンドをまとめて実行して、遅いリクエストに引きずられるような事象が発生していて、今も完全には対策できてない。

もちろん基本的は異常に早いので、入れてよかったと思っている。


データ連携編


Service Bus

メッセージングサービス。 Queue Storage との違いは何かと聞かれると、長期間保存、 1対多配信、スケジュール配信、重複検知、リトライ、Dead Letter Queue などの高機能が付与されていること。

今回のシステムでは、システム間連携で1対多配信を多用し、簡易的なデータ配信基盤として活躍してもらった。

配信先には Azure Functions が待っていてデータ投入先のストレージ(Cosmos や Azure Search, Blob) に合わせたデータ変更を行う感じ。

ほどよい粒度のサービスであり、私のお気に入り。他のベンダはこれと同じ機能をもつものはないような気がする。

ただし、 Azure Portal から参照できる情報があまりに薄く、Service Bus Explorer という外部ツールでステータスの把握やテストデータ投入をしていた。


Event Hubs

こちらもメッセージングサービスだが、Service Bus より機能を落として高スループットにしている。 AWS の Kinessis や Kafka に近く、Kafka 互換のAPIの最近プレビュー提供された。

今回のシステムでの利点は、大量データ受け入れのバッファとして使用できることと、 Azure Functions でバッチ処理ができることに尽きる。

システムに投入されるデータがスパイクして大量になった場合、その大量のデータをそのまま後ろ(SQL Database や Azure Searchなどのストレージ) に流すと上手く処理できないことが多い。

Event Hubs は 2 から 32 のパーティションによるバッファを提供するため、後ろに流すデータ量をパーティション数で調整できる。また Azure Functions はパーティションから複数件のメッセージをまとめて1度の関数実行に渡すことができるため、 同時接続数が低いストレージサービスであっても一度の接続で効率よく複数メッセージを処理できる。

そのため、大量の非同期処理を支える基盤として活躍してもらった。

リトライが難しいのでそこは設計で工夫。読み書きデータ量が課金に影響するので、メッセージサイズはなるべく小さめに。


Notification Hubs

iOS, GCM へのプッシュ通知を仲介してくれるサービス。モバイルやるなら入れとく必要がある。

MSがあまり力を入れてないのか、ドキュメントがすくなかったり、.NET Framework 用の SDK はあっても .NET Core 向けは無かったり、

Azure Portal に表示される内容も貧弱だったりで、内部調査にえらく苦労した。

今でもたまに謎の不調が起きることがあり、気持ちを把握するのが難しい。


Logic Apps

GUI で色々な連携処理をつないで、ノンプログラミングで複雑な処理を組むもの。

例えば、キューにメッセージが来たら、それを Blob に出力、何かのジョブを実行する、結果をメールすみたいな内容がポチポチと GUI から設定するだけで行ける。

楽に作れるが、そこそこ複雑なことをやろうとすると GUI でいじるのは結構面倒になってくる。また、可搬性がなく、定義は JSON に落とせるが別の環境にそのままインポートできなかったりする。

ある程度の粒度の処理を、Azure Functions で書いておいて、それを Logic Apps で呼び出し順を制御して実行するくらいがちょうどいい。


ネットワーク編


TraficManager

Route 53 的なやつ。いずれマルチリージョンにすることを視野に入れつつ導入。トラフィックの振り分けやメンテ時の Sorry への案内を行っている。


Application Gateway

L7 ロードバランサー。 URLレベルでバックエンドへの振り分けを行う必要があり導入。

他にも WAF があるのでセキュリティ目的で導入している。

ただし WAF が割とレスポンスタイムに影響を与えるのでそこは注意。


Azure CDN

Blob Storage のリソースのキャッシュとして。

やはり導入するとリソース取得が早いので、システムの特性に応じて導入する。

TTL の設定には注意。

開発中はしょっちゅう CDN にキャッシュされたコンテンツを削除とかしていたので、色々と勿体なかった。

開発環境に応じて CDN の利用可否を変えれるようにしたいところ。


監視編


Application Insights

悪いことは言わないので絶対に入れておいたほうがいいサービスの一つ。

複数サービスにまたがるログを収集し、分析や検索ができる。

Datadog みたいなもの。

異常検知のアラートを出すこともできるし、ログにユーザーIDなどをいれることでサービスをまたいだ行動履歴をグラフ化できたりする。

主に障害調査用途で現在は使っていて、何か障害起きたらまず Insights 見に行けばいいくらいになっている。

他に、SQLライクな問い合わせを使用して、リクエストURL 別の応答時間をグラフ化したり、

検索に時間のかかるクエリを洗い出したりといった色々な分析で使える。

あと、Azure Functions のログは基本的に Application Insights に出力される。


その他


Azure Container Registry

Docker イメージのレジストリ。ちょっとだけ VM でDocker 使ったので可も不可もなく。


コンテナ監視ソリューション

VM や オンプレ、 k8s などで動いているコンテナの情報を収集し、色々とダッシュボードを出したり標準出力を取得したり、アラート出したりといったことが横断的にできる。

コンテナの死活監視に使用していてめっちゃ便利。


ACS engine

自分の VNET 上に k8s クラスタを構築してくれるライブラリ。

システムの一部でどうしても VNET 上で動く必要があるサービスが必要になったため、せっかくだし k8s でやるかと思ってやってみた。

クラスタの作成自体は問題なくできたし勉強にもなったのだが、色々あって使わないことになった。


Azure Batch

必要な時だけ VM クラスタを立てて任意の処理を実行して VM を撤収するようなサービス。

最初バッチ処理をどう実現するかを検討していた時に試していて、結局処理量的に Functions でいいやとなったので使わないことになった。割と準備が面倒だったし。

扱うデータ量が多くなった時に使うかもしれないが、AKS や ACI もあるし選択に迷うところ。

コスト面を考えるとスポットインスタンス的なものが使えるのでそこはいいのかもしれない。

どうでもいいけど、バッチに対する認識が私だと JCLでスケジュール立てて巨大な処理を実行するものという感じですが、 Azure Batch や AWS Batch だと巨大なデータを並列処理で一気にやるぜみたいなスタンスなので、海外だとあんまりジョブスケジューラって使ってないんですかね。


全体編


Azure AD まわり

いまだに結局良くわかってない。

ディレクトリとか、 アカウントとかサブスクリプションとかをちゃんと体系的に説明するものが必要だと思っている。

後サービスの実行権限(Service Principal)周りも。

AWS の IAM も複雑すぎて嫌いなのだけど、こっちもこっちでなんだか良くわからない。


シークレットキーについて

各サービスを呼び出したりするのにシークレットキーを接続文字列に設定するものが多い。

環境変数で渡すようにすれば問題ないのだが、正直カジュアルすぎて怖い。

IAM Role 的な、シークレットキーを手動で設定せずに済む仕組みはないものだろうか。


まとめ

なるべく VM 使わずに行こうというスタンスで進めたので偏りはあるとは思いますが、総じて使いやすい粒度のサービスがそろっていると思いました。

今後は、 AKS だったりマニアックそうな Azure Fabric 何かも使ってみたいところ。