はじめに
はじめまして。技術本部 Sansan Engineering Unit Data Hubグループの木下です。2021年に新卒入社をして早3年が経過しました。そろそろ何か発信していこうかしらねということで筆を手にしてみました。
軽く自己紹介しておくと、私の好きなことはソフトウェアエンジニアリングという領域に対する技術的アプローチ全般です。一番好きなことを強いてあげるなら、モデリングなんだろうなぁと思います。
自分語りはこの辺にして本題に移ります。私が所属しているチームでは、Sansan Data Hubというプロダクトを開発しています。Sansan Data HubはAzure上でサービスを展開しています。
設計上いくつかのドメインで区切っていて、各ドメインに適したデータベースサービスを採用しています。今回はその中でもAzure SQL Database Hyperscaleの運用に関するお話をします。
先日勉強会でも発表しましたが、このBlogでは実際のカスタムメトリクスの実装方法とか、文字の方が伝わりやすいものを発信します。
Hyperscaleとは
Azure Hyperscaleとは、Azure SQL DatabaseのvCoreモデルのservice tierの一種です。購入モデルとしては他にDTUがあり、service tierとしてはGeneral Purposeなどがあります。
Hyperscaleの特徴に関しては以下のドキュメントが参考になります。
What is the Hyperscale service tier? - Azure SQL Database
Hyperscaleにはデータベースのレプリカ(複製)を作る機能が組み込まれており、レプリカにはいくつかの種類があります。
- High Availabilityレプリカ(HAレプリカ)
- Namedレプリカ
- Geoレプリカ
Sansan Data HubではHAレプリカを使っています。HAの特徴としては以下があります。
- プライマリ(レプリカの元になるデータべ-ス)とレプリカのデータベース名が同じ
- レプリカの数は0~4で指定できる
HAレプリカはSQL Databaseをゾーン冗長するには必ず一つ必要になります。新しくほかのレプリカを追加してしまうとコストもかかってしまうため、このHAレプリカを負荷分散用に使いたいよねぇという事で使っています。
後々別タイプのレプリカにしたくても、その変更はデプロイ後も可能なので、楽に始められるところからやってみよう、というスタンスです。
HAレプリカ運用のつらみ
先述の通り、私たちのプロダクトではHAレプリカを採用しましたが、運用をしてみるとやはりつらみも出てきました。ドキュメントにも記載がありますが、HAレプリカのメトリクスはAzure Portal上で確認が出来ません(※2024年8月時点)。
そのため、例えばReadを行う処理のSQLのDurationが増加傾向にある際の調査では、SQL Serverのリソースの状態を確認するためにわざわざSSMSなどのクライアントアプリケーションを使いログインをする必要がありました。
(レプリカの状態を確認する都合上、SSMSの接続先もレプリカにしないといけません。そのために接続文字列にReadOnly
を含めなければいけませんが、これも地味にきつい….。)
SSMSにログインをし、リソースの状態を調べるクエリを実行するという手順を作っていましたが、都度それをやるのも面倒です。また、その時点での状態しか見られないので、時間経過と共に何がどう変化したのかわからない、というつらみもありました。
Namedにすれば個別のDBとしてメトリクスも確認できるので、Namedに切り替える手段もありました。ただHyperscaleでゾーン冗長を行うとHAレプリカを1つ用意するのは必須になります。NamedにするとHA1つ、Named1つとお金がかかってしまうので、HAのままやる選択を取っています。
なので、HAレプリカを使い続けつつも少しずつ現状を改善していこうという事で、HAレプリカのメトリクスを取得する、という試みを実施しました。
監視の仕組み
簡単にですが、HAレプリカのメトリクスを監視する仕組みの全体像を紹介します。Sansan Data HubはAzure Functionをコア技術として構築しています。監視の仕組みもAzure Functionをベースにしています。
この図の左側の部分が監視の仕組みのために追加したものです。
Azure FunctionはTimer Triggerで1分に一度起動するようにしています。(後述しますが)起動するたびにSQL DatabaseのDMVを参照し、その値をメトリクスとして使うようにしています。
メトリクスの送信部分は(後述しますが)Azure Monitor の REST API を使っています。このAPIを使うとユーザが定義したカスタムのメトリクスを Azure Monitor に対して送信できます。
メトリクスはAzure上のリソース単位で送信できます。送信したメトリクスはAzure Portal上で各リソースの標準メトリクスと同様に確認できます。
動的管理ビューを使ってHAレプリカの状態を監視する
動的管理ビュー(以下、DMV)はSQL Serverインスタンスの状態などが記録されたViewのことです。例えばCPU使用率だったり、Indexの断片化率だったり、監視に使えるデータが記録されています。実際、Azure Portalで確認できるSQL Databaseのメトリクスも出本はDMVだったりします。
DMVを使えば多岐に渡ってさまざまなデータを参照できます。本ブログでは一部のViewしか取り上げないため、他にも知りたい事がある場合はドキュメントを参照ください。
Hyperscale HAレプリカ監視を行うために使ったDMVは sys.dm_db_resource_stats です。このDMVではCPUやMemoryなど、リソースの使用量が確認できます。
今後も別のDMVを使って監視を行う事はあるかもしれません(メトリクス以外の手段かもしれません)が、まずはプライマリでも確認できている情報を見られるようにしようという事で、こちらのDMVを使っています。
具体的に参照しているのは以下のカラムです。
カラム | 説明 |
---|---|
avg_cpu_percent | Service Tierの制限に対するCPU使用率の平均値。 |
avg_data_io_percent | Service Tierの制限に対するData IO使用率の平均値。 |
avg_memory_usage_percent | Service Tierの制限に対するMemory使用率の平均値。 |
avg_log_write_percent | Service Tierの制限に対するトランザクションログ(MB/s)の平均書き込み量の割合。 |
max_session_percent | Service Tierの制限に対する最大同時セッションの割合。(最大同時ワーカ数が100で50ワーカ使っていれば50%) |
max_worker_percent | Service Tierの制限に対する最大同時ワーカの割合。(最大同時ワーカ数が100で50ワーカ使っていれば50%) |
カスタムメトリクス用のREST APIを使う
REST APIを使ってAzure Monitorに対してメトリクスを送信できます。送信したメトリクスはAzure Portlの各リソースのメトリクスページで確認できます。
このAPIの使い方を簡単に説明しておくと、
- リージョン、リソースIDを使ってメトリクス送信対象リソースを指定する
- 対象リソースに対しメトリクスを送信する
です。めちゃくちゃシンプルで簡単に使えます。
このAPIを使ってHAレプリカで取得したDMVの値をメトリクスとしてAzure Monitorに送信します。
Sansan Data HubではSQL Database以外の種類のリソースにもカスタムメトリクスを送信しています。なので、いい感じに使いまわせるようにAPI Clientを作って扱っています。コードは以下のような感じです。
public class MetricsDataPlaneClient { private const string AccessTokenResource = "https://monitoring.azure.com/"; private static readonly HttpClientHandler InnerHttpClientHandler = new(); private readonly AzureAccessTokenHandler _azureAccessTokenHandler; public MetricsDataPlaneClient(TokenCredential defaultAzureCredential) { _azureAccessTokenHandler = new AzureAccessTokenHandler(defaultAzureCredential, AccessTokenResource, InnerHttpClientHandler); } public async Task CreateAsync(string region, string resourceId, Metric metric, CancellationToken cancellationToken = default) { var url = $"https://{region}.monitoring.azure.com{resourceId}/metrics"; using var httpClient = GetHttpClient(); var content = new StringContent(JsonConvert.SerializeObject(metric), Encoding.UTF8, "application/json"); var response = await httpClient.PostAsync(url, content, cancellationToken); response.EnsureSuccessStatusCode(); } private HttpClient GetHttpClient() => new(_azureAccessTokenHandler, false); }
REST APIなのでHTTP Clientをベースに実装をしています。メトリクスAPIのendpointを持っておいて、メトリクス送信対象リソースのリージョン、リソースIDとメトリクスを実行時に渡す感じですね。
認証部分はDelegatingHandlerを使っています。具体的なコードは割愛しますが、APIにリクエストを飛ばす際にインターセプトして、Access Tokenが有効期限切れになっていれば取得しなおす、という感じですね。割とあるあるな実装かなと思います。
Metric
の部分も詳細は割愛しますが、APIに送るリクエストボディを単に型化しているだけです。どのようなリクエストを受け付けるのかはドキュメントに書いてあるので、それをベースにC#コードに落としているだけですね。
一点詰まってしまった点としては、seriesとして送るmin
, max
, sum
, count
の整合性を保たねばいけない部分です。API側でseriesの値に対してバリデーションを行っており、整合性が取れない場合にエラーが返されます。具体的には以下のバリデーションをしています。
- 4種類の値は必ずセットになっていなければならない
- それぞれの値が論理的に正しくなければならない
- [1, 2, 3] という値を一つのメトリクスとして送る時、min=1, max=3, sum=6, count=3となっていなければならない
普通に考えれば適切なバリデーションがなされているなぁとなるのですが、ラフに動作確認をしたくてmax
だけ送る、とかしてしまうとAPIからは400エラーが返ってきてしまいます。エラーメッセージとしてmin, sum, countは必須です、ということが含まれているわけではないので詰まってしまいましたね…。
また、ドキュメントには記載がないのですが、浮動小数も送信できます。今回使ったsys.dm_db_resource_stats
はdecimal(5,2)です。そのまま送ってもエラーではじかれることはありません。
運用してみて
実際にAzure Portal上で確認できるメトリクスです。
UIの部分に関してはAzure Portal上にうまく乗っかれるのが嬉しいですね。メトリックを送信すれば、何をどのように表示するか、の部分はAzure側が賄ってくれます。単にAPIを使ってメトリクスを送信するだけで良いというのは考える事が減って楽ですね。
今回はHAレプリカのリソース状況を監視するための工夫点について紹介しました。こういった工夫点が、皆さまの運用の改善につながれば幸いです。