前書き

多くの場合、データベースにはインフラストラクチャの最も価値のある情報の一部が格納されます。 このため、事故やハードウェア障害が発生した場合のデータ損失を防ぐために、信頼できるバックアップを作成することが重要です。

Percona XtraBackupバックアップツールは、システムの実行中にMySQLデータの「ホット」バックアップを実行する方法を提供します。 これを行うには、ファイルシステムレベルでデータファイルをコピーし、クラッシュリカバリを実行してデータセット内の一貫性を実現します。

https://www.digitalocean.com/community/tutorials/how-to-configure-mysql-backups-with-percona-xtrabackup-on-ubuntu-16-04 [以前のガイド]では、Perconaのバックアップユーティリティとローカルバックアップのローテーションを実行する一連のスクリプトを作成しました。 これは、データを別のドライブまたはネットワークにマウントされたボリュームにバックアップして、データベースマシンの問題を処理するのに役立ちます。 ただし、ほとんどの場合、データはオフサイトでバックアップする必要があります。この場合、データは簡単に保守および復元できます。 このガイドでは、以前のバックアップシステムを拡張して、圧縮および暗号化されたバックアップファイルをオブジェクトストレージサービスにアップロードします。 このガイドでは例としてhttps://www.digitalocean.com/products/object-storage/[DigitalOcean Spaces]を使用しますが、基本的な手順は他のS3互換オブジェクトストレージソリューションにも適用される可能性があります。

前提条件

このガイドを開始する前に、前のガイドで説明したローカルPerconaバックアップソリューションで構成されたMySQLデータベースサーバーが必要です。 従う必要があるガイドの完全なセットは次のとおりです。

上記のチュートリアルに加えて、アクセスキーとシークレットキーを生成して、APIを使用してオブジェクトストレージアカウントとやり取りする必要があります。 DigitalOcean Spacesを使用している場合は、https://www.digitalocean.com/community/tutorials/how-to-create-a-digitalocean-space-and-api-keyに従ってこれらの資格情報を生成する方法を見つけることができます。 [DigitalOceanスペースとAPIキーの作成方法]ガイド。 APIアクセスキーとAPIシークレット値の両方を保存する必要があります。

前のガイドを終了したら、開始するには `+ sudo +`ユーザーとしてサーバーに再度ログインします。

依存関係をインストールする

いくつかのPythonおよびBashスクリプトを使用してバックアップを作成し、それらをリモートオブジェクトストレージにアップロードして保管します。 https://developers.digitalocean.com/documentation/spaces/ [オブジェクトストレージAPI]とやり取りするには、 + boto3 + Pythonライブラリが必要です。 Pythonのパッケージマネージャーである `+ pip +`でこれをダウンロードできます。

ローカルパッケージインデックスを更新してから、次のように入力して、 `+ apt-get `を使用してUbuntuのデフォルトリポジトリからPython 3バージョンの ` pip +`をインストールします。

sudo apt-get update
sudo apt-get install python3-pip

Ubuntuは独自のパッケージライフサイクルを維持しているため、Ubuntuリポジトリ内の `+ pip`のバージョンは、最近のリリースと同期していません。 ただし、ツール自体を使用して、 `+ pip `の新しいバージョンに更新できます。 ` sudo `を使用してグローバルにインストールし、 ` -H `フラグを含めて、 ` $ HOME `変数を ` pip +`が期待する値に設定します。

sudo -H pip3 install --upgrade pip

その後、 `+ boto3 `を ` pytz +`モジュールと一緒にインストールできます。これは、オブジェクトストレージAPIが返すオフセット対応フォーマットを使用して時間を正確に比較するために使用します。

sudo -H pip3 install boto3 pytz

これで、オブジェクトストレージAPIとやり取りするために必要なすべてのPythonモジュールができました。

オブジェクトストレージ構成ファイルを作成する

バックアップとダウンロードのスクリプトは、オブジェクトストレージAPIとやり取りして、復元する必要があるときにファイルをアップロードし、古いバックアップアーティファクトをダウンロードする必要があります。 前提条件セクションで生成したアクセスキーを使用する必要があります。 これらの値をスクリプト自体に保持するのではなく、スクリプトで読み取り可能な専用ファイルに配置します。 このようにして、資格情報を公開することを恐れずにスクリプトを共有でき、スクリプト自体よりも資格情報を厳しくロックダウンできます。

https://www.digitalocean.com/community/tutorials/how-to-configure-mysql-backups-with-percona-xtrabackup-on-ubuntu-16-04 [最後のガイド]では、 `+ / backups / mysql + `ディレクトリ。バックアップと暗号化キーを保存します。 構成ファイルを他のアセットと一緒にここに配置します。 `+ object_storage_config.sh +`というファイルを作成します:

sudo nano /backups/mysql/object_storage_config.sh

内部で、次の内容を貼り付け、アクセスキーとシークレットキーをオブジェクトストレージアカウントから取得した値に変更し、バケット名を一意の値に変更します。 エンドポイントURLとリージョン名をオブジェクトストレージサービスから提供された値に設定します(ここでは、SpacesのDigitalOceanのNYC3リージョンに関連付けられた値を使用します)。

/backups/mysql/object_storage_config.sh

#!/bin/bash

export MYACCESSKEY=""
export MYSECRETKEY=""
export MYBUCKETNAME=""
export MYENDPOINTURL="https://nyc3.digitaloceanspaces.com"
export MYREGIONNAME="nyc3"

これらの行は、アクセスキーと秘密キーをそれぞれ保持するために、「+ MYACCESSKEY 」と「 MYSECRETKEY 」という2つの環境変数を定義します。 ` MYBUCKETNAME `変数は、バックアップファイルの保存に使用するオブジェクトストレージバケットを定義します。 バケット名は普遍的に一意である必要があるため、他のユーザーが選択していない名前を選択する必要があります。 スクリプトは、バケットの値をチェックして、別のユーザーによって既に要求されているかどうかを確認し、使用可能な場合は自動的に作成します。 スクリプト内から呼び出すプロセスがこれらの値にアクセスできるように、定義した変数を「 export」します。

`+ MYENDPOINTURL `および ` MYREGIONNAME `変数には、オブジェクトストレージプロバイダーによって提供されるAPIエンドポイントと特定の地域識別子が含まれます。 DigitalOceanスペースの場合、エンドポイントは ` https://。digitaloceanspaces.com +`になります。 DigitalOceanコントロールパネルでSpacesの利用可能なリージョンを見つけることができます(この記事の執筆時点では、「nyc3」のみが利用可能です)。

完了したら、ファイルを保存して閉じます。

APIキーにアクセスできる人は誰でもオブジェクトストレージアカウントに完全にアクセスできるため、設定ファイルへのアクセスを「+ backup 」ユーザーに制限することが重要です。 「 backup +」ユーザーとグループにファイルの所有権を付与し、次のように入力して他のすべてのアクセスを取り消すことができます。

sudo chown backup:backup /backups/mysql/object_storage_config.sh
sudo chmod 600 /backups/mysql/object_storage_config.sh

これで、 `+ object_storage_config.sh `ファイルは ` backup +`ユーザーのみがアクセスできるようになります。

リモートバックアップスクリプトの作成

オブジェクトストレージ構成ファイルができたので、先に進み、スクリプトの作成を開始できます。 次のスクリプトを作成します。

  • + object_storage.py +:このスクリプトは、オブジェクトストレージAPIと対話して、バケットの作成、ファイルのアップロード、コンテンツのダウンロード、および古いバックアップの整理を行います。 他のスクリプトは、リモートオブジェクトストレージアカウントと対話する必要があるときはいつでもこのスクリプトを呼び出します。

  • + remote-backup-mysql.sh +:このスクリプトは、ファイルを暗号化および圧縮して単一のアーティファクトにした後、リモートオブジェクトストアにアップロードすることにより、MySQLデータベースをバックアップします。 毎日の初めに完全バックアップを作成し、その後1時間ごとに増分バックアップを作成します。 リモートバケットから30日以上経過したすべてのファイルを自動的にプルーニングします。

  • + download-day.sh +:このスクリプトにより、特定の日に関連付けられたすべてのバックアップをダウンロードできます。 バックアップスクリプトは毎朝フルバックアップを作成し、その後1日を通して増分バックアップを作成するため、このスクリプトは1時間ごとのチェックポイントに復元するために必要なすべての資産をダウンロードできます。

上記の新しいスクリプトとともに、前のガイドの「+ extract-mysql.sh 」および「 prepare-mysql.sh +」スクリプトを活用して、ファイルを復元します。 GitHubのこのチュートリアルのリポジトリでいつでもスクリプトを表示できます。 以下の内容をコピーして貼り付けたくない場合は、次のように入力してGitHubから新しいファイルを直接ダウンロードできます。

cd /tmp
curl -LO https://raw.githubusercontent.com/do-community/ubuntu-1604-mysql-backup/master/object_storage.py
curl -LO https://raw.githubusercontent.com/do-community/ubuntu-1604-mysql-backup/master/remote-backup-mysql.sh
curl -LO https://raw.githubusercontent.com/do-community/ubuntu-1604-mysql-backup/master/download-day.sh

スクリプトをダウンロードした後、それらが正常に取得され、実行するアクションを承認することを確認してください。 満足したら、スクリプトを実行可能としてマークし、次を入力してそれらを `+ / usr / local / bin +`ディレクトリに移動します:

chmod +x /tmp/{remote-backup-mysql.sh,download-day.sh,object_storage.py}
sudo mv /tmp/{remote-backup-mysql.sh,download-day.sh,object_storage.py} /usr/local/bin

次に、これらの各スクリプトを設定し、それらについて詳しく説明します。

object_storage.pyスクリプトを作成します

GitHubから `+ object_storage.py `スクリプトをダウンロードしていない場合は、 ` object_storage.py `という名前の新しいファイルを ` / usr / local / bin +`ディレクトリに作成します。

sudo nano /usr/local/bin/object_storage.py

スクリプトの内容をコピーしてファイルに貼り付けます。

/usr/local/bin/object_storage.py

#!/usr/bin/env python3

import argparse
import os
import sys
from datetime import datetime, timedelta

import boto3
import pytz
from botocore.client import ClientError, Config
from dateutil.parser import parse

# "backup_bucket" must be a universally unique name, so choose something
# specific to your setup.
# The bucket will be created in your account if it does not already exist
backup_bucket = os.environ['MYBUCKETNAME']
access_key = os.environ['MYACCESSKEY']
secret_key = os.environ['MYSECRETKEY']
endpoint_url = os.environ['MYENDPOINTURL']
region_name = os.environ['MYREGIONNAME']


class Space():
   def __init__(self, bucket):
       self.session = boto3.session.Session()
       self.client = self.session.client('s3',
                                         region_name=region_name,
                                         endpoint_url=endpoint_url,
                                         aws_access_key_id=access_key,
                                         aws_secret_access_key=secret_key,
                                         config=Config(signature_version='s3')
                                         )
       self.bucket = bucket
       self.paginator = self.client.get_paginator('list_objects')

   def create_bucket(self):
       try:
           self.client.head_bucket(Bucket=self.bucket)
       except ClientError as e:
           if e.response['Error']['Code'] == '404':
               self.client.create_bucket(Bucket=self.bucket)
           elif e.response['Error']['Code'] == '403':
               print("The bucket name \"{}\" is already being used by "
                     "someone.  Please try using a different bucket "
                     "name.".format(self.bucket))
               sys.exit(1)
           else:
               print("Unexpected error: {}".format(e))
               sys.exit(1)

   def upload_files(self, files):
       for filename in files:
           self.client.upload_file(Filename=filename, Bucket=self.bucket,
                                   Key=os.path.basename(filename))
           print("Uploaded {} to \"{}\"".format(filename, self.bucket))

   def remove_file(self, filename):
       self.client.delete_object(Bucket=self.bucket,
                                 Key=os.path.basename(filename))

   def prune_backups(self, days_to_keep):
       oldest_day = datetime.now(pytz.utc) - timedelta(days=int(days_to_keep))
       try:
           # Create an iterator to page through results
           page_iterator = self.paginator.paginate(Bucket=self.bucket)
           # Collect objects older than the specified date
           objects_to_prune = [filename['Key'] for page in page_iterator
                               for filename in page['Contents']
                               if filename['LastModified'] < oldest_day]
       except KeyError:
           # If the bucket is empty
           sys.exit()
       for object in objects_to_prune:
           print("Removing \"{}\" from {}".format(object, self.bucket))
           self.remove_file(object)

   def download_file(self, filename):
       self.client.download_file(Bucket=self.bucket,
                                 Key=filename, Filename=filename)

   def get_day(self, day_to_get):
       try:
           # Attempt to parse the date format the user provided
           input_date = parse(day_to_get)
       except ValueError:
           print("Cannot parse the provided date: {}".format(day_to_get))
           sys.exit(1)
       day_string = input_date.strftime("-%m-%d-%Y_")
       print_date = input_date.strftime("%A, %b. %d %Y")
       print("Looking for objects from {}".format(print_date))
       try:
           # create an iterator to page through results
           page_iterator = self.paginator.paginate(Bucket=self.bucket)
           objects_to_grab = [filename['Key'] for page in page_iterator
                              for filename in page['Contents']
                              if day_string in filename['Key']]
       except KeyError:
           print("No objects currently in bucket")
           sys.exit()
       if objects_to_grab:
           for object in objects_to_grab:
               print("Downloading \"{}\" from {}".format(object, self.bucket))
               self.download_file(object)
       else:
           print("No objects found from: {}".format(print_date))
           sys.exit()


def is_valid_file(filename):
   if os.path.isfile(filename):
       return filename
   else:
       raise argparse.ArgumentTypeError("File \"{}\" does not exist."
                                        .format(filename))


def parse_arguments():
   parser = argparse.ArgumentParser(
       description='''Client to perform backup-related tasks with
                    object storage.''')
   subparsers = parser.add_subparsers()

   # parse arguments for the "upload" command
   parser_upload = subparsers.add_parser('upload')
   parser_upload.add_argument('files', type=is_valid_file, nargs='+')
   parser_upload.set_defaults(func=upload)

   # parse arguments for the "prune" command
   parser_prune = subparsers.add_parser('prune')
   parser_prune.add_argument('--days-to-keep', default=30)
   parser_prune.set_defaults(func=prune)

   # parse arguments for the "download" command
   parser_download = subparsers.add_parser('download')
   parser_download.add_argument('filename')
   parser_download.set_defaults(func=download)

   # parse arguments for the "get_day" command
   parser_get_day = subparsers.add_parser('get_day')
   parser_get_day.add_argument('day')
   parser_get_day.set_defaults(func=get_day)

   return parser.parse_args()


def upload(space, args):
   space.upload_files(args.files)


def prune(space, args):
   space.prune_backups(args.days_to_keep)


def download(space, args):
   space.download_file(args.filename)


def get_day(space, args):
   space.get_day(args.day)


def main():
   args = parse_arguments()
   space = Space(bucket=backup_bucket)
   space.create_bucket()
   args.func(space, args)


if __name__ == '__main__':
   main()

このスクリプトは、オブジェクトストレージアカウント内のバックアップを管理します。 ファイルのアップロード、ファイルの削除、古いバックアップの整理、オブジェクトストレージからのファイルのダウンロードが可能です。 オブジェクトストレージAPIと直接やり取りするのではなく、他のスクリプトはここで定義された機能を使用してリモートリソースとやり取りします。 定義するコマンドは次のとおりです。

  • + upload +:引数として渡された各ファイルをオブジェクトストレージにアップロードします。 複数のファイルを指定できます。

  • + download:引数として渡されるリモートオブジェクトストレージから単一のファイルをダウンロードします。

  • + prune +:特定の年齢より古いすべてのファイルをオブジェクトの保存場所から削除します。 デフォルトでは、これは30日より古いファイルを削除します。 これを調整するには、「+ prune 」を呼び出すときに「-days-to-keep +」オプションを指定します。

  • + get_day +:標準の日付形式(日付に空白が含まれる場合は引用符を使用)を使用して、引数としてダウンロードする日を渡すと、ツールはその日付を解析し、その日付からすべてのファイルをダウンロードしようとします。

スクリプトは、環境変数からオブジェクトストレージの認証情報とバケット名を読み取ろうとするため、 `+ object_storage.py `スクリプトを呼び出す前に、これらが ` object_storage_config.sh +`ファイルから設定されていることを確認する必要があります。

終了したら、ファイルを保存して閉じます。

次に、まだ実行していない場合は、次のように入力してスクリプトを実行可能にします。

sudo chmod +x /usr/local/bin/object_storage.py

`+ object_storage.py +`スクリプトがAPIとやり取りできるようになったので、これを使用してファイルをバックアップおよびダウンロードするBashスクリプトを作成できます。

remote-backup-mysql.shスクリプトを作成します

次に、 `+ remote-backup-mysql.sh `スクリプトを作成します。 これにより、オリジナルの ` backup-mysql.sh +`ローカルバックアップスクリプトと同じ機能の多くが実行され、より基本的な組織構造(ローカルファイルシステムでバックアップを維持する必要がないため)およびオブジェクトストレージにアップロードするいくつかの追加手順が実行されます。 。

リポジトリからスクリプトをダウンロードしていない場合は、 `+ / usr / local / bin `ディレクトリに ` remote-backup-mysql.sh +`というファイルを作成して開きます。

sudo nano /usr/local/bin/remote-backup-mysql.sh

内部に、次のスクリプトを貼り付けます。

/usr/local/bin/remote-backup-mysql.sh

#!/bin/bash

export LC_ALL=C

days_to_keep=30
backup_owner="backup"
parent_dir="/backups/mysql"
defaults_file="/etc/mysql/backup.cnf"
working_dir="${parent_dir}/working"
log_file="${working_dir}/backup-progress.log"
encryption_key_file="${parent_dir}/encryption_key"
storage_configuration_file="${parent_dir}/object_storage_config.sh"
now="$(date)"
now_string="$(date -d"${now}" +%m-%d-%Y_%H-%M-%S)"
processors="$(nproc --all)"

# Use this to echo to standard error
error () {
   printf "%s: %s\n" "$(basename "${BASH_SOURCE}")" "${1}" >&2
   exit 1
}

trap 'error "An unexpected error occurred."' ERR

sanity_check () {
   # Check user running the script
   if [ "$(id --user --name)" != "$backup_owner" ]; then
       error "Script can only be run as the \"$backup_owner\" user"
   fi

   # Check whether the encryption key file is available
   if [ ! -r "${encryption_key_file}" ]; then
       error "Cannot read encryption key at ${encryption_key_file}"
   fi

   # Check whether the object storage configuration file is available
   if [ ! -r "${storage_configuration_file}" ]; then
       error "Cannot read object storage configuration from ${storage_configuration_file}"
   fi

   # Check whether the object storage configuration is set in the file
   source "${storage_configuration_file}"
   if [ -z "${MYACCESSKEY}" ] || [ -z "${MYSECRETKEY}" ] || [ -z "${MYBUCKETNAME}" ]; then
       error "Object storage configuration are not set properly in ${storage_configuration_file}"
   fi
}

set_backup_type () {
   backup_type="full"


   # Grab date of the last backup if available
   if [ -r "${working_dir}/xtrabackup_info" ]; then
       last_backup_date="$(date -d"$(grep start_time "${working_dir}/xtrabackup_info" | cut -d' ' -f3)" +%s)"
   else
           last_backup_date=0
   fi

   # Grab today's date, in the same format
   todays_date="$(date -d "$(date -d "${now}" "+%D")" +%s)"

   # Compare the two dates
   (( $last_backup_date == $todays_date ))
   same_day="${?}"

   # The first backup each new day will be a full backup
   # If today's date is the same as the last backup, take an incremental backup instead
   if [ "$same_day" -eq "0" ]; then
       backup_type="incremental"
   fi
}

set_options () {
   # List the xtrabackup arguments
   xtrabackup_args=(
       "--defaults-file=${defaults_file}"
       "--backup"
       "--extra-lsndir=${working_dir}"
       "--compress"
       "--stream=xbstream"
       "--encrypt=AES256"
       "--encrypt-key-file=${encryption_key_file}"
       "--parallel=${processors}"
       "--compress-threads=${processors}"
       "--encrypt-threads=${processors}"
       "--slave-info"
   )

   set_backup_type

   # Add option to read LSN (log sequence number) if taking an incremental backup
   if [ "$backup_type" == "incremental" ]; then
       lsn=$(awk '/to_lsn/ {print $3;}' "${working_dir}/xtrabackup_checkpoints")
       xtrabackup_args+=( "--incremental-lsn=${lsn}" )
   fi
}

rotate_old () {
   # Remove previous backup artifacts
   find "${working_dir}" -name "*.xbstream" -type f -delete

   # Remove any backups from object storage older than 30 days
   /usr/local/bin/object_storage.py prune --days-to-keep "${days_to_keep}"
}

take_backup () {
   find "${working_dir}" -type f -name "*.incomplete" -delete
   xtrabackup "${xtrabackup_args[@]}" --target-dir="${working_dir}" > "${working_dir}/${backup_type}-${now_string}.xbstream.incomplete" 2> "${log_file}"

   mv "${working_dir}/${backup_type}-${now_string}.xbstream.incomplete" "${working_dir}/${backup_type}-${now_string}.xbstream"
}

upload_backup () {
   /usr/local/bin/object_storage.py upload "${working_dir}/${backup_type}-${now_string}.xbstream"
}

main () {
   mkdir -p "${working_dir}"
   sanity_check && set_options && rotate_old && take_backup && upload_backup

   # Check success and print message
   if tail -1 "${log_file}" | grep -q "completed OK"; then
       printf "Backup successful!\n"
       printf "Backup created at %s/%s-%s.xbstream\n" "${working_dir}" "${backup_type}" "${now_string}"
   else
       error "Backup failure! If available, check ${log_file} for more information"
   fi
}

main

このスクリプトは、実際のMySQLバックアップ手順を処理し、バックアップスケジュールを制御し、リモートストレージから古いバックアップを自動的に削除します。 `+ days_to_keep +`変数を調整することで、手元に残したいバックアップの日数を選択できます。

前回の記事で使用したローカルの `+ backup-mysql.sh +`スクリプトは、毎日のバックアップ用に個別のディレクトリを維持していました。 バックアップをリモートで保存しているため、バックアップ専用のディスク領域を最小限に抑えるために、最新のバックアップのみをローカルに保存します。 以前のバックアップは、復元の必要に応じてオブジェクトストレージからダウンロードできます。

前のスクリプトと同様に、いくつかの基本的な要件が満たされていることを確認し、実行するバックアップの種類を構成した後、各バックアップを暗号化して単一のファイルアーカイブに圧縮します。 以前のバックアップファイルはローカルファイルシステムから削除され、 `+ days_to_keep +`で定義された値より古いリモートバックアップはすべて削除されます。

完了したら、ファイルを保存して閉じます。 その後、次のように入力して、スクリプトが実行可能であることを確認します。

sudo chmod +x /usr/local/bin/remote-backup-mysql.sh

このスクリプトは、このシステム上の `+ backup-mysql.sh +`スクリプトの代わりとして使用して、ローカルバックアップからリモートバックアップに切り替えることができます。

download-day.shスクリプトを作成します

最後に、 `+ / usr / local / bin `ディレクトリ内で ` download-day.sh +`スクリプトをダウンロードまたは作成します。 このスクリプトを使用して、特定の日に関連付けられたすべてのバックアップをダウンロードできます。

スクリプトファイルを以前にダウンロードしていない場合は、テキストエディターで作成します。

sudo nano /usr/local/bin/download-day.sh

内部に、次の内容を貼り付けます。

/usr/local/bin/download-day.sh

#!/bin/bash

export LC_ALL=C

backup_owner="backup"
storage_configuration_file="/backups/mysql/object_storage_config.sh"
day_to_download="${1}"

# Use this to echo to standard error
error () {
   printf "%s: %s\n" "$(basename "${BASH_SOURCE}")" "${1}" >&2
   exit 1
}

trap 'error "An unexpected error occurred."' ERR

sanity_check () {
   # Check user running the script
   if [ "$(id --user --name)" != "$backup_owner" ]; then
       error "Script can only be run as the \"$backup_owner\" user"
   fi

   # Check whether the object storage configuration file is available
   if [ ! -r "${storage_configuration_file}" ]; then
       error "Cannot read object storage configuration from ${storage_configuration_file}"
   fi

   # Check whether the object storage configuration is set in the file
   source "${storage_configuration_file}"
   if [ -z "${MYACCESSKEY}" ] || [ -z "${MYSECRETKEY}" ] || [ -z "${MYBUCKETNAME}" ]; then
       error "Object storage configuration are not set properly in ${storage_configuration_file}"
   fi
}

main () {
   sanity_check
   /usr/local/bin/object_storage.py get_day "${day_to_download}"
}

main

このスクリプトを呼び出して、特定の日のすべてのアーカイブをダウンロードできます。 毎日は完全バックアップから始まり、その日の残りの時間を通して増分バックアップを蓄積するため、これにより、1時間ごとのスナップショットへの復元に必要な関連ファイルがすべてダウンロードされます。

スクリプトは、日付または日である単一の引数を取ります。 Pythonの `+ dateutil.parser.parse +`関数を使用して、引数として提供された日付文字列を読み取り、解釈します。 この関数はかなり柔軟で、たとえば「金曜日」などの相対文字列を含むさまざまな形式で日付を解釈できます。 ただし、あいまいさを避けるため、より明確に定義された日付を使用することをお勧めします。 使用する形式に空白が含まれる場合は、日付を引用符で囲むようにしてください。

続行する準備ができたら、ファイルを保存して閉じます。 次を入力して、スクリプトを実行可能にします。

sudo chmod +x /usr/local/bin/download-day.sh

復元する特定の日付に、オブジェクトストレージからバックアップファイルをダウンロードできるようになりました。

リモートMySQLバックアップおよびダウンロードスクリプトのテスト

スクリプトが準備できたので、テストして、期待どおりに機能することを確認する必要があります。

完全バックアップを実行する

最初に、 `+ backup `ユーザーで ` remote-mysql-backup.sh +`スクリプトを呼び出します。 このコマンドを実行するのはこれが初めてなので、MySQLデータベースの完全バックアップを作成する必要があります。

sudo -u backup remote-backup-mysql.sh

すべてがうまくいくと、次のような出力が表示されます。

OutputUploaded /backups/mysql/working/full-10-17-2017_19-09-30.xbstream to ""
Backup successful!
Backup created at /backups/mysql/working/full-10-17-2017_19-09-30.xbstream

これは、 `+ / backups / mysql / working`ディレクトリ内に完全バックアップが作成されたことを示します。 また、 `+ object_storage_config.sh +`ファイルで定義されたバケットを使用して、リモートオブジェクトストレージにアップロードされています。

`+ / backups / mysql / working `ディレクトリ内を見ると、最後のガイドの ` backup-mysql.sh +`スクリプトによって生成されたファイルに似たファイルを見ることができます。

ls /backups/mysql/working
Outputbackup-progress.log  full-10-17-2017_19-09-30.xbstream  xtrabackup_checkpoints  xtrabackup_info

`+ backup-progress.log `ファイルには ` xtrabackup `コマンドからの出力が含まれ、 ` xtrabackup_checkpoints `および ` xtrabackup_info +`には使用されたオプション、バックアップのタイプとスコープ、およびその他のメタデータに関する情報が含まれます。

増分バックアップを実行する

最初のバックアップでは見つからない追加のデータを作成するために、「+ equipment」テーブルに小さな変更を加えましょう。 次のように入力して、テーブルに新しい行を入力できます。

mysql -u root -p -e 'INSERT INTO playground.equipment (type, quant, color) VALUES ("sandbox", 4, "brown");'

データベースの管理パスワードを入力して、新しいレコードを追加します。

これで、追加のバックアップを作成できます。 スクリプトを再度呼び出すと、前回のバックアップと同じ日である限り(サーバーの時計に従って)、増分バックアップを作成する必要があります。

sudo -u backup remote-backup-mysql.sh
OutputUploaded /backups/mysql/working/incremental-10-17-2017_19-19-20.xbstream to ""
Backup successful!
Backup created at /backups/mysql/working/incremental-10-17-2017_19-19-20.xbstream

上記の出力は、バックアップが同じディレクトリ内でローカルに作成され、再びオブジェクトストレージにアップロードされたことを示しています。 `+ / backups / mysql / working`ディレクトリを確認すると、新しいバックアップが存在し、以前のバックアップが削除されていることがわかります。

ls /backups/mysql/working
Outputbackup-progress.log  incremental-10-17-2017_19-19-20.xbstream  xtrabackup_checkpoints  xtrabackup_info

ファイルはリモートでアップロードされるため、ローカルコピーを削除すると、使用されるディスク容量を削減できます。

指定した日のバックアップをダウンロードする

バックアップはリモートに保存されるため、ファイルを復元する必要がある場合はリモートファイルをプルダウンする必要があります。 これを行うために、 `+ download-day.sh +`スクリプトを使用できます。

`+ backup +`ユーザーが安全に書き込みできるディレクトリを作成してから移動することから始めます:

sudo -u backup mkdir /tmp/backup_archives
cd /tmp/backup_archives

次に、 + backup-`ユーザーとして + download-day.sh + `スクリプトを呼び出します。 ダウンロードしたいアーカイブの日を渡します。 日付形式はかなり柔軟ですが、明確にすることをお勧めします。

sudo -u backup download-day.sh ""

指定した日付と一致するアーカイブがある場合、それらは現在のディレクトリにダウンロードされます。

OutputLooking for objects from Tuesday, Oct. 17 2017
Downloading "full-10-17-2017_19-09-30.xbstream" from
Downloading "incremental-10-17-2017_19-19-20.xbstream" from

ファイルがローカルファイルシステムにダウンロードされたことを確認します。

ls
Outputfull-10-17-2017_19-09-30.xbstream  incremental-10-17-2017_19-19-20.xbstream

圧縮され、暗号化されたアーカイブが再びサーバーに戻ります。

バックアップの抽出と準備

ファイルが収集されると、ローカルバックアップの処理と同じ方法でファイルを処理できます。

最初に、 `+ backup `ユーザーを使用して ` .xbstream `ファイルを ` extract-mysql.sh +`スクリプトに渡します。

sudo -u backup extract-mysql.sh *.xbstream

これにより、アーカイブが復号化され、「+ restore 」というディレクトリに解凍されます。 そのディレクトリに入り、 ` prepare-mysql.sh +`スクリプトでファイルを準備します:

cd restore
sudo -u backup prepare-mysql.sh
OutputBackup looks to be fully prepared.  Please check the "prepare-progress.log" file
to verify before continuing.

If everything looks correct, you can apply the restored files.

First, stop MySQL and move or remove the contents of the MySQL data directory:

       sudo systemctl stop mysql
       sudo mv /var/lib/mysql/ /tmp/

Then, recreate the data directory and  copy the backup files:

       sudo mkdir /var/lib/mysql
       sudo xtrabackup --copy-back --target-dir=/tmp/backup_archives/restore/full-10-17-2017_19-09-30

Afterward the files are copied, adjust the permissions and restart the service:

       sudo chown -R mysql:mysql /var/lib/mysql
       sudo find /var/lib/mysql -type d -exec chmod 750 {} \;
       sudo systemctl start mysql

これで、 `+ / tmp / backup_archives / restore +`ディレクトリ内の完全バックアップを準備する必要があります。 出力の指示に従って、システムにMySQLデータを復元できます。

バックアップデータをMySQLデータディレクトリに復元する

バックアップデータを復元する前に、現在のデータを邪魔にならないように移動する必要があります。

データベースを破損したり、データファイルを置き換えるときにサービスがクラッシュしたりしないように、MySQLをシャットダウンすることから始めます。

sudo systemctl stop mysql

次に、現在のデータディレクトリを `+ / tmp `ディレクトリに移動できます。 これにより、復元に問題がある場合に簡単に戻すことができます。 最後の記事でファイルを ` / tmp / mysql `に移動したため、今回はファイルを ` / tmp / mysql-remote +`に移動できます。

sudo mv /var/lib/mysql/ /tmp/mysql-remote

次に、空の `+ / var / lib / mysql`ディレクトリを作成します:

sudo mkdir /var/lib/mysql

これで、バックアップファイルを `+ / var / lib / mysql `ディレクトリにコピーするために ` prepare-mysql.sh `コマンドが提供した ` xtrabackup +`復元コマンドを入力できます。

sudo xtrabackup --copy-back --target-dir=

プロセスが完了したら、ディレクトリのアクセス許可と所有権を変更して、MySQLプロセスがアクセスできるようにします。

sudo chown -R mysql:mysql /var/lib/mysql
sudo find /var/lib/mysql -type d -exec chmod 750 {} \;

これが終了したら、MySQLを再度起動し、データが適切に復元されたことを確認します。

sudo systemctl start mysql
mysql -u root -p -e 'SELECT * FROM playground.equipment;'
Output+----+---------+-------+--------+
| id | type    | quant | color  |
+----+---------+-------+--------+
|  1 | slide   |     2 | blue   |
|  2 | swing   |    10 | yellow |
|  3 | sandbox |     4 | brown  |
+----+---------+-------+--------+

データが利用可能であることは、データが正常に復元されたことを示しています。

データを復元した後、戻って復元ディレクトリを削除することが重要です。 将来の増分バックアップは、完全バックアップが準備された後は完全バックアップに適用できないため、削除する必要があります。 さらに、セキュリティ上の理由から、バックアップディレクトリをディスク上で暗号化しないでください。

cd ~
sudo rm -rf /tmp/backup_archives/restore

次にバックアップディレクトリのクリーンコピーが必要になったときに、バックアップアーカイブファイルからそれらを再度抽出できます。

1時間ごとにバックアップを実行するためのCronジョブの作成

最後のガイドでは、データベースをローカルで自動的にバックアップするための「+ cron」ジョブを作成しました。 新しい `+ cron `ジョブを設定して、リモートバックアップを取得し、ローカルバックアップジョブを無効にします。 ` cron +`スクリプトを有効または無効にすることで、必要に応じてローカルバックアップとリモートバックアップを簡単に切り替えることができます。

まず、 + / etc / cron.hourly`ディレクトリに + remote-backup-mysql`というファイルを作成します:

sudo nano /etc/cron.hourly/remote-backup-mysql

内部では、 `+ systemd-cat `コマンドを使用して ` backup `ユーザーで ` remote-backup-mysql.sh `スクリプトを呼び出します。これにより、出力を ` journald +`に記録できます。

/etc/cron.hourly/remote-backup-mysql

#!/bin/bash
sudo -u backup systemd-cat --identifier=remote-backup-mysql /usr/local/bin/remote-backup-mysql.sh

完了したら、ファイルを保存して閉じます。

両方のファイルで「+ executable 」許可ビットを操作して、新しい「 cron +」ジョブを有効にし、古いジョブを無効にします。

sudo chmod -x /etc/cron.hourly/backup-mysql
sudo chmod +x /etc/cron.hourly/remote-backup-mysql

スクリプトを手動で実行して、新しいリモートバックアップジョブをテストします。

sudo /etc/cron.hourly/remote-backup-mysql

プロンプトが返されたら、 `+ journalctl +`でログエントリを確認できます。

sudo journalctl -t remote-backup-mysql
[seconary_label Output]
-- Logs begin at Tue 2017-10-17 14:28:01 UTC, end at Tue 2017-10-17 20:11:03 UTC. --
Oct 17 20:07:17 myserver remote-backup-mysql[31422]: Uploaded /backups/mysql/working/incremental-10-17-2017_22-16-09.xbstream to ""
Oct 17 20:07:17 myserver remote-backup-mysql[31422]: Backup successful!
Oct 17 20:07:17 myserver remote-backup-mysql[31422]: Backup created at /backups/mysql/working/incremental-10-17-2017_20-07-13.xbstream

数時間後に戻って、追加のバックアップがスケジュールどおりに行われていることを確認してください。

抽出キーのバックアップ

処理する必要がある最後の考慮事項の1つは、暗号化キーをバックアップする方法です( `+ / backups / mysql / encryption_key +`にあります)。

このプロセスを使用してバックアップされたファイルを復元するには、暗号化キーが必要ですが、データベースファイルと同じ場所に暗号化キーを保存すると、暗号化による保護がなくなります。 このため、データベースサーバーに障害が発生した場合や再構築が必要な場合にバックアップアーカイブを使用できるように、暗号化キーのコピーを別の場所に保管することが重要です。

非データベースファイルの完全なバックアップソリューションはこの記事の範囲外ですが、キーをローカルコンピューターにコピーして保管することができます。 これを行うには、次のように入力してファイルの内容を表示します。

sudo less /backups/mysql/encryption_key

ローカルコンピューターでテキストファイルを開き、内部に値を貼り付けます。 バックアップを別のサーバーに復元する必要がある場合は、ファイルの内容を新しいマシンの `+ / backups / mysql / encryption_key +`にコピーし、このガイドで説明されているシステムをセットアップしてから、提供されたスクリプトを使用して復元します。

結論

このガイドでは、MySQLデータベースの1時間ごとのバックアップを取り、リモートオブジェクトストレージスペースに自動的にアップロードする方法を説明しました。 システムは毎朝フルバックアップを実行し、その後1時間ごとの増分バックアップを実行して、1時間ごとのチェックポイントに復元できるようにします。 バックアップスクリプトを実行するたびに、30日以上経過したオブジェクトストレージのバックアップをチェックして削除します。