AWS Database Migration Serviceを使ってオンプレミスのMySQLをAmazon Auroraに移行する

AWS Database Migration Serviceはデータベースの移行支援サービスです。
今回は、オンプレミスで稼働するMySQLからAWS上のAmazon Aurora MySQLに移行した際に利用しました。

MySQLのバージョンに制約があります。今回は条件を満たしていなかったのでオンプレミスのMySQLバージョンアップを行い利用可能にしました。

MySQL バージョン 5.5、5.6、5.7、8.0

MySQL 互換データベースの AWS DMS のソースとしての使用 - AWS Database Migration Service

Black Belt Online


必要なもの

データを移行する際は、データ移行タスクを作成することになりますがその中で下記リソースが必要になるので事前に作成が必要です。

  • レプリケーションインスタンス
  • ソースデータベースエンドポイント
  • ターゲットデータベースエンドポイント

また、レプリケーションインスタンスはサブネットグループに配置します。
デフォルトでも用意されていますが必要であればサブネットグループも事前に作成が必要です。

レプリケーションインスタンス

レプリケーションインスタンスは、ソースデータベースから移行対象のデータを取得してターゲットデータベースのエンジンに合わせてデータを変換してからターゲットデータベースにデータを流します。
レプリケーションインスタンスを作成する際は、インスタンスクラスを設定することになりますが移行対象のデータ量によって要求スペックがかなり変わってきます。
レプリケーションインスタンスのモニタリングをしつつ、必要であればインスタンスタイプを上げるなどの対応が必要になります。

また、複数のデータ移行タスクを1つのレプリケーションインスタンスで行うことも可能です。

ソース、ターゲットデータベースエンドポイント

名前の通り移行元及び移行先のデータベースの情報を設定します。
また、エンドポイントでは追加の接続設定が可能です。主にタイムゾーンの問題で利用する機会がありました。後述します。

データベース移行タスク

データベース移行タスクでは、前述のレプリケーションインスタンス、ソースデータベースエンドポイント、ターゲットデータベースエンドポイントを指定した上で移行タイプについて設定していきます。

移行タイプ

  1. 既存のデータを移行する
  2. 既存のデータを移行して、継続的な変更をレプリケートする
  3. データ変更のみをレプリケートする

上記は、マネジメントコンソール上の日本語表記になりますが

  • 「既存のデータを移行する」->「Full load」
  • 「変更をレプリケート」->「CDC」(Change Data Capture)

英語表記の方が使いやすいのでそちらで呼称していましたし、以後の記載もそうします。

  1. Full load
  2. Full load + CDC
  3. CDC
タイムゾーン問題

移行タイプ決定の際にタイムゾーンの問題があり選択肢が限定されたのでまずその事象を記載します。

前提

データベース ソース ターゲット
タイムゾーン Asia/Tokyo Asia/Tokyo

結果
ターゲットにUTCの時刻が登録されてしまう(9時間前の時間)

Stack Overflowを参考にFull loadの場合以下設定で解決しました。

ソースエンドポイント追加属性

serverTimezone=Asia/Tokyo;


ターゲットエンドポイント追加属性

initstmt=SET time_zone='Asia/Tokyo'

ただCDCの場合これでは解決しません。

AWS DMS(Data Migration Service)の CDC(Change Data Capture)で MySQL(RDS / Aurora MySQL を含む)から MySQL(同)にデータをレプリケーションで移行するときは、 ソースエンドポイントのタイムゾーンを設定しない

小ネタ/AWS DMS(CDC)で MySQL to MySQL 移行時のタイムゾーン指定

感謝。つまりCDCの場合は下記のみ設定する必要があります。
ターゲットエンドポイント追加属性

initstmt=SET time_zone='Asia/Tokyo'

以上のことからFull loadとCDCで別のエンドポイントを使用しなければならず、データベース移行タスクも必然的に分けることになるのでFull load+CDCは使用できないという結果になりました。

Full load 移行タスク

Full loadは、既存のデータを移行してくれるものでMySQLでいうとmysqldumpをrestoreすることに近いです。
ただ、Full loadにもかなり注意しなければならない点があり最終的には不採用になりました。

1番影響が大きかったのは下記のセカンダリインデックスが作成されない点です。

ただし、AWS DMS では、ターゲットデータベースのセカンダリインデックス、外部キー、ユーザーアカウントなどは自動的に作成されません。

AWS Database Migration Service のベストプラクティス - AWS Database Migration Service

当然のことながらデータベースを運用する中で、複数のテーブルで様々なインデックスを作成しておりますが
それが移行されないのはかなり苦しく初期データに関してはFull loadを利用せずmysqldumpをrestoreする形で移行することにしました。

CDC 移行タスク

CDCは、データの変更があった際にその変更をターゲット側にも適応してくれるもので所謂レプリケーションです。
バイナリログファイルの位置ベースのレプリケーションで利用しているバイナリログをDMSで利用することで実現するので、ソースデータベースの設定ファイルに設定が必要です。

server-id レプリケーショントポロジ内で一意な自然数
log-bin バイナリログファイル名
binlog_format ROW
expire_logs_days 1以上
binlog_checksum NONE
binlog_row_image FULL
log_slave_updates レプリカをソースにしたい場合はTRUE

log_slave_updatesについては、オンプレミスでのレプリケーションでのレプリカでDMSとレプリケーションしたほうがオンプレミスへの影響が少ないので今回は有効にしてやりました。

あとは、CDC開始ポイントをオンプレミスのレプリカから取得して設定すればCDCに関する設定は完了です。

テーブルマッピングによりCDC対象のスキーマとテーブルを絞ることができます。ここでは割愛します。

まとめ

全体的に気にしなければならないことが多いのですが、それでもマネジメントコンソール上でCDCのON/OFFが簡単に切り替えられたり、
網羅的に状況を見れるという点では使ってよかったです。

AWS認定SysOpsアドミニストレーター アソシエイト合格しました

クラウドプラクティショナーとソリューションアーキテクトアソシエイト取得から少し間が空いてしまったが無事合格できた。

他の資格の記事

これまでオンライン受験しかしたことなかったが、コロナ禍で休業していたテストセンターが再開したこともあってテストセンターで受験した。
注意する点としては、身分証明書が2セット必要。ピアソンのページで解説されているので確認しておくこと。

教材

まず、このテキストを1周。やはり実務で触ってないところは全くわからなくて練習問題でもかなり間違えた。

Udemy 【SOA-C02版】AWS 認定SysOpsアドミニストレーター アソシエイト模擬試験問題集(全4回分294問)

現在は配信されていないコースのようだが購入済みだったので活用させていただいた。
全4回のテスト全てで初回は50%くらいしか正解することができなくてかなり厳しかった。
2回目やった時は80%くらい取れたが、原理を理解しているというよりは問題で記憶してしまっている感が強くあり不安が残る。

最後にこのテキスト。模擬問題でわからなかったところを重点的に調べながら1周した。
SBクリエイティブのテキストと比べて解説の部分は似通っているが問題の部分は結構違ってて2冊触ってよかった感じ。

AWS Skill Builder AWS Certified SysOps Administrator - Associate 公式練習問題集

公式でも模擬試験を提供していて無料で20問受けることができた。
この試験で70%の点数が取れてようやく合格ラインにたどり着いたがボーダーライン上なので全く安心できず。


あとは間違えた点だけまとめたメモを作って直前はひたすらそれを見ていた。
予想以上に難しかった。次はDVAかSAP!

AWS FirelensでCloudWatch Logsにログを送信する

FirelensでFluentBitを用いてCloudWatch Logsにログを送る場合にはFluentBitのプラグインが利用可能です。

利用方法は簡単でOUTPUTセクションでNameにcloudwatchを指定して必要なパラメータを設定すればいいです。

[OUTPUT]
    Name cloudwatch
    Match   *
    region us-east-1
    log_group_name fluent-bit-cloudwatch-$(uuid)-$(tag)
    log_stream_name from-fluent-bit-$(pam['item2']['subitem'])-$(ecs_task_id)-$(ecs_cluster)
    auto_create_group true
https://github.com/aws/amazon-cloudwatch-logs-for-fluent-bit

ただ、aws-for-fluent-bit 2.29.0を利用した際に下記エラーに遭遇しました。

plugin 'cloudwatch' failed to initialize

調査したところcloudwatchというプラグインは旧プラグインで新しいプラグインはcloudwatch_logsという名称で提供されているようです。

cloudwatch_logsに変えるだけでOKとならない部分があります。
log_group_nameやlog_stream_nameで変数展開する機能が新プラグインでは下位互換性がない状態です。

READMEに旧プラグインについて記載があります。

Do you plan to deprecate this older plugin?
At this time, we do not. This plugin will continue to be supported. It contains features that have not been ported to the higher performance version. Specifically, the feature for templating of log group name and streams with ECS Metadata or values in the logs. While simple templating support now exists in the high performance plugin, it does not have all of the features of the plugin in this repo. Some users will continue to need the features in this repo.

https://github.com/aws/amazon-cloudwatch-logs-for-fluent-bit

ロググループ名、ログストリーム名についてECSのメタデータやログ内のデータを使ってテンプレート化する機能が含まれていないとのこと。
このテンプレート化という部分にはリンクが貼られていて遷移先にかかれているのが

Templating Log Group and Stream Names
A template in the form of $(variable) can be set in log_group_name or log_stream_name. variable can be a map key name in the log message. To access sub-values in the map use the form $(variable['subkey']). Also, it can be replaced with special values to insert the tag, ECS metadata or a random string in the name.

https://github.com/aws/amazon-cloudwatch-logs-for-fluent-bit

まさに前述したサンプルで記述のあった内容です。

今回、ログストリーム名をログ内の値を使った名前を設定したかったので直撃しました。
cloudwatch_logsプラグインでどうにかやる方法が確認できたので記載していきます。

log_stream_prefixというパラメータがあって指定した文字列をログストリーム名のprefixにつけてくれます。

このパラメータを指定するとログストリーム名はprefix+tagで作成されることがわかりました。
つまり、必要な値があればtagの時点でつけてしまえばいいのだと考えました。

FILTERセクションでrewrite_tagをするときに正規表現で必要な値をキャプチャしてnew_tagにつけることによって実現できました。

簡単な例

[FILTER]
  Name rewrite_tag
  Match *-firelens-*
  Rule $log (.*) new_tag.$1 false