EC2インスタンスを毎日自動バックアップする処理をAWS SDK for PHP 2で作成する

* インスタンスのバックアップ(AMI)を自動で1日1回作りたい。
* 任意の時間にcrontabで１回実行しよう。
* 保存期間を3日間～7日間、または任意の日数保存しておきたい。
* 古いAMIは自動で削除したい。AMIとスナップショットを両方消す必要がある。タグAutoCreatedに作成日を付けて取得しよう。
* バックアップしたいEC2インスタンスにタグDailyBackupを付けて制御したい。

# いきなりプログラムをコピペする

```php:create_image.php
<?php

// Include the SDK using the Composer autoloader
require 'vendor/autoload.php';

use Aws\Ec2\Ec2Client;

$today = date('Y-m-d H:i:s');
echo "---BEGIN--- Create Image {$today}\n";

# バックアップの保存日数
$delete_date = date('Y-m-d', strtotime('- 3 day'));
define('DELETE_DATE', $delete_date);

# ap-northeast-1    アジアパシフィック（東京）
# ap-southeast-1    アジアパシフィック (シンガポール)
# ap-southeast-2    アジアパシフィック（シドニー）
# eu-central-1      欧州 (フランクフルト)
# eu-west-1         欧州 (アイルランド)
# sa-east-1         南米（サンパウロ）
# us-east-1         US East (N. Virginia)
# us-west-1         米国西部（北カリフォルニア）
# us-west-2         米国西部（オレゴン）
# 処理実行するリージョン
# eu-central-1      欧州 (フランクフルト) ここは有効領域でないので外すこと
$regions = [
    'ap-northeast-1',
    'ap-southeast-1',
    'ap-southeast-2',
    'eu-west-1',
    'sa-east-1',
    'us-east-1',
    'us-west-1',
    'us-west-2',
];

# 各リージョン毎にバックアップ
$return = array_map('backup', $regions);
# 各リージョン毎に保存期間の過ぎたAMIとSnapshotを消す
$return = array_map('delete_images', $regions);

function backup($region)
{
    echo "---$region BIGIN--- Auto Backup\n";

    # クライアント作成
    $ec2client = Ec2Client::factory(['region' => $region]);

    # 対象instance_ids取得
    $target_instances = $ec2client->describeInstances(
        [
            'Filters' => [
                [
                    'Name' => 'tag:DailyBackup',
                    'Values' => ['true'],
                ],
            ],
        ]
    );

    $target_ids = $target_instances->getPath('Reservations/*/Instances/0/InstanceId');

    # id無いなら処理終了
    if (empty($target_ids))
    {
        echo "---$region FINISH--- Target insetances is empty. Not back up anything.\n";

        return;
    }

    # AMIをinstance_ids分作る。
    foreach ($target_ids as $id)
    {
        $result = create_image($ec2client, $id);
        $image_id = $result['ImageId'];

        # Imageが作成されるまで少し待つ。sleep無いと、タグが付かない事があった
        sleep(1);
        $result = add_tag($ec2client, $image_id, 'AutoCreated', date('Y-m-d'));

        $image_info = $ec2client->describeImages(
            [
                'ImageIds' => [$image_id],
            ]
        );
        $snapshot_id = $image_info->getPath('Images/0/BlockDeviceMappings/0/Ebs/SnapshotId');
        $result = add_tag($ec2client, $snapshot_id, 'AutoCreated', date('Y-m-d'));
        echo "instance_id: $id AMI: $image_id Snapshot_id: $snapshot_id \n";
    }
    echo "---$region FINISH--- Auto Backup\n";
}

function create_image($ec2client, $instance_id)
{
    $result = $ec2client->createImage(
        [
            'InstanceId' => $instance_id,
            'Name' => $instance_id . '_autobackup_' . date('Y-m-d-H-i-s'),
            'Description' => 'autobackup create' . date('Y-m-d-H-i-s'),
            'NoReboot' => true,
        ]
    );

    return $result;
}

function add_tag($ec2client, $resource_id, $key, $value)
{
    $result = $ec2client->createTags(
        [
            'Resources' => [$resource_id],
            'Tags' => [
                [
                    'Key' => $key,
                    'Value' => $value,
                ],
            ],
        ]
    );

    return $result;
}

function delete_images($region)
{
    echo "---$region BIGIN--- Delete AMI and SnapShot\n";

    # クライアント作成
    $ec2client = Ec2Client::factory(['region' => $region]);

    # 削除対象のIDを取得する
    list($image_ids, $snapshot_ids) = get_delete_image_ids($ec2client);

    # IDが無いなら処理終了
    if (empty($image_ids))
    {
        echo "---$region FINISH--- Image ids is empty. Not delete anything\n";

        return;
    }

    # AMIを削除する
    $result = deregister_images($ec2client, $image_ids);

    # SnapShotを削除する
    $result = delete_snapshot($ec2client, $snapshot_ids);

    echo "---$region FINISH--- Delete AMI and SnapShot\n";
}

function get_delete_image_ids($ec2client)
{
    $result = $ec2client->describeImages([
        'Filters' => [
            [
                'Name' => 'tag:AutoCreated',
                'Values' => [DELETE_DATE],
            ],
        ],
    ]);

    # image_idを取得するついでにSnapshot_idも取得できるので取得する
    $image_ids = $result->getPath('Images/*/ImageId');
    $snapshot_ids = $result->getPath('Images/*/BlockDeviceMappings/0/Ebs/SnapshotId');

    return [$image_ids, $snapshot_ids];
}

function deregister_images($ec2client, $image_ids)
{
    foreach ($image_ids as $id)
    {
        $result = $ec2client->deregisterImage(
            [
                'ImageId' => $id,
            ]
        );
        echo 'deregister Image: ', $id, "\n";
    }

    return $result;
}

function delete_snapshot($ec2client, $snapshot_ids)
{
    foreach ($snapshot_ids as $id)
    {
        $result = $ec2client->deleteSnapshot(
            [
                'SnapshotId' => $id,
            ]
        );
        echo 'delete snapshot:  ', $id, "\n";
    }

    return $result;
}

echo "---END--- Create Image \n";

```

# 実行

* タグDailyBackup:trueのインスタンスがバックアップされる
* タグAutoCreated：YY-mm-ddとついていて、３日前のAMIと対応するSnapShotが削除される。

```
$ php create_image.php
---BEGIN--- Create Image 2015-04-30 16:00:11
---sa-east-1 BIGIN--- Auto Backup
---sa-east-1 FINISH--- Target insetances is empty. Not back up anything.
---us-east-1 BIGIN--- Auto Backup
instance_id: i-e74019b7 AMI: ami-82e5eaea Snapshot_id: snap-c03c2559
instance_id: i-ac737157 AMI: ami-66e4eb0e Snapshot_id: snap-183e2781
---us-east-1 FINISH--- Auto Backup
---sa-east-1 BIGIN--- Delete AMI and SnapShot
---sa-east-1 FINISH--- Image ids is empty. Not delete anything
---us-east-1 BIGIN--- Delete AMI and SnapShot
---us-east-1 FINISH--- Image ids is empty. Not delete anything
---END--- Create Image
```

## Crontabで１日一回 自動実行させる

```file:crontab
SHELL=/bin/bash
# root PATHを設定
PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/opt/aws/bin:/home/ec2-user/bin:/opt/aws/bin
# mailを送信したくない時
MAILTO=""
AWS=/home/ec2-user/aws-php

# 時間はJSTかUSTかシステムに合わせる。今回はJST。 ログはmessageに吐き出す。
# EC2インスタンスを業務時間だけ自動起動する処理
27 8 * * 1-5 php $AWS/ec2_start.php  2>&1 | logger -t AWS_SDK_AUTO -p local0.info
# EC2インスタンスを毎日自動バックアップする処理
21 8 * * * php $AWS/create_image.php  2>&1 | logger -t AWS_SDK_AUTO -p local0.info
```

## 関連するページ

[EC2インスタンスを業務時間だけ自動起動する処理をAWS SDK for PHP 2で作成する](http://qiita.com/shojimotio/items/0fe9e81da40524ffafc0)
[自動起動処理するEC2インスタンス自身をオートスケールで1時間だけ起動する](http://qiita.com/shojimotio/items/081fb7c21dee029137a0)