がじぇ

お金と家電とプログラミングのブログ

【AWS】Lambda(Python)からRDSのレプリケーション監視を行う

こんにちわ

がじぇったー (@hackmylife7) | Twitter


です。


仕事でPythonを書く機会があったのでそのCodeを共有します





RDS⇄RDSのレプリケーション監視は前日書いた以下の記事で紹介しましたが、
今日はEC2→RDSなどレプリケーション元がRDS以外の場合にレプリケーションの失敗を監視する方法を記載します。
gadgeterkun.hatenablog.com

プログラム中のコメントにも書きましたが、show slave statusコマンドの結果、以下の2項目がどちらもyesになっていることでスレーブが問題なく動作していると判断します

  • Slave_IO_Running: Yes (I/O スレッドが起動され、マスターに正常に接続したかどうか.ConnectingなどになっているとSlave→Master側へのアクセスがうまくいっていない)
  • Slave_SQL_Running: Yes (SQL スレッドが起動されたかどうか. NoになっているとMaster⇄Slave のデータ整合が合わなくなっている)

監視構成

  • Lambdaからレプリケーション先となるRDSに"SHOW SLAVE STATUS;"コマンドを実行し、その結果を判断することにより監視を行う
  • LambdaはRDSと同一のVPC内で実行する必要がある
  • RDSへ接続するために使用するライブラリはpymysqlを用いる
  • 監視が失敗した際の通知先はslackにする

f:id:gadgeterkun:20200118174040p:plain

Code

動作するCodeは以下です。
プログラム上部の環境変数は自身の環境に合わせてください。

import sys
import logging
import pymysql
import traceback
import urllib.request
import json

#rds settings
rds_host  = "RDSのエンドポイント"
name = "admin"
password = "test"
db_name = "mysql"
WEBHOOK_URL = "webhookのURL"


logger = logging.getLogger()
logger.setLevel(logging.DEBUG)


def _notify():
    """
    slack通知を行う
    Args:
        None
    Returns:
        None
    """
    logger.debug("Notify Script Start.")
    color = 'danger'
    payload = {
        'attachments': [
            {
          "title": "Notify Replication Error",
          "text": "レプリケーションエラーを検知しました。データベースを確認してください",
          "color": "danger"
        }
            ]
    }
    try:
        request = urllib.request.Request(
            WEBHOOK_URL,
            data=json.dumps(payload).encode(),
            headers={'Content-Type': 'application/json'},
            method='POST'
        )
        with urllib.request.urlopen(request) as response:
            return response.read().decode('utf-8')
    except Exception as e:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        stack_trace = traceback.format_exception(
            exc_type, exc_value, exc_traceback)
        logger.error(''.join(stack_trace))


def main(event, context):
    try:
        logger.debug("Script Start.")
        logger.debug(f"User: {name}")
        logger.debug(f"Host: {rds_host}")
        conn = pymysql.connect(rds_host, user=name, passwd=password, db=db_name, connect_timeout=5, cursorclass=pymysql.cursors.DictCursor)
        logger.info("SUCCESS: Connection to RDS MySQL instance succeeded")

        with conn.cursor() as cur:
            """
            show slave statusコマンドの結果、以下の2項目がどちらもyesになっていることでスレーブが問題なく動作していると判断する
                ・Slave_IO_Running: Yes (I/O スレッドが起動され、マスターに正常に接続したかどうか.ConnectingなどになっているとSlave→Master側へのアクセスがうまくいっていない)
                ・Slave_SQL_Running: Yes (SQL スレッドが起動されたかどうか. NoになっているとMaster⇄Slave のデータ整合が合わなくなっている)
            """
            cur.execute("show slave status")
            result = cur.fetchall()
            logger.debug(f"result: {result}")
            result_list = [i for i in (result[0]["Slave_IO_Running"], result[0]["Slave_SQL_Running"])]
            logger.debug(f"result_list: {result_list}")
            
            result_list = ["NO"]
            # どちらかがNoの場合、Slackに通知する
            if all([x == "Yes" for x in result_list]):
                logger.info(f"Message: Database is running normally")
                sys.exit(0)

            logger.info(f"Message: Notify Replication ERROR")
            _notify()


        return "succeeded"
    except Exception as e:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        stack_trace = traceback.format_exception(
            exc_type, exc_value, exc_traceback)
        logger.error(''.join(stack_trace))
if __name__ == "__main__":
    main()


LambdaへCodeをアップする際はpymysqlのライブラリもアップロードする必要があるので下記の手順でzip化してアップしましょう

❯ pip install pymysql -t ./
❯ chmod -R 755 ./*
❯ zip -r zip_file ./*

↓手を動かして学べる最高の本