Insight Technology

2021.12.16

ロールが設定された EC2 インスタンスで DownloadCompleteDBLogFile を用いて RDS からログをダウンロードする

このエントリーをはてなブックマークに追加

こんにちは。インサイトテクノロジーの松尾です!

12月になり最高気温がほぼ0度となる日も増えてきました。とはいえ、つい最近家族から、「ダウンはまだ早い!」と怒られました!(着てますが)

今回のブログでは、必要な IAM ロールが設定された EC2 インスタンスで、アクセスキーやシークレットアクセスキーを用いずに DownloadCompleteDBLogFile を用いて Amazon RDS からログをダウンロードする方法を紹介します。言語は Python を使用します。

アクセスキーやシークレットアクセスキーを用いたサンプルは各所で紹介されておりますが、通常 EC2 上で AWS に対する処理をする場合は、固定のアクセスキーとシークレットアクセスキーを使用せずに EC2 に設定した IAM ロールを利用することが望ましいとされており、DownloadCompleteDBLogFile を利用する際もできればアクセスキーを使いたくない、と思ったのが、ことの発端です。

DownloadCompleteDBLogFile とは

DownloadCompleteDBLogFile は Amazon RDS からデータベースのログファイルをダウンロードする API の一つです。

Amazon RDS の API ではログファイルをダウンロードする API として DownloadDBLogFilePortionDownloadCompleteDBLogFile とがあります。前者は AWS の API リファレンスにも載っていますが、後者のDownloadCompleteDBLogFile はリファレンスへの記載がありません。ただ DownloadCompleteDBLogFile も前述の AWS のドキュメントに利用例が記載されているなど、ログ取得に利用可能な API とはされているようです。

機能的には、DownloadDBLogFilePortion は差分のダウンロードも可能とのことで、一見便利そうなのですが、一つ、日本の利用者にとって大きな問題があります。日本語がログに入っていた場合に文字化けしてしまうのです。このことから、特に日本では、やむを得ず DownloadCompleteDBLogFile を利用されている方も多いのではないでしょうか。

DownloadCompleteDBLogFile を使ってログをダウンロードするには

今回使う Python では、AWS を操作する SDK として boto3 があります。

しかし DownloadCompleteDBLogFile は API リファレンスにも載っていないこともそうですが、”正式な” API ではないのか、boto3 や aws cli でも対応していないため、自力で REST API を呼び出して実行する必要があるようです。またその際にヘッダーに認証情報を付与する必要があり、このプロセスが、単純に boto3 を使用することに比べると若干ですが煩雑です。

Python で DownloadCompleteDBLogFile を使ってログをダウンロード!

ではやってみましょう。

RDS for MySQL のログ出力設定

以降の例では、MySQL の一般ログを例に紹介します。まず最初にログファイルにクエリー情報が出力されるよう設定します。ログが出力されると、AWS コンソールからもログ出力が行われていることを確認できます。

IAM ロールの設定

ログをプログラムから取得するには、以下のような IAM ポリシーが必要となります( DescribeDBLogFiles はファイルの一覧を取得するのに使用)。なお、以前は rds:DownloadCompleteDBLogFile は json を直接修正して追加する必要がありましたが、最近、ビジュアルエディタ上で選択可能になったようです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "rds:DescribeDBLogFiles",
                "rds:DownloadCompleteDBLogFile"
            ],
            "Resource": [
                "arn:aws:rds:*:1234567890:db:*"
            ]
        }
    ]
}

この作成したポリシーから EC2 用の IAM ロールを作成し、EC2 インスタンスにロールを設定します。

Python処理の実装

呼び出す REST API は参考ドキュメントにあるように、以下の形式となります。

GET /v13/downloadCompleteLogFile/DBInstanceIdentifier/LogFileName HTTP/1.1
Content-type: application/json
host: rds.region.amazonaws.com

この呼び出し先に対して、以下の参考ドキュメントにあるような署名を付与して呼び出すことになりますが、参考ドキュメントにある例では、アクセスキーとシークレットアクセスキーがあることが前提です。

EC2 にロールが付与されている場合、以下の参考ドキュメントのように「一時的な認証情報」を利用する必要があるようで、X-Amz-Security-Token というパラメータをヘッダー情報に付加します。これがポイントです。

では、これらをもとに実装してみましょう!

以下では、Amazon RDS for MySQLから一般ログ ( General Log )を、ファイル名決め打ちでダウンロードしています。

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

region = 'ap-northeast-1'
instance_id = 'rds-instance-name'

file_name = 'general/mysql-general.log'
remote_host = 'rds.' + region + '.amazonaws.com'
url = 'https://' + remote_host + '/v13/downloadCompleteLogFile/' + instance_id + '/' + file_name

session = boto3.session.Session()
credentials = session.get_credentials()
sigv4auth = auth.SigV4Auth(credentials, 'rds', region)

awsreq = AWSRequest(method = 'GET', url = url)
sigv4auth.add_auth(awsreq)

res = requests.get(url, stream=True, headers={
        'Authorization': awsreq.headers['Authorization'],
        'X-Amz-Date': awsreq.context['timestamp'],
        'X-Amz-Security-Token': credentials.token
    })

if (res.status_code < 200 or res.status_code >= 300):
    print('http status-error : ' + str(res.status_code));
    res.close()
    sys.exit()

f = open('out.log', mode='w', encoding='utf-8')
for chunk in res.iter_lines():
    wstr = chunk.decode("utf-8")
    wstr += '\n';
    f.write(wstr)

res.close()
f.close()

本コードを実行すると general.log の内容が out.log という名前でダウンロードされることを確認できます。Amazon RDS のログは、AWSコンソールからもダウンロードできるので、正しいファイルがダウンロードされているかをぜひ確認してみてください。

また、インスタンスを指定して、全ログファイルを取得したい、というケースもあるかもしれません。その場合は、boto3 でまずファイルの一覧を取得し、それぞれに対してダウンロードを行いましょう。以下のコードはログファイル名を単に出力しています。

import boto3

region = 'ap-northeast-1'
instance_id = 'rds-instance-name'

session = boto3.session.Session()
rds_client = session.client('rds', region_name=region)
response = rds_client.describe_db_log_files(DBInstanceIdentifier = instance_id)
for file in response["DescribeDBLogFiles"]:
    file_name = file["LogFileName"]
    print(file_name)

最後に、上記二つのソースコードを組み合わせて、全ファイルをダウンロードします!
たとえばこんな感じでしょうか。

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

region = 'ap-northeast-1'
instance_id = 'rds-instance-name'

def get_file_names_from_rds():
    session = boto3.session.Session()
    rds_client = session.client('rds', region_name=region)
    response = rds_client.describe_db_log_files(DBInstanceIdentifier = instance_id)
    return [file["LogFileName"] for file in response["DescribeDBLogFiles"]]

def download_log_file_from_rds(file_name):
    remote_host = 'rds.' + region + '.amazonaws.com'
    url = 'https://' + remote_host + '/v13/downloadCompleteLogFile/' + instance_id + '/' + file_name

    session = boto3.session.Session()
    credentials = session.get_credentials()
    sigv4auth = auth.SigV4Auth(credentials, 'rds', region)

    awsreq = AWSRequest(method = 'GET', url = url)
    sigv4auth.add_auth(awsreq)

    res = requests.get(url, stream=True, headers={
            'Authorization': awsreq.headers['Authorization'],
            'X-Amz-Date': awsreq.context['timestamp'],
            'X-Amz-Security-Token': credentials.token
        })

    if (res.status_code < 200 or res.status_code >= 300):
        print('http status-error : ' + str(res.status_code));
        res.close()
        return

    out_file_name = file_name.replace('/','-')
    f = open(out_file_name, mode='w', encoding='utf-8')
    for chunk in res.iter_lines():
        wstr = chunk.decode("utf-8")
        wstr += '\n';
        f.write(wstr)
    f.close()

    res.close()

file_names = get_file_names_from_rds()
for file_name in file_names:
    print(file_name)
    download_log_file_from_rds(file_name)

ダウンロードできましたか?

おわりに

今回はのブログでは、

  • 必要なロールが設定された EC2 インスタンスで
  • アクセスキーやシークレットアクセスキーを用いずに
  • Python を使って
  • DownloadCompleteDBLogFile を用いて
  • Amazon RDS からログをダウンロード

する方法を紹介しました。何かのお役に立てば幸いです。

開発メンバー募集中デス。

インサイトテクノロジーにご興味を持たれたエンジニアの方、ぜひご連絡をお待ちしております♪

ページトップへ