RE:NODE

restic forget და prune: retention პოლიტიკა S3-ზე

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


restic-ის retention პოლიტიკა ორი ბრძანებაა, რომლებსაც სხვადასხვა სამუშაო აქვთ და ძალიან განსხვავებული ფასი. restic forget იაფია: ის კითხულობს შენს პოლიტიკას, წყვეტს, რომელი snapshot-ის ჩანაწერები წაშალოს და შლის მათ - რამდენიმე პატარა ობიექტს. restic prune ძვირია: ის ითვლის, რომელი მონაცემები აღარ სჭირდება არცერთ დარჩენილ snapshot-ს, შემდეგ შლის მთლიან pack ფაილებს და თავიდან წერს ნაწილობრივ გამოყენებულებს. S3-ზე ეს ნიშნავს რეალური მონაცემების ჩამოტვირთვას და ატვირთვას, სანამ რეპოზიტორიაზე ექსკლუზიური ლოქი გიჭირავს. უმეტესი სერვერისთვის გონივრული სქემა ასეთია: forget ყოველი backup-ის შემდეგ, prune კვირაში ერთხელ, --max-unused-ით 5%-იან ნაგულისხმევ მნიშვნელობაზე ან მასზე მაღლა, ორივე კი systemd ტაიმერებით, --retry-lock-ით, რომ ერთმანეთს დაელოდონ და არ ჩავარდნენ. ამ პოსტის დანარჩენი ნაწილი ის დეტალებია, რაც ამ სქემას აამუშავებს: რას ითვლის სინამდვილეში --keep-* ფლაგები, რატომ რჩება snapshot-ები, რომლებიც პოლიტიკას უნდა წაეშალა, რა ღირს prune და რა უნდა გააკეთო, როცა რეპოზიტორია ამბობს, რომ დაბლოკილია.

ეს ერთი თემის ღრმა ნაწილია. რეპოზიტორიის შექმნა, ფაილებისა და ბაზების backup-ი, შემოწმება და აღდგენა აღწერილია პოსტში restic backups to S3; რამდენ ადგილს დაიკავებს ისტორია, იხილე S3 storage sizing and the 3-2-1 rule-ში.

რას ითვლის forget სინამდვილეში#

--keep-* ფლაგები იოლი წასაკითხია და იოლი გასაგებად. წესი ყველა მათგანის უკან ერთია: restic snapshot-ებს ახალიდან ძველისკენ ალაგებს, დროს ყოფს მოცემული ზომის ბაკეტებად (საათი, დღე, კვირა, თვე, წელი) და ინახავს უახლეს snapshot-ს n ყველაზე ახალ იმ ბაკეტიდან თითოეულში, რომელშიც snapshot არსებობს.

აქედან სამი შედეგი მოდის და თითოეული ვიღაცას აკვირვებს.

  • ის ითვლის ბაკეტებს snapshot-ებით, და არა კალენდარულ ერთეულებს. --keep-daily 7 ინახავს თითო snapshot-ს ბოლო შვიდი დღიდან, როცა backup შესრულდა. თუ სერვერი ორი კვირა გამორთული იყო, შვიდი შენახული დღე სამი კვირის სიღრმეში გადაიჭიმება. ეს განზრახაა: backup-ის დუმილით გაჩერებული ამოცანა დარჩენილ ისტორიას ნელ-ნელა არ შლის.
  • ბაკეტში მხოლოდ უახლესი snapshot ითვლება. თუ backup ყოველ ექვს საათში ხდება და გაქვს --keep-daily 7, ინახება ყოველი დღის ბოლო snapshot და არა პირველი, დანარჩენი სამი კი იშლება, თუ სხვა წესი არ ინახავს.
  • წესები გაერთიანებაა. snapshot ინახება, თუ რომელიმე წესი მას ინახავს. უახლესი snapshot ჩვეულებრივ ერთდროულად დღიურიცაა, კვირეულიც და თვიურიც, ამიტომ --keep-daily 7 --keep-weekly 4 --keep-monthly 6 დაახლოებით თხუთმეტ snapshot-ს ინახავს და არა ჩვიდმეტს.
ფლაგიბაკეტიშენიშვნა
--keep-last nარცერთი - უბრალოდ n ყველაზე ახალიდროს სრულიად უგულებელყოფს
--keep-hourly nსაათიგამოსადეგია მხოლოდ ხშირი backup-ის დროს
--keep-daily nკალენდარული დღემთავარი სამუშაო ცხენი
--keep-weekly nკალენდარული კვირაკვირა ორშაბათით იწყება
--keep-monthly nკალენდარული თვე
--keep-yearly nკალენდარული წელი
--keep-tag tბაკეტი არ არისინახავს ყველა snapshot-ს t თეგით

ბაკეტები დგინდება snapshot-ის საკუთარი დროის ნიშნულით, იმ მანქანის ლოკალური დროის სარტყლით, რომელიც forget-ს უშვებს. UTC-ზე მომუშავე სერვერი, რომელიც 23:30-ზე აკეთებს backup-ს, და UTC+4-ში მყოფი ლეპტოპი, რომელიც იმავე რეპოზიტორიას კითხულობს, დღის საზღვრებს სხვადასხვა ადგილას გაავლებენ. იმავე მიზეზით forget გაუშვი იმ მანქანიდან, რომელიც backup-ებს აკეთებს და არა შენი სამუშაო კომპიუტერიდან.

keep-within და მისი ვარიანტები

--keep-within 30d ინახავს ყველა snapshot-ს, რომელიც მოცემულ ხანგრძლივობაზე ახალია. ხანგრძლივობაში წლები, თვეები, დღეები და საათები შეიძლება გაერთიანდეს: 1y6m, 14d, 2d12h. ხაფანგი საწყის წერტილშია: ხანგრძლივობა იზომება ჯგუფის უახლესი snapshot-იდან და არა ახლანდელი მომენტიდან. თუ backup სამი თვის წინ შეწყდა, --keep-within 30d მაინც ინახავს ბოლო backup-მდე ოცდაათ დღეს - იგივე დამცავი ქცევა, რაც ბაკეტების დათვლისას.

restic 0.13-მა დაამატა --keep-within-hourly, --keep-within-daily, --keep-within-weekly, --keep-within-monthly და --keep-within-yearly. ისინი ორივე იდეას აერთიანებენ: თითო snapshot ყოველ ბაკეტზე, ხანგრძლივობის შიგნით მოქცეული ყველა ბაკეტისთვის. --keep-within-daily 7d --keep-within-weekly 1m --keep-within-monthly 1y ბუნებრივად იკითხება, ვიდრე ბაკეტების დათვლა, და უკეთ იქცევა, როცა backup-ის განრიგი იცვლება, რადგან ხანგრძლივობას არ აინტერესებს, რამდენჯერ შესრულდა backup მის შიგნით.

პოლიტიკის არჩევა#

სწორი პოლიტიკა არ არსებობს, არსებობს კომპრომისი იმას შორის, რამდენად შორს შეგიძლია დაბრუნება და რამდენს ინახავ. ორი კითხვა ყველაფერს წყვეტს. რამდენ ხანს შეიძლება პრობლემა შეუმჩნეველი დარჩეს? დაზიანებული world ფაილი ან წაშლილი ცხრილი ხშირად დღეების შემდეგ ვლინდება, ამიტომ კვირაზე მოკლე დღიური ისტორია ოპტიმისტურია. და რამდენად შორს აღადგენდი ოდესმე? თითქმის არავინ აღადგენს game server-ს თერთმეტი თვის წინანდელი მდგომარეობით, მაგრამ ვინმეს შეიძლება წინა კვარტლის ბაზა დასჭირდეს საბუღალტრო კითხვისთვის.

სერვერიპოლიტიკადაახლოებით ინახავს
პატარა game server--keep-daily 7 --keep-weekly 4კვირის დღეებს, თვის კვირებს
ვებსაიტი ან აპლიკაცია--keep-daily 14 --keep-weekly 8 --keep-monthly 6ექვს თვეს
ბიზნეს ბაზა--keep-hourly 24 --keep-daily 14 --keep-monthly 12საათობრივს ერთი დღით, თვეებს ერთი წლით
განახლებამდეrestic tag --add keep-foreverსანამ თეგს არ მოაცილებ

უფრო გრძელი პოლიტიკის შენახვის ღირებულება იმაზე პატარაა, ვიდრე ადამიანები ფიქრობენ, რადგან ყოველი დამატებითი snapshot მხოლოდ იმ chunk-ებს ამატებს, რომლებიც მეზობელი snapshot-იდან შეიცვალა. თვიური snapshot, რომელიც ნახევარი წელია უნიკალურია, ღირს იმდენი, რამდენიც იმ თვეში შეიცვალა და არა სრული ასლი. sizing პოსტი ამ არითმეტიკას ამუშავებს; მოკლედ, 7 დღიურიდან 7 დღიურზე და 6 თვიურზე გადასვლა რეპოზიტორიას იშვიათად ორმაგებს და ხშირად მესამედით ზრდის.

ხარჯი, რომელიც პოლიტიკის სიგრძესთან ერთად მართლაც იზრდება, დროა. ყოველი snapshot მეტამონაცემია, რომელიც forget-მა, prune-მა და check-მა უნდა წაიკითხონ, ხოლო დატვირთულ სერვერზე --keep-hourly 720 ყოველ მოვლის გაშვებას ანელებს ძალიან მცირე სარგებლისთვის. ისტორია დაძველებასთან ერთად გააიშვიათე: საათობრივი ერთი დღით, დღიური კვირებით, თვიური მის მიღმა.

snapshot-ების დამაგრება თეგებით

--keep-tag გამოსავალია. snapshot-ს თეგი დაადე და დროზე დაფუძნებული არცერთი წესი მას აღარ წაშლის:

bash
$ restic tag --add keep-forever --tag nightly a81d0b37$ restic forget --keep-daily 7 --keep-weekly 4 --keep-tag keep-forever --prune

restic tag ასევე იღებს --remove და --set-ს. თეგირება snapshot-ის ჩანაწერს თავიდან წერს, ამიტომ snapshot ახალ ID-ს იღებს; ძველი restic snapshots-იდან ქრება. გამოიყენე ეს თამაშის დიდ განახლებამდე, ბაზის მიგრაციამდე ან ნებისმიერ სხვა რამემდე, რისი გაუქმებაც მომავალ თვეში შეიძლება მოგინდეს, და თეგი მოხსენი, როცა დარწმუნდები, რომ აღარ დაგჭირდება.

გახსოვდეს, რომ --keep-tag forget-ის ყოველ გაშვებაში უნდა იყოს. ერთჯერადი ბრძანება მის გარეშე, ჩქარობაში აკრეფილი, დამაგრებულ snapshot-ებს სხვებივით წაშლის.

ჯგუფები: რატომ არ ქრება ზოგიერთი snapshot#

forget პოლიტიკას მთელ რეპოზიტორიაზე ერთბაშად არ ატარებს. ჯერ snapshot-ებს ჯგუფებად ყოფს და პოლიტიკას თითოეულ ჯგუფზე ცალ-ცალკე იყენებს. ნაგულისხმევია --group-by host,paths: ერთი hostname-ის snapshot-ები, რომლებსაც backup-ში ჩართული ბილიკების იგივე სია აქვთ, ერთ ჯგუფს ქმნიან.

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

  • backup-ის ბილიკები შეიცვალა. /srv-ის backup-ის ამოცანაში /etc-ის დამატება ახალ ჯგუფს ქმნის. ძველი, მხოლოდ /srv-ის ჯგუფი თავის ბოლო 7 დღიურ, 4 კვირეულ და 6 თვიურ snapshot-ს უსასრულოდ ინახავს.
  • hostname შეიცვალა. კონტეინერები ყოველი ხელახალი აგებისას ახალ შემთხვევით hostname-ს იღებენ, ხოლო თავიდან დაყენებულ მანქანას შეიძლება ძველი სახელი აღარ დაუბრუნდეს. ახალი hostname-იდან ყოველი backup ახალი ჯგუფია. გაასწორე ეს backup-ის დროს: restic backup --host web-01 ჩაწერს ფიქსირებულ სახელს, რეალური სახელის მიუხედავად.
  • ბილიკები სხვადასხვანაირადაა დაწერილი. restic აბსოლუტურ ბილიკებს ინახავს, ამიტომ /srv/app და /srv/app/ ერთნაირად ნორმალიზდება, მაგრამ სიმბოლური ბმული და მისი სამიზნე - არა.

ამას dry-run-ის გამოტანაში ნახავ, რომელიც ყოველ ჯგუფზე ცალკე ბლოკს ბეჭდავს:

code
Applying Policy: keep 7 daily, 4 weekly, 6 monthly snapshotssnapshots for (host [web-01], paths [/srv]):keep 6 snapshots:...snapshots for (host [web-01], paths [/etc /srv]):keep 14 snapshots:...

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

bash
$ restic forget --host web-01 --path /srv --keep-last 1 --dry-run

ალტერნატივა დაჯგუფების შეცვლაა. --group-by host ერთი host-ის ყველა ბილიკს ერთ ისტორიად თვლის, რაც ბილიკების ცვლილებას უძლებს, მაგრამ ფაილებისა და ბაზის snapshot-ებს ერთ ქრონოლოგიად ათავსებს - --keep-daily 7-ის დროს ყოველი დღის ორიდან მხოლოდ უახლესი გადარჩება. --group-by host,tags ყველაზე სუფთა არჩევანია, როცა ყველა ამოცანას საკუთარი თეგი აქვს (files, mysql, postgres), რადგან მაშინ ისტორიას თეგი განსაზღვრავს და ბილიკები თავისუფლად შეიძლება შეიცვალოს. --group-by '' დაჯგუფებას საერთოდ თიშავს, რაც ერთზე მეტი ამოცანის მქონე რეპოზიტორიაზე თითქმის არასოდეს გჭირდება.

forget-ზე --host, --path და --tag ფილტრებია და არა დაჯგუფებები: ისინი ზღუდავენ, რომელ snapshot-ებს განიხილავს ბრძანება საერთოდ. restic forget --tag mysql --keep-daily 30 ოცდაათდღიან პოლიტიკას ბაზის snapshot-ებზე იყენებს და ყველა დანარჩენს ხელს არ ახლებს, რაც ერთ რეპოზიტორიას ამოცანების მიხედვით სხვადასხვა პოლიტიკის ტარების საშუალებას აძლევს.

forget-ის უსაფრთხოდ გაშვება#

პირველად და პოლიტიკის ყოველი ცვლილებისას dry run წაიკითხე:

bash
$ restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --dry-run

გამოტანა თითო ჯგუფზე ჩამოთვლის შესანახ snapshot-ებს თითოეულის შენახვის მიზეზით (daily snapshot, weekly snapshot, last snapshot და ა.შ.) და წასაშლელ snapshot-ებს. თუ წასაშლელ სიაში ისეთია, რომლის შენახვასაც ელოდი, ეს თითქმის ყოველთვის დაჯგუფების პრობლემაა ან ბაკეტის საზღვარი სხვა დროის სარტყელში, და ორივე მიზეზების სვეტში ჩანს.

restic 0.16 და უფრო ახალი უარს ამბობს ისეთი პოლიტიკის გაშვებაზე, რომელიც ჯგუფის ყველა snapshot-ს წაშლიდა; ამისთვის --unsafe-allow-remove-all არის საჭირო. forget საერთოდ პოლიტიკის გარეშე არაფერს შლის. ორივე დაცვაა იმ shell სკრიპტისგან, რომელიც ბრძანებას ცარიელი ცვლადიდან აგებს.

forget ასევე პირდაპირ იღებს snapshot-ების ID-ებს - restic forget a81d0b37 c5e9f210 - რაც ცნობილი ცუდი snapshot-ის, მაგალითად ისეთის, რომელიც ბაზის დამპის ჩავარდნისას გაკეთდა, მის თვიურ სლოტს გაფუჭებამდე მოსაცილებლად მოწესრიგებული გზაა.

რას აკეთებს prune და რა პარამეტრები მართავენ მას#

forget მხოლოდ snapshot-ის ჩანაწერებს შლის. მონაცემები რჩება, სანამ prune არ გაეშვება. prune ეტაპობრივად მუშაობს:

  1. ის კითხულობს ყველა დარჩენილ snapshot-ს და აგებს ჯერ კიდევ გამოყენებული chunk-ების სიმრავლეს.
  2. ყოველ pack ფაილს ახარისხებს: სრულად გამოყენებული, სრულად გამოუყენებელი ან ნაწილობრივ გამოყენებული.
  3. სრულად გამოუყენებელი pack-ები პირდაპირ იშლება - იაფია, თითო წაშლის მოთხოვნა.
  4. ნაწილობრივ გამოყენებული pack-ები თავიდან იწყობა: ჩამოიტვირთება, მათი ცოცხალი chunk-ები ახალ pack-ებში იწერება, ახალი pack-ები იტვირთება და ძველები იშლება.
  5. ინდექსი თავიდან იწერება, რომ მას შეესაბამებოდეს.

დრო და ტრაფიკი მე-4 ეტაპზე იხარჯება, და პარამეტრები სწორედ მას მართავენ:

პარამეტრინაგულისხმევიეფექტი
--max-unused5%გამოუყენებელი ადგილი, რომელიც თავიდან აწყობამდე შეიძლება დარჩეს
--max-repack-sizeunlimitedერთ გაშვებაში თავიდან აწყობილი მონაცემების ზღვარი, მაგ. 2G
--repack-cacheable-onlyoffთავიდან აწყობს მხოლოდ tree და index pack-ებს
--repack-uncompressedoffძველ შეუკუმშავ მონაცემებს შეკუმშულად გადაწერს
--dry-runoffაჩვენებს, რა მოხდებოდა, არაფერს ცვლის

--max-unused მთავარია. ის იღებს პროცენტს, აბსოლუტურ ზომას, მაგალითად 10G, ან unlimited-ს. 5%-იანი ნაგულისხმევის დროს prune ნაწილობრივ გამოყენებულ pack-ებს თავიდან აწყობს, სანამ დარჩენილი ნაგავი რეპოზიტორიის 5%-ზე ნაკლები არ გახდება. unlimited-ის დროს ის არასოდეს აწყობს თავიდან: შლის სრულად გამოუყენებელ pack-ებს და ჩერდება. ეს ცვლის გარკვეულ დაკარგულ ადგილს - pack, რომელშიც ორმოცდაათიდან ერთი chunk ჯერ კიდევ გამოიყენება, მთლიანად რჩება - ისეთ prune-ზე, რომელიც თითქმის არაფერს ჩამოტვირთავს.

--max-repack-size სამუშაოს პირიქით, ანაწილებს. კვირეული prune --max-repack-size 2G-ით ერთ გაშვებაში დაახლოებით ორ გიგაბაიტზე მეტს არ გადაადგილებს, ამიტომ პოლიტიკის ცვლილების შემდეგ დაგროვილი დიდი ნაშთი რამდენიმე კვირაში იწმინდება და არა ერთ ღამეში, რომელიც შემდეგ backup-ს გადაფარავს.

--repack-uncompressed მნიშვნელოვანია მხოლოდ იმ რეპოზიტორიებისთვის, რომლებიც restic 0.14-მდე შეიქმნა და restic migrate upgrade_repo_v2-ით ფორმატის მე-2 ვერსიაზე განახლდა. ძველი მონაცემები შეუკუმშავი რჩება, სანამ თავიდან არ აეწყობა, და ეს ფლაგი ამას თანდათან აკეთებს. ახალ რეპოზიტორიაზე არაფერს აკეთებს.

ამ პარამეტრების შეცვლამდე ყოველთვის -v-ით dry run-ს დახედე. ის ბეჭდავს, რამდენი წაიშლებოდა, რამდენი აეწყობოდა თავიდან და რამდენი გამოუყენებელი ადგილი დარჩებოდა, ანუ სწორედ იმ რიცხვს, რომელსაც ცვლი.

prune-ის ფასი S3-ზე#

ლოკალურ დისკზე prune უბრალოდ I/O-ა. S3-ზე ყოველი ნაბიჯი მოთხოვნაა, და იმ პროვაიდერებზე, რომლებიც მათ ანგარიშს უწევენ, ხარჯები ოთხ სახეობად იყოფა:

  • გამავალი ტრაფიკი (egress). თავიდან აწყობა pack ფაილებს ჩამოტვირთავს. პროვაიდერზე, რომელიც გამავალ ტრაფიკში ფულს იღებს, ეს ყველაზე დიდი ხარჯია და სწორედ მას აკონტროლებს --max-unused.
  • მოთხოვნები. bucket-ის სიის წაკითხვა, ინდექსების კითხვა, pack-ების წაშლა. თითო მოთხოვნა იაფია, მაგრამ რამდენიმე ასეულ გიგაბაიტიან რეპოზიტორიაში ათიათასობით ობიექტია.
  • ატვირთვა. ახალი pack-ები თავიდან აწყობიდან. ჩვეულებრივ შემომავალი ტრაფიკი უფასოა, მაგრამ ნელ გამავალ არხზე დრო მიდის.
  • ადრეული წაშლა. იშვიათი წვდომის კლასებს შენახვის მინიმალური ვადა აქვთ - 30 დღე Amazon-ის STANDARD_IA-სთვის, 90 Glacier Instant Retrieval-ისთვის - და pack-ის უფრო ადრე წაშლა ისე ითვლება, თითქოს დარჩენილიყო.

ბოლო მიზეზი იმისთვისაა, რომ -o s3.storage-class=STANDARD_IA-მდე ორჯერ დაფიქრდე. restic-ის რეპოზიტორიაში pack-ები მუდმივად იშლება prune-ის გაშვებისას, ამიტომ უფრო იაფი შენახვის კლასი შეიძლება უფრო ძვირი დაჯდეს, როცა ადრეული წაშლისა და მოძიების გადასახადებს დათვლი. საარქივო კლასები, რომლებსაც ობიექტის წასაკითხად restore მოთხოვნა სჭირდებათ, როგორიცაა Glacier Flexible Retrieval და Deep Archive, restic-ის რეპოზიტორიისთვის საერთოდ არ მუშაობს, რადგან restic მოელის, რომ ნებისმიერი pack მოთხოვნისთანავე წაიკითხოს.

პრაქტიკული პარამეტრები სიტუაციის მიხედვით:

სიტუაციაprune-ის პარამეტრირატომ
egress ფასიანია--max-unused unlimited ყოველკვირეულადშლის, არასოდეს ჩამოტვირთავს
ფიქსირებული ფასის საცავინაგულისხმევი 5% ყოველკვირეულადbucket-ს სუფთად ინახავს
დიდი ნაშთი--max-repack-size 2Gსამუშაოს ანაწილებს
ნელი ატვირთვის არხიprune თვეში ერთხელნაკლები გრძელი ლოქი

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

ლოქის შეცდომები და მოძველებული ლოქები#

restic იყენებს ლოქ ფაილებს, რომლებიც რეპოზიტორიაში locks/-ის ქვეშ ობიექტებად ინახება, რათა ორმა პროცესმა ერთმანეთი არ დააზიანოს. backup, restore და snapshots იღებენ გაზიარებულ ლოქს; რამდენიმე ერთდროულად შეიძლება მუშაობდეს. forget და prune იღებენ ექსკლუზიურ ლოქს და რეპოზიტორია მხოლოდ საკუთარი თავისთვის სჭირდებათ. პროცესი მუშაობისას თავის ლოქს ყოველ რამდენიმე წუთში ანახლებს.

როცა ექსკლუზიური ოპერაცია სხვა ლოქს გადაეყრება, ან backup მიმდინარე prune-ს, ის ჩერდება დაახლოებით ასეთი შეტყობინებით (ფორმულირება ვერსიებს შორის ოდნავ განსხვავდება):

code
unable to create lock in backend: repository is already locked by PID 41234on web-01 by root (UID 0, GID 0)lock was created at 2026-10-12 04:30:02 (6h12m10s ago)storage ID 8c1d2e4fthe `unlock` command can be used to remove stale locks

რაიმეს გაკეთებამდე ასაკი და host წაიკითხე. რამდენიმე წუთის ლოქი იმ პროცესს ეკუთვნის, რომელიც ჯერ კიდევ მუშაობს: დაელოდე. რამდენიმე საათის ლოქი იმავე host-იდან, როცა ps ამ PID-ით restic პროცესს არ აჩვენებს, მოძველებულია - დარჩენილია გაშვებიდან, რომელიც შეწყვეტილი იყო, მეხსიერების ამოწურვით გაჩერდა ან მანქანა გადაიტვირთა.

  • `restic unlock` შლის მხოლოდ მოძველებულ ლოქებს: ლოქებს, რომელთა პროცესი ამ host-ზე აღარ არსებობს, ან რომლებიც ნახევარ საათზე მეტია არ განახლებულა. მისი გაშვება ნებისმიერ დროს უსაფრთხოა.
  • `restic unlock --remove-all` შლის ყველა ლოქს, ცოცხლებსაც. გაუშვი მხოლოდ მაშინ, როცა დარწმუნებული ხარ, რომ არცერთი restic პროცესი არსად რეპოზიტორიას არ იყენებს; მიმდინარე prune-ის ლოქის წაშლა და backup-ის დაწყება სწორედ ის გზაა, რომლითაც რეპოზიტორიები ზიანდება.
  • `--retry-lock 30m` (restic 0.16 და უფრო ახალი) ნებისმიერ ბრძანებაზე აიძულებს მას, ლოქს დაელოდოს მითითებული დრომდე და არა მაშინვე ჩავარდეს. ეს არის ნამდვილი გამოსწორება დაგეგმილი ამოცანებისთვის, რომლებიც ხანდახან ერთმანეთს გადაფარავენ.

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

თუ prune ლოქის ნაცვლად დაკარგულ pack-ებზე ან არათანმიმდევრულ ინდექსზე ჩივის, გაჩერდი და გაუშვი restic check. restic repair index (0.16-მდე rebuild-index ერქვა) ინდექსს თავიდან აგებს რეალურად არსებული pack-ებიდან და ჩვეულებრივ პირველი ნაბიჯია, სანამ prune ისევ გაიშვება.

დაგეგმვა systemd ტაიმერებით#

ამ სამუშაოსთვის systemd ტაიმერი cron-ზე უკეთესია: გამოტოვებული გაშვებები გადატვირთვის შემდეგ შეიძლება ჩატარდეს, ერთი unit-ის ორი გაშვება არასოდეს ემთხვევა, გამოტანა დროის ნიშნულებით journal-ში მიდის, ჩავარდნამ კი სხვა unit-ის გაშვება შეიძლება გამოიწვიოს. გჭირდება მანქანა, სადაც systemd-ს თავად აკონტროლებ - შენი VPS ან dedicated server - და ზოგადი სქემა აღწერილია systemd services for your apps-ში.

systemd-ის EnvironmentFile-ს უბრალო KEY=value ხაზები უნდა, ამიტომ მას საკუთარი ფაილი მიეცი და არა export ხაზებით სავსე shell ფაილის ხელახლა გამოყენება:

/etc/restic/web-01.env
AWS_ACCESS_KEY_ID=RNAKEXAMPLE123AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEYAWS_DEFAULT_REGION=us-east-1RESTIC_REPOSITORY=s3:https://s3.example.com/my-bucket/restic/web-01RESTIC_PASSWORD_FILE=/etc/restic/web-01.passwordRESTIC_CACHE_DIR=/var/cache/restic

backup სერვისი backup-ს და იაფ forget-ს ასრულებს:

/etc/systemd/system/restic-backup.service
[Unit]Description=restic backup and forgetWants=network-online.targetAfter=network-online.targetOnFailure=restic-alert@%n.service[Service]Type=oneshotEnvironmentFile=/etc/restic/web-01.envNice=10IOSchedulingClass=idleExecStart=/usr/bin/restic backup /srv /etc --host web-01 --tag files \    --one-file-system --retry-lock 30mExecStart=/usr/bin/restic forget --host web-01 --tag files \    --keep-daily 7 --keep-weekly 4 --keep-monthly 6 \    --keep-tag keep-forever --retry-lock 30m
/etc/systemd/system/restic-backup.timer
[Unit]Description=Nightly restic backup[Timer]OnCalendar=*-*-* 04:30:00RandomizedDelaySec=15mPersistent=true[Install]WantedBy=timers.target

oneshot სერვისს შეიძლება რამდენიმე ExecStart= ხაზი ჰქონდეს; ისინი თანმიმდევრობით სრულდება და სერვისი პირველივე ჩავარდნილზე ვარდება, ამიტომ ჩავარდნილი backup forget-ს გამოტოვებს. prune-ს საკუთარი სერვისი და კვირეული ტაიმერი აქვს, რომ ნელმა prune-მა backup არასოდეს დააგვიანოს:

/etc/systemd/system/restic-prune.service
[Unit]Description=restic prune and checkOnFailure=restic-alert@%n.service[Service]Type=oneshotEnvironmentFile=/etc/restic/web-01.envNice=15IOSchedulingClass=idleExecStart=/usr/bin/restic prune --max-unused 10% --retry-lock 2hExecStart=/usr/bin/restic check --read-data-subset=5% --retry-lock 2h

მისი ტაიმერი backup-ის ტაიმერის იდენტურია, OnCalendar=Sun *-*-* 06:00:00-ით. შემდეგ:

bash
$ systemctl daemon-reload$ systemctl enable --now restic-backup.timer restic-prune.timer$ systemctl list-timers 'restic-*'$ systemctl start restic-backup.service      # run once now, to test$ journalctl -u restic-backup.service -n 50

Persistent=true გამოტოვებულ ამოცანას ერთხელ გაუშვებს ჩატვირთვისას, თუ მანქანა დაგეგმილ დროს გამორთული იყო. RandomizedDelaySec ერთ საცავის endpoint-ზე backup-ის მქონე რამდენიმე სერვერს ანაწილებს, რომ ყველა ერთ წამში არ დაიწყოს. OnFailure= იწყებს შაბლონურ unit-ს, რომელსაც თავად წერ - მოკლე სკრიპტი, რომელიც webhook-ზე ან ელფოსტით შეტყობინებას აგზავნის - არგუმენტად ჩავარდნილი unit-ის სახელით. ეს ბოლო ნაწილი არჩევითი არ არის: ტაიმერი, რომელიც ყოველ ღამე ჩუმად ვარდება, ზუსტად ისე გამოიყურება, როგორც მომუშავე ტაიმერი, და სწორედ ამ აზრს უბრუნდება ისევ და ისევ monitoring that tells you something.

თუ cron გირჩევნია, იგივე ბრძანებები --retry-lock-ით wrapper სკრიპტში შედის, ხოლო განრიგის სინტაქსს cron expressions explained განმარტავს. სკრიპტის გარშემო flock დაამატე, რომ ორი გაშვება არასოდეს გადაფარდეს, რასაც systemd უფასოდ გაძლევს.

პრობლემების გადაჭრა#

forget-ის შემდეგ რეპოზიტორია იმავე ზომისაა. forget --prune-ის გარეშე გაუშვი და prune არასოდეს გაგიშვია. ან prune --max-unused unlimited-ით გაეშვა და ადგილი ნაწილობრივ გამოყენებულ pack-ებშია. -v-ით dry run აჩვენებს, რამდენია გამოუყენებელი.

ძველი snapshot-ები არ ვადაგადის. მოძველებული ჯგუფია - შეცვლილი ბილიკები ან შეცვლილი hostname. forget --dry-run-ში ზედმეტი ბლოკი მოძებნე და გაასუფთავე --host/--path ფილტრით, შემდეგ კი --host ყოველ backup-ზე დააყენე.

forget-მა წაშალა snapshot-ები, რომლებიც უნდა შეენახა. იმ გაშვებაში --keep-tag არ იყო, --group-by-მ ორი ამოცანა გააერთიანა, ან დღის საზღვარი სხვა დროის სარტყელშია. dry run-ის მიზეზების სვეტი ყოველ შენახულ snapshot-ს განმარტავს; რაც იქ არ ჩანს, განზრახ წაიშალა.

backup-ები ყოველ კვირა დილით ვარდება. ისინი prune-ის ექსკლუზიურ ლოქს ეჯახება. backup-ს --retry-lock დაამატე ან prune გადაიტანე საათზე, როცა სხვა არაფერი მუშაობს.

prune საათობით მუშაობს და ბევრს ჩამოტვირთავს. პოლიტიკის დიდი ცვლილება ან პირველი prune რამდენიმე თვის შემდეგ. შეზღუდე --max-repack-size-ით ან გაზარდე --max-unused, სანამ ნაშთი არ გაქრება.

cache დირექტორია განუწყვეტლივ იზრდება. /var/cache/restic ინახავს მეტამონაცემებს ყველა რეპოზიტორიისთვის, რომელიც ამ მანქანამ გახსნა. restic cache --cleanup შლის ქეშებს იმ რეპოზიტორიებისთვის, რომლებიც ბოლო დროს არ გამოყენებულა.

რა პოლიტიკასაც არ უნდა აირჩიო, ერთხელ მაინც აღადგინე რამე იმ ყველაზე ძველი snapshot-იდან, რომელსაც ის ინახავს. ეს ერთადერთი დასტურია, რომ პოლიტიკა იმას ინახავს, რასაც ფიქრობ, ხოლო testing a restore before you need it გიჩვენებს, როგორ გახადო ეს რუტინა.

FAQ#

უნდა გავუშვა თუ არა prune ყოველი backup-ის შემდეგ?

არა. forget ყოველი backup-ის შემდეგ იაფია და snapshot-ების სიას მოწესრიგებულად ინახავს. prune დროს, ტრაფიკს და ექსკლუზიურ ლოქს ხარჯავს, ხოლო ადგილი, რომელსაც ორ მომდევნო ღამეს შორის ათავისუფლებს, მცირეა. უმეტესი სერვერისთვის კვირეული საკმარისია; ნელ არხზე ან როცა egress ფასიანია, თვეში ერთხელიც კარგია.

რა განსხვავებაა --keep-last-სა და --keep-daily-ს შორის?

--keep-last 7 ინახავს შვიდ ყველაზე ახალ snapshot-ს, როდისაც არ უნდა იყოს გაკეთებული - საათობრივი backup-ის დროს ეს შვიდი საათის ისტორიაა. --keep-daily 7 ინახავს უახლეს snapshot-ს ბოლო შვიდი დღიდან თითოეულიდან, რომელსაც ის აქვს, ანუ შვიდი დღის ისტორიას backup-ის სიხშირის მიუხედავად.

უსაფრთხოა თუ არა forget-ის გაშვება backup-ის მუშაობისას?

restic ამას არ დაუშვებს. backup გაზიარებულ ლოქს ინახავს, forget-ს კი ექსკლუზიური სჭირდება, ამიტომ forget ან ლოქის შეცდომით ვარდება, ან --retry-lock-ით backup-ის დასრულებას ელოდება. იგივე ეხება prune-საც.

შეიძლება თუ არა ერთი რეპოზიტორიის სხვადასხვა ამოცანას სხვადასხვა retention ჰქონდეს?

დიახ. ყოველ ამოცანას backup-ის დროს თეგი დაადე და თითო თეგზე ერთი forget გაუშვი საკუთარი პოლიტიკით, მაგალითად --tag mysql --keep-daily 30 და --tag files --keep-daily 7. --tag ფილტრი ყოველ გაშვებას მხოლოდ თავის snapshot-ებს ახებინებს.

ზოგავს თუ არა უფრო იაფი S3 შენახვის კლასი ფულს restic-თან?

იშვიათად. იშვიათი წვდომის კლასები მოძიებასა და მინიმალურ ასაკამდე ობიექტების წაშლას ახდევინებენ, prune კი pack-ებს მუდმივად შლის და თავიდან წერს. საარქივო კლასები, რომლებსაც წაკითხვამდე restore სჭირდებათ, restic-თან საერთოდ არ მუშაობს. სწორი ნაგულისხმევი სტანდარტული საცავია.

როგორ შევინახო ერთი snapshot სამუდამოდ?

დაადე თეგი - restic tag --add keep-forever <id> - და --keep-tag keep-forever ჩართე forget-ის ყოველ ბრძანებაში. თეგირება snapshot-ის ID-ს ცვლის, ამიტომ შემდეგ ის restic snapshots-ით თავიდან მოძებნე.