Sichern von MySQL-Datenbanken im Objektspeicher mit Percona unter Ubuntu 16.04

Einführung

Datenbanken speichern häufig einige der wertvollsten Informationen in Ihrer Infrastruktur. Aus diesem Grund ist es wichtig, zuverlässige Backups zu haben, um Datenverlusten bei Unfällen oder Hardwareausfällen vorzubeugen.

Die Percona XtraBackup-Sicherungstools bieten eine Methode zur Durchführung von "Hot" -Sicherungen von MySQL-Daten, während das System ausgeführt wird. Dazu kopieren sie die Datendateien auf Dateisystemebene und führen anschließend eine Absturzwiederherstellung durch, um die Konsistenz innerhalb des Datasets zu gewährleisten.

In a previous guide haben wir die Sicherungsdienstprogramme und von Percona installiert hat eine Reihe von Skripten erstellt, um rotierende lokale Sicherungen durchzuführen. Dies eignet sich gut zum Sichern von Daten auf einem anderen Laufwerk oder einem über das Netzwerk bereitgestellten Volume, um Probleme mit Ihrem Datenbankcomputer zu beheben. In den meisten Fällen sollten Daten jedoch außerhalb des Standorts gesichert werden, wo sie einfach gewartet und wiederhergestellt werden können. In diesem Handbuch erweitern wir unser bisheriges Sicherungssystem, um unsere komprimierten, verschlüsselten Sicherungsdateien auf einen Objektspeicherdienst hochzuladen. Wir werden DigitalOcean Spaces als Beispiel in diesem Handbuch verwenden, aber die grundlegenden Verfahren sind wahrscheinlich auch für andere S3-kompatible Objektspeicherlösungen anwendbar.

Voraussetzungen

Bevor Sie dieses Handbuch starten, benötigen Sie einen MySQL-Datenbankserver, der mit der in unserem vorherigen Handbuch beschriebenen lokalen Percona-Sicherungslösung konfiguriert ist. Die vollständigen Anleitungen, die Sie befolgen müssen, sind:

Zusätzlich zu den obigen Lernprogrammen müssen Sie einen Zugriffsschlüssel und einen geheimen Schlüssel generieren, um mit Ihrem Objektspeicherkonto mithilfe der API interagieren zu können. Wenn Sie DigitalOcean Spaces verwenden, erfahren Sie unter https://www.digitalocean.com/community/tutorials/how-to-create-a-digitalocean-space-and-api-key, wie Sie diese Anmeldeinformationen generieren Anleitung zum Erstellen eines DigitalOcean Space und eines API-Schlüssels. Sie müssen sowohl den API-Zugriffsschlüssel als auch den geheimen API-Wert speichern.

Wenn Sie mit den vorherigen Handbüchern fertig sind, melden Sie sich erneut bei Ihrem Server als "+ sudo +" - Benutzer an, um loszulegen.

Installieren Sie die Abhängigkeiten

Wir werden einige Python- und Bash-Skripte verwenden, um unsere Backups zu erstellen und sie zur sicheren Aufbewahrung in den Remote-Objektspeicher hochzuladen. Wir benötigen die + boto3 + - Python-Bibliothek, um mit der object storage API interagieren zu können. Wir können dies mit + pip +, dem Paketmanager von Python, herunterladen.

Aktualisieren Sie unseren lokalen Paketindex und installieren Sie dann die Python 3-Version von "+ pip " aus den Ubuntu-Standardrepositorys mit " apt-get +", indem Sie Folgendes eingeben:

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

Da Ubuntu seinen eigenen Paketlebenszyklus beibehält, wird die Version von + pip in Ubuntu-Repositorys nicht mit den jüngsten Versionen synchronisiert. Wir können jedoch mit dem Tool selbst auf eine neuere Version von "+ pip " aktualisieren. Wir werden ` sudo ` für die globale Installation verwenden und das ` -H ` -Flag einschließen, um die Variable ` $ HOME ` auf einen Wert zu setzen, den ` pip +` erwartet:

sudo -H pip3 install --upgrade pip

Anschließend können wir + boto3 + zusammen mit dem + pytz + - Modul installieren, mit dem wir die Zeiten unter Verwendung des offset-fähigen Formats, das die Objektspeicher-API zurückgibt, genau vergleichen:

sudo -H pip3 install boto3 pytz

Wir sollten jetzt alle Python-Module haben, die wir zur Interaktion mit der Objektspeicher-API benötigen.

Erstellen Sie eine Objektspeicherkonfigurationsdatei

Unsere Backup- und Download-Skripte müssen mit der Objektspeicher-API interagieren, um Dateien hochladen und ältere Backup-Artefakte herunterladen zu können, wenn eine Wiederherstellung erforderlich ist. Sie müssen die Zugriffsschlüssel verwenden, die wir im Voraussetzungsabschnitt generiert haben. Anstatt diese Werte in den Skripten selbst beizubehalten, platzieren wir sie in einer dedizierten Datei, die von unseren Skripten gelesen werden kann. Auf diese Weise können wir unsere Skripte freigeben, ohne befürchten zu müssen, unsere Anmeldeinformationen preiszugeben, und wir können die Anmeldeinformationen stärker als das Skript selbst sperren.

In der last guide haben wir das `+ / backups / mysql + `Verzeichnis zum Speichern unserer Backups und unseres Verschlüsselungsschlüssels. Wir werden die Konfigurationsdatei hier zusammen mit unseren anderen Assets ablegen. Erstellen Sie eine Datei mit dem Namen "+ object_storage_config.sh +":

sudo nano /backups/mysql/object_storage_config.sh

Fügen Sie den folgenden Inhalt ein, und ändern Sie den Zugriffsschlüssel und den geheimen Schlüssel in die Werte, die Sie von Ihrem Objektspeicherkonto erhalten haben, und den Bucket-Namen in einen eindeutigen Wert. Stellen Sie die Endpunkt-URL und den Regionsnamen auf die von Ihrem Objektspeicherdienst bereitgestellten Werte ein (wir verwenden hier die Werte, die der NYC3-Region von DigitalOcean für Spaces zugeordnet sind):

/backups/mysql/object_storage_config.sh

#!/bin/bash

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

Diese Zeilen definieren zwei Umgebungsvariablen mit den Namen "+ MYACCESSKEY " und " MYSECRETKEY ", um unseren Zugriffs- und unseren geheimen Schlüssel zu speichern. Die Variable ` MYBUCKETNAME ` definiert den Objektspeicherbereich, den wir zum Speichern unserer Sicherungsdateien verwenden möchten. Bucket-Namen müssen universell eindeutig sein, daher müssen Sie einen Namen auswählen, den kein anderer Benutzer ausgewählt hat. Unser Skript überprüft den Bucket-Wert, um festzustellen, ob er bereits von einem anderen Benutzer beansprucht wurde, und erstellt ihn automatisch, wenn er verfügbar ist. Wir " exportieren" die Variablen, die wir definieren, damit alle Prozesse, die wir in unseren Skripten aufrufen, Zugriff auf diese Werte haben.

Die Variablen "+ MYENDPOINTURL " und " MYREGIONNAME " enthalten den API-Endpunkt und die spezifische Regionskennung, die Ihr Objektspeicheranbieter anbietet. Bei DigitalOcean-Räumen ist der Endpunkt " https: //. Digitaloceanspaces.com +". Die verfügbaren Regionen für Spaces finden Sie in der DigitalOcean-Systemsteuerung (zum Zeitpunkt dieses Schreibens ist nur „nyc3“ verfügbar).

Speichern und schließen Sie die Datei, wenn Sie fertig sind.

Jeder, der auf unsere API-Schlüssel zugreifen kann, hat vollständigen Zugriff auf unser Objektspeicherkonto. Daher ist es wichtig, den Zugriff auf die Konfigurationsdatei auf den Benutzer "+ backup " zu beschränken. Wir können dem Benutzer " backup +" und der Gruppe das Eigentumsrecht für die Datei geben und dann alle anderen Zugriffe widerrufen, indem wir Folgendes eingeben:

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

Unsere Datei "+ object_storage_config.sh " sollte jetzt nur für den Benutzer " backup +" zugänglich sein.

Erstellen der Remote-Backup-Skripte

Nachdem wir eine Konfigurationsdatei für den Objektspeicher haben, können wir mit der Erstellung unserer Skripte beginnen. Wir werden die folgenden Skripte erstellen:

  • + object_storage.py +: Dieses Skript ist für die Interaktion mit der Objektspeicher-API verantwortlich, um Buckets zu erstellen, Dateien hochzuladen, Inhalte herunterzuladen und ältere Sicherungen zu bereinigen. Unsere anderen Skripte rufen dieses Skript immer dann auf, wenn sie mit dem Remote-Objektspeicherkonto interagieren müssen.

  • + remote-backup-mysql.sh +: Dieses Skript sichert die MySQL-Datenbanken, indem es die Dateien in einem einzigen Artefakt verschlüsselt und komprimiert und anschließend in den Remote-Objektspeicher hochlädt. Es erstellt zu Beginn eines jeden Tages eine vollständige Sicherung und anschließend jede Stunde eine inkrementelle Sicherung. Es werden automatisch alle Dateien aus dem Remote-Bucket entfernt, die älter als 30 Tage sind.

  • + download-day.sh +: Mit diesem Skript können wir alle Backups herunterladen, die einem bestimmten Tag zugeordnet sind. Da unser Sicherungsskript jeden Morgen eine vollständige Sicherung und anschließend den ganzen Tag über inkrementelle Sicherungen erstellt, kann dieses Skript alle für die Wiederherstellung an einem beliebigen stündlichen Prüfpunkt erforderlichen Ressourcen herunterladen.

Zusammen mit den oben genannten neuen Skripten werden wir die Skripten + extract-mysql.sh + und + prepare-mysql.sh + aus dem vorherigen Handbuch verwenden, um die Wiederherstellung unserer Dateien zu unterstützen. Sie können die Skripte jederzeit im repository für dieses Tutorial auf GitHub anzeigen. Wenn Sie den folgenden Inhalt nicht kopieren und einfügen möchten, können Sie die neuen Dateien direkt von GitHub herunterladen, indem Sie Folgendes eingeben:

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

Überprüfen Sie die Skripts nach dem Herunterladen, um sicherzustellen, dass sie erfolgreich abgerufen wurden und dass Sie die von ihnen ausgeführten Aktionen genehmigen. Wenn Sie zufrieden sind, markieren Sie die Skripte als ausführbar und verschieben Sie sie in das Verzeichnis "+ / usr / local / bin +", indem Sie Folgendes eingeben:

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

Als nächstes werden wir jedes dieser Skripte einrichten und sie detaillierter besprechen.

Erstellen Sie das Script object_storage.py

Wenn Sie das Skript "+ object_storage.py " nicht von GitHub heruntergeladen haben, erstellen Sie eine neue Datei im Verzeichnis " / usr / local / bin " mit dem Namen " object_storage.py +":

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

Kopieren Sie den Skriptinhalt und fügen Sie ihn in die Datei ein:

/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()

Dieses Skript ist für die Verwaltung der Sicherungen in Ihrem Objektspeicherkonto verantwortlich. Es kann Dateien hochladen, Dateien entfernen, alte Backups entfernen und Dateien aus dem Objektspeicher herunterladen. Anstatt direkt mit der Objektspeicher-API zu interagieren, verwenden unsere anderen Skripts die hier definierte Funktionalität, um mit Remote-Ressourcen zu interagieren. Die Befehle, die es definiert, sind:

  • + upload +: Lädt jede der als Argumente übergebenen Dateien in den Objektspeicher hoch. Es können mehrere Dateien angegeben werden.

  • + download: Lädt eine einzelne Datei aus dem Remote-Objektspeicher herunter, die als Argument übergeben wird.

  • + prune +: Entfernt alle Dateien, die älter als ein bestimmtes Alter sind, aus dem Objektspeicherort. Standardmäßig werden Dateien entfernt, die älter als 30 Tage sind. Sie können dies anpassen, indem Sie die Option "+ - Tage " angeben, wenn Sie " prune +" aufrufen.

  • + get_day +: Übergeben Sie den Tag zum Herunterladen als Argument in einem Standard-Datumsformat (unter Verwendung von Anführungszeichen, wenn das Datum Leerzeichen enthält), und das Tool versucht, ihn zu analysieren und alle Dateien von diesem Datum herunterzuladen.

Das Skript versucht, die Anmeldeinformationen für den Objektspeicher und den Bucket-Namen aus Umgebungsvariablen zu lesen. Daher müssen Sie sicherstellen, dass diese aus der Datei "+ object_storage_config.sh " stammen, bevor Sie das Skript " object_storage.py +" aufrufen.

Wenn Sie fertig sind, speichern und schließen Sie die Datei.

Wenn Sie dies noch nicht getan haben, können Sie das Skript ausführbar machen, indem Sie Folgendes eingeben:

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

Nachdem das Skript "+ object_storage.py +" für die Interaktion mit der API verfügbar ist, können wir die Bash-Skripte erstellen, die es zum Sichern und Herunterladen von Dateien verwenden.

Erstellen Sie das Skript remote-backup-mysql.sh

Als nächstes erstellen wir das Skript + remote-backup-mysql.sh +. Dies führt viele der gleichen Funktionen wie das ursprüngliche lokale Sicherungsskript "+ backup-mysql.sh +" aus, mit einer grundlegenderen Organisationsstruktur (da das Verwalten von Sicherungen im lokalen Dateisystem nicht erforderlich ist) und einigen zusätzlichen Schritten zum Hochladen in den Objektspeicher .

Wenn Sie das Skript nicht aus dem Repository heruntergeladen haben, erstellen und öffnen Sie eine Datei mit dem Namen "+ remote-backup-mysql.sh " im Verzeichnis " / usr / local / bin +":

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

Fügen Sie das folgende Skript ein:

/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

Dieses Skript behandelt die eigentliche MySQL-Sicherungsprozedur, steuert den Sicherungszeitplan und entfernt ältere Sicherungen automatisch vom Remotespeicher. Sie können festlegen, wie viele Tage an Backups Sie aufbewahren möchten, indem Sie die Variable "+ days_to_keep +" anpassen.

Das lokale Skript "+ backup-mysql.sh +", das wir im letzten Artikel verwendet haben, unterhielt separate Verzeichnisse für die täglichen Sicherungen. Da wir Backups remote speichern, speichern wir das neueste Backup nur lokal, um den für Backups reservierten Speicherplatz zu minimieren. Frühere Sicherungen können bei Bedarf für die Wiederherstellung aus dem Objektspeicher heruntergeladen werden.

Wie beim vorherigen Skript wird nach Überprüfung einiger grundlegender Anforderungen und Konfiguration des zu erstellenden Sicherungstyps jede Sicherung verschlüsselt und in ein einziges Dateiarchiv komprimiert. Die vorherige Sicherungsdatei wird aus dem lokalen Dateisystem entfernt, und alle Remote-Sicherungen, die älter sind als der in + days_to_keep + definierte Wert, werden entfernt.

Speichern und schließen Sie die Datei, wenn Sie fertig sind. Stellen Sie anschließend sicher, dass das Skript ausführbar ist, indem Sie Folgendes eingeben:

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

Dieses Skript kann als Ersatz für das Skript "+ backup-mysql.sh +" auf diesem System verwendet werden, um von lokalen Sicherungen zu Remote-Sicherungen zu wechseln.

Erstellen Sie das download-day.sh-Skript

Laden Sie abschließend das Skript "+ download-day.sh " im Verzeichnis " / usr / local / bin +" herunter oder erstellen Sie es. Mit diesem Skript können alle mit einem bestimmten Tag verknüpften Sicherungen heruntergeladen werden.

Erstellen Sie die Skriptdatei in Ihrem Texteditor, wenn Sie sie nicht zuvor heruntergeladen haben:

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

Fügen Sie den folgenden Inhalt ein:

/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

Dieses Skript kann aufgerufen werden, um alle Archive eines bestimmten Tages herunterzuladen. Da jeder Tag mit einer vollständigen Sicherung beginnt und über den Rest des Tages inkrementelle Sicherungen sammelt, werden alle relevanten Dateien heruntergeladen, die zum Wiederherstellen eines beliebigen stündlichen Snapshots erforderlich sind.

Das Skript verwendet ein einzelnes Argument, das ein Datum oder ein Tag ist. Es verwendet die Python-Funktion + dateutil.parser.parse +, um eine als Argument angegebene Datumszeichenfolge zu lesen und zu interpretieren. Die Funktion ist ziemlich flexibel und kann Datumsangaben in einer Vielzahl von Formaten interpretieren, einschließlich relativer Zeichenfolgen wie z. B. "Freitag". Um Unklarheiten zu vermeiden, ist es jedoch am besten, klarere Daten zu verwenden. Achten Sie darauf, Datumsangaben in Anführungszeichen zu setzen, wenn das gewünschte Format Leerzeichen enthält.

Wenn Sie fortfahren möchten, speichern und schließen Sie die Datei. Machen Sie das Skript ausführbar, indem Sie Folgendes eingeben:

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

Wir haben jetzt die Möglichkeit, die Sicherungsdateien für ein bestimmtes Datum, an dem die Wiederherstellung erfolgen soll, aus dem Objektspeicher herunterzuladen.

Testen der Remote-MySQL-Sicherungs- und -Download-Skripte

Nachdem wir unsere Skripte installiert haben, sollten wir testen, ob sie wie erwartet funktionieren.

Führen Sie eine vollständige Sicherung durch

Rufen Sie zunächst das Skript "+ remote-mysql-backup.sh " mit dem Benutzer " backup +" auf. Da dies das erste Mal ist, dass wir diesen Befehl ausführen, sollte eine vollständige Sicherung unserer MySQL-Datenbank erstellt werden.

sudo -u backup remote-backup-mysql.sh

Wenn alles gut geht, wird eine Ausgabe ähnlich der folgenden angezeigt:

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

Dies zeigt an, dass eine vollständige Sicherung im Verzeichnis "+ / backups / mysql / working" erstellt wurde. Es wurde auch mit dem in der Datei + object_storage_config.sh + definierten Bucket in den Remote-Objektspeicher hochgeladen.

Wenn wir uns das Verzeichnis + / backups / mysql / working + ansehen, sehen wir Dateien, die denen des Skripts + backup-mysql.sh + aus dem letzten Handbuch ähneln:

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

Die Datei "+ backup-progress.log " enthält die Ausgabe des Befehls " xtrabackup ", während " xtrabackup_checkpoints " und " xtrabackup_info +" Informationen zu den verwendeten Optionen, zu Art und Umfang der Sicherung und zu anderen Metadaten enthalten.

Führen Sie eine Teilsicherung durch

Nehmen Sie eine kleine Änderung in der Tabelle "+ Geräte" vor, um zusätzliche Daten zu erstellen, die in unserer ersten Sicherung nicht gefunden wurden. Wir können eine neue Zeile in die Tabelle eingeben, indem wir Folgendes eingeben:

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

Geben Sie das Administratorkennwort Ihrer Datenbank ein, um den neuen Datensatz hinzuzufügen.

Jetzt können wir ein zusätzliches Backup erstellen. Wenn wir das Skript erneut aufrufen, sollte ein inkrementelles Backup erstellt werden, solange es noch am selben Tag wie das vorherige Backup ist (entsprechend der Uhr des Servers):

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

Die obige Ausgabe gibt an, dass die Sicherung lokal im selben Verzeichnis erstellt und erneut in den Objektspeicher hochgeladen wurde. Wenn wir das Verzeichnis "+ / backups / mysql / working" überprüfen, werden wir feststellen, dass die neue Sicherung vorhanden ist und dass die vorherige Sicherung entfernt wurde:

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

Da unsere Dateien remote hochgeladen werden, können Sie durch Löschen der lokalen Kopie den verwendeten Speicherplatz reduzieren.

Laden Sie die Backups von einem bestimmten Tag herunter

Da unsere Backups remote gespeichert werden, müssen wir die Remote-Dateien herunterladen, wenn wir unsere Dateien wiederherstellen müssen. Dazu können wir das Skript + download-day.sh + verwenden.

Beginnen Sie, indem Sie ein Verzeichnis erstellen und dieses dann in ein Verzeichnis verschieben, in das der Benutzer sicher schreiben kann:

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

Rufen Sie als Nächstes das Skript "+ download-day.sh " als Benutzer " backup +" auf. Geben Sie den Tag des Archivs ein, das Sie herunterladen möchten. Das Datumsformat ist ziemlich flexibel, aber es ist am besten, zu versuchen, eindeutig zu sein:

sudo -u backup download-day.sh ""

Wenn es Archive gibt, die mit dem von Ihnen angegebenen Datum übereinstimmen, werden sie in das aktuelle Verzeichnis heruntergeladen:

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

Stellen Sie sicher, dass die Dateien in das lokale Dateisystem heruntergeladen wurden:

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

Die komprimierten, verschlüsselten Archive befinden sich jetzt wieder auf dem Server.

Backups extrahieren und vorbereiten

Sobald die Dateien gesammelt sind, können wir sie genauso verarbeiten wie lokale Sicherungen.

Übergeben Sie zunächst die Dateien "+ .xbstream " mit dem Benutzer " backup " an das Skript " extract-mysql.sh +":

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

Dies entschlüsselt und dekomprimiert die Archive in ein Verzeichnis mit dem Namen "+ restore ". Geben Sie dieses Verzeichnis ein und bereiten Sie die Dateien mit dem Skript ` prepare-mysql.sh +` vor:

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

Die vollständige Sicherung im Verzeichnis "+ / tmp / backup_archives / restore +" sollte jetzt vorbereitet werden. Wir können den Anweisungen in der Ausgabe folgen, um die MySQL-Daten auf unserem System wiederherzustellen.

Stellen Sie die Sicherungsdaten im MySQL-Datenverzeichnis wieder her

Bevor wir die Sicherungsdaten wiederherstellen, müssen wir die aktuellen Daten aus dem Weg räumen.

Fahren Sie zunächst MySQL herunter, um eine Beschädigung der Datenbank oder einen Absturz des Dienstes beim Ersetzen der Datendateien zu vermeiden.

sudo systemctl stop mysql

Als nächstes können wir das aktuelle Datenverzeichnis in das Verzeichnis + / tmp + verschieben. Auf diese Weise können wir es problemlos zurücksetzen, wenn bei der Wiederherstellung Probleme auftreten. Da wir die Dateien im letzten Artikel nach "+ / tmp / mysql" verschoben haben, können wir diesmal die Dateien nach "+ / tmp / mysql-remote +" verschieben:

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

Als nächstes erstellen Sie ein leeres + / var / lib / mysql Verzeichnis:

sudo mkdir /var/lib/mysql

Jetzt können wir den Befehl + xtrabackup + restore eingeben, der mit dem Befehl + prepare-mysql.sh + bereitgestellt wurde, um die Sicherungsdateien in das Verzeichnis + / var / lib / mysql + zu kopieren:

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

Ändern Sie nach Abschluss des Vorgangs die Verzeichnisberechtigungen und den Besitz, um sicherzustellen, dass der MySQL-Prozess Zugriff hat:

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

Wenn dies abgeschlossen ist, starten Sie MySQL erneut und überprüfen Sie, ob unsere Daten ordnungsgemäß wiederhergestellt wurden:

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  |
+----+---------+-------+--------+

Die Daten sind verfügbar, was darauf hinweist, dass sie erfolgreich wiederhergestellt wurden.

Nach dem Wiederherstellen Ihrer Daten ist es wichtig, das Wiederherstellungsverzeichnis zu löschen. Zukünftige inkrementelle Sicherungen können nach der Vorbereitung nicht mehr auf die vollständige Sicherung angewendet werden. Daher sollten wir sie entfernen. Darüber hinaus sollten die Sicherungsverzeichnisse aus Sicherheitsgründen nicht unverschlüsselt auf der Festplatte belassen werden:

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

Wenn Sie das nächste Mal saubere Kopien der Sicherungsverzeichnisse benötigen, können Sie diese erneut aus den Sicherungsarchivdateien extrahieren.

Erstellen eines Cron-Jobs zum stündlichen Ausführen von Sicherungen

Wir haben einen '+ cron'-Job erstellt, um Ihre Datenbank im letzten Handbuch automatisch lokal zu sichern. Wir werden einen neuen Cron-Job einrichten, um Remote-Backups zu erstellen und dann den lokalen Backup-Job zu deaktivieren. Wir können bei Bedarf einfach zwischen lokalen und Remote-Backups wechseln, indem wir die Skripte + cron + aktivieren oder deaktivieren.

Erstellen Sie zunächst eine Datei mit dem Namen "+ remote-backup-mysql" im Verzeichnis "+ / etc / cron.hourly":

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

Im Inneren rufen wir unser Skript "+ remote-backup-mysql.sh " mit dem Benutzer " backup " über den Befehl " systemd-cat " auf, mit dem wir die Ausgabe in " journald +" protokollieren können:

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

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

Speichern und schließen Sie die Datei, wenn Sie fertig sind.

Wir werden unseren neuen Cron-Job aktivieren und den alten Job deaktivieren, indem wir das Berechtigungsbit "+ executable +" für beide Dateien bearbeiten:

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

Testen Sie den neuen Remote-Sicherungsjob, indem Sie das Skript manuell ausführen:

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

Sobald die Eingabeaufforderung zurückkehrt, können wir die Protokolleinträge mit + journalctl + überprüfen:

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

Versuchen Sie es in ein paar Stunden erneut, um sicherzustellen, dass weitere Sicherungen planmäßig erstellt werden.

Den Extraktionsschlüssel sichern

Eine letzte Überlegung, mit der Sie sich befassen müssen, ist das Sichern des Verschlüsselungsschlüssels (zu finden unter + / backups / mysql / encryption_key +).

Der Verschlüsselungsschlüssel ist erforderlich, um alle mit diesem Verfahren gesicherten Dateien wiederherzustellen. Durch das Speichern des Verschlüsselungsschlüssels am selben Speicherort wie die Datenbankdateien wird jedoch der durch die Verschlüsselung gebotene Schutz aufgehoben. Aus diesem Grund ist es wichtig, eine Kopie des Verschlüsselungsschlüssels an einem separaten Speicherort aufzubewahren, damit Sie die Sicherungsarchive weiterhin verwenden können, wenn Ihr Datenbankserver ausfällt oder neu erstellt werden muss.

Während eine vollständige Sicherungslösung für Nicht-Datenbankdateien außerhalb des Bereichs dieses Artikels liegt, können Sie den Schlüssel zur sicheren Aufbewahrung auf Ihren lokalen Computer kopieren. Zeigen Sie dazu den Inhalt der Datei an, indem Sie Folgendes eingeben:

sudo less /backups/mysql/encryption_key

Öffnen Sie eine Textdatei auf Ihrem lokalen Computer und fügen Sie den Wert ein. Wenn Sie Backups jemals auf einem anderen Server wiederherstellen müssen, kopieren Sie den Inhalt der Datei nach "+ / backups / mysql / encryption_key +" auf dem neuen Computer, richten Sie das in diesem Handbuch beschriebene System ein und stellen Sie es mit den bereitgestellten Skripten wieder her.

Fazit

In diesem Handbuch wird erläutert, wie Sie stündliche Sicherungen einer MySQL-Datenbank vornehmen und diese automatisch in einen Remote-Objektspeicher hochladen. Das System erstellt jeden Morgen eine vollständige Sicherung und anschließend stündlich inkrementelle Sicherungen, um die Wiederherstellung an jedem stündlichen Prüfpunkt zu ermöglichen. Bei jeder Ausführung des Sicherungsskripts wird im Objektspeicher nach Sicherungen gesucht, die älter als 30 Tage sind, und diese werden entfernt.