こんにちは、サーバーワークス 技術1課の三井です。 ご存知の方も多いとは思いますが、RDSにはスナップショットを異なるリージョンにコピーする機能があります。
予めスナップショットを別リージョンにコピーしておいて、プライマリリージョンが利用不可能になるような障害が起こった場合に、コピー先リージョンでスナップショットからRDSをリストアする、といったディザスタリカバリ用途での活用が期待されています。 さて、コピー先リージョンでRDSスナップショットをリストアするためには事前にいくつか準備が必要になります。最低限必要なところだと、
- セキュリティグループの作成
- DBサブネット・サブネットグループの作成
- DBパラメータグループの作成
- DBオプショングループの作成
といったところでしょうか。これらは作成したリージョンに固有のものですので、リストア前に形だけでも作っておく必要があります。
DBパラメータグループはリージョン間コピーできません、でもコピーしたい
いざコピー先リージョンでスナップショットからリストアする、というときにパラメータの差異があるのは具合が悪いです。アプリケーションが想定外の挙動をしかねないですし、パフォーマンスの問題にも直結します。なので基本的に、DBパラメータグループはコピー元リージョンとコピー先リージョンとで同じになるよう設定しておく必要があります。とはいえ設定項目数も多いので、手で設定するのはしんどいです。 というわけで、DBパラメータグループもリージョン間コピーしたいよね、というのが今回のざっくりした目的です。 本日時点ではまだ、標準機能として「DBパラメータグループのコピー」という機能は実装されていません。これをどうにかして実現する方法を考えます。
どうやるか
目diff
職人が磨きあげた勘を駆使し、熟練の目つきでRDS Management Consoleの画面と睨めっこする、トラディショナルなアプローチです。「手で設定するのはしんどいです」って言っておいてこれかよ、っていう。
MySQL5.6の場合、Management Console上で編集できるパラメータは288個。
1項目の比較を取るのに5秒かかると仮定すると、288 x 5 = 1440、実に24分もかかることになります。しんどいです。
職人の神経もそれなりにすり減りますし、パラメータグループを変更するたびにこの不毛極まりない作業が発生するのかと思うと気が遠なってくるのでこのへんで考えるのをやめておきます。
AWS CLIを使う
結局これが書きたかっただけなんですけどね。AWS CLIでサラッとやります。やることはシンプルで、
1. コピー元DBパラメータグループからユーザ定義パラメータを出力
2. 出力したデータを元に、コピー先DBパラメータグループの設定変更
と、これだけです。以前弊社ブログでも紹介したAWS CLIのオプションをフル活用して実現してみます。
AWS CLIでDBパラメータグループのコピー(っぽいこと)をする
今回の検証用に、orig-param というMySQL5.6用のDBパラメータグループを作成して、ざっくりと以下のような設定変更を行いました。
character_set_server = utf8 collation_server = utf8_general_ci general_log = TRUE(1) slow_query_log = TRUE(1) long_query_time= 30
試しに1つパラメータ取ってみます。
aws rds describe-db-parameters --profile mydev --db-parameter-group-name orig-param --query "Parameters[?ParameterName==`character_set_server`]"
レスポンスは以下の通り。
[ { "Description": "The server's default character set.", "DataType": "string", "IsModifiable": true, "AllowedValues": "big5,dec8,cp850,hp8,koi8r,latin1,latin2,swe7,ascii,ujis,sjis,hebrew,tis620,euckr,koi8u,gb2312,greek,cp1250,gbk,latin5,armscii8,utf8,ucs2,cp866,keybcs2,macce,macroman,cp852,latin7,utf8mb4,cp1251,utf16,cp1256,cp1257,utf32,binary,geostd8,cp932,eucjpms", "Source": "user", "ParameterValue": "utf8", "ParameterName": "character_set_server", "ApplyType": "dynamic" } ]
このJSON構造をふわっと覚えておきます。 DBパラメータグループの修正は modify-db-parameter-group サブコマンドで行います。
--cli-input-json を使って一気にJSONを流し込めそうな気がするので、まずは構造を確認すべく --generate-cli-skelton します。
aws rds modify-db-parameter-group --generate-cli-skeleton
以下のスケルトンが標準出力に書き出されます。
{ "DBParameterGroupName": "", "Parameters": [ { "ParameterName": "", "ParameterValue": "", "Description": "", "Source": "", "ApplyType": "", "DataType": "", "AllowedValues": "", "IsModifiable": true, "MinimumEngineVersion": "", "ApplyMethod": "" } ] }
見比べてみると、Parameters 配列の中のオブジェクトはほぼ同じですね。
describe-db-parameters が出力するJSONをほとんど加工せずに食わせられそうです。 差分は MinimumEngineVersion, ApplyMethod。
ApplyMethod は「変更をすぐにRDSインスタンスに適用するか (immediate)、再起動後まで待つか (pending-reboot)」を指定する項目で、パラメータグループの設定変更時には指定必須の項目です。本来なら関連付けられているRDSインスタンスへの影響を考慮して慎重に設定しますが、今回は「とりあえずDBパラメータグループをコピーする」という用途しか考えていないので、RDSインスタンスに考慮せず即時適用 (immediate) ということにします。
コピー元DBパラメータグループの情報を出力
というわけで、まずは元のDBパラメータグループを適当なファイルに出力します。
全部のパラメータを書き出すと結構なボリュームになって無駄が多いので、--source user と指定して「ユーザが明示的に変更したパラメータ」だけを出力するように絞った上で、実行結果を mysqlparams.json というファイルに書き出しています。
aws rds describe-db-parameters --profile mydev --db-parameter-group-name orig-param --source user > mysqlparams.json
先ほど確認しましたが、改めてきちんと中身を見て全体の構造を確認します。
{ "Parameters": [ { "Description": "The server's default character set.", "DataType": "string", "IsModifiable": true, "AllowedValues": "big5,dec8,cp850,hp8,koi8r,latin1,latin2,swe7,ascii,ujis,sjis,hebrew,tis620,euckr,koi8u,gb2312,greek,cp1250,gbk,latin5,armscii8,utf8,ucs2,cp866,keybcs2,macce,macroman,cp852,latin7,utf8mb4,cp1251,utf16,cp1256,cp1257,utf32,binary,geostd8,cp932,eucjpms", "Source": "user", "ParameterValue": "utf8", "ParameterName": "character_set_server", "ApplyType": "dynamic" }, (以下略)
先述の必須パラメータ、ApplyMethod は存在しないので、ここはひとまずjqでゴニョゴニョして書き足してやります。
cat mysqlparams.json | jq '{Parameters:[ (.Parameters[] * {"ApplyMethod":"immediate"})]}' | cat > mysqlparams.json
一応確認しときますか。
{ "Parameters": [ { "Description": "The server's default character set.", "DataType": "string", "IsModifiable": true, "AllowedValues": "big5,dec8,cp850,hp8,koi8r,latin1,latin2,swe7,ascii,ujis,sjis,hebrew,tis620,euckr,koi8u,gb2312,greek,cp1250,gbk,latin5,armscii8,utf8,ucs2,cp866,keybcs2,macce,macroman,cp852,latin7,utf8mb4,cp1251,utf16,cp1256,cp1257,utf32,binary,geostd8,cp932,eucjpms", "Source": "user", "ParameterValue": "utf8", "ParameterName": "character_set_server", "ApplyType": "dynamic", "ApplyMethod": "immediate" }, (以下略)
"ApplyMethod"が追記されていればOK。
コピー先の器となるDBパラメータグループを作る
初回のみ、コピー先のDBパラメータグループを作成します。すでにコピー先となるDBパラメータグループが存在する場合には不要。
aws rds create-db-parameter-group --profile mydev --db-parameter-group-family MySQL5.6 --db-parameter-group-name dest-param --description "This is destination parameter group" --region us-east-1
今回使っているprofile mydevはデフォルトリージョンが ap-northeast-1 なので、コピー先は us-east-1 を明示的に指定して、dest-param というDBパラメータグループを作成しました。
DBパラメータグループ間でパラメータコピーするだけなので、別にリージョン変える必要はないんですが、どうせなら「東京からヴァージニアにコピーしたったで!」の方が盛り上がるし、そもそも冒頭でリージョン間コピーやりたい、って書いちゃったから引き下がれない感じです。それだけの理由です。
パラメータ設定の投入
--cli-input-json の引数に先ほど書き出したJSONファイルを指定し、modify-db-parameter-group を実行します。
aws rds modify-db-parameter-group --profile mydev --region us-east-1 --db-parameter-group-name dest-param --cli-input-json file://mysqlparams.json
正常にリクエストが完了するとDBパラメータグループ名が返ってきます。
{ "DBParameterGroupName": "dest-param" }
念のため、コピー元である orig-param と、us-east-1 にコピーした dest-param の内容が同一かどうかをdiffコマンドで確認してみます。毎度やる必要はないですが、不安な人のために。
diff -s <(aws rds describe-db-parameters --profile mydev --db-parameter-group-name orig-param) <(aws rds describe-db-parameters --profile mydev --region us-east-1 --db-parameter-group-name dest-param)
以下のようにidenticalと表示されていればOK。
Files /dev/fd/11 and /dev/fd/12 are identical
さいごに
ちょっと無理矢理ではありますが、DBパラメータグループのリージョン間コピーができました。 DBパラメータグループのコピーだけに限らず、やりたいと思ったことがAWS側の標準APIとして提供されていないケースもまだあります。が、ひと工夫して既存のAPIをうまいこと使えば大抵のことは出来そうな気がしています。 AWS側でも日々エンハンスメントが行われていますが、それでも足りないところはユーザの工夫次第でいくらでも補える、これはAWSの大きな魅力のひとつかなと思っています。