読者です 読者をやめる 読者になる 読者になる

RDSの本番データをテスト環境へ自動で移行させる

こんにちは。おいかわです。
ゴルフ場web予約システムの開発・運用・保守をやってます。

最近はジョジョSSというスマホゲームにハマりまくっております。
ちなみに最近のお気に入りはガンガン闘うを装備させたジョセフ・シーザーのコンビメダル(通称ジョーザー)です!

ところで開発時やテストの段階から本番データ使ってやれると便利じゃないですか?
何より運用していて作られた実際のデータっていうのは、テストデータでは見つけられないバグを発見する手助けになりますよね。

てことで弊社では本番データをスクリプトで定期的にテスト環境に自動反映させてます。

概要

本番環境のRDSスナップショットをリストアし、テスト環境のRDSを再構築します。
この方法のメリットはmysqldumpなどを使用する方法に比べ、本番環境に全く影響も負荷も与えないところです。

環境

本番・テスト環境ともにAWSのEC2+RDSで構築してます。
また、移行スクリプトPHPで記述していますのでAWS SDK for PHPをインストールしてます。
SDKのインストールはComposerを使ったこちらが簡単でオススメです

実行スクリプト
// AWS SDK for PHPを使うための記述
require_once('vendor/autoload.php');
use Aws\Common\Aws;

// 本番環境DB(リストア元)インスタンス名
$org_DBInstanceIdentifier = 'test1';
// テスト環境(リストア先)インスタンス名
$rst_DBInstanceIdentifier = 'test2';
// VPCセキュリティグループID
$vpcSecurityGroupId = 'xx-xxxxxxx';
// Availability Zone
$availabilityZone = 'ap-northeast-1c';

// AWSへのアクセスキー
$aws = Aws::factory(array(
    'key'    => 'xxxxxxxxxxxxxxx',
    'secret' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    'region' => 'ap-northeast-1'
));


$client = $aws->get('Rds');

// スナップショットのリストを取得
$result = $client->describeDBSnapshots(array(
    'DBInstanceIdentifier' => $org_DBInstanceIdentifier
));

$compare_time = date("Y-m-d", strtotime("-1 day"));
$DBSnapshotIdentifier = "";

foreach ($result["DBSnapshots"] as $snapshot) {
    if (strtotime($snapshot['SnapshotCreateTime']) >= strtotime($compare_time)) {
    // リストから1日前のスナップショットの名前を取得
    $DBSnapshotIdentifier = $snapshot['DBSnapshotIdentifier'];
    break;
    }
}

// スナップショット名が取得できたら
if (!empty($DBSnapshotIdentifier)) {
    // テスト環境インスタンスを削除する(念のためスナップショットもとっておく)
    $del_result = $client->deleteDBInstance(array(
    'DBInstanceIdentifier' => $rst_DBInstanceIdentifier,
        'SkipFinalSnapshot' => false,
        'FinalDBSnapshotIdentifier' => 'deleteDBInstance-'.date("Y-m-d"),
    ));

    // スナップショットからリストア
    if ($del_result) {

        // 削除を待つ
        do {
            try {
                sleep(60);
                $result = $client->describeDBInstances(array(
                              'DBInstanceIdentifier' => $rst_DBInstanceIdentifier,
                                )
                            );
            } catch (Exception $e) {
                // 削除されたら例外が返るので抜ける
                break;
            }
        } while(1);

        // リストア実施
        $restore_result = $client->restoreDBInstanceFromDBSnapshot(array(
            'DBInstanceIdentifier' => $rst_DBInstanceIdentifier,
            'DBSnapshotIdentifier' => $DBSnapshotIdentifier,
            'DBInstanceClass' => 'db.t1.micro', // インスタンスサイズも小さくしておく
            'AvailabilityZone' => $availabilityZone,
            )
        );

    // 起動を待つ
        do {
            try {
                sleep(60);
                $result = $client->describeDBInstances(array(
                              'DBInstanceIdentifier' => $rst_DBInstanceIdentifier,
                            )
                        );
                if ($result['DBInstances'][0]['DBInstanceStatus'] == 'available') {
                    // 起動完了したら抜ける
                    break;
                }
            } catch (Exception $e) {
                // 起動中は例外が返る
                continue;
            }
        } while(1);

        // セキュリティグループの変更
        $client->modifyDBInstance(array(
            'DBInstanceIdentifier' => $rst_DBInstanceIdentifier,
            'VpcSecurityGroupIds' => array($vpcSecurityGroupId),
        ));
    }
}

上記を同じVPC内のEC2からCronで実行します。
常に最新である必要は無いので週一くらいですね。
あと、本番データに含まれる個人情報とか本番用の設定を書き換えるのも忘れずに!

ちなみに原作では5部が好きです。
いずれ「ブチャラティに学ぶリーダー論」とかここでアップしたいと思ってます。