Thứ năm, 19/10/2017 | 00:00 GMT+7

Cách backup database MySQL vào lưu trữ đối tượng với Percona trên Ubuntu 16.04

Database thường lưu trữ một số thông tin có giá trị nhất trong cơ sở hạ tầng của bạn. Do đó, điều quan trọng là phải có các bản backup tin cậy để đề phòng mất dữ liệu trong trường hợp xảy ra sự cố hoặc lỗi phần cứng.

Các công cụ backup Percona XtraBackup cung cấp phương pháp thực hiện backup “nóng” dữ liệu MySQL trong khi hệ thống đang chạy. Họ thực hiện việc này bằng cách sao chép các file dữ liệu ở phạm vi hệ thống file và sau đó thực hiện khôi phục sự cố để đạt được tính nhất quán trong tập dữ liệu.

Trong hướng dẫn trước , ta đã cài đặt các tiện ích backup của Percona và tạo một loạt các tập lệnh để thực hiện backup local luân phiên. Điều này hoạt động tốt để backup dữ liệu vào một ổ đĩa khác hoặc ổ đĩa được gắn mạng để xử lý các sự cố với máy database của bạn. Tuy nhiên, trong hầu hết các trường hợp, dữ liệu nên được backup bên ngoài trang web để có thể dễ dàng bảo trì và khôi phục. Trong hướng dẫn này, ta sẽ mở rộng hệ thống backup trước đây của bạn để tải các file backup đã được nén, mã hóa lên dịch vụ lưu trữ đối tượng. Ta sẽ sử dụng DigitalOcean Spaces làm ví dụ trong hướng dẫn này, nhưng các quy trình cơ bản cũng có thể áp dụng cho các giải pháp lưu trữ đối tượng tương thích S3 khác.

Yêu cầu

Trước khi bắt đầu hướng dẫn này, bạn cần một server database MySQL được cấu hình với giải pháp backup Percona local được nêu trong hướng dẫn trước của ta . Toàn bộ hướng dẫn bạn cần làm theo là:

Ngoài các hướng dẫn trên, bạn cũng cần tạo khóa truy cập và khóa bí mật để tương tác với account lưu trữ đối tượng của bạn bằng API. Nếu bạn đang sử dụng DigitalOcean , bạn có thể tìm hiểu cách tạo các thông tin xác thực này theo hướng dẫn Cách tạo DigitalOcean và Khóa API của ta . Bạn cần lưu cả khóa truy cập API và giá trị bí mật của API.

Khi bạn hoàn thành các hướng dẫn trước đó, hãy đăng nhập lại vào server của bạn với quyền là user sudo của bạn để bắt đầu.

Cài đặt các phụ thuộc

Ta sẽ sử dụng một số tập lệnh Python và Bash để tạo bản backup của ta và tải chúng lên bộ lưu trữ đối tượng từ xa để bảo vệ an toàn. Ta cần thư viện Python boto3 để tương tác với API lưu trữ đối tượng . Ta có thể download cái này bằng pip , trình quản lý gói của Python.

Cập nhật OS local của ta và sau đó cài đặt version Python 3 của pip từ repository mặc định của Ubuntu bằng cách sử dụng apt-get bằng lệnh :

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

Vì Ubuntu duy trì vòng đời gói của riêng mình, version của pip trong repository của Ubuntu không được giữ đồng bộ với các bản phát hành gần đây. Tuy nhiên, ta có thể cập nhật lên version pip mới hơn bằng chính công cụ này. Ta sẽ sử dụng sudo để cài đặt trên phạm vi global và bao gồm cờ -H để đặt biến $HOME thành giá trị mà pip mong đợi:

  • sudo -H pip3 install --upgrade pip

Sau đó, ta có thể cài đặt boto3 cùng với module pytz , ta sẽ sử dụng để so sánh thời gian một cách chính xác bằng cách sử dụng định dạng nhận biết độ lệch mà API lưu trữ đối tượng trả về:

  • sudo -H pip3 install boto3 pytz

Bây giờ ta sẽ có tất cả các module Python mà ta cần để tương tác với API lưu trữ đối tượng.

Tạo file cấu hình lưu trữ đối tượng

Các tập lệnh backup và download của ta cần phải tương tác với API lưu trữ đối tượng để tải file lên và download các tạo tác backup cũ hơn khi ta cần khôi phục. Họ cần sử dụng các khóa truy cập mà ta đã tạo trong phần yêu cầu . Thay vì giữ các giá trị này trong chính các tập lệnh, ta sẽ đặt chúng vào một file chuyên dụng có thể đọc được bởi các tập lệnh của ta . Bằng cách này, ta có thể chia sẻ tập lệnh của bạn mà không sợ bị lộ thông tin đăng nhập và ta có thể khóa thông tin đăng nhập nhiều hơn chính tập lệnh.

Trong hướng dẫn cuối cùng , ta đã tạo folder /backups/mysql để lưu trữ các bản backup và khóa mã hóa của ta . Ta sẽ đặt file cấu hình ở đây cùng với các nội dung khác của ta . Tạo một file có tên object_storage_config.sh :

  • sudo nano /backups/mysql/object_storage_config.sh

Bên trong, hãy dán các nội dung sau, thay đổi khóa truy cập và khóa bí mật thành các giá trị bạn nhận được từ account lưu trữ đối tượng và tên group thành một giá trị duy nhất. Đặt URL điểm cuối và tên vùng thành các giá trị được cung cấp bởi dịch vụ lưu trữ đối tượng của bạn ( ta sẽ sử dụng các giá trị được liên kết với vùng NYC3 của DigitalOcean cho Spaces tại đây):

/backups/mysql/object_storage_config.sh
#!/bin/bash  export MYACCESSKEY="my_access_key" export MYSECRETKEY="my_secret_key" export MYBUCKETNAME="your_unique_bucket_name" export MYENDPOINTURL="https://nyc3.digitaloceanspaces.com" export MYREGIONNAME="nyc3" 

Các dòng này xác định hai biến môi trường được gọi là MYACCESSKEYMYSECRETKEY để giữ các khóa truy cập và khóa bí mật của ta tương ứng. Biến MYBUCKETNAME xác định group lưu trữ đối tượng mà ta muốn sử dụng để lưu trữ các file backup của bạn . Tên group phải là duy nhất trên phạm vi global , vì vậy bạn phải chọn tên mà chưa có user nào khác chọn. Tập lệnh của ta sẽ kiểm tra giá trị group để xem liệu nó đã được user khác xác nhận quyền sở hữu hay chưa và tự động tạo nếu nó có sẵn. Ta export các biến ta xác định để bất kỳ quy trình nào ta gọi từ bên trong các tập lệnh của ta sẽ có quyền truy cập vào các giá trị này.

Các MYENDPOINTURLMYREGIONNAME chứa điểm cuối API và số nhận dạng vùng cụ thể do nhà cung cấp lưu trữ đối tượng của bạn cung cấp. Đối với DigitalOcean , điểm cuối sẽ là https:// region_name .digitaloceanspaces.com . Bạn có thể tìm thấy các khu vực có sẵn cho Spaces trong Control panel DigitalOcean (tại thời điểm viết bài này, chỉ có “nyc3”).

Lưu file khi bạn hoàn tất.

Bất kỳ ai có thể truy cập khóa API của ta đều có toàn quyền truy cập vào account lưu trữ đối tượng của ta , vì vậy điều quan trọng là hạn chế quyền truy cập vào file cấu hình đối với user backup . Ta có thể cấp cho user backup và quyền sở hữu group của file , sau đó thu hồi tất cả các quyền truy cập khác bằng lệnh :

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

object_storage_config.sh của ta giờ đây chỉ user backup mới có thể truy cập được.

Tạo tập lệnh backup từ xa

Bây giờ ta đã có file cấu hình lưu trữ đối tượng, ta có thể tiếp tục và bắt đầu tạo các tập lệnh của bạn . Ta sẽ tạo các tập lệnh sau:

  • object_storage.py : Tập lệnh này chịu trách nhiệm tương tác với API lưu trữ đối tượng để tạo group , tải file lên, download nội dung và cắt bớt các bản backup cũ hơn. Các tập lệnh khác của ta sẽ gọi tập lệnh này bất cứ lúc nào chúng cần để tương tác với account lưu trữ đối tượng từ xa.
  • remote-backup-mysql.sh : Tập lệnh này backup database MySQL bằng cách mã hóa và nén các file thành một cấu phần duy nhất và sau đó tải nó lên repository đối tượng từ xa. Nó tạo một bản backup đầy đủ vào đầu mỗi ngày và sau đó là một bản backup gia tăng mỗi giờ sau đó. Nó tự động cắt tất cả các file từ containers từ xa cũ hơn 30 ngày.
  • download-day.sh : Tập lệnh này cho phép ta download tất cả các bản backup liên quan đến một ngày nhất định. Vì tập lệnh backup của ta tạo một bản backup đầy đủ vào mỗi buổi sáng và sau đó backup gia tăng trong suốt cả ngày, tập lệnh này có thể download tất cả các nội dung cần thiết để khôi phục về bất kỳ điểm kiểm tra hàng giờ nào.

Cùng với các kịch bản mới trên, ta sẽ tận dụng extract-mysql.shprepare-mysql.sh kịch bản từ hướng dẫn trước để giúp khôi phục file của ta . Bạn có thể xem các tập lệnh trong kho cho hướng dẫn này trên GitHub bất kỳ lúc nào. Nếu bạn không muốn copy paste nội dung bên dưới, bạn có thể download các file mới trực tiếp từ GitHub bằng lệnh :

  • 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

Hãy nhớ kiểm tra các tập lệnh sau khi download đảm bảo chúng được truy xuất thành công và bạn chấp thuận các hành động mà chúng sẽ thực hiện. Nếu bạn hài lòng, hãy đánh dấu các tập lệnh là có thể thực thi và sau đó di chuyển chúng vào folder /usr/local/bin bằng lệnh :

  • 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

Tiếp theo, ta sẽ cài đặt từng script này và thảo luận chi tiết hơn về chúng.

Tạo tập lệnh object_storage.py

Nếu bạn không download tập lệnh object_storage.py từ GitHub, hãy tạo một file mới trong folder /usr/local/bin tên object_storage.py :

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

Copy paste nội dung tập lệnh vào file :

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

Tập lệnh này chịu trách nhiệm quản lý các bản backup trong account lưu trữ đối tượng của bạn. Nó có thể tải lên file , xóa file , cắt bỏ các bản backup cũ và tải file xuống từ bộ nhớ đối tượng. Thay vì tương tác trực tiếp với API lưu trữ đối tượng, các tập lệnh khác của ta sẽ sử dụng chức năng được xác định ở đây để tương tác với các tài nguyên từ xa. Các lệnh nó định nghĩa là:

  • upload : Tải lên bộ nhớ đối tượng từng file được chuyển vào dưới dạng đối số. Nhiều file có thể được chỉ định.
  • download : Download một file duy nhất từ bộ nhớ đối tượng từ xa, file này được chuyển vào dưới dạng đối số.
  • prune : Loại bỏ mọi file cũ hơn một độ tuổi nhất định khỏi vị trí lưu trữ đối tượng. Theo mặc định, điều này sẽ xóa các file cũ hơn 30 ngày. Bạn có thể điều chỉnh điều này bằng cách chỉ định tùy chọn --days-to-keep khi gọi prune .
  • get_day : get_day ngày để download dưới dạng đối số sử dụng định dạng ngày chuẩn (sử dụng dấu ngoặc kép nếu ngày có khoảng trắng trong đó) và công cụ sẽ cố gắng phân tích cú pháp và download tất cả các file từ ngày đó.

Tập lệnh cố gắng đọc thông tin xác thực lưu trữ đối tượng và tên group từ các biến môi trường, vì vậy ta cần đảm bảo chúng được điền từ file object_storage_config.sh trước khi gọi tập lệnh object_storage.py .

Khi bạn hoàn tất, hãy lưu file .

Tiếp theo, nếu bạn chưa làm như vậy, hãy làm cho tập lệnh có thể thực thi được bằng lệnh :

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

Bây giờ tập lệnh object_storage.py có sẵn để tương tác với API, ta có thể tạo các tập lệnh Bash sử dụng nó để backup và download file .

Tạo tập lệnh remote-backup-mysql.sh

Tiếp theo, ta sẽ tạo tập lệnh remote-backup-mysql.sh . Điều này sẽ thực hiện nhiều chức năng giống như tập lệnh backup local backup-mysql.sh root , với cấu trúc tổ chức cơ bản hơn (vì không cần duy trì các bản backup trên hệ thống file local ) và một số bước bổ sung để tải lên bộ nhớ đối tượng.

Nếu bạn không download tập lệnh từ repository , hãy tạo và mở một file có tên remote-backup-mysql.sh trong folder /usr/local/bin :

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

Bên trong, dán tập lệnh sau:

/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 

Tập lệnh này xử lý quy trình backup MySQL thực tế, kiểm soát lịch trình backup và tự động xóa các bản backup cũ hơn khỏi bộ nhớ từ xa. Bạn có thể chọn số ngày backup bạn muốn tiếp tục bằng cách điều chỉnh biến days_to_keep .

Tập lệnh backup-mysql.sh local mà ta đã sử dụng trong bài viết trước duy trì các folder riêng biệt cho các bản backup hàng ngày. Vì ta đang lưu trữ các bản backup từ xa, ta sẽ chỉ lưu trữ local bản backup mới nhất để giảm thiểu dung lượng ổ đĩa dành cho các bản backup . Các bản backup trước đó có thể được download từ bộ lưu trữ đối tượng nếu cần để khôi phục.

Như với tập lệnh trước, sau khi kiểm tra xem một số yêu cầu cơ bản đã được thỏa mãn và cấu hình loại backup sẽ được thực hiện, ta mã hóa và nén mỗi bản backup thành một file lưu trữ duy nhất. Tệp backup trước đó bị xóa khỏi hệ thống file local và mọi bản backup từ xa cũ hơn giá trị được xác định trong days_to_keep sẽ bị xóa.

Lưu file khi bạn hoàn tất. Sau đó, hãy đảm bảo tập lệnh có thể thực thi được bằng lệnh :

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

Tập lệnh này được dùng thay thế cho tập lệnh backup-mysql.sh trên hệ thống này để chuyển từ tạo backup local sang backup từ xa.

Tạo tập lệnh download-day.sh

Cuối cùng, download hoặc tạo tập lệnh download-day.sh trong folder /usr/local/bin . Tập lệnh này được dùng để download tất cả các bản backup liên quan đến một ngày cụ thể.

Tạo file tập lệnh trong editor của bạn nếu bạn không download trước đó:

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

Bên trong, dán các nội dung sau:

/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 

Tập lệnh này có thể được gọi để download tất cả các repository từ một ngày cụ thể. Vì mỗi ngày bắt đầu với một bản backup đầy đủ và tích lũy các bản backup gia tăng trong suốt thời gian còn lại của ngày, điều này sẽ download tất cả các file liên quan cần thiết để khôi phục vào bất kỳ ảnh chụp nhanh hàng giờ nào.

Tập lệnh nhận một đối số duy nhất là ngày hoặc ngày. Nó sử dụng hàm dateutil.parser.parse của Python để đọc và diễn giải chuỗi ngày được cung cấp dưới dạng đối số. Hàm này khá linh hoạt và có thể diễn giải ngày tháng ở nhiều định dạng khác nhau, bao gồm cả các chuỗi tương đối như “Friday” chẳng hạn. Tuy nhiên, để tránh sự mơ hồ, cách tốt nhất là sử dụng các ngày được xác định rõ hơn. Đảm bảo đặt ngày tháng trong dấu ngoặc kép nếu định dạng bạn muốn sử dụng có chứa khoảng trắng.

Khi đã sẵn sàng để tiếp tục, hãy lưu file . Làm cho tập lệnh có thể thực thi bằng lệnh :

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

Bây giờ ta có khả năng download các file backup từ bộ nhớ đối tượng cho một ngày cụ thể khi ta muốn khôi phục.

Kiểm tra tập lệnh download và backup MySQL từ xa

Bây giờ ta đã có các tập lệnh của bạn , ta nên kiểm tra đảm bảo chúng hoạt động như mong đợi.

Thực hiện backup đầy đủ

Bắt đầu bằng cách gọi tập lệnh remote-mysql-backup.sh với user backup . Vì đây là lần đầu tiên ta chạy lệnh này, nó sẽ tạo một bản backup đầy đủ cho database MySQL của ta .

  • sudo -u backup remote-backup-mysql.sh

Lưu ý: Nếu bạn nhận được lỗi cho biết rằng tên group bạn chọn đã được sử dụng, bạn sẽ phải chọn một tên khác. Thay đổi giá trị của MYBUCKETNAME trong /backups/mysql/object_storage_config.sh file và xóa các folder backup local ( sudo rm -rf /backups/mysql/working ) để các kịch bản có thể cố gắng một bản backup đầy đủ với tên xô mới. Khi đã sẵn sàng , hãy chạy lại lệnh trên để thử lại.

Nếu mọi thứ suôn sẻ, bạn sẽ thấy kết quả tương tự như sau:

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

Điều này cho biết một bản backup đầy đủ đã được tạo trong folder /backups/mysql/working . Nó cũng đã được tải lên bộ nhớ đối tượng từ xa bằng cách sử dụng group được xác định trong file object_storage_config.sh .

Nếu ta nhìn vào folder /backups/mysql/working , ta có thể thấy các file tương tự như các file được tạo bởi tập lệnh backup-mysql.sh từ hướng dẫn cuối cùng:

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

Tệp backup-progress.log xtrabackup chứa kết quả từ lệnh xtrabackup , trong khi xtrabackup_checkpointsxtrabackup_info chứa thông tin về các tùy chọn được sử dụng, loại và phạm vi backup và các metadata khác.

Thực hiện backup gia tăng

Hãy thực hiện một thay đổi nhỏ đối với bảng equipment của ta để tạo thêm dữ liệu không có trong bản backup đầu tiên của ta . Ta có thể nhập một hàng mới trong bảng bằng lệnh :

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

Nhập password quản trị database của bạn để thêm bản ghi mới.

Bây giờ, ta có thể thực hiện một bản backup bổ sung. Khi ta gọi lại tập lệnh, một bản backup gia tăng sẽ được tạo miễn là nó vẫn cùng ngày với bản backup trước đó (theo đồng hồ của server ):

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

Kết quả ở trên cho biết bản backup đã được tạo trong cùng một folder local và lại được tải lên bộ nhớ đối tượng. Nếu ta kiểm tra folder /backups/mysql/working , ta sẽ thấy rằng bản backup mới hiện có và bản backup trước đó đã bị xóa:

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

Vì các file của ta được tải lên từ xa nên việc xóa bản sao local sẽ giúp giảm dung lượng ổ đĩa được sử dụng.

Download các bản backup từ một ngày đã chỉ định

Vì các bản backup của ta được lưu trữ từ xa, ta cần phải kéo các file từ xa xuống nếu cần khôi phục file của bạn . Để làm điều này, ta có thể sử dụng tập lệnh download-day.sh .

Bắt đầu bằng cách tạo và sau đó chuyển vào folder mà user backup có thể ghi vào một cách an toàn:

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

Tiếp theo, gọi tập lệnh download-day.sh làm user backup . Chuyển qua ngày lưu trữ bạn muốn download . Định dạng ngày khá linh hoạt, nhưng tốt nhất bạn nên cố gắng rõ ràng:

  • sudo -u backup download-day.sh "Oct. 17"

Nếu có các tài liệu lưu trữ trùng với ngày bạn đã cung cấp, chúng sẽ được download folder hiện tại:

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

Xác minh các file đã được download hệ thống file local :

  • ls
Output
full-10-17-2017_19-09-30.xbstream incremental-10-17-2017_19-19-20.xbstream

Các file lưu trữ được nén, mã hóa hiện đã trở lại trên server .

Extract và chuẩn bị backup

Sau khi các file được thu thập, ta có thể xử lý chúng giống như cách ta xử lý các bản backup local .

Thứ nhất, vượt qua .xbstream file vào extract-mysql.sh kịch bản bằng cách sử dụng backup user:

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

Thao tác này sẽ giải mã và extract các repository thành một folder được gọi là restore . Nhập folder đó và chuẩn bị các file bằng tập lệnh prepare-mysql.sh :

  • cd restore
  • sudo -u backup prepare-mysql.sh
Output
Backup 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

Bản backup đầy đủ trong /tmp/backup_archives/restore bây giờ nên được chuẩn bị. Ta có thể làm theo các hướng dẫn trong kết quả để khôi phục dữ liệu MySQL trên hệ thống của ta .

Khôi phục dữ liệu backup vào Thư mục dữ liệu MySQL

Trước khi khôi phục dữ liệu backup , ta cần di chuyển dữ liệu hiện tại ra khỏi đường đi.

Bắt đầu bằng cách tắt MySQL để tránh làm hỏng database hoặc làm hỏng dịch vụ khi ta thay thế các file dữ liệu của nó.

  • sudo systemctl stop mysql

Tiếp theo, ta có thể di chuyển folder dữ liệu hiện tại sang /tmp . Bằng cách này, ta có thể dễ dàng di chuyển nó trở lại nếu quá trình khôi phục có vấn đề. Vì ta đã di chuyển file sang /tmp/mysql trong bài viết trước, ta có thể di chuyển file sang /tmp/mysql-remote lần này:

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

Tiếp theo, tạo lại một folder /var/lib/mysql :

  • sudo mkdir /var/lib/mysql

Bây giờ, ta có thể gõ lệnh khôi phục xtrabackup mà lệnh prepare-mysql.sh đã cung cấp để sao chép các file backup vào folder /var/lib/mysql :

  • sudo xtrabackup --copy-back --target-dir=/tmp/backup_archives/restore/full-10-17-2017_19-09-30

Khi hoàn tất , hãy sửa đổi quyền và quyền sở hữu folder đảm bảo rằng quá trình MySQL có quyền truy cập:

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

Khi quá trình này kết thúc, hãy khởi động lại MySQL và kiểm tra xem dữ liệu của ta đã được khôi phục đúng cách chưa:

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

Dữ liệu có sẵn, cho biết rằng nó đã được khôi phục thành công.

Sau khi khôi phục dữ liệu , điều quan trọng là phải quay lại và xóa folder khôi phục. Các bản backup gia tăng trong tương lai không thể được áp dụng cho bản backup đầy đủ khi nó đã được chuẩn bị, vì vậy ta nên xóa nó. Hơn nữa, không nên để các folder backup không được mã hóa trên đĩa vì lý do bảo mật:

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

Lần tới khi ta cần bản sao sạch của các folder backup , ta có thể extract chúng từ các file lưu trữ backup .

Tạo công việc Cron để chạy backup hàng giờ

Ta đã tạo một cron việc cron để tự động backup database của ta local trong hướng dẫn cuối cùng. Ta sẽ cài đặt một cron việc cron mới để thực hiện backup từ xa và sau đó vô hiệu hóa công việc backup local . Ta có thể dễ dàng chuyển đổi giữa các bản backup local và từ xa khi cần thiết bằng cách bật hoặc tắt các tập lệnh cron .

Để bắt đầu, hãy tạo một file có tên là remote-backup-mysql trong folder /etc/cron.hourly :

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

Bên trong, ta sẽ gọi tập lệnh remote-backup-mysql.sh của remote-backup-mysql.sh với user backup thông qua lệnh systemd-cat , cho phép ta ghi lại kết quả kết quả cho 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 

Lưu file khi bạn hoàn tất.

Ta sẽ kích hoạt cron việc cron mới của bạn và vô hiệu hóa công việc cũ bằng cách thao tác bit quyền executable trên cả hai file :

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

Kiểm tra công việc backup từ xa mới bằng cách thực thi tập lệnh theo cách thủ công:

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

Sau khi dấu nhắc trả về, ta có thể kiểm tra các mục log bằng 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 "your_bucket_name" 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 

Kiểm tra lại sau vài giờ đảm bảo rằng các bản backup bổ sung đang được thực hiện đúng lịch trình.

Backup khóa extract

Một cân nhắc cuối cùng mà bạn sẽ phải xử lý là làm thế nào để backup khóa mã hóa (tìm thấy tại /backups/mysql/encryption_key ).

Khóa mã hóa được yêu cầu để khôi phục các file nào được backup bằng quy trình này, nhưng việc lưu trữ khóa mã hóa ở cùng vị trí với file database sẽ loại bỏ khả năng bảo vệ do mã hóa cung cấp. Do đó, điều quan trọng là phải giữ một bản sao của khóa mã hóa ở một vị trí riêng biệt để bạn vẫn có thể sử dụng các repository dự phòng nếu server database của bạn bị lỗi hoặc cần được xây dựng lại.

Mặc dù giải pháp backup hoàn chỉnh cho các file không phải database nằm ngoài phạm vi của bài viết này, bạn có thể sao chép khóa vào máy tính local của bạn để lưu giữ an toàn. Để làm như vậy, hãy xem nội dung của file bằng lệnh :

  • sudo less /backups/mysql/encryption_key

Mở file văn bản trên máy tính local của bạn và dán giá trị vào bên trong. Nếu bạn cần khôi phục các bản backup trên một server khác, hãy sao chép nội dung của file vào /backups/mysql/encryption_key trên máy mới, cài đặt hệ thống được nêu trong hướng dẫn này, sau đó khôi phục bằng các tập lệnh được cung cấp.

Kết luận

Trong hướng dẫn này, ta đã trình bày cách thực hiện backup database MySQL hàng giờ và tải chúng tự động lên không gian lưu trữ đối tượng từ xa. Hệ thống sẽ backup toàn bộ vào mỗi buổi sáng và sau đó backup gia tăng hàng giờ sau đó để cung cấp khả năng khôi phục về bất kỳ điểm kiểm tra hàng giờ nào. Mỗi khi chạy tập lệnh backup , nó sẽ kiểm tra các bản backup trong bộ nhớ đối tượng cũ hơn 30 ngày và xóa chúng.


Tags:

Các tin liên quan

Cách cài đặt Lighttpd với MySQL và PHP trên FreeBSD 11.0
2017-09-18
Cách cấu hình backup MySQL với Percona XtraBackup trên Ubuntu 16.04
2017-08-30
Cách cấu hình sao chép nhóm MySQL trên Ubuntu 16.04
2017-04-13
Cách cài đặt MySQL mới nhất trên Ubuntu 16.04
2017-04-06
Cách cấu hình SSL / TLS cho MySQL trên Ubuntu 16.04
2017-03-20
Cách Import, Export database trong MySQL hoặc MariaDB
2016-12-21
Cách thay đổi thư mục dữ liệu MySQL sang vị trí mới trên CentOS 7
2016-12-19
Cách thay đổi thư mục dữ liệu MySQL sang vị trí mới bằng cách sử dụng liên kết biểu tượng
2016-12-02
Cách cài đặt MySQL trên CentOS 7
2016-12-01
Cách cài đặt MySQL trên Ubuntu 16.04
2016-11-23