継続は力なり

タイトル通り定期的な更新を心掛けるブログです。

Python で RDS に格納されている各種ログをダウンロードする

タダです.

AWS 利用料のうち CloudWatch Logs への取り込みが高くかかっていました( DataProcessing-Bytes という項目).そこで,下記のページを参照して調べてみたところ一番容量があったのが Aurora のログでした.そのログを CloudWatch Logs に出力せずダウンロードし,S3 などにアップロードしていけばコスト削減に繋がりそうです.この記事では,ダウンロードを Python でやってみた内容をまとめていきます.

aws.amazon.com

Python で RDS ログダウンロードのための手段

boto3 のドキュメントを見てみたらログダウンロードをするのに, download_db_log_file_portion があります.ただし,説明に記載のようにこの API では 1MB までのダウンロードになると記載があるため,別のアプローチを調べてみました.

Downloads all or a portion of the specified log file, up to 1 MB in size

調べてみたところ, DownloadCompleteLogFile を使って REST API でのダウンロードであれば,容量制限もドキュメントに記載がなくダウンロードできそうなため,この手法を使ってみることにしました.

docs.aws.amazon.com

DownloadCompleteLogFile でのログダウンロードを試してみる

Python でログをダウンロードするコードを書いてみました.SigV4 署名での GET リクエストをダウンロードしたい DB インスタンスのログごとに実行します.そのため,download_rds_logfile 関数を実行する時に対象の DB インスタンスとファイルを get_db_instance_identifiersget_log_file_names_from_aurora で抽出しています.また,デフォルトだとログファイル名を1000件しか取得できないため,Pagenation で全件取得するようにしています.ログダウンロードのリクエストを投げた後に tempfile に書き出しており,このファイルをそのまま S3 にアップロードしたりもできます.

import boto3
from botocore.awsrequest import AWSRequest
import botocore.auth as auth
import requests
import tempfile

region = 'ap-northeast-1'
session = boto3.session.Session()
rds_endpoint = 'rds.' + region + '.amazonaws.com'
rds = boto3.client('rds')
parser = argparse.ArgumentParser()
parser.add_argument('aurora_cluster_name', type=str)
args = parser.parse_args()
aurora_cluster_name = args.aurora_cluster_name

def get_db_instance_identifiers(aurora_cluster_name: str) -> list[str]:
    target_db_instance_identifiers = []
    response = rds.describe_db_clusters(DBClusterIdentifier=aurora_cluster_name)
    members = response['DBClusters'][0]['DBClusterMembers']
    for member in members:
        target_db_instance_identifiers.append(member['DBInstanceIdentifier'])
    return target_db_instance_identifiers

def get_log_file_names_from_aurora(db_instance_identifier: str) -> list[str]:
    download_log_files = []
    describe_db_log_files_pagenator = rds.get_paginator('describe_db_log_files')
    describe_db_log_files_page_iterator = describe_db_log_files_pagenator.paginate(DBInstanceIdentifier = db_instance_identifier)
    for describe_db_log_files_page in describe_db_log_files_page_iterator:
        for describe_db_log_file in describe_db_log_files_page['DescribeDBLogFiles']:
            download_log_files.append(describe_db_log_file['LogFileName'])

def download_rds_logfile(db_instance_identifier:str, log_file_name:str, region:str, rds_endpoint:str) -> str:
    download_complete_logfile_url = 'https://' + rds_endpoint + '/v13/downloadCompleteLogFile/' + db_instance_identifier + '/' + log_file_name
    credential = session.get_credentials()
    awsreq = AWSRequest(method = 'GET', url = download_complete_logfile_url)
    sigv4auth = auth.SigV4Auth(credentials, 'rds', region)
    sigv4auth.add_auth(awsreq)
    res = requests.get(download_complete_logfile_url, stream=True, headers={
            'Authorization': awsreq.headers['Authorization'],
            'X-Amz-Date': awsreq.context['timestamp'],
            'X-Amz-Security-Token': credential.token
    })
    log_file_content = res.text
    res.close
    return log_file_content

def main():
    db_instance_identifiers = get_db_instance_identifiers(aurora_cluster_name)
    for db_instance_identifier in db_instance_identifiers:
        log_file_names = get_log_file_names_from_aurora(db_instance_identifier)
        for log_file_name in log_file_names:
            with download_rds_logfile(db_instance_identifier, log_file_name, region, rds_endpoint) as log_file_content:
                with tempfile.NamedTemporaryFile(dir=".",mode='w') as fp:
                    fp.write(log_file_content)
                    fp.seek(0)
 
if __name__ == '__main__':
    main()

まとめ

Python で Aurora の各種ログをダウンロードする処理を書く経験したのでまとめました.