以前、AWSのRDSを起動していて停止し忘れていたらスケジュールで止めるLambdaを作成しました。
RDS停止を忘れていたら止めるLambda - たぶん動く...
Azureを最近使用しており、Azure Database for MySQLを使用後、停止するのを忘れて不用意に課金されてしまう事態が度々発生しました。
AWSだとLambdaをスケジュールでRDSを起動、停止するPythonスクリプトがネットで探すとよく出て来ますが、Azure Functionsで同じような試みをしている記事が出てこないので作ってみました。
今回は、StopDBというAzure Functionsを作成し、その中でStopMySQLという関数を作成します。Azure Functionsを毎日22時に起動する設定とします。
- サクッと作りたい人へ
- 諸注意
- ローカル環境を整備する
- ローカルで関数プロジェクト作成
- ローカル関数コーディング
- 関数アプリ作成
- 関数アプリのデプロイ
- カスタムロール作成
- ロール付与
- PostgreSQLはどうする?
- データベースを起動したい
- 今回作成したリソース
- 参考文献
サクッと作りたい人へ
- StopDBというpythonの関数アプリを作成します。システム割り当てマネージドIDの割り当てを許可します。
- cloud shell上へGitHubからリポジトリをcloneしてきます。サブスクリプションIDとリソースグループ名をfunction_app.pyに入力、起動時刻を修正します。
- 目的の関数プロジェクトのディレクトリへ移動し、デプロイします。
$ git clone https://github.com/TTY6335/StopAzureDB.git $ cd StopMySQL/ (function_app.pyを編集) $ az functionapp create --resource-group <RESOURCE_GROUP> ---consumption-plan-location <REGION> --runtime python --runtime-version 3.9 --functions-version 4 --name StopDB --os-type linux --storage-account <STORAGE_NAME> $ func azure functionapp publish StopDB
4.RBACディレクトリにあるjsonファイルを参考にしてロールを作成し、データベースへ付与します。
諸注意
作成、運用上での注意事項を先に述べておきます。
2023年11月現在、Azure Functionsで用意されているエディターで関数を作成する場合、必要なpythonモジュールを追加することができません。ここで紹介するコードをAzure コピペして動かそうとした場合、そのAzure Functionsが壊れます。 必ずローカル環境のコマンドラインかVSCodeで作成してください。
実行ステータスを出力するように作っていません。メトリックから「実行した」ことは確認できますが、「データベースサーバが停止したか」どうかは出力されません。
ローカル環境を整備する
前提条件としてazure-cliまたはAzure Power Shelが開発PCにインストールされているものとします。 pythonの仮想環境はお好みで設定してください。 ローカルで開発、テストするにあたり、以下のコマンドで必要なAzure SDK for Pythonモジュールをインストールします。
pip install azure-identity pip install azure-mgmt-rdbms
ローカルで関数プロジェクト作成
ローカルでAzure Functionで動かす関数プロジェクトファイルを作成します。今回はMySQLのDBサーバを止める関数なのでStopMySQLとしました。
$ func init StopMySQL --python -m V2 Found Python version 3.10.12 (python3). The new Python programming model is generally available. Learn more at https://aka.ms/pythonprogrammingmodel Writing requirements.txt Writing function_app.py Writing .gitignore Writing host.json Writing local.settings.json Writing /media/Data/StopMySQL/.vscode/extensions.json $ cd StopMySQL/ ; ls function_app.py host.json local.settings.json requirements.txt
Found Python version 〜 とありますが、現在実行しているPythonのバージョンになります。このバージョンはAzure上でAzure Functionを作成する際に重要になってきます。
ローカル関数コーディング
いよいよMySQLサーバを止めるスクリプトを書いていきます。
function_app.py
import azure.functions as func import datetime import json import logging from azure.identity import DefaultAzureCredential # Module for Mysql - Single Server from azure.mgmt.rdbms import mysql # Module for MySQL - Flexible Server from azure.mgmt.rdbms import mysql_flexibleservers # Azure Subscription ID subscription_id = '<YOUR_SUBSCRIPTION_ID>' # Azure Resource Group Name resource_group_name = '<YOUR_RESOURCE_GROUP_NAME>' app = func.FunctionApp() # Set Schedule in UTC {second} {minute} {hour} {day} {month} {day-of-week} @app.timer_trigger(schedule="0 0 13 * * *", arg_name="myTimer", run_on_startup=False,use_monitor=False) def stopdb(myTimer: func.TimerRequest) -> None: # Get Azure Credentials credentials = DefaultAzureCredential() # Make single server for MySQL management client mysql_client = mysql.MySQLManagementClient(credentials, subscription_id) # Get list of single servers servers = mysql_client.servers.list_by_resource_group(resource_group_name) logging.info(servers) for server in servers: #Stop Mysql - Single Server logging.info('STOP %s',server.name) mysql_client.servers.begin_stop(resource_group_name, server.name) # Make flexible servers for MySQL management client mysql_flex_client = mysql_flexibleservers.MySQLManagementClient(credentials, subscription_id) # Get list of flexible servers servers = mysql_flex_client.servers.list_by_resource_group(resource_group_name) logging.info(servers) for server in servers: #Stop MySQL - Flexible Server logging.info('STOP %s',server.name) mysql_flex_client.servers.begin_stop(resource_group_name, server.name)
現在、Azureのデータベースサービスは単一サーバとフレキシブルサーバの2つが存在します。その両方に対応するようにコーディングしているので長くなっています。使用しているデータベースサービスが1つであればコードはもっと短くなるはずです。
22行目timer_triggerでスクリプトの起動時刻を指定します。一見cronのようですが、項目が6つあり、一番左は秒を指定します。 今回は毎日22:00:00(UTC 13:00:00)に起動するように時刻を指定しました。
requirements.txtには起動するために必要なモジュールを記載します。
azure-functions azure-identity azure-mgmt-rdbms
azure-cliのfuncコマンドでローカルで実行してスクリプトを実行して、誤りが無いか確認します。 開発PC、ユーザにDBの操作権限がある場合、スクリプトが起動しDBが停止するので十分注意してください。
$ func start Found Python version 3.10.12 (python3). Azure Functions Core Tools Core Tools Version: 4.0.5441 Commit hash: N/A (64-bit) Function Runtime Version: 4.25.3.21264 [2023-11-04T13:54:59.949Z] Worker process started and initialized. Functions: StopMySQL: timerTrigger For detailed output, run func with --verbose flag. [2023-11-04T13:55:00.061Z] Executing 'Functions.StopMySQL' (Reason='Timer fired at 2023-11-04T22:55:00.0370491+09:00', Id=135cbc94-fb90-4b9e-8ee1-fee1b4fb5c46) [2023-11-04T13:55:00.062Z] Trigger Details: UnscheduledInvocationReason: RunOnStartup
関数アプリ作成
このあたりはAzure Portal上で操作して作成したほうが早いかもです。
Azure上にAzure Functionのアプリを作成し、ローカルで開発したコードをアップロードします。
まずは、アプリを格納するためのストレージアカウントを作成します。
$ az storage account create --name <STORAGE_NAME> --location <REGION> --resource-group <RESOURCE_GROUP> --sku Standard_LRS
関数アプリを作成します。 pythonのバージョンはローカルで開発した際のバージョンに合わせてください。
作成が完了するまでしばらく待ちます。
$ az functionapp create --resource-group <RESOURCE_GROUP> ---consumption-plan-location <REGION> --runtime python --runtime-version 3.9 --functions-version 4 --name StopDB --os-type linux --storage-account <STORAGE_NAME>
関数アプリのデプロイ
ローカルのpythonスクリプトを書いたディレクトリで以下のコマンドを実行し、Azureにアップロードします。 最後にRemote build succeeded!と表示されればデプロイ成功です。
$ func azure functionapp publish StopDB Getting site publishing info... [2023-11-04T14:18:06.997Z] Starting the function app deployment... Removing WEBSITE_CONTENTAZUREFILECONNECTIONSTRING app setting. Removing WEBSITE_CONTENTSHARE app setting. Creating archive for current directory... Performing remote build for functions project. Uploading 2.28 KB [###############################################################################] .... Remote build succeeded! [2023-11-04T14:18:57.100Z] Syncing triggers... Functions in StopDB: StopMySQL - [timerTrigger]
カスタムロール作成
作成したAzure FunctionにMySQL データベースを操作する権限を与えるロールを作成します。このAzure Functionsを動作させるためには、MySQLデータベースをlist化する権限と停止する権限が必要です。
Stop-MySQL
{ "id": "<ID>", "properties": { "roleName": "Stop-MySQL", "description": "データベースサーバを止めるロール", "assignableScopes": [ "<RESOURCE_GROUP_ID>" ], "permissions": [ { "actions": [ "Microsoft.DBforMySQL/flexibleServers/read", "Microsoft.DBforMySQL/flexibleServers/stop/action", "Microsoft.DBforMySQL/servers/stop/action", "Microsoft.DBforMySQL/servers/read" ], "notActions": [], "dataActions": [], "notDataActions": [] } ] } }
ロール付与
MySQLデータベースで先程作成したロールをStopDBに付与します。
PostgreSQLはどうする?
GitHubでStopPostgreSQLとしてコードをおいてあります。
データベースを起動したい
function_app.pyのbegin_stopをbegin_startに変更してください。また、ロールにはstart/action権限を付与してください。
今回作成したリソース
GitHubに今回作成したコードを置いています。
GitHub - TTY6335/StopAzureDB: Azure Functions for stopping Azure Database Services on scheduled time.
参考文献
Azure Functionsをローカルで開発・デバッグする
コマンド ラインから Python 関数を作成する - Azure Functions | Microsoft Learn
Azure Functions のタイマー トリガー | Microsoft Learn
azure.mgmt.rdbms package | Microsoft Learn