[Azure] Cosmos DBのRU定期スケーリングを設定してみた

#はじめに
Azure Cosmos DBの料金に関わる因子として予約済みスループット(RU)があります。
例えば、1ヵ月間RUの値を固定して運用した場合、400RU/s(2,943円)と10000RU/s(73,584円)とではおよそ7万円の差が生じます。(2019年6月3日時点、東日本リージョン)
※消費ストレージによる料金は考慮しておりません。

RUはAzureポータル上からもポチポチ変更することができますが、RUを要する日時が決まっている場合はAzure CLIで定期的にスケーリングする設定をしておくと便利です。

今回はcronを使って、RUを定期的に変更する仕組みを設定しました。
また、RUが変更されたことをSlackに通知する設定も入れていますので、必要に応じて設定してみてください。

## 0. 環境
- OS: Ubuntu 18.04 (今回はAzureでサイズB1sのVMを作成して試しました。)

## 1. Azure CLIのインストール
Azure CLIを使えるようにLinuxにインストールします。

- [apt を使用して Linux に Azure CLI をインストールする \| Microsoft Docs](https://docs.microsoft.com/ja-jp/cli/azure/install-azure-cli-apt?view=azure-cli-latest)

~~~
$ curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
~~~
※Azure CLIのインストール方法はOS毎に異なるので、公式ドキュメントを参照してください。

Azure CLIをインストールしたら、

~~~
$ az login
~~~
を実行して、表示されたURLをブラウザから開きます。
コンソールに表示されたコードを入力してサインインしてください。

## 2. サービスプリンシパルの作成
Azureリソースにアクセスするために、サービスプリンシパルを作成します。
今回はパスワードベースの認証でサービスプリンシパルを作成しました。

- [Azure CLI で Azure サービス プリンシパルを使用する \| Microsoft Docs](https://docs.microsoft.com/ja-jp/cli/azure/create-an-azure-service-principal-azure-cli?view=azure-cli-latest)

~~~
$ az ad sp create-for-rbac --name <サービスプリンシパル名>
~~~

出力は以下のようになります。passwordは二度と表示されないので、メモしておきます。

~~~
{
  "appId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "displayName": "<サービスプリンシパル名>",
  "name": "<サービスプリンシパル名>",
  "password": "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy",
  "tenant": "zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzzz"
}
~~~


## 3. シェルスクリプトを作成
下記ドキュメントに記載されている、`$ az cosmosdb collection update`コマンドの`--throughput`オプション使えば、RUを変更できます。

- [az cosmosdb collection \| Microsoft Docs](https://docs.microsoft.com/en-us/cli/azure/cosmosdb/collection?view=azure-cli-latest#az-cosmosdb-collection-update)

今回はホームディレクトリにて、

~~~
$ vi changeRU.sh
~~~
で、RUを変更するためのシェルスクリプトを作成しました。

また、SlackのIncoming Webhooksを利用してスクリプトの実行結果を通知する処理も組み込みました。
必要に応じて、Slackの設定とWebhookURLの取得をしてください。
※今回は下記弊社記事を参考に、Slackの設定とWebhookURLを取得しました。

- [[Slack] 外部から特定のチャンネルに投稿する - Qiita](https://qiita.com/naonaka/items/23c4119906ed1b233e64)

~~~shell:changeRU.sh
#!/bin/bash

set -e

# 変数設定
resourceGroupName='<リソースグループ名>'
accountName='<Cosmos DB アカウント名>'
databaseName='<Cosmos DB DB名>'
targetCollectionName=$1
newThroughput=$2

#以下3行は、2.の工程で出力された値を使用
appId='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
password='yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy'
tenantId='zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzzz'

#Slack通知する場合は、以下1行追記
slackWebhookURL='<WebhookURL>'

# エラー時は処理を中断しaz logout(Slack通知する場合は、以下1行目と2行目のコメントアウトを入れ替える)
trap "az logout" ERR
# trap "curl -X POST --data-urlencode 'payload={\"username\": \"Cosmos DB\", \"text\": \"${targetCollectionName}のRU変更でエラーが発生しました\"}' $slackWebhookURL ; az logout" ERR

# サービスプリンシパルでlogin
az login --service-principal \
        --username $appId \
        --password $password \
        --tenant $tenantId

# RU変更
az cosmosdb collection update \
    --collection-name $targetCollectionName \
    --name $accountName \
    --db-name $databaseName \
    --resource-group $resourceGroupName \
    --throughput $newThroughput

# logout
az logout

# Slack通知する場合は、以下1行追記
curl -X POST --data-urlencode "payload={\"username\": \"Cosmos db\", \"text\": \"${targetCollectionName} を ${newThroughput}RU/s に変更しました\"}" $slackWebhookURL
~~~

スクリプトを直接実行する際は、`$ bash changeRU.sh <targetCollectionName> <newThroughput>` というように、コレクション名と変更したいRUを引数で指定するようにします。

## 4. cronでシェルスクリプト実行をスケジュール
3.の工程で作成したシェルスクリプトを定期的に実行するよう、

~~~
$ crontab -e
~~~
でcronを設定します。

下記の例では、毎月1日の0時に10000RU/sまで上げて、12時に400RU/sまで下げるように設定しています。
※環境に合わせてタイムゾーンを設定してください。今回はJSTに設定してあります。

~~~
# m h  dom mon dow   command

0 0     1 * *    /home/<ユーザ名>/changeRU.sh <targetCollectionName> 10000 2>>/tmp/err.log
0 12     1 * *    /home/<ユーザ名>/changeRU.sh <targetCollectionName> 400 2>>/tmp/err.log
~~~

編集後、

~~~
$ sudo /bin/systemctl restart cron.service
~~~
で再起動しました。

以上で設定完了です。
あとは時間が来ればRUが変更され、実行結果がSlackに通知されます。

## 終わりに
ポイントは`$ az cosmosdb collection update`コマンドでRUを変更できることです。
今回は仮想Linuxマシンを使っての運用ですが、他にもっと上手い方法もあるかと思います。
最近、PowerShellコマンドレットでもRUを変更できるようになったようです。（当記事を書いている途中で気づきました。）
Azure Automationを使ったスケーリングもできそうですね。

- [Azure PowerShell script - Azure Cosmos DB Update RU/s for SQL (Core) API \| Microsoft Docs](https://docs.microsoft.com/en-us/azure/cosmos-db/scripts/powershell/sql/ps-sql-ru-update?toc=%2fpowershell%2fmodule%2ftoc.json)