こんにちは。インサイトテクノロジーの松尾です!
12月になり最高気温がほぼ0度となる日も増えてきました。とはいえ、つい最近家族から、「ダウンはまだ早い!」と怒られました!(着てますが)
今回のブログでは、必要な IAM ロールが設定された EC2 インスタンスで、アクセスキーやシークレットアクセスキーを用いずに DownloadCompleteDBLogFile を用いて Amazon RDS からログをダウンロードする方法を紹介します。言語は Python を使用します。
アクセスキーやシークレットアクセスキーを用いたサンプルは各所で紹介されておりますが、通常 EC2 上で AWS に対する処理をする場合は、固定のアクセスキーとシークレットアクセスキーを使用せずに EC2 に設定した IAM ロールを利用することが望ましいとされており、DownloadCompleteDBLogFile を利用する際もできればアクセスキーを使いたくない、と思ったのが、ことの発端です。
DownloadCompleteDBLogFile とは
DownloadCompleteDBLogFile は Amazon RDS からデータベースのログファイルをダウンロードする API の一つです。
Amazon RDS の API ではログファイルをダウンロードする API として DownloadDBLogFilePortion
と DownloadCompleteDBLogFile
とがあります。前者は 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 からログをダウンロード
する方法を紹介しました。何かのお役に立てば幸いです。
開発メンバー募集中デス。
インサイトテクノロジーにご興味を持たれたエンジニアの方、ぜひご連絡をお待ちしております♪