RE:NODE

MySQL backup-ები: mysqldump cron-ზე, S3 და restore ტესტი

ავტორი RE:NODE-ის გუნდი
19 წუთის საკითხავი


MySQL-ის backup, რომელსაც შეგიძლია ენდო, ერთი კი არა, სამი ცალკე სამუშაოა. პირველი არის ასლი, რომელიც გრაფიკით იქმნება. მეორე არის ასლი, რომელიც ბაზისგან განცალკევებით ინახება. მესამე არის რეგულარული, მოსაწყენი დადასტურება, რომ მეორე ასლიდან მუშა ბაზის აღდგენა შეიძლება. RE:NODE-ზე პირველს პანელში აკეთებ: გრაფიკი, რომელიც backup-ს ქმნის. მეორე და მესამე კი შენს საკუთარ მანქანაზე cron-ით გაშვებული mysqldump-ია, S3 storage-ში ატვირთვა და სკრიპტი, რომელიც წუხანდელ ფაილს დროებით MySQL-ში ტვირთავს და ამოწმებს. ეს სტატია სამივეს აგებს იმ თანმიმდევრობით, რომლითაც ისინი უნდა გაიშვას, და დროებით, რომელიც ერთმანეთს ხელს არ შეუშლის.

თუ მხოლოდ mysqldump-ის პარამეტრები გაინტერესებს, mysqldump backup and restore თითოეულს განმარტავს, ხოლო database dumps to S3 on a schedule სამი ძრავისთვის აღწერს S3-ში pipe-ის ზოგად სქემას. ეს სტატია უფრო ვიწროა: ერთი MySQL ბაზა, ერთი გრაფიკი და restore ტესტი, რომელსაც მართლა გააგრძელებ.

გეგმა: სამი ასლი, სამი გრაფიკი#

თითოეული ფენა სხვადასხვა უბედურებისგან იცავს და სხვადასხვა დროს ეშვება, რათა ერთსა და იმავე ბაზას არ შეეჯიბრონ.

ფენარისგან იცავსსად ინახება
პანელის backup სლოტიცუდი მიგრაცია, წაშლილი ცხრილი, სწრაფი უკან დაბრუნებაბაზის მანქანის გარეთ, პანელის სლოტში
mysqldump ფაილი S3-შიწაშლილი სერვერი, დაკარგული ანგარიში, სხვა ჰოსტზე გადასვლაშენი bucket, ცალკე სერვისი
Restore ტესტიbackup, რომელიც არსებობს, მაგრამ არ იტვირთებადროებითი MySQL, რომელიც შემდეგ იშლება

პანელის backup სწრაფია. RE:NODE-ის პანელი ყველა გეგმაზე backup-ებს სლოტებში ინახავს, ქმნის მოთხოვნით ან გრაფიკით, აღადგენს ღილაკით, გაძლევს ჩამოტვირთვის საშუალებას და ერთ backup-ს ბრუნვისგან ბლოკავს. ისინი ინახება იმ მანქანის გარეთ, რომელსაც იცავენ. რასაც ჩვენ არ ვაქვეყნებთ, არის ბრუნვის გარდა შენახვის ვადა, ზომის ლიმიტი და ის, ფიზიკურად სად დევს მანქანის გარეთა ასლები. ამიტომ სლოტი მიიღე როგორც მოსახერხებელი პირველი რგოლი და არა როგორც ერთადერთი ასლი. სლოტების რაოდენობა დონეზეა დამოკიდებული; MySQL-ისთვის ერთიდან ოთხამდეა, ამიტომ შენს გეგმაში გადაამოწმე. სერვერის წაშლა მისი backup-ებსაც შლის, დაბლოკილებსაც, და ეს მეორე ფენის ყველაზე ძლიერი არგუმენტია.

mysqldump ფაილი გადატანადია. ის უბრალო SQL-ია, ამიტომ შეგიძლია სხვა ჰოსტზე, უფრო ახალ MySQL-ში ან ლეპტოპზე ჩატვირთო. restore ტესტს ხალხი ყველაზე ხშირად გამოტოვებს, და სწორედ ის წყვეტს, ჩაითვლება თუ არა დანარჩენი ორი.

backup slotmysqldumpaws s3 cpdownload, loadპანელის გრაფიკიslot backup, 03:00Runner მანქანაcron, mysqldumpMySQL სერვერიგეგმის host და portS3 bucketდამპები, checksum-ებიდროებითი MySQLDocker, კვირაში
სამი ფენა, სამი გრაფიკი

ბაზისთვის, რომელიც დილის პირველ საათებში ყველაზე მშვიდია, გონივრული საათი ასეთია: პანელის backup 03:00-ზე, დამპი 03:30-ზე, ატვირთვა მის უშუალოდ შემდეგ და restore ტესტი კვირაში ერთხელ, კვირას 06:00-ზე. შორის დროზე მეტად თანმიმდევრობა მნიშვნელოვანია. პანელის backup-ი და დამპი ორივე მკითხველია, ამიტომ შეიძლება გადაფარონ, მაგრამ გრძელი დამპი snapshot-ს ღიად ინახავს, და პატარა გეგმაზე ორი ასეთი ერთდროულად არ გინდა.

სად ეშვება დამპის სამუშაო#

mysqldump კლიენტია. ბაზას ქსელით უკავშირდება, როგორც ნებისმიერი აპლიკაცია, და ბაზის მანქანაზე shell არ სჭირდება, რაც გამართლებაა, რადგან ბაზის გეგმა შენი ხელსაწყოების დასაყენებელი ადგილი არ არის. RE:NODE-ზე MySQL სერვერი გაძლევს გენერირებულ root პაროლს, აპლიკაციის ბაზას და აპლიკაციის მომხმარებელს, რომლებიც გეგმის host-სა და port-ზეა მისაწვდომი. თვითონ ბაზის სერვერზე shell-ს ჩვენ არ ვაღწერთ, ამიტომ ჩათვალე, რომ mysqldump სხვაგან ეშვება და ისე დაგეგმე.

runner-ის ვარიანტები, უპირატესობის მიხედვით:

  • პატარა VDS ან ნებისმიერი Linux მანქანა, რომელსაც უკვე ამუშავებ, cron-ითა და MySQL კლიენტის ხელსაწყოებით. ეს ჩვეულებრივი პასუხია.
  • აპლიკაციის სერვერი, თუ ეს მანქანაა, სადაც პაკეტების დაყენება და crontab-ის რედაქტირება შეგიძლია. ფრთხილად: წაშლილი აპლიკაციის სერვერი backup სამუშაოსაც თან მიაქვს.
  • შენი კომპიუტერი ან სახლის სერვერი, რომლის დადებითი მხარე ისაა, რომ ბაზისგანაც და bucket-ისგანაც სხვა ადგილას დგას.

პანელის Schedules ჩანართს შეუძლია სერვერზე console ბრძანების, backup-ის ან power action-ის გაშვება. backup-ის ამოცანისთვის და გადატვირთვებისთვის ეს სწორედ ის არის, რაც გჭირდება. console ბრძანება სერვერის console-ში იწერება და არა ზოგად shell-ში, ამიტომ ნუ ელი, რომ ის ბაზაზე mysqldump-ს გაუშვებს. პანელის გრაფიკი გამოიყენე იმისთვის, რასაც პანელი აკეთებს, ხოლო დამპისთვის - cron შენს runner-ზე.

კლიენტის ვერსია სერვერის ვერსიას უნდა ემთხვეოდეს. ეს სტატია MySQL 8.4 LTS-ს გულისხმობს, ამიტომ runner-ზე 8.4-ის კლიენტის ხელსაწყოები დააყენე. გაცილებით ძველი გამოშვების mysqldump შეიძლება ისეთ პარამეტრებზე გაჩერდეს, რომლებიც მიმდინარე სერვერმა იცის, ხოლო MariaDB-ის კლიენტით MySQL-ის დამპი backup-ისთვის ისაა, რასაც თავი უნდა აარიდო. შეამოწმე, რა გაქვს:

bash
$ mysqldump --version$ mysql --version

პირველი ნაბიჯი: პანელის backup-ების გრაფიკი#

გახსენი სერვერი პანელში, გადადი Schedules-ზე და შექმენი გრაფიკი. გრაფიკს აქვს cron გამოსახულება და ამოცანების დალაგებული სია, რომელთაგან თითოეულს შეუძლია წინას შემდეგ მითითებული დაყოვნება დაელოდოს. ბაზისთვის სასარგებლო ფორმა ერთი ამოცანაა, რომელიც backup-ს ქმნის. თუ cron გამოსახულება არასდროს დაგიწერია, cron expressions explained ხუთ წუთში იკითხება.

ყოველდღიური backup 03:00-ზე ასეთია:

code
0 3 * * *

ხუთი ველია: წუთი, საათი, თვის დღე, თვე და კვირის დღე. საათის არჩევამდე შეამოწმე, რომელ დროის სარტყელს იყენებს Schedules ჩანართი, რადგან პანელის საათი და შენი runner-ის საათი აუცილებლად ერთი არ არის. გრაფიკებისთვის სარტყელს ჩვენ არ ვაცხადებთ, ამიტომ ნახე, როგორ აჩვენებს ჩანართი შემდეგი გაშვების დროს, და თუ ეკრანი გაუგებარია, შექმენი ერთი საცდელი გრაფიკი.

შემდეგ სლოტებზე იფიქრე. MySQL-ის გეგმაზე ერთიდან ოთხამდე სლოტით ყოველდღიური backup სწრაფად ბრუნავს. ორი ჩვევა გეხმარება:

  1. მეორე, ყოველკვირეული გრაფიკი მხოლოდ მაშინ დატოვე, თუ გეგმას საკმარისი სლოტი რჩება, და სლოტები ისტორიად კი არა, ბოლო მდგომარეობების მოკლე ფანჯარად მიიჩნიე.
  2. ჩაკეტე backup, რომლის შენახვაც გინდა, მაგალითად ის, რომელიც სარისკო მიგრაციამდე აიღე. დაბლოკილი backup-ები ბრუნვიდან გამოიყოფა, ამიტომ გახსოვდეს, რომ ისინი განბლოკვამდე სლოტს იკავებენ.

backup-ის საიმედოობას პანელიდან ვერ გაიგებ. პირველი გრაფიკული გაშვების შემდეგ გახსენი Backups და დარწმუნდი, რომ ჩანაწერი მოსალოდნელ დროს გამოჩნდა და ზომა დამაჯერებელია. guides გვერდი Schedules ჩანართს განიხილავს, ხოლო backups განყოფილება - Backups ჩანართს.

მეორე ნაბიჯი: backup მომხმარებელი mysqldump-ისთვის#

cron სამუშაო root-ით ნუ გაუშვებ. root-ის პაროლი მთელი სერვერის გასაღებია, ხოლო cron-ის გარემო მისი შესანახად ცუდი ადგილია. აპლიკაციის მომხმარებელი, რომელსაც RE:NODE აგენერირებს, უკეთესი არჩევანია, რადგან ის მხოლოდ შენს ბაზას ეხება. თუ ცალკე ანგარიშის შექმნა შეგიძლია, მხოლოდ ის უფლებები მიეცი, რაც დამპს სჭირდება. ერთი ბაზისთვის ეს ასეთია:

sql
CREATE USER 'backup'@'%' IDENTIFIED BY RANDOM PASSWORD;GRANT SELECT, SHOW VIEW, TRIGGER, LOCK TABLES, EVENT ON appdb.* TO 'backup'@'%';

IDENTIFIED BY RANDOM PASSWORD გენერირებულ პაროლს ერთხელ ბეჭდავს; პირდაპირ ქვემოთ მოცემულ credentials ფაილში გადაიტანე. გაქვს თუ არა შენს გეგმაზე დამატებითი მომხმარებლების შექმნის საშუალება, შენს ანგარიშში პანელიდან გაიგებ, ამიტომ მასზე დაყრდნობამდე გადაამოწმე; თუ არ შეგიძლია, გენერირებული აპლიკაციის მომხმარებელი საკმარისია საკუთარი ბაზის დასამპად. თუ --routines უფლების შეცდომით ვარდება, MySQL users and privileges განმარტავს SHOW_ROUTINE უფლებას, რომელიც ამას ასწორებს.

credentials ისე შეინახე ფაილში, რომ პროცესების სიაში და shell-ის ისტორიაში არასდროს გამოჩნდეს:

/etc/backup/my.cnf
[client]host = db.example.netport = 30412user = backuppassword = paste-the-generated-password-here

host და port შეცვალე იმით, რასაც პანელი შენი ბაზისთვის აჩვენებს. შემდეგ ფაილზე წვდომა დახურე:

bash
$ sudo mkdir -p /etc/backup$ sudo chown root:root /etc/backup && sudo chmod 700 /etc/backup$ sudo chmod 600 /etc/backup/my.cnf

თუ სამუშაო ჩვეულებრივი მომხმარებლით ეშვება, დირექტორიის მფლობელად ის მომხმარებელი დანიშნე. აზრი ისაა, რომ runner-ზე სხვას ვერ წაეკითხა. კავშირი შეამოწმე, სანამ რომელიმე სკრიპტს დაწერ:

bash
$ mysql --defaults-extra-file=/etc/backup/my.cnf -e 'SELECT VERSION(), NOW();'

მესამე ნაბიჯი: დამპის სკრიპტი#

კონვეიერს mysqldump | gzip > file ცნობილი ხარვეზი აქვს: თუ mysqldump შუაში ჩერდება, კომპრესორი მაინც სუფთად მთავრდება და პატარა, ერთი შეხედვით ვალიდურ ფაილს ტოვებს. ქვემოთ მოცემული სკრიპტი ამას pipefail-ით გვერდს უვლის, დროებით სახელზე წერს და მხოლოდ მას შემდეგ აძლევს საბოლოო სახელს, რაც დაამტკიცებს, რომ ფაილი სრულია. სრული mysqldump ფაილი მთავრდება კომენტარის ხაზით, რომელიც -- Dump completed-ით იწყება; შეწყვეტილში ეს ხაზი არ არის, ამიტომ ის იაფი და გულწრფელი შემოწმებაა.

/usr/local/bin/mysql-backup.sh
#!/usr/bin/env bashset -euo pipefailDB=appdbWORK=/var/backups/mysqlBUCKET=s3://db-dumps/mysql/$DBPROFILE=storeKEEP_LOCAL_DAYS=3STAMP=$(date -u +%F_%H%M)NAME="$DB-$STAMP.sql.zst"mkdir -p "$WORK"cd "$WORK"mysqldump --defaults-extra-file=/etc/backup/my.cnf \  --single-transaction --routines --events --triggers --hex-blob \  --set-gtid-purged=OFF --no-tablespaces "$DB" \  | zstd -q -T0 > "$NAME.part"zstd -t -q "$NAME.part"zstd -dc "$NAME.part" | tail -n 1 | grep -q '^-- Dump completed'mv "$NAME.part" "$NAME"sha256sum "$NAME" > "$NAME.sha256"aws s3 cp "$NAME" "$BUCKET/$NAME" --profile "$PROFILE" --only-show-errorsaws s3 cp "$NAME.sha256" "$BUCKET/$NAME.sha256" --profile "$PROFILE" --only-show-errorsfind "$WORK" -name "$DB-*.sql.zst*" -mtime +"$KEEP_LOCAL_DAYS" -deleteecho "ok $NAME $(stat -c %s "$NAME") bytes"

გადაიარე, რას აკეთებს, რადგან თითოეულ ხაზს თავისი მიზეზი აქვს:

  • set -euo pipefail ჩერდება პირველ შეცდომაზე, გაუცხადებელ ცვლადზე და pipe-ის ნებისმიერი საფეხურის წარუმატებლობაზე. pipefail-ის გარეშე დამპის კონვეიერის გასვლის სტატუსი კომპრესორის სტატუსი იქნებოდა.
  • --single-transaction ყველა InnoDB ცხრილს ერთი თანმიმდევრული snapshot-იდან კითხულობს და ჩამწერებს არ ბლოკავს. --routines --events --triggers ფაილში ინახავს შენახულ პროცედურებს, დაგეგმილ ივენთებს და ტრიგერებს. --hex-blob ბინარულ სვეტებს უსაფრთხოდ წერს. --set-gtid-purged=OFF და --no-tablespaces თავიდან იცილებს ორ შეცდომას, რომლებიც ყველაზე ხშირად აჩერებს ჩვეულებრივი მომხმარებლის აღებულ დამპს.
  • zstd -t შეკუმშული ფაილის მთლიანობას ამოწმებს. შემდეგი ხაზი მას ხსნის და ბოლო ხაზს ამოწმებს, რაც ამტკიცებს, რომ თვით დამპი ნორმალურად დასრულდა და არა მხოლოდ შეკუმშვა იყო მთლიანი.
  • .part სახელის გამო ნახევრად ჩაწერილი ფაილი backup-ად ვერ შეგეშლება. საბოლოო სახელს მხოლოდ ის ფაილი იღებს, რომელმაც ორივე შემოწმება გაიარა.
  • checksum დამპის გვერდით იტვირთება. restore ტესტი მას გამოიყენებს დაზიანებული ჩამოტვირთვის ან გაუმართავი ატვირთვის აღმოსაჩენად.
  • ლოკალური ასლები სამი დღის შემდეგ იშლება. გრძელ ისტორიას bucket ინახავს; runner-ს მხოლოდ ბოლო რამდენიმე სჭირდება სწრაფი აღდგენისთვის.

ხელსაწყოები დააყენე და ფაილი გახადე შესრულებადი. ქვემოთ პაკეტების სახელები Debian-ისა და Ubuntu-ისთვისაა:

bash
$ sudo apt install mysql-client zstd awscli$ sudo chmod 755 /usr/local/bin/mysql-backup.sh$ sudo /usr/local/bin/mysql-backup.sh

გრაფიკზე დასმამდე ერთხელ ხელით გაუშვი და გამოტანილი ტექსტი წაიკითხე. ჩაიწერე ფაილის ზომა და რამდენ ხანს გაგრძელდა. ორივე რიცხვი შენი საწყისი მაჩვენებელია.

მეოთხე ნაბიჯი: S3 storage როგორც გარე ასლი#

ატვირთვა AWS CLI-ს იყენებს, რადგან ის ყველა პლატფორმაზეა და RE:NODE-ის S3 storage მასთან მუშაობს. ყოველ S3 სერვერს ახლავს მისთვის გენერირებული access key და secret key და უკვე შექმნილი პირველი bucket, path-style მისამართებით და region-ის ნებისმიერი სახელით. endpoint უბრალო HTTP-ით მუშაობს თავის port-ზე, ხოლო HTTPS გეგმის proxy slot-ზე გადის. დამპისთვის ამას პრაქტიკული შედეგი აქვს: ფაილი ქსელში გადის, ამიტომ ან HTTPS მისამართი გამოიყენე, ან runner-იდან გასვლამდე დაშიფრე. დამპი შეიცავს შენი აპლიკაციის ყველა მომხმარებელს, ყველა ჰეშსა და ყველა მისამართს.

პროფილი runner-ზე ერთხელ დააყენე. ქვემოთ endpoint იმ მისამართის placeholder-ია, რომელსაც შენი გეგმა აჩვენებს:

~/.aws/config
[profile store]region = us-east-1endpoint_url = https://s3.example.comrequest_checksum_calculation = when_requiredresponse_checksum_validation = when_requireds3 =  addressing_style = path
~/.aws/credentials
[store]aws_access_key_id = YOUR_ACCESS_KEYaws_secret_access_key = YOUR_SECRET_KEY

სიით შეამოწმე. პირველი გამოძახება ხმამაღლა ვარდება, თუ endpoint, გასაღებები ან მისამართების სტილი არასწორია:

bash
$ aws s3 ls s3://db-dumps/ --profile store

S3 storage with the AWS CLI endpoint-ისა და checksum-ის პარამეტრებს უფრო დეტალურად განიხილავს. თუ rclone გირჩევნია, rclone with S3 storage-ში ეკვივალენტური remote არის.

დასაშიფრად ყველაზე იაფი გზა age-ის კომპრესორსა და ფაილს შორის ჩადებაა: დამპის ხაზის ბოლო შეცვალე ასე: | zstd -q -T0 | age -r YOUR_PUBLIC_KEY > "$NAME.part", ხოლო სახელი და შემოწმებები შესაბამისად გაასწორე, რაც ნიშნავს, რომ სისრულის შემოწმებამ ჯერ უნდა გაშიფროს, კერძო გასაღებით, რომელიც runner-ზე არ უნდა დატოვო. ამ დაძაბულობის გამო ბევრი პირველ ვერსიაში HTTPS-სა და დაკეტილ bucket-ს ირჩევს, დაშიფვრას კი მოგვიანებით ამატებს. რასაც არ უნდა აირჩევ, გაშიფვრის გასაღები ისეთ ადგილას შეინახე, რომელიც runner-ის დაკარგვას გადაურჩება.

S3 storage გეგმა ერთ ასლს ინახავს NVMe-ზე, ერთ ადგილას, რეპლიკაციის გარეშე. ეს დამპების მეორე სახლია და არა მესამე. თუ მონაცემები მნიშვნელოვანია, S3 storage sizing and the 3-2-1 rule-ის 3-2-1 იდეა ვრცელდება: პანელის backup, დამპი bucket-ში და დროდადრო ჩამოტვირთვა სრულიად სხვა ადგილას. bucket-ის ზომა პოლიტიკიდან გამოთვალე. 300 MB შეკუმშული ფაილის ოცდაათი ყოველდღიური დამპი დაახლოებით 9 GB-ია, რაც 10 GB-იან გეგმაში ძლივს ეტევა, ამიტომ გეგმის არჩევამდე საკუთარი პირველი ფაილი გაზომე.

მეხუთე ნაბიჯი: დაგეგმე cron-ით#

cron runner-ზე გრაფიკის შემსრულებელია. ჩაასწორე იმ მომხმარებლის crontab, რომელიც credentials-ს ფლობს:

bash
$ crontab -e
code
MAILTO=you@example.com30 3 * * * flock -n /tmp/mysql-backup.lock /usr/local/bin/mysql-backup.sh >> /var/log/mysql-backup.log 2>&1

MAILTO-ში შენი მისამართი ჩაწერე; cron აგზავნის იმ სამუშაოს გამოტანას, რომელიც გადამისამართებული არ არის, თუმცა აქ ამას ლოგის ფაილი იღებს. 30 3 * * * არის ყოველდღე 03:30 runner-ის დროის სარტყელში, რომელსაც timedatectl-ით შეამოწმებ. flock -n უარს ამბობს მეორე ასლის გაშვებაზე, თუ წინა ჯერ კიდევ მუშაობს, რაც მზარდ ბაზაზე ორი გადაფარებული დამპის გაჩენის გზაა. ლოგი უნდა არსებობდეს და სამუშაოს მომხმარებლისთვის ჩაწერადი იყოს:

bash
$ sudo touch /var/log/mysql-backup.log$ sudo chown "$USER" /var/log/mysql-backup.log

cron მინიმალური PATH-ით მუშაობს, ამიტომ სკრიპტი, რომელიც შენს shell-ში მუშაობს და cron-იდან ვარდება, თითქმის ყოველთვის aws-ს ან zstd-ს ვერ პოულობს. ან გაუშვი command -v aws zstd mysqldump და დირექტორიები crontab-ის თავში PATH= ხაზში ჩაწერე, ან სკრიპტში სრული გზები გამოიყენე. ლოგი პირველი გაშვების მეორე დილით შეამოწმე და არა ერთი კვირის შემდეგ.

სამუშაო, რომელიც შეჩერდა, გარეგნულად ისეთივეა, როგორც სამუშაო, რომელიც მუშაობს, ამიტომ დუმილის სიგნალიზაცია დაამატე. ბევრი უფასო მონიტორინგის სერვისი გაძლევს URL-ს; სკრიპტი მას მხოლოდ წარმატებული გაშვების ბოლოს ping-ავს, შენ კი გაფრთხილებას იღებ, როცა ping აკლია. სკრიპტის ბოლო ბრძანებად ერთი ხაზი დაამატე:

bash
curl -fsS -m 10 --retry 3 "https://hc.example.com/ping/your-uuid" > /dev/null

ასევე ადევნე თვალი ზომის ხაზს, რომელსაც სკრიპტი ლოგში წერს. დამპი, რომელიც უცებ რამდენიმე კილობაიტამდე ეცემა, ისეთი გზით დაიმსხვრა, რომელიც გასვლის კოდებმა ვერ დაიჭირა, მაგალითად იმიტომ, რომ აპლიკაციის მომხმარებელმა უფლებები დაკარგა.

მეექვსე ნაბიჯი: restore ტესტი#

backup, რომელიც არავის აღუდგენია, ჰიპოთეზაა. restore ტესტი მას ფაქტად აქცევს და ავტომატიზაციად ღირს, რადგან ხელით ვერსია მხოლოდ ერთხელ ხდება. იდეა მარტივია: bucket-იდან ყველაზე ახალ დამპს ჩამოტვირთავ, checksum-ს შეამოწმებ, იმავე ვერსიის დროებით MySQL-ში ჩატვირთავ, რომელიც მხოლოდ ტესტისთვის არსებობს, გაუშვებ რამდენიმე მოთხოვნას, რომელიც მხოლოდ ნამდვილ მონაცემებზე წარმატებულდება, დაბეჭდავ, რამდენი დრო დასჭირდა, და ყველაფერს წაშლი.

დროებითი სერვერისთვის Docker ყველაზე მარტივია და ტესტს შენს ნამდვილ ბაზას აშორებს. გაამაგრე სერვერის ვერსია, აქ 8.4:

/usr/local/bin/mysql-restore-test.sh
#!/usr/bin/env bashset -euo pipefailDB=appdbBUCKET=s3://db-dumps/mysql/$DBPROFILE=storeCTR=restore-test-$$EXPECT_TABLES=12WORK=$(mktemp -d)cleanup() { docker rm -f "$CTR" > /dev/null 2>&1 || true; rm -rf "$WORK"; }trap cleanup EXITcd "$WORK"LATEST=$(aws s3 ls "$BUCKET/" --profile "$PROFILE" \  | awk '{print $4}' | grep '\.sql\.zst$' | sort | tail -n 1)[ -n "$LATEST" ] || { echo "no dump found"; exit 1; }aws s3 cp "$BUCKET/$LATEST" . --profile "$PROFILE" --only-show-errorsaws s3 cp "$BUCKET/$LATEST.sha256" . --profile "$PROFILE" --only-show-errorssha256sum -c "$LATEST.sha256"docker run -d --name "$CTR" -e MYSQL_ROOT_PASSWORD=scratch \  -e MYSQL_DATABASE="$DB" mysql:8.4 > /dev/nulluntil docker exec "$CTR" mysql -h127.0.0.1 -uroot -pscratch -e 'SELECT 1' \    > /dev/null 2>&1; do sleep 2; doneSTART=$(date +%s)zstd -dc "$LATEST" | docker exec -i "$CTR" mysql -uroot -pscratch "$DB"END=$(date +%s)TABLES=$(docker exec "$CTR" mysql -uroot -pscratch -N -e \  "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='$DB'")echo "tables: $TABLES (expected at least $EXPECT_TABLES)"[ "$TABLES" -ge "$EXPECT_TABLES" ]docker exec "$CTR" mysql -uroot -pscratch -N "$DB" -e \  "SELECT COUNT(*) AS orders, MAX(created_at) AS newest FROM orders"echo "restored $LATEST in $((END - START)) s"

რამდენიმე დეტალს მნიშვნელობა აქვს. ოფიციალური MySQL image ინიციალიზაციისას დროებით სერვერს უშვებს და შემდეგ რესტარტს აკეთებს. ეს დროებითი სერვერი TCP-ზე არ უსმენს, ამიტომ მზადყოფნის ციკლი -h127.0.0.1-ით უკავშირდება და მხოლოდ მაშინ გადის, როცა ნამდვილი სერვერი ამუშავდა; უბრალო mysqladmin ping socket-ით შეიძლება ზედმეტად ადრე გაიმარჯვოს და იმპორტი რესტარტს წააწყდეს. EXPECT_TABLES რიცხვია, რომელსაც ხელით ადგენ, იმ ცხრილების რაოდენობა, რაც შენს ბაზას უნდა ჰქონდეს, და ტესტი ვარდება, თუ აღდგენილ ასლს ნაკლები აქვს. ბოლო მოთხოვნა ისაა, რომელიც უნდა მოარგო: აიღე ნებისმიერი ცხრილი და დროის სვეტი, რომელსაც შენი აპლიკაცია ყოველ ცვლილებაზე წერს, რომ ერთი შეხედვით დაინახო, რომ ყველაზე ახალი ჩანაწერი გუშინდელია და არა გასულ თვეში.

trap კონტეინერსა და დროებით დირექტორიას შლის, ტესტი წარმატებით დასრულდეს თუ ჩავარდეს, ამიტომ წარუმატებლობა არაფერს ტოვებს. დროებითი root პაროლი scratch უვნებელია, რადგან კონტეინერი არც ერთ port-ს არ აქვეყნებს.

პირველად ხელით გაუშვი და წაიკითხე, რას ამბობს. შემდეგ კვირაში ერთხელ cron-ში ჩააყენე, დამპის ფანჯრის გარეთ:

code
0 6 * * 0 /usr/local/bin/mysql-restore-test.sh >> /var/log/mysql-restore-test.log 2>&1

პირველი გაშვებიდან შესანახი რიცხვი აღდგენის დროა. რამდენიმე ასეული მეგაბაიტის დამპი ლეპტოპზე წუთებში იტვირთება, მაგრამ ჩატვირთვა ყველა ინდექსს თავიდან აშენებს, და აღდგენა ხშირად რამდენჯერმე უფრო ნელია, ვიდრე დამპი იყო. როცა რიცხვი იმ შეფერხებაზე დიდია, რომელსაც მოითმენ, პასუხი უფრო სწრაფი სკრიპტი კი არა, MySQL Shell-ით პარალელური დამპი და ჩატვირთვაა, რომელიც აღწერილია mysqldump backup and restore-ში, ან უფრო პატარა ბაზებია. Test a backup restore: a 30-minute drill იმავე იდეას გუნდის რუტინად აქცევს.

რეალური აღდგენა#

როცა დღე დადგება, ორი გზა გაქვს და სწრაფს პანელი ირჩევს. ბოლო რამდენიმე დღის შეცდომისთვის slot backup პირველი, რაც უნდა სცადო: აღადგინე Backups ჩანართის ღილაკით. დაჭერამდე გახსოვდეს, რომ აღდგენა მიმდინარე მდგომარეობას ანაცვლებს, ამიტომ თუ ბაზაში ისეთი ცვლილებებია, რომლებიც გჭირდება, ჯერ ახალი backup აიღე ან იმ სლოტს ჩამოტვირთე, რომლის ზემოდანაც აპირებ აღდგენას.

როცა სლოტი აღარ არის ან სერვერი აღარ არის, დამპი გამოიყენე. ახალ სერვერზე ბაზა შექმენი და ფაილი პირდაპირ bucket-იდან ჩატვირთე:

bash
$ aws s3 cp s3://db-dumps/mysql/appdb/appdb-2026-10-23_0330.sql.zst - --profile store \    | zstd -dc \    | mysql --defaults-extra-file=/etc/backup/my.cnf appdb

აღადგინე ცარიელ ბაზაში. დამპი ცხრილებს, რომლებსაც შეიცავს, წყვეტს და თავიდან ქმნის, მაგრამ სხვა ცხრილებს ხელს არ ახლებს, ამიტომ ცოცხალ ბაზაზე „ზემოდან“ ჩატვირთვა ნარევს გაძლევს. თუ აპლიკაციამ ნახევრად ჩატვირთული ბაზა არ უნდა დაინახოს, აღადგინე ახალი ბაზის სახელით და connection string გადართე, როცა ყველაფერი სრულია. თუ სრულიად სხვა ჰოსტზე გადადიხარ, migrating MySQL to a new host მომხმარებლებს, definer-ებს და გადართვას განიხილავს.

რა ფუჭდება#

ხარვეზები რამდენიმე მიზეზის გარშემო იყრის თავს, და უმეტესობა ჩუმია.

  • სამუშაო არასდროს გაშვებულა. cron-ის PATH მინიმალურია, crontab სხვა მომხმარებელს ეკუთვნის ან მანქანა თავიდან აიგო. მონიტორინგის სერვისისგან ping-ის არარსებობა ისაა, რითაც ერთ დღეში გაიგებ. მის გარეშე restore-ის დროს გაიგებ.
  • Access denied tablespaces-ისთვის. MySQL 8.0.21-დან tablespace-ის ინფორმაციის დამპს გლობალური PROCESS უფლება სჭირდება. სკრიპტი გადასცემს --no-tablespaces-ს, რაც აპლიკაციის დამპს სჭირდება.
  • დამპი პატარაა. მომხმარებელი უკავშირდება, მაგრამ არაფერს ხედავს, ან ბაზის სახელი არასწორია. mysqldump შეიძლება ცარიელ ბაზაზე წარმატებით დასრულდეს. ლოგში ზომის ხაზი შეინახე და გუშინდელს შეადარე.
  • `Dump completed` აკლია. კავშირი დამპის შუაში გაწყდა, ჩვეულებრივ გრძელი გაშვებისას არასანდო არხზე. სკრიპტი ასეთ ფაილს არ აწინაურებს, შენ კი ლოგში შეცდომას ხედავ და bucket-ში ახალი ობიექტი არ არის.
  • ატვირთვა checksum-ის ან მისამართების შეცდომით ვარდება. პროფილში ორი checksum პარამეტრი და addressing_style = path S3-თან თავსებადი endpoint-ების ჩვეულებრივი გამოსწორებაა.
  • აღდგენა ისე არის არასწორი, რომ არც ერთი შემოწმება ამას ვერ იჭერს. სტრიქონების რაოდენობა ემთხვევა და ყველაზე ახალი დროის ნიშნული სწორია, მაგრამ შენახული პროცედურა აკლია. ეს არის დამპის ბრძანებიდან გამოტოვებული --routines და --events, და ამიტომ სკრიპტს ისინი აქვს.
  • დამპი მიგრაციას ემთხვევა. --single-transaction არ გიცავს სქემის ცვლილებებისგან დამპის შუაში. 03:25-სა და იმ დროს შორის, როცა დამპი ჩვეულებრივ მთავრდება, მიგრაციებს ნუ დაადებ.

თუ ბაზა პატარაა, მთელი რუტინა ყოველ ღამე რამდენიმე ასეულ მეგაბაიტ ადგილს და runner-ის რამდენიმე წუთს იყენებს. თუ დიდია, ფიქრს აღდგენის დროზე დახარჯავ, და ამის გასაგებად სწორი დრო კვირის ტესტია და არა ავარია.

FAQ#

შემიძლია mysqldump პანელის Schedules ჩანართიდან გავუშვა?

ბაზის დასამპად - არა. გრაფიკი სერვერზე console ბრძანებას, backup-ს ან power action-ს ასრულებს, console ბრძანება კი სერვერის console-ში მიდის და არა ზოგად shell-ში. გრაფიკი backup-ებისა და გადატვირთვებისთვის გამოიყენე, mysqldump კი cron-ით ცალკე მანქანიდან გაუშვი.

პანელის რამდენი backup შევინახო?

რამდენსაც შენი გეგმის სლოტები იძლევა, ბოლო მდგომარეობების მოკლე ფანჯრად მიჩნეული. MySQL გეგმას დონის მიხედვით ერთიდან ოთხამდე სლოტი აქვს. სარისკო ცვლილებამდე დაბლოკე ის, რომლის დაკარგვაც არ შეგიძლია, ხოლო გრძელი ისტორია დამპების სახით S3-ში შეინახე.

რამდენად ხშირად გავუშვა restore ტესტი?

მნიშვნელოვანი ბაზისთვის კვირაში ერთხელ კარგი ნაგულისხმევია, ასევე დამპის სკრიპტის, MySQL-ის ვერსიის ან credentials-ის ნებისმიერი ცვლილების შემდეგ. ტესტი სათადარიგო მანქანის რამდენიმე წუთი ჯდება, და პირველი ჩავარდნა, რომელსაც დაიჭერს, ყველა უპრობლემო გაშვებას გადაიხდის.

დავშიფრო დამპები?

დიახ, თუ ფაილი HTTPS-ის გარეშე გადის ქსელში ან bucket-ის გასაღებები შეიძლება გაჟონოს, რადგან დამპი ბაზის ყველა მომხმარებელსა და ჰეშს შეიცავს. S3 endpoint თავის port-ზე უბრალო HTTP-ით მუშაობს, ამიტომ გეგმის proxy slot-ის უკან HTTPS მისამართი გამოიყენე ან ატვირთვამდე age-ის მსგავსი ხელსაწყოთი დაშიფრე და გასაღები runner-იდან განცალკევებით შეინახე.

რა ვქნა, თუ ბაზა ერთი mysqldump ფაილისთვის ძალიან დიდია?

დაახლოებით 20-50 GB-ის შემდეგ ერთნაკადიანი დამპი და განსაკუთრებით ერთნაკადიანი აღდგენა ნელდება. MySQL Shell-ის dump და load უტილიტები პარალელურად მუშაობს და ჩვეულებრივი შემდეგი ნაბიჯია. ჯერ აღდგენის დრო გაზომე, რადგან სწორედ ეს რიცხვი წყვეტს, გჭირდება თუ არა რამის შეცვლა.

გამოგადგათ?