概要
Lambdaのスケジュール実行でRDS(MySQL)のスロークエリログを監視し
サイズが一定以上ならSlackに通知する仕組みを作ります。
Lambdaファンクションのランタイムには'Python 2.7'を使います。
事前準備
- RDS(MySQL)
- EC2(MySQLにアクセスできればOK)
- Slackチーム
手順
RDSでスロークエリ出力を設定する
RDSのインスタンスに設定されているパラメータグループを変更します。
以下の3項目を変更してください。
パラメータ | 変更前 | 変更後 | 説明 |
---|---|---|---|
slow_query_log | engine-default | 1 | スロークエリを出力します |
long_query_time | engine-default | 2 | 2秒以上のクエリをスロークエリとします |
log_outut | TABLE | FILE | スロークエリはファイルとして出力します |
インデックスを使わないクエリを出力するlog_queries_not_using_indexes
もありますが
今回の判断軸はクエリが早いか遅いかのみなので、割愛します。
試しにスロークエリを発生させてみます。
意図的にスロークエリを発生させるためにSLEEP()
を使いました。
これで実行時間を伸ばしたクエリを自作のテーブルに投げます。
mysql> CREATE TABLE item (id int);
Query OK, 0 rows affected (0.02 sec)
mysql> INSERT INTO item VALUES(1);
Query OK, 1 row affected (0.01 sec)
mysql> SELECT id, SLEEP(5) FROM item;
+------+----------+
| id | SLEEP(5) |
+------+----------+
| 1 | 0 |
+------+----------+
1 row in set (5.00 sec)
これでRDSインスタンスのLogsからmysql-slowquery.logを開くと
以下のようにスロークエリが記録されるのがわかります。
SlackのWebhook用URLを準備する
以降の手順で使用するLambdaファンクションにおいて
以下のような準備が必要であると記載されています。
Follow these steps to configure the webhook in Slack:
- Navigate to
https://<your-team-domain>.slack.com/services/new
- Search for and select "Incoming WebHooks".
- Choose the default channel where messages will be sent and click "Add Incoming WebHooks Integration".
- Copy the webhook URL from the setup instructions and use it in the next section.
Follow these steps to encrypt your Slack hook URL for use in this function:
- Create a KMS key - http://docs.aws.amazon.com/kms/latest/developerguide/create-keys.html.
- Encrypt the event collector token using the AWS CLI.
$ aws kms encrypt --key-id alias/<KMS key name> --plaintext "<SLACK_HOOK_URL>"
Note: You must exclude the protocol from the URL (e.g. "hooks.slack.com/services/abc123"). - Copy the base-64 encoded, encrypted key (CiphertextBlob) to the ENCRYPTED_HOOK_URL variable.
- Give your function's role permission for the kms:Decrypt action.
全体を要約すると、Slackの'Incoming Webhooks'でURLを取得し
それをAWSのKMSという暗号化サービスで暗号化しましょうという流れです。
下記ページの説明がとてつもなくわかりやすいので参考にしました。
3節から5節まででURLの取得と暗号化が行われています。
LambdaのBlueprintを使って、CloudWatchのアラームをSlackに投稿
Lambdaファンクションを作成する
RDSを配置しているリージョンでLambdaのコンソールを開き
'Create a Lambda Function'を選択します。
ブループリントの中のcloudwatch-alarm-to-slack-python
を選択します。
(参考のために使用しますが、cloudwatchは使いません)
NameとDescriptionは適当につけましょう。
Runtimeは前述の通り'Python 2.7'です。
肝心のコードの部分は以下の通りです。
大部分はブループリントのコードをそのまま使用しています。
説明はインラインで記載しました。
import boto3
import json
import logging
from base64 import b64decode
from urllib2 import Request, urlopen, URLError, HTTPError
# スロークエリを調べたいDBのID
RDS_ID = "{id}"
# この数値を超えるログを、スロークエリ増加のトリガーとする
THRESHOLD_BYTE = {byte}
# 暗号化したSlackのWebhookURL
ENCRYPTED_HOOK_URL = '{url}' # Enter the base-64 encoded, encrypted key (CiphertextBlob)
# 通知をしたいSlackチャンネル
SLACK_CHANNEL = '{channel}' # Enter the Slack channel to send a message to
# 暗号化されたURLをKMSでデコード
HOOK_URL = "https://" + boto3.client('kms').decrypt(CiphertextBlob=b64decode(ENCRYPTED_HOOK_URL))['Plaintext']
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
client = boto3.client("rds")
# 対象のDBでサイズが閾値を超えるスロークエリログを取得
log_files = client.describe_db_log_files(
DBInstanceIdentifier=RDS_ID,
FileSize=THRESHOLD_BYTE,
FilenameContains='slowquery')
# 取得できたログがなければここで終了
if len(log_files['DescribeDBLogFiles']) == 0:
logger.info("Slow query was not found.")
return
# Slackで通知する内容の設定
slack_message = {
'channel': SLACK_CHANNEL,
'username': 'Alert from RDS',
'icon_emoji': ':exclamation:',
'text': 'Slow query was found.'
}
# Slackへのリクエスト
req = Request(HOOK_URL, json.dumps(slack_message))
try:
response = urlopen(req)
response.read()
logger.info("Message posted to %s", slack_message['channel'])
except HTTPError as e:
logger.error("Request failed: %d %s", e.code, e.reason)
except URLError as e:
logger.error("Server connection failed: %s", e.reason)
ロールは新しく作成し、既に設定されている通り
KMS decryption permission
を付与してください。
その他はすべてデフォルトの設定で、VPCも未使用です。
これでLambdaファンクションが実行できるようになりましたが
このまま実行すると権限の問題でこけてしまいます。
IAMのコンソールから、先程作成したロールにRDSへのアクセス権限を追加します。
追加するポリシーはAmazonRDSReadOnlyAccess
です。
Lambdaのスケジュール実行を設定する
作成したLambdaファンクションのトリガーを作成します。
Triggersタブの'Add Trigger'をクリックします。
トリガータイプとして'CloudWatch Events - Schedule'を選択し
表示された項目を埋めていきます。
RDSのslowquery.logはデフォルトで1時間間隔で24時間分のデータが残るため
平日の1日ごとにファンクションが実行されるように設定しました。
Cronの時刻設定はUTCであることに注意が必要です。
実行間隔は運用方法に応じて(コードも含めて)変更してください。
以上の設定で、スロークエリがあればSlackに通知が行われます。
まとめ
スロークエリログの増加をチャット通知する仕組みについて書きました。
なお、download_db_log_file_postion()
メソッドを使えば
ログの中身を直接チャット通知することもできそうですが
DBのスキーマやデータに関する情報を載せるのに抵抗があったため避けました。
そもそも通知の発生頻度は高くない(高い状態がおかしい)ので
存在さえ認知できれば十分かなと思っています。