シェルスクリプトでMySQLデータベースのダンプをS3にバックアップする(aws cli + mysqldump)

# 概要

以下の機能を持つシェルスクリプトの導入手順です。

- MySQLインスタンス内にある複数のデータベースのDDLとデータ(INSERT文)をテキスト形式で出力（gzip圧縮)
- 出力したファイルをS3バケットにアップロード
- 保持期間を経過したファイルは削除

実行すると以下のようにS3バケット内に日付フォルダが作成されて、その下にバックアップファイル群が格納されます。
それぞれのファイルはスクリプト内で指定した期間保持されます。

![](https://i.gyazo.com/143b5df6469757699dba0ea40f06414d.png)

# 実装した環境

- EC2 (Amazon Linux)
- MySQL 5.6.34
- aws-cli 1.15.83 
- Python 2.7.14 
- Linux 4.14.42-52.37.amzn1.x86_64 
- botocore 1.10.82

※aws-cli以降は```$ aws --version```の出力結果

# 準備するもの・使用するツール

- バックアップ用S3バケット
- バックアップ用IAMユーザ（アカウントキーとシークレットキー）
- mysqldump
- aws cli
- cron, シェルスクリプト（したがってLinux)

# 手順

## バックアップ用S3バケットの作成

バックアップ用のS3バケットを作成します。手順は公式ドキュメントを参照してください。

[S3 バケットを作成する方法 - Amazon Simple Storage Service](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/user-guide/create-bucket.html)


## バックアップ用IAMユーザの作成

バックアップバケットに限定した権限を持つポリシーを作成して、それをユーザに割り当てます。
ユーザはプログラムから利用するためにアクセスキー（とシークレットキー）を発行します。

### ポリシーの作成
AWSコンソールのサービスメニュー > IAM > ポリシー > ポリシーの作成

![](https://i.gyazo.com/765f34d567f0165aa7a1117eac025895.png)
===
![](https://i.gyazo.com/017abbca51db910f07baa8e02c0e465d.png)

**ポリシー**

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:ListAllMyBuckets"
            ],
            "Effect": "Allow",
            "Resource": "arn:aws:s3:::*"
        },
        {
            "Action": "s3:*",
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::<バックアップ用バケット名>",
                "arn:aws:s3:::<バックアップ用バケット名>/*"
            ]
        }
    ]
}
```

**ユーザ作成**
AWSコンソールのサービスメニュー > IAM > ユーザ > ユーザの作成

![](https://i.gyazo.com/f3f1987698809bc585249a488f1a3e6a.png)
===
![](https://i.gyazo.com/5858d75358b1b0973b8729c52debabd1.png)
===
![](https://i.gyazo.com/56eb527cd15fd95ddbc3b5f7dd450d71.png)

## aws cliのインストール

AWS公式サイトの手順に従ってインストールします。

[AWS Command Line Interface のインストール - AWS Command Line Interface](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/installing.html)


## mysqldump（mysql）のインストール

mysqldumpコマンドはmysqlに含まれています。
インストール方法は実行端末のOS・バージョン、使用しているMySQLバージョンにあわせて行います。

以下は当方環境（Amazon Linux)で実行したコマンドです。
リポジトリのMySQLが古いのでrpmファイルをダウンロードしてインストールしています。

```bash
# cd /usr/local/src/
# mkdir mysql-client
# cd mysql-client
# wget https://repo.mysql.com/yum/mysql-5.6-community/el/6/x86_64/mysql-community-common-5.6.34-2.el6.x86_64.rpm
# wget https://repo.mysql.com/yum/mysql-5.6-community/el/6/x86_64/mysql-community-libs-5.6.34-2.el6.x86_64.rpm
# wget https://repo.mysql.com/yum/mysql-5.6-community/el/6/x86_64/mysql-community-libs-compat-5.6.34-2.el6.x86_64.rpm
# wget https://repo.mysql.com/yum/mysql-5.6-community/el/6/x86_64/mysql-community-devel-5.6.34-2.el6.x86_64.rpm
# wget https://repo.mysql.com/yum/mysql-5.6-community/el/6/x86_64/mysql-community-client-5.6.34-2.el6.x86_64.rpm
# yum install -y mysql-community-common-5.6.34-2.el6.x86_64.rpm
# yum install -y mysql-community-libs-5.6.34-2.el6.x86_64.rpm
# yum install -y mysql-community-libs-compat-5.6.34-2.el6.x86_64.rpm
# yum install -y mysql-community-devel-5.6.34-2.el6.x86_64.rpm
# yum install -y mysql-community-client-5.6.34-2.el6.x86_64.rpm
```

## バックアップ用データベースユーザ作成

ここでは exporter という名前にしました。

```
# ユーザの作成
mysql > CREATE USER 'exporter'@'%'

# バックアップ用の権限付与
# データベースの状態によって必要な権限は代わります。例えばVIEWを利用していない場合は SHOW VIEWは不要です。
mysql > GRANT PROCESS, REPLICATION CLIENT, SHOW VIEW, SELECT ON *.* TO 'exporter'@'%';

# パスワード設定
mysql > SET PASSWORD FOR exporter = PASSWORD('xxxxxxx')
```

## バックアップ用awsプロファイル作成

バックアップを実行する端末での作業です。

ここで作成したプロファイル名は、バックアップスクリプトで使用します。

```
$ aws configure --profile プロファイル名
  AWS Access Key ID [None]: AKIXXXXXXXXXXXXXXXX
  AWS Secret Access Key [None]: ***********************             
  Default region name [None]: ap-northeast-1
  Default output format [None]: json
```

プロファイルの詳細はAWSの公式ドキュメントを参照してください。
[名前付きプロファイル - AWS Command Line Interface](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-multiple-profiles.html)

## バックアップスクリプトの編集

バックアップスクリプト（内容は後述）の定数部分を自身の環境にあわせて編集します。

**定義する項目**

| 定数名 | 説明 | 設定値の例 |
|:-- |:-- |:--|
| AWS_PROFILE | プロファイル名 | s3_backupdb_user |
| AWS_COMMAND | awsコマンドのフルパス($ which aws で確認) | $HOME/.local/bin/aws |
| DBHOST | データベースホスト名 | - |
| DBUSER | データベースユーザ名 | - |
| DBPASSWORD | データベースパスワード | - |
| BACKUP_DIR | $HOME/backup | - |
| PREFIX | バックアップファイル名の先頭文字列（空でもOK） | database_backup |
| REMOTE_BACKUP_DIR | s3のバックアップ用バケット | s3://mycompany-backup |
| KEEP_DAYS | バックアップファイルの保持期間（単位：日）(*1) | 7 |
| DATABASES | バックアップしたいデータベース名（複数指定する場合スペース区切り） | "redmine wordpress service1 service2" |

(*1)注意事項。実行日からN日前のフォルダ内ファイルを削除という実装なので値を変更する場合は注意してください。たとえばしばらく運用してから値を7から2に変えた場合、6、5、4、3日前のファイルは残ってしまいます。（保持期間というよりも「実行日からN日前のフォルダを削除」する指定といったほうが良いかもしれません。）

## 配置と動作確認

バックアップスクリプトを任意の場所に配置して、実行許可を付与してテスト実行します。
以下は例です。

```bash
$ vi $HOME/scripts/backup_databases.sh
$ chmod +x !$
$ /bin/sh !$
```

### トラブルシューティング

後日記載予定


## 定期実行を設定

バックアップスクリプトをcronで定期実行させます。
以下は、ホームディレクトリ直下のscriptsディレクトリ内に backup_databases.sh として保存した場合の定義です。
1日1回午前3時10分に実行します。

```
$ crontab -e
  10 3 * * *  $HOME/scripts/backup_databases.sh
```

# バックアップスクリプトの内容

```bash
#!/bin/sh

# S3アクセス権限を持つユーザを指定(awsコマンドごとの --profile <profilename>の指定が不要になる）
export AWS_PROFILE=プロファイル名

# aws コマンドをフルパス指定（cronだとパスが通っていない場合があるため）
AWS_COMMAND=< $which aws の結果を設定(例えば/usr/bin/aws）> 

DBHOST=データベースホスト名
DBUSER=データベースユーザ名
DBPASSWORD=データベースパスワード  # スクリプトが漏れた場合を考慮してパラメタとして渡すほうが好ましい
BACKUP_DIR=$HOME/backup
PREFIX=database_backup # バックアップファイル名の先頭文字列（空でも問題ない）

REMOTE_BACKUP_DIR=s3://バックアップ用バケット

# バックアップ対象のデータベースを指定
DATABASES="データベース名1 データベース名2 ..."

# バックアップとS3へのアップロード
for database in $DATABASES
do
  FILE_NAME=${PREFIX}_${database}
  BK_FILE_PATH=${BACKUP_DIR}/${FILE_NAME}_${TODAY}.sql.gz
  echo "exporting $database"
  mysqldump -u ${DBUSER} -p${DBPASSWORD} --lock-tables=false -h ${DBHOST}  ${database}  | gzip > $BK_FILE_PATH
  echo "done exporting"

  echo "uploading $BK_FILE_PATH to $REMOTE_BACKUP_DIR/${TODAY}"
  AWS_COMMAND s3 cp ${BK_FILE_PATH} $REMOTE_BACKUP_DIR/${TODAY}/ --acl private
  echo "done upload"
done

# バックアップ端末上のバックアップファイルを削除
find ${BACKUP_DIR}/*.sql.gz -mtime +1 -exec rm -f {} \;

# 保持日数を超えたS3上のバックアップファイルをディレクトリごと削除
KEEP_DAYS=7
TODAY=`date '+%Y%m%d'`
DELETE_DAY=`date '+%Y%m%d' --date "${KEEP_DAYS} days ago"`
AWS_COMMAND s3 rm $REMOTE_BACKUP_DIR/${DELETE_DAY} --recursive

```