ما هو القاسم المشترك بين LVM وMatryoshka؟

سلام عليكم.
أود أن أشارك المجتمع تجربتي العملية في بناء نظام تخزين بيانات لـ KVM باستخدام md RAID + LVM.

سيتضمن البرنامج:

  • بناء md RAID 1 من NVMe SSD.
  • تجميع md RAID 6 من SATA SSD ومحركات الأقراص العادية.
  • ميزات عملية TRIM/DISCARD على SSD RAID 1/6.
  • إنشاء مصفوفة md RAID 1/6 قابلة للتمهيد على مجموعة مشتركة من الأقراص.
  • تثبيت النظام على NVMe RAID 1 في حالة عدم وجود دعم NVMe في BIOS.
  • استخدام ذاكرة التخزين المؤقت LVM ورقيقة LVM.
  • استخدام لقطات BTRFS وإرسال/استقبال للنسخ الاحتياطي.
  • استخدام لقطات LVM الرقيقة و Thin_delta للنسخ الاحتياطية لنمط BTRFS.

إذا كنت مهتما، يرجى الاطلاع على القط.

Заявление

لا يتحمل المؤلف أي مسؤولية عن عواقب استخدام أو عدم استخدام المواد/الأمثلة/الكود/النصائح/البيانات من هذه المقالة. من خلال قراءة أو استخدام هذه المواد بأي شكل من الأشكال، فإنك تتحمل المسؤولية عن جميع النتائج المترتبة على هذه التصرفات. تشمل العواقب المحتملة ما يلي:

  • محركات أقراص NVMe SSD المقلية المقرمشة.
  • تم استهلاك موارد التسجيل بالكامل وفشل محركات أقراص SSD.
  • الفقدان الكامل لجميع البيانات الموجودة على كافة محركات الأقراص، بما في ذلك النسخ الاحتياطية.
  • أجهزة الكمبيوتر الخاطئة.
  • ضيعت الوقت والأعصاب والمال.
  • أي عواقب أخرى غير المذكورة أعلاه.

حديد

المتاحة كانت:

اللوحة الأم من عام 2013 تقريبًا مع مجموعة شرائح Z87، كاملة مع Intel Core i7 / Haswell.

  • المعالج 4 أنوية، 8 خيوط
  • ذاكرة عشوائية 32 جيجابايت DDR3
  • 1 × 16 أو 2 × 8 PCIe 3.0
  • 1 × 4 + 1 × 1 PCIe 2.0
  • موصلات SATA 6 بسرعة 6 × 3 جيجابت في الثانية

تومض محول SAS LSI SAS9211-8I إلى وضع IT / HBA. تم استبدال البرامج الثابتة التي تدعم RAID عمدًا بالبرامج الثابتة HBA من أجل:

  1. يمكنك التخلص من هذا المحول في أي وقت واستبداله بأي محول آخر تصادفه.
  2. يعمل TRIM/Discard بشكل طبيعي على الأقراص، لأن... في البرامج الثابتة لـ RAID، لا يتم دعم هذه الأوامر على الإطلاق، ولا يهتم HBA بشكل عام بالأوامر التي يتم إرسالها عبر الناقل.

محركات الأقراص الصلبة - 8 قطع من HGST Travelstar 7K1000 بسعة 1 تيرابايت في شكل عامل 2.5، كما هو الحال في أجهزة الكمبيوتر المحمولة. كانت محركات الأقراص هذه موجودة سابقًا في صفيف RAID 6. وسيكون لهم أيضا استخدام في النظام الجديد. لتخزين النسخ الاحتياطية المحلية.

تمت إضافة بالإضافة إلى ذلك:

6 قطع SATA SSD موديل Samsung 860 QVO 2TB. تتطلب محركات أقراص SSD هذه حجمًا كبيرًا، وكان من المرغوب فيه وجود ذاكرة تخزين مؤقت SLC وموثوقية وسعر منخفض. كان دعم التجاهل/الصفر مطلوبًا، وهو ما يتم التحقق منه بواسطة السطر في dmesg:

kernel: ata1.00: Enabling discard_zeroes_data

قطعتين من NVMe SSD موديل Samsung SSD 2 EVO 970GB.

بالنسبة لمحركات أقراص SSD هذه، تعد سرعة القراءة/الكتابة العشوائية وسعة الموارد التي تلبي احتياجاتك أمرًا مهمًا. المبرد لهم. بالضرورة. قطعاً. بخلاف ذلك، قم بقليها حتى تصبح مقرمشة أثناء مزامنة RAID الأولى.

محول StarTech PEX8M2E2 لـ 2 x NVMe SSD مثبت في فتحة PCIe 3.0 8x. هذا، مرة أخرى، مجرد HBA، ولكن لـ NVMe. وهو يختلف عن المحولات الرخيصة من حيث أنه لا يتطلب دعم تشعب PCIe من اللوحة الأم بسبب وجود مفتاح PCIe مدمج. ستعمل حتى في أقدم نظام مزود بـ PCIe، حتى لو كانت فتحة PCIe 1 x1.0. وبطبيعة الحال، بالسرعة المناسبة. لا توجد RAIDs هناك. لا يوجد BIOS مدمج على اللوحة. لذلك، لن يتعلم نظامك التشغيل بطريقة سحرية باستخدام NVMe، ناهيك عن NVMe RAID بفضل هذا الجهاز.

كان هذا المكون يرجع فقط إلى وجود 8x PCIe 3.0 مجاني واحد فقط في النظام، وإذا كان هناك فتحتان مجانيتان، فيمكن استبداله بسهولة بفتحتين PEX2M4E2 أو نظائرهما، والتي يمكن شراؤها في أي مكان بسعر 1 دولار. روبل.

تم رفض جميع أنواع الأجهزة أو الشرائح المدمجة/BIOS RAIDs بشكل متعمد، حتى تتمكن من استبدال النظام بأكمله بالكامل، باستثناء SSD/HDD نفسها، مع الاحتفاظ بجميع البيانات. من الناحية المثالية، حتى تتمكن من حفظ نظام التشغيل المثبت عند الانتقال إلى أجهزة جديدة/مختلفة تمامًا. الشيء الرئيسي هو أن هناك منافذ SATA و PCIe. إنه يشبه القرص المضغوط المباشر أو محرك أقراص فلاش قابل للتمهيد، ولكنه سريع جدًا وضخم قليلاً.

Юморخلاف ذلك، أنت تعرف ما يحدث - في بعض الأحيان تحتاج بشكل عاجل إلى أخذ المصفوفة بأكملها معك لإزالتها. لكنني لا أريد أن أفقد البيانات. وللقيام بذلك، يتم وضع جميع الوسائط المذكورة في مكان ملائم على الشرائح الموجودة في فتحات العلبة القياسية التي تبلغ سعتها 5.25 بوصة.

حسنًا، وبالطبع، لتجربة طرق مختلفة للتخزين المؤقت لـ SSD في Linux.

مداهمات الأجهزة مملة. قم بتشغيله. إما أن يعمل أو لا يعمل. ومع mdadm هناك دائما خيارات.

البرمجيات

في السابق، تم تثبيت Debian 8 Jessie على الأجهزة، وهو قريب من EOL. تم تجميع RAID 6 من محركات الأقراص الصلبة المذكورة أعلاه المقترنة بـ LVM. تم تشغيل الأجهزة الافتراضية في kvm/libvirt.

لأن يتمتع المؤلف بخبرة مناسبة في إنشاء محركات أقراص فلاش SATA/NVMe قابلة للتمهيد، وأيضًا، من أجل عدم كسر القالب المناسب المعتاد، تم اختيار Ubuntu 18.04 كنظام مستهدف، والذي تم بالفعل تثبيته بشكل كافٍ، ولكن لا يزال لديه 3 سنوات من الخبرة الدعم في المستقبل.

يحتوي النظام المذكور على جميع برامج تشغيل الأجهزة التي نحتاجها خارج الصندوق. لا نحتاج إلى أي برامج أو برامج تشغيل تابعة لجهة خارجية.

التحضير للتثبيت

لتثبيت النظام نحتاج إلى Ubuntu Desktop Image. يحتوي نظام الخادم على نوع من المثبت القوي، والذي يُظهر الاستقلال المفرط الذي لا يمكن تعطيله عن طريق دفع قسم نظام UEFI إلى أحد الأقراص، مما يفسد كل الجمال. وفقا لذلك، يتم تثبيته فقط في وضع UEFI. لا يقدم أي خيارات.

نحن لسنا سعداء بهذا.

لماذا؟لسوء الحظ، فإن تمهيد UEFI غير متوافق للغاية مع برنامج التمهيد RAID، لأن... لا أحد يقدم لنا حجوزات لقسم UEFI ESP. هناك وصفات عبر الإنترنت تقترح وضع قسم ESP على محرك أقراص فلاش في منفذ USB، ولكن هذه نقطة الفشل. هناك وصفات تستخدم برنامج mdadm RAID 1 مع بيانات التعريف الإصدار 0.9 والتي لا تمنع UEFI BIOS من رؤية هذا القسم، ولكن هذا يستمر حتى اللحظة السعيدة عندما يكتب BIOS أو نظام تشغيل جهاز آخر شيئًا ما إلى ESP وينسى مزامنته مع أجهزة أخرى مرايا.

بالإضافة إلى ذلك، يعتمد تمهيد UEFI على NVRAM، والذي لن ينتقل مع الأقراص إلى النظام الجديد، لأن هو جزء من اللوحة الأم.

لذلك، لن نعيد اختراع عجلة جديدة. لدينا بالفعل دراجة جد جاهزة تم اختبارها عبر الزمن، تسمى الآن Legacy/BIOS boot، وتحمل الاسم الفخور CSM على الأنظمة المتوافقة مع UEFI. سنقوم فقط بإزالتها من الرف، وتشحيمها، وضخ الإطارات ومسحها بقطعة قماش مبللة.

لا يمكن أيضًا تثبيت إصدار سطح المكتب من Ubuntu بشكل صحيح باستخدام أداة تحميل التشغيل القديمة، ولكن هنا، كما يقولون، على الأقل هناك خيارات.

وهكذا، نقوم بجمع الأجهزة وتحميل النظام من محرك الأقراص المحمول القابل للتمهيد Ubuntu Live. سنحتاج إلى تنزيل الحزم، لذلك سنقوم بإعداد الشبكة التي تناسبك. إذا لم ينجح الأمر، فيمكنك تحميل الحزم الضرورية على محرك أقراص فلاش مسبقًا.

نذهب إلى بيئة سطح المكتب، ونطلق المحاكي الطرفي، ونبدأ:

#sudo bash

كيف…؟السطر أعلاه هو المحفز الأساسي لـ holiwars حول sudo. ج بоفرص أكبر تأتي وоمسؤولية أكبر. والسؤال هو ما إذا كان يمكنك أن تأخذ ذلك على عاتقك. يعتقد الكثير من الناس أن استخدام sudo بهذه الطريقة ليس حذرًا على الأقل. لكن:

#apt-get install mdadm lvm2 thin-provisioning-tools btrfs-tools util-linux lsscsi nvme-cli mc

لماذا لا ZFS...؟عندما نقوم بتثبيت برنامج على جهاز الكمبيوتر الخاص بنا، فإننا نقرض أجهزتنا بشكل أساسي لمطوري هذا البرنامج لتشغيلها.
عندما نثق في هذا البرنامج لسلامة بياناتنا، فإننا نحصل على قرض يساوي تكلفة استعادة هذه البيانات، والذي سيتعين علينا سداده يومًا ما.

من وجهة النظر هذه، ZFS هي سيارة فيراري، وmdadm+lvm أشبه بالدراجة.

بشكل شخصي، يفضل المؤلف إعارة دراجة بالدين لأفراد مجهولين بدلاً من سيارة فيراري. هناك سعر الإصدار ليس مرتفعا. لا حاجة للحقوق. أبسط من قواعد المرور. مواقف السيارات مجانية. القدرة عبر البلاد أفضل. يمكنك دائمًا ربط أرجل الدراجة، ويمكنك إصلاح الدراجة بيديك.

لماذا إذن BTRFS...؟من أجل تشغيل نظام التشغيل، نحتاج إلى نظام ملفات مدعوم في Legacy/BIOS GRUB خارج الصندوق، وفي نفس الوقت يدعم اللقطات المباشرة. سوف نستخدمه لقسم /boot. بالإضافة إلى ذلك، يفضل المؤلف استخدام FS هذا لـ / (الجذر)، ولا ينسى ملاحظة أنه بالنسبة لأي برنامج آخر، يمكنك إنشاء أقسام منفصلة على LVM وتثبيتها في الأدلة الضرورية.

لن نقوم بتخزين أي صور للأجهزة الافتراضية أو قواعد البيانات على FS هذا.
سيتم استخدام FS هذا فقط لإنشاء لقطات للنظام دون إيقاف تشغيله ثم نقل هذه اللقطات إلى قرص النسخ الاحتياطي باستخدام الإرسال/التلقي.

بالإضافة إلى ذلك، يفضل المؤلف عمومًا الاحتفاظ بالحد الأدنى من البرامج مباشرة على الأجهزة وتشغيل جميع البرامج الأخرى في الأجهزة الافتراضية باستخدام أشياء مثل إعادة توجيه وحدات معالجة الرسومات ووحدات تحكم مضيف PCI-USB إلى KVM عبر IOMMU.

الأشياء الوحيدة المتبقية على الجهاز هي تخزين البيانات والمحاكاة الافتراضية والنسخ الاحتياطي.

إذا كنت تثق بـ ZFS أكثر، فمن حيث المبدأ، فهي قابلة للتبديل بالنسبة للتطبيق المحدد.

ومع ذلك، يتجاهل المؤلف عمدًا ميزات النسخ المتطابق/RAID والتكرار المضمنة في ZFS وBRTS وLVM.

كوسيطة إضافية، يتمتع BTRFS بالقدرة على تحويل عمليات الكتابة العشوائية إلى عمليات كتابة تسلسلية، مما له تأثير إيجابي للغاية على سرعة مزامنة اللقطات/النسخ الاحتياطية على محرك الأقراص الثابتة.

دعونا نعيد فحص جميع الأجهزة:

#udevadm control --reload-rules && udevadm trigger

دعونا نلقي نظرة حولنا:

#lsscsi && nvme list
[0:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sda
[1:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sdb
[2:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sdc
[3:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sdd
[4:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sde
[5:0:0:0] disk ATA Samsung SSD 860 2B6Q /dev/sdf
[6:0:0:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdg
[6:0:1:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdh
[6:0:2:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdi
[6:0:3:0] disk ATA HGST HTS721010A9 A3B0 /dev/sdj
[6:0:4:0] disk ATA HGST HTS721010A9 A3B0 /dev/sdk
[6:0:5:0] disk ATA HGST HTS721010A9 A3B0 /dev/sdl
[6:0:6:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdm
[6:0:7:0] disk ATA HGST HTS721010A9 A3J0 /dev/sdn
Node SN Model Namespace Usage Format FW Rev
---------------- -------------------- ---------------------------------------- --------- -------------------------- ---------------- --------
/dev/nvme0n1 S466NXXXXXXX15L Samsung SSD 970 EVO 500GB 1 0,00 GB / 500,11 GB 512 B + 0 B 2B2QEXE7
/dev/nvme1n1 S5H7NXXXXXXX48N Samsung SSD 970 EVO 500GB 1 0,00 GB / 500,11 GB 512 B + 0 B 2B2QEXE7

تخطيط القرص

NVMe SSD

لكننا لن نضع علامة عليها بأي شكل من الأشكال. كل نفس، BIOS لدينا لا يرى محركات الأقراص هذه. لذلك، سوف يذهبون بالكامل إلى برنامج RAID. لن نقوم حتى بإنشاء أقسام هناك. إذا كنت تريد اتباع "canon" أو "بشكل أساسي"، فقم بإنشاء قسم واحد كبير، مثل محرك الأقراص الثابتة (HDD).

ساتا هد

ليست هناك حاجة لاختراع أي شيء خاص هنا. سنقوم بإنشاء قسم واحد لكل شيء. سنقوم بإنشاء قسم لأن BIOS يرى هذه الأقراص وقد يحاول التمهيد منها. سنقوم أيضًا بتثبيت GRUB على هذه الأقراص لاحقًا حتى يتمكن النظام من القيام بذلك فجأة.

#cat >hdd.part << EOF
label: dos
label-id: 0x00000000
device: /dev/sdg
unit: sectors

/dev/sdg1 : start= 2048, size= 1953523120, type=fd, bootable
EOF
#sfdisk /dev/sdg < hdd.part
#sfdisk /dev/sdh < hdd.part
#sfdisk /dev/sdi < hdd.part
#sfdisk /dev/sdj < hdd.part
#sfdisk /dev/sdk < hdd.part
#sfdisk /dev/sdl < hdd.part
#sfdisk /dev/sdm < hdd.part
#sfdisk /dev/sdn < hdd.part

SATA SSD

هذا هو المكان الذي تصبح فيه الأمور مثيرة للاهتمام بالنسبة لنا.

أولاً، يبلغ حجم محركات الأقراص لدينا 2 تيرابايت. وهذا ضمن النطاق المقبول لـ MBR، وهو ما سنستخدمه. إذا لزم الأمر، يمكن استبداله بـ GPT. تحتوي أقراص GPT على طبقة توافق تسمح للأنظمة المتوافقة مع MBR برؤية الأقسام الأربعة الأولى إذا كانت موجودة ضمن أول 4 تيرابايت. الشيء الرئيسي هو أن قسم التمهيد وقسم bios_grub على هذه الأقراص يجب أن يكونا في البداية. يتيح لك هذا أيضًا التمهيد من محركات أقراص GPT Legacy/BIOS.

ولكن هذه ليست حالتنا.

هنا سوف نقوم بإنشاء قسمين. سيكون حجم الأول 1 غيغابايت وسيتم استخدامه لـ RAID 1 /boot.

سيتم استخدام الثاني لـ RAID 6 وسيشغل كل المساحة الحرة المتبقية باستثناء مساحة صغيرة غير مخصصة في نهاية محرك الأقراص.

ما هي هذه المنطقة غير المحددة؟وفقًا لمصادر على الشبكة، تحتوي محركات أقراص SATA SSD الخاصة بنا على ذاكرة تخزين مؤقت SLC قابلة للتوسيع ديناميكيًا ويتراوح حجمها من 6 إلى 78 جيجابايت. نحصل على 6 غيغابايت "مجانًا" بسبب الفرق بين "غيغابايت" و"جيبيبايت" في ورقة بيانات محرك الأقراص. يتم تخصيص الـ 72 جيجا بايت المتبقية من المساحة غير المستخدمة.

هنا تجدر الإشارة إلى أن لدينا ذاكرة تخزين مؤقت SLC، والمساحة مشغولة في وضع MLC 4 بت. وهذا يعني بالنسبة لنا فعليًا أنه مقابل كل 4 جيجابايت من المساحة الحرة، سنحصل فقط على 1 جيجابايت من ذاكرة التخزين المؤقت لـ SLC.

اضرب 72 جيجا بايت في 4 لتحصل على 288 جيجا بايت. هذه هي المساحة الحرة التي لن نقوم بتحديدها للسماح لمحركات الأقراص بالاستفادة الكاملة من ذاكرة التخزين المؤقت SLC.

وبالتالي، سنحصل بشكل فعال على ما يصل إلى 312 جيجابايت من ذاكرة التخزين المؤقت SLC من إجمالي ستة محركات أقراص. من بين جميع محركات الأقراص، سيتم استخدام محركين في RAID للتكرار.

سيسمح لنا هذا القدر من ذاكرة التخزين المؤقت بأن نواجه في الحياة الواقعية موقفًا نادرًا للغاية حيث لا تنتقل الكتابة إلى ذاكرة التخزين المؤقت. يعوض هذا بشكل جيد للغاية عن العيب الأكثر حزنًا في ذاكرة QLC - سرعة الكتابة المنخفضة للغاية عند كتابة البيانات بتجاوز ذاكرة التخزين المؤقت. إذا كانت الأحمال الخاصة بك لا تتوافق مع هذا، فأنا أوصي بالتفكير مليًا في المدة التي سيستمر فيها SSD الخاص بك تحت هذا الحمل، مع مراعاة TBW من ورقة البيانات.

#cat >ssd.part << EOF
label: dos
label-id: 0x00000000
device: /dev/sda
unit: sectors

/dev/sda1 : start= 2048, size= 2097152, type=fd, bootable
/dev/sda2 : start= 2099200, size= 3300950016, type=fd
EOF
#sfdisk /dev/sda < ssd.part
#sfdisk /dev/sdb < ssd.part
#sfdisk /dev/sdc < ssd.part
#sfdisk /dev/sdd < ssd.part
#sfdisk /dev/sde < ssd.part
#sfdisk /dev/sdf < ssd.part

إنشاء المصفوفات

أولا، نحن بحاجة إلى إعادة تسمية الجهاز. يعد هذا ضروريًا لأن اسم المضيف هو جزء من اسم المصفوفة في مكان ما داخل mdadm ويؤثر على شيء ما في مكان ما. بالطبع، يمكن إعادة تسمية المصفوفات لاحقًا، لكن هذه خطوة غير ضرورية.

#mcedit /etc/hostname
#mcedit /etc/hosts
#hostname
vdesk0

NVMe SSD

#mdadm --create --verbose --assume-clean /dev/md0 --level=1 --raid-devices=2 /dev/nvme[0-1]n1

لماذا -تفترض-نظيفة...؟لتجنب تهيئة المصفوفات. وهذا صالح لكل من مستويي RAID 1 و6. كل شيء يمكن أن يعمل بدون تهيئة إذا كان مصفوفة جديدة. علاوة على ذلك، فإن تهيئة مصفوفة SSD عند الإنشاء تعد مضيعة لموارد TBW. نحن نستخدم TRIM/DISCARD حيثما أمكن ذلك على صفائف SSD المجمعة "لتهيئتها".

بالنسبة لمصفوفات SSD، يتم دعم RAID 1 DISCARD خارج الصندوق.

بالنسبة لمصفوفات SSD RAID 6 DISCARD، يجب عليك تمكينها في معلمات وحدة kernel.

يجب أن يتم ذلك فقط إذا كانت جميع محركات أقراص SSD المستخدمة في صفائف المستوى 4/5/6 في هذا النظام تتمتع بدعم عملي لـ Disk_zeroes_data. في بعض الأحيان تصادف محركات أقراص غريبة تخبر النواة بأن هذه الوظيفة مدعومة، لكنها في الواقع غير موجودة، أو أن الوظيفة لا تعمل دائمًا. في الوقت الحالي، يتوفر الدعم في كل مكان تقريبًا، ومع ذلك، توجد محركات أقراص قديمة وبرامج ثابتة بها أخطاء. لهذا السبب، يتم تعطيل دعم DISCARD افتراضيًا لـ RAID 6.

انتبه، سيؤدي الأمر التالي إلى تدمير جميع البيانات الموجودة على محركات أقراص NVMe عن طريق "تهيئة" المصفوفة باستخدام "الأصفار".

#blkdiscard /dev/md0

إذا حدث خطأ ما، فحاول تحديد خطوة.

#blkdiscard --step 65536 /dev/md0

SATA SSD

#mdadm --create --verbose --assume-clean /dev/md1 --level=1 --raid-devices=6 /dev/sd[a-f]1
#blkdiscard /dev/md1
#mdadm --create --verbose --assume-clean /dev/md2 --chunk-size=512 --level=6 --raid-devices=6 /dev/sd[a-f]2

لماذا كبيرة جدا...؟إن زيادة حجم القطعة له تأثير إيجابي على سرعة القراءة العشوائية في الكتل حتى حجم القطعة ضمناً. يحدث هذا لأنه يمكن إكمال عملية واحدة بالحجم المناسب أو أصغر بالكامل على جهاز واحد. ولذلك، يتم تلخيص IOPS من كافة الأجهزة. وفقا للإحصاءات، 99٪ من IO لا يتجاوز 512K.

RAID 6 IOPS لكل كتابة دائما أقل من أو يساوي IOPS لمحرك أقراص واحد. عندما تكون القراءة العشوائية، يمكن أن يكون IOPS أكبر بعدة مرات من محرك أقراص واحد، وهنا يكون حجم الكتلة ذا أهمية أساسية.
لا يرى المؤلف أي فائدة من محاولة تحسين المعلمة السيئة في التصميم الفرعي لـ RAID 6 وبدلاً من ذلك يقوم بتحسين ما يجيده RAID 6.
سنقوم بتعويض الكتابة العشوائية الضعيفة لـ RAID 6 باستخدام ذاكرة تخزين مؤقت NVMe وحيل التزويد الرقيق.

لم نقم بعد بتمكين DISCARD لـ RAID 6. لذلك لن نقوم "بتهيئة" هذه المصفوفة في الوقت الحالي. سنفعل ذلك لاحقًا، بعد تثبيت نظام التشغيل.

ساتا هد

#mdadm --create --verbose --assume-clean /dev/md3 --chunk-size=512 --level=6 --raid-devices=8 /dev/sd[g-n]1

LVM على NVMe RAID

من أجل السرعة، نريد وضع نظام الملفات الجذر على NVMe RAID 1 وهو /dev/md0.
ومع ذلك، سنظل بحاجة إلى هذه المصفوفة السريعة لاحتياجات أخرى، مثل المبادلة والبيانات الوصفية وذاكرة التخزين المؤقت LVM والبيانات التعريفية الرقيقة LVM، لذلك سنقوم بإنشاء LVM VG على هذه المصفوفة.

#pvcreate /dev/md0
#vgcreate root /dev/md0

لنقم بإنشاء قسم لنظام الملفات الجذر.

#lvcreate -L 128G --name root root

لنقم بإنشاء قسم للتبديل وفقًا لحجم ذاكرة الوصول العشوائي (RAM).

#lvcreate -L 32G --name swap root

تثبيت نظام التشغيل

في المجمل، لدينا كل ما هو ضروري لتثبيت النظام.

قم بتشغيل معالج تثبيت النظام من بيئة Ubuntu Live. التثبيت العادي. فقط في مرحلة اختيار الأقراص للتثبيت، تحتاج إلى تحديد ما يلي:

  • /dev/md1, - نقطة التثبيت /التمهيد, FS - BTRFS
  • /dev/root/root (المعروف أيضًا باسم /dev/mapper/root-root)، - نقطة التثبيت / (root)، FS - BTRFS
  • /dev/root/swap (المعروف أيضًا باسم /dev/mapper/root-swap)، - يُستخدم كقسم مبادلة
  • قم بتثبيت أداة تحميل التشغيل على /dev/sda

عند تحديد BTRFS كنظام ملفات جذر، سيقوم المثبت تلقائيًا بإنشاء مجلدي BTRFS باسم "@" لـ / (الجذر)، و"@home" لـ /home.

لنبدأ بالتثبيت...

سينتهي التثبيت بمربع حوار مشروط يشير إلى وجود خطأ في تثبيت أداة تحميل التشغيل. لسوء الحظ، لن تتمكن من الخروج من مربع الحوار هذا باستخدام الوسائل القياسية ومتابعة التثبيت. نقوم بتسجيل الخروج من النظام ثم تسجيل الدخول مرة أخرى، وينتهي بنا الأمر في سطح مكتب Ubuntu Live نظيف. افتح المحطة، ومرة ​​أخرى:

#sudo bash

قم بإنشاء بيئة chroot لمواصلة التثبيت:

#mkdir /mnt/chroot
#mount -o defaults,space_cache,noatime,nodiratime,discard,subvol=@ /dev/mapper/root-root /mnt/chroot
#mount -o defaults,space_cache,noatime,nodiratime,discard,subvol=@home /dev/mapper/root-root /mnt/chroot/home
#mount -o defaults,space_cache,noatime,nodiratime,discard /dev/md1 /mnt/chroot/boot
#mount --bind /proc /mnt/chroot/proc
#mount --bind /sys /mnt/chroot/sys
#mount --bind /dev /mnt/chroot/dev

لنقم بتكوين الشبكة واسم المضيف في chroot:

#cat /etc/hostname >/mnt/chroot/etc/hostname
#cat /etc/hosts >/mnt/chroot/etc/hosts
#cat /etc/resolv.conf >/mnt/chroot/etc/resolv.conf

دعنا نذهب إلى بيئة chroot:

#chroot /mnt/chroot

أولاً، سنقوم بتسليم الطرود:

apt-get install --reinstall mdadm lvm2 thin-provisioning-tools btrfs-tools util-linux lsscsi nvme-cli mc debsums hdparm

دعونا نتحقق من جميع الحزم التي تم تثبيتها بشكل ملتوي ونصلحها بسبب عدم اكتمال تثبيت النظام:

#CORRUPTED_PACKAGES=$(debsums -s 2>&1 | awk '{print $6}' | uniq)
#apt-get install --reinstall $CORRUPTED_PACKAGES

إذا لم ينجح شيء ما، فقد تحتاج إلى تعديل /etc/apt/sources.list أولاً

دعونا نضبط معلمات وحدة RAID 6 لتمكين TRIM/DISCARD:

#cat >/etc/modprobe.d/raid456.conf << EOF
options raid456 devices_handle_discard_safely=1
EOF

دعونا نعدل صفائفنا قليلاً:

#cat >/etc/udev/rules.d/60-md.rules << EOF
SUBSYSTEM=="block", KERNEL=="md*", ACTION=="change", TEST=="md/stripe_cache_size", ATTR{md/stripe_cache_size}="32768"
SUBSYSTEM=="block", KERNEL=="md*", ACTION=="change", TEST=="md/sync_speed_min", ATTR{md/sync_speed_min}="48000"
SUBSYSTEM=="block", KERNEL=="md*", ACTION=="change", TEST=="md/sync_speed_max", ATTR{md/sync_speed_max}="300000"
EOF
#cat >/etc/udev/rules.d/62-hdparm.rules << EOF
SUBSYSTEM=="block", ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", RUN+="/sbin/hdparm -B 254 /dev/%k"
EOF
#cat >/etc/udev/rules.d/63-blockdev.rules << EOF
SUBSYSTEM=="block", ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", RUN+="/sbin/blockdev --setra 1024 /dev/%k"
SUBSYSTEM=="block", ACTION=="add|change", KERNEL=="md*", RUN+="/sbin/blockdev --setra 0 /dev/%k"
EOF

ماذا كان..؟لقد أنشأنا مجموعة من قواعد udev التي ستقوم بما يلي:

  • قم بتعيين حجم ذاكرة التخزين المؤقت للكتلة لـ RAID 2020 ليكون مناسبًا لعام 6. ويبدو أن القيمة الافتراضية لم تتغير منذ إنشاء Linux، ولم تكن كافية لفترة طويلة.
  • احتفظ بحد أدنى من عمليات الإدخال/الإخراج طوال مدة عمليات فحص/مزامنة الصفيف. هذا لمنع المصفوفات الخاصة بك من التعثر في حالة المزامنة الأبدية تحت الحمل.
  • تحديد الحد الأقصى للإدخال/الإخراج أثناء عمليات التحقق/مزامنة المصفوفات. يعد ذلك ضروريًا حتى لا تؤدي مزامنة/فحص SSD RAIDs إلى تشويش محركات الأقراص لديك بشكل واضح. هذا ينطبق بشكل خاص على NVMe. (هل تتذكر المبرد؟ لم أكن أمزح.)
  • منع الأقراص من إيقاف دوران المغزل (HDD) عبر APM وضبط مهلة السكون لوحدات تحكم القرص على 7 ساعات. يمكنك تعطيل APM تمامًا إذا كانت محركات الأقراص لديك قادرة على القيام بذلك (-B 255). باستخدام القيمة الافتراضية، ستتوقف محركات الأقراص بعد خمس ثوانٍ. ثم يريد نظام التشغيل إعادة تعيين ذاكرة التخزين المؤقت على القرص، وسوف تدور الأقراص مرة أخرى، وسيبدأ كل شيء من جديد. تحتوي الأقراص على حد أقصى محدود لعدد دورات المغزل. يمكن لمثل هذه الدورة الافتراضية البسيطة أن تقتل الأقراص بسهولة في غضون عامين. لا تعاني جميع الأقراص من هذا، ولكن أقراصنا هي أقراص "كمبيوتر محمول"، مع الإعدادات الافتراضية المناسبة، والتي تجعل RAID يبدو وكأنه خادم صغير.
  • تثبيت القراءة الأمامية على الأقراص (الدوارة) 1 ميجابايت - كتلتين متتاليتين/قطعة RAID 6
  • قم بتعطيل القراءة المسبقة على المصفوفات نفسها.

لنعدّل /etc/fstab:

#cat >/etc/fstab << EOF
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
# file-system mount-point type options dump pass
/dev/mapper/root-root / btrfs defaults,space_cache,noatime,nodiratime,discard,subvol=@ 0 1
UUID=$(blkid -o value -s UUID /dev/md1) /boot btrfs defaults,space_cache,noatime,nodiratime,discard 0 2
/dev/mapper/root-root /home btrfs defaults,space_cache,noatime,nodiratime,discard,subvol=@home 0 2
/dev/mapper/root-swap none swap sw 0 0
EOF

لماذا هذا..؟سنبحث عن قسم /boot بواسطة UUID. يمكن أن تتغير تسمية المصفوفة نظريًا.

سوف نبحث عن الأقسام المتبقية حسب أسماء LVM في تدوين /dev/mapper/vg-lv، لأن يحددون الأقسام بشكل فريد تمامًا.

نحن لا نستخدم UUID لـ LVM لأنه يمكن أن يكون UUID الخاص بوحدات تخزين LVM ولقطاتها هو نفسه.جبل /dev/mapper/root-root.. مرتين؟نعم. بالضبط. مميزات BTRFS. يمكن تركيب نظام الملفات هذا عدة مرات بمجلدات فرعية مختلفة.

ونظرًا لهذه الميزة نفسها، أوصي بعدم إنشاء لقطات LVM لوحدات تخزين BTRFS النشطة مطلقًا. قد تحصل على مفاجأة عند إعادة التشغيل.

دعونا نعيد إنشاء تكوين mdadm:

#/usr/share/mdadm/mkconf | sed 's/#DEVICE/DEVICE/g' >/etc/mdadm/mdadm.conf

دعونا نضبط إعدادات LVM:

#cat >>/etc/lvm/lvmlocal.conf << EOF

activation {
thin_pool_autoextend_threshold=90
thin_pool_autoextend_percent=5
}
allocation {
cache_pool_max_chunks=2097152
}
devices {
global_filter=["r|^/dev/.*_corig$|","r|^/dev/.*_cdata$|","r|^/dev/.*_cmeta$|","r|^/dev/.*gpv$|","r|^/dev/images/.*$|","r|^/dev/mapper/images.*$|","r|^/dev/backup/.*$|","r|^/dev/mapper/backup.*$|"] issue_discards=1
}
EOF

ماذا كان..؟لقد قمنا بتمكين التوسيع التلقائي لمجموعات LVM الرقيقة عند الوصول إلى 90% من المساحة المشغولة بنسبة 5% من الحجم.

لقد قمنا بزيادة الحد الأقصى لعدد كتل ذاكرة التخزين المؤقت لذاكرة التخزين المؤقت LVM.

لقد منعنا LVM من البحث عن وحدات تخزين LVM (PV) على:

  • الأجهزة التي تحتوي على ذاكرة تخزين مؤقت LVM (cdata)
  • الأجهزة المخزنة مؤقتًا باستخدام ذاكرة التخزين المؤقت LVM، وتجاوز ذاكرة التخزين المؤقت ( _كوريج). في هذه الحالة، سيتم فحص الجهاز المخزن مؤقتًا نفسه من خلال ذاكرة التخزين المؤقت (فقط ).
  • الأجهزة التي تحتوي على بيانات تعريف ذاكرة التخزين المؤقت LVM (cmeta)
  • جميع الأجهزة في VG مع صور الاسم. سيكون لدينا هنا صور قرص للأجهزة الافتراضية، ولا نريد أن يقوم LVM على المضيف بتنشيط وحدات التخزين التابعة لنظام التشغيل الضيف.
  • جميع الأجهزة في VG مع النسخة الاحتياطية للاسم. سيكون لدينا هنا نسخ احتياطية من صور الجهاز الظاهري.
  • جميع الأجهزة التي ينتهي اسمها بـ "gpv" (الحجم الفعلي للضيف)

لقد قمنا بتمكين دعم DISCARD عند تحرير مساحة خالية على LVM VG. احرص. وهذا سيجعل حذف LVs على SSD يستغرق وقتًا طويلاً. ينطبق هذا بشكل خاص على SSD RAID 6. ومع ذلك، وفقًا للخطة، سنستخدم التزويد الدقيق، لذلك لن يعيقنا هذا على الإطلاق.

لنقم بتحديث صورة initramfs:

#update-initramfs -u -k all

تثبيت وتكوين اليرقة:

#apt-get install grub-pc
#apt-get purge os-prober
#dpkg-reconfigure grub-pc

أي الأقراص تختار؟جميع الذين هم SD *. يجب أن يكون النظام قادرًا على التمهيد من أي محرك أقراص SATA أو SSD عامل.

لماذا أضافوا os-prober..؟من أجل الاستقلال المفرط والأيدي المرحة.

ولا يعمل بشكل صحيح إذا كان أحد RAIDs في حالة متدهورة. يحاول البحث عن نظام التشغيل على الأقسام المستخدمة في الأجهزة الافتراضية التي تعمل على هذا الجهاز.

إذا كنت في حاجة إليها، يمكنك تركها، ولكن ضع في اعتبارك كل ما سبق. أوصي بالبحث عن وصفات للتخلص من الأيدي المشاغبة عبر الإنترنت.

وبهذا نكون قد أكملنا التثبيت الأولي. حان الوقت لإعادة التشغيل إلى نظام التشغيل المثبت حديثًا. لا تنس إزالة القرص المضغوط/USB المباشر القابل للتمهيد.

#exit
#reboot

حدد أيًا من محركات أقراص SATA SSD كجهاز التمهيد.

LVM على SATA SSD

في هذه المرحلة، قمنا بالفعل بالتمهيد إلى نظام التشغيل الجديد، وقمنا بتكوين الشبكة، وفتحنا المحاكي الطرفي، وقمنا بتشغيل:

#sudo bash

فلنكمل.

"تهيئة" المصفوفة من SATA SSD:

#blkdiscard /dev/md2

إذا لم ينجح الأمر، فحاول:

#blkdiscard --step 65536 /dev/md2
إنشاء LVM VG على SATA SSD:

#pvcreate /dev/md2
#vgcreate data /dev/md2

لماذا VG آخر..؟في الواقع، لدينا بالفعل جذر VG مسمى. لماذا لا تضيف كل شيء في VG واحد؟

إذا كان هناك العديد من PVs في VG، لكي يتم تنشيط VG بشكل صحيح، يجب أن تكون جميع PVs موجودة (عبر الإنترنت). الاستثناء هو LVM RAID، الذي لا نستخدمه عمدًا.

نريد حقًا أنه في حالة حدوث فشل (قراءة فقدان البيانات) في أي من مصفوفات RAID 6، سيتم تشغيل نظام التشغيل بشكل طبيعي ويمنحنا الفرصة لحل المشكلة.

للقيام بذلك، في المستوى الأول من التجريد، سنقوم بعزل كل نوع من "الوسائط" المادية في VG منفصل.

من الناحية العلمية، تنتمي مصفوفات RAID المختلفة إلى "مجالات موثوقية" مختلفة. لا يجب عليك إنشاء نقطة فشل مشتركة إضافية لهم عن طريق حشرهم في VG واحد.

إن وجود LVM على مستوى "الأجهزة" سيسمح لنا بقطع أجزاء من مصفوفات RAID المختلفة بشكل تعسفي من خلال دمجها بطرق مختلفة. على سبيل المثال - تشغيل في نفس الوقت bcache + LVM Thin، bcache + BTRFS، LVM Cache + LVM Thin، تكوين ZFS معقد مع ذاكرة تخزين مؤقت، أو أي مزيج جهنمي آخر لمحاولة مقارنتها جميعًا.

على مستوى "الأجهزة"، لن نستخدم أي شيء آخر غير وحدات تخزين LVM "السميكة" القديمة الجيدة. قد يكون الاستثناء لهذه القاعدة هو قسم النسخ الاحتياطي.

أعتقد أنه بحلول هذه اللحظة، بدأ العديد من القراء بالفعل في الشك في شيء ما بشأن دمية التعشيش.

LVM على الأقراص الصلبة SATA

#pvcreate /dev/md3
#vgcreate backup /dev/md3

VG جديد مرة أخرى..؟نريد حقًا أنه في حالة فشل مصفوفة الأقراص التي سنستخدمها للنسخ الاحتياطي للبيانات، سيستمر نظام التشغيل لدينا في العمل بشكل طبيعي، مع الحفاظ على الوصول إلى البيانات غير الاحتياطية كالمعتاد. لذلك، لتجنب مشكلات تنشيط VG، نقوم بإنشاء VG منفصل.

إعداد ذاكرة التخزين المؤقت LVM

لنقم بإنشاء LV على NVMe RAID 1 لاستخدامه كجهاز تخزين مؤقت.

#lvcreate -L 70871154688B --name cache root

لماذا هناك القليل جدا ...؟الحقيقة هي أن محركات أقراص NVMe SSD الخاصة بنا تحتوي أيضًا على ذاكرة تخزين مؤقت SLC. 4 غيغابايت "مجانية" و18 غيغابايت من الديناميكية نظرًا للمساحة الحرة المشغولة في MLC 3 بت. بمجرد استنفاد ذاكرة التخزين المؤقت هذه، لن تكون محركات أقراص NVMe SSDs أسرع بكثير من أقراص SATA SSD المزودة بذاكرة تخزين مؤقت. في الواقع، لهذا السبب، ليس من المنطقي بالنسبة لنا أن نجعل قسم ذاكرة التخزين المؤقت LVM أكبر بكثير من ضعف حجم ذاكرة التخزين المؤقت SLC لمحرك NVMe. بالنسبة لمحركات NVMe المستخدمة، يرى المؤلف أنه من المعقول إنشاء 32-64 غيغابايت من ذاكرة التخزين المؤقت.

حجم القسم المحدد مطلوب لتنظيم 64 جيجابايت من ذاكرة التخزين المؤقت وبيانات تعريف ذاكرة التخزين المؤقت والنسخ الاحتياطي للبيانات التعريفية.

بالإضافة إلى ذلك، ألاحظ أنه بعد إيقاف تشغيل النظام بشكل سيئ، سيقوم LVM بوضع علامة على ذاكرة التخزين المؤقت بأكملها على أنها قذرة وسيقوم بالمزامنة مرة أخرى. علاوة على ذلك، سيتم تكرار ذلك في كل مرة يتم فيها استخدام lvchange على هذا الجهاز حتى يتم إعادة تشغيل النظام مرة أخرى. لذلك، أوصي بإعادة إنشاء ذاكرة التخزين المؤقت على الفور باستخدام البرنامج النصي المناسب.

لنقم بإنشاء LV على SATA RAID 6 لاستخدامه كجهاز مخبأ.

#lvcreate -L 3298543271936B --name cache data

لماذا ثلاثة تيرابايت فقط..؟لذلك، إذا لزم الأمر، يمكنك استخدام SATA SSD RAID 6 لبعض الاحتياجات الأخرى. يمكن زيادة حجم المساحة المخزنة مؤقتًا ديناميكيًا، بسرعة، دون إيقاف النظام. للقيام بذلك، تحتاج إلى إيقاف ذاكرة التخزين المؤقت مؤقتًا وإعادة تمكينها، ولكن الميزة المميزة لـ LVM-cache، على سبيل المثال، هي أنه يمكن القيام بذلك بسرعة.

لنقم بإنشاء VG جديد للتخزين المؤقت.

#pvcreate /dev/root/cache
#pvcreate /dev/data/cache
#vgcreate cache /dev/root/cache /dev/data/cache

لنقم بإنشاء LV على الجهاز المخزن مؤقتًا.

#lvcreate -L 3298539077632B --name cachedata cache /dev/data/cache

قمنا هنا على الفور بشغل كل المساحة الحرة على /dev/data/cache بحيث يتم إنشاء جميع الأقسام الضرورية الأخرى فورًا على /dev/root/cache. إذا قمت بإنشاء شيء ما في المكان الخطأ، فيمكنك نقله باستخدام pvmove.

لنقم بإنشاء ذاكرة التخزين المؤقت وتمكينها:

#lvcreate -y -L 64G -n cache cache /dev/root/cache
#lvcreate -y -L 1G -n cachemeta cache /dev/root/cache
#lvconvert -y --type cache-pool --cachemode writeback --chunksize 64k --poolmetadata cache/cachemeta cache/cache
#lvconvert -y --type cache --cachepool cache/cache cache/cachedata

لماذا هذا الحجم الكبير..؟من خلال التجارب العملية، تمكن المؤلف من معرفة أن أفضل نتيجة يتم تحقيقها إذا تزامن حجم كتلة ذاكرة التخزين المؤقت LVM مع حجم كتلة LVM الرقيقة. علاوة على ذلك، كلما كان الحجم أصغر، كان أداء التكوين أفضل في التسجيل العشوائي.

64 كيلو هو الحد الأدنى لحجم الكتلة المسموح به لرقيقة LVM.

انتبه للكتابة..!نعم. يؤدي هذا النوع من ذاكرة التخزين المؤقت إلى تأجيل مزامنة الكتابة إلى الجهاز المخزن مؤقتًا. وهذا يعني أنه في حالة فقدان ذاكرة التخزين المؤقت، قد تفقد البيانات الموجودة على الجهاز المخبأ. لاحقًا، سيخبرك المؤلف بالتدابير التي يمكن اتخاذها، بالإضافة إلى NVMe RAID 1، للتعويض عن هذا الخطر.

تم اختيار نوع ذاكرة التخزين المؤقت هذا عن عمد للتعويض عن ضعف أداء الكتابة العشوائية في RAID 6.

دعونا نتحقق مما حصلنا عليه:

#lvs -a -o lv_name,lv_size,devices --units B cache
LV LSize Devices
[cache] 68719476736B cache_cdata(0)
[cache_cdata] 68719476736B /dev/root/cache(0)
[cache_cmeta] 1073741824B /dev/root/cache(16384)
cachedata 3298539077632B cachedata_corig(0)
[cachedata_corig] 3298539077632B /dev/data/cache(0)
[lvol0_pmspare] 1073741824B /dev/root/cache(16640)

يجب أن يكون موقع [cachedata_corig] فقط موجودًا على /dev/data/cache. إذا كان هناك شيء خاطئ، فاستخدم pvmove.

يمكنك تعطيل ذاكرة التخزين المؤقت إذا لزم الأمر باستخدام أمر واحد:

#lvconvert -y --uncache cache/cachedata

ويتم ذلك عبر الإنترنت. سيقوم LVM ببساطة بمزامنة ذاكرة التخزين المؤقت مع القرص، وإزالتها، وإعادة تسمية ذاكرة التخزين المؤقت_corig إلى ذاكرة التخزين المؤقت.

إعداد LVM رقيق

دعونا نقدر تقريبًا مقدار المساحة التي نحتاجها للبيانات الوصفية الرقيقة لـ LVM:

#thin_metadata_size --block-size=64k --pool-size=6terabytes --max-thins=100000 -u bytes
thin_metadata_size - 3385794560 bytes estimated metadata area size for "--block-size=64kibibytes --pool-size=6terabytes --max-thins=100000"

تقريب حتى 4 جيجا بايت: 4294967296B

اضرب في اثنين وأضف 4194304B للبيانات التعريفية LVM PV: 8594128896B
لنقم بإنشاء قسم منفصل على NVMe RAID 1 لوضع بيانات تعريف LVM الرفيعة ونسختها الاحتياطية عليه:

#lvcreate -L 8594128896B --name images root

لماذا..؟وهنا قد يطرح السؤال: لماذا يتم وضع البيانات التعريفية الرفيعة لـ LVM بشكل منفصل إذا كانت ستظل مخزنة مؤقتًا على NVMe وستعمل بسرعة.

ورغم أن السرعة مهمة هنا، إلا أنها ليست السبب الرئيسي. الشيء هو أن ذاكرة التخزين المؤقت هي نقطة الفشل. قد يحدث شيء ما، وإذا تم تخزين البيانات الوصفية الرقيقة لـ LVM مؤقتًا، فسيؤدي ذلك إلى فقدان كل شيء تمامًا. وبدون البيانات الوصفية الكاملة، سيكون من المستحيل تقريبًا تجميع مجلدات صغيرة.

من خلال نقل البيانات التعريفية إلى وحدة تخزين منفصلة غير مخزنة مؤقتًا، ولكن سريعة، فإننا نضمن سلامة البيانات التعريفية في حالة فقدان ذاكرة التخزين المؤقت أو تلفها. في هذه الحالة، سيتم تحديد جميع الأضرار الناجمة عن فقدان ذاكرة التخزين المؤقت داخل مجلدات رقيقة، مما سيؤدي إلى تبسيط إجراء الاسترداد بأوامر من حيث الحجم. مع احتمال كبير، سيتم استعادة هذه الأضرار باستخدام سجلات FS.

علاوة على ذلك، إذا تم التقاط لقطة لحجم رفيع مسبقًا، وبعد ذلك تمت مزامنة ذاكرة التخزين المؤقت بالكامل مرة واحدة على الأقل، فنظرًا للتصميم الداخلي لـ LVM الرفيع، سيتم ضمان سلامة اللقطة في حالة فقدان ذاكرة التخزين المؤقت .

لنقم بإنشاء VG جديد سيكون مسؤولاً عن التزويد الدقيق:

#pvcreate /dev/root/images
#pvcreate /dev/cache/cachedata
#vgcreate images /dev/root/images /dev/cache/cachedata

لنقم بإنشاء تجمع:

#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T images/thin-pool
لماذا -Z ذبالإضافة إلى الغرض الفعلي من هذا الوضع - لمنع تسرب البيانات من جهاز افتراضي إلى جهاز افتراضي آخر عند إعادة توزيع المساحة - يُستخدم التصفير أيضًا لزيادة سرعة الكتابة العشوائية في كتل أصغر من 64 كيلو بايت. أي كتابة أقل من 64 كيلو بايت في منطقة غير مخصصة مسبقًا من الحجم الرقيق ستصبح 64 كيلو بايت محاذاة للحافة في ذاكرة التخزين المؤقت. سيسمح هذا بتنفيذ العملية بالكامل من خلال ذاكرة التخزين المؤقت، وتجاوز الجهاز المخبأ.

دعنا ننقل LVs إلى PVs المقابلة:

#pvmove -n images/thin-pool_tdata /dev/root/images /dev/cache/cachedata
#pvmove -n images/lvol0_pmspare /dev/cache/cachedata /dev/root/images
#pvmove -n images/thin-pool_tmeta /dev/cache/cachedata /dev/root/images

دعونا تحقق:

#lvs -a -o lv_name,lv_size,devices --units B images
LV LSize Devices
[lvol0_pmspare] 4294967296B /dev/root/images(0)
thin-pool 274877906944B thin-pool_tdata(0)
[thin-pool_tdata] 274877906944B /dev/cache/cachedata(0)
[thin-pool_tmeta] 4294967296B /dev/root/images(1024)

لنقم بإنشاء حجم رفيع للاختبارات:

#lvcreate -V 64G --thin-pool thin-pool --name test images

سنقوم بتثبيت حزم للاختبارات والمراقبة:

#apt-get install sysstat fio

هذه هي الطريقة التي يمكنك من خلالها مراقبة سلوك تكوين التخزين الخاص بنا في الوقت الفعلي:

#watch 'lvs --rows --reportformat basic --quiet -ocache_dirty_blocks,cache_settings cache/cachedata && (lvdisplay cache/cachedata | grep Cache) && (sar -p -d 2 1 | grep -E "sd|nvme|DEV|md1|md2|md3|md0" | grep -v Average | sort)'

هذه هي الطريقة التي يمكننا بها اختبار التكوين الخاص بنا:

#fio --loops=1 --size=64G --runtime=4 --filename=/dev/images/test --stonewall --ioengine=libaio --direct=1
--name=4kQD32read --bs=4k --iodepth=32 --rw=randread
--name=8kQD32read --bs=8k --iodepth=32 --rw=randread
--name=16kQD32read --bs=16k --iodepth=32 --rw=randread
--name=32KQD32read --bs=32k --iodepth=32 --rw=randread
--name=64KQD32read --bs=64k --iodepth=32 --rw=randread
--name=128KQD32read --bs=128k --iodepth=32 --rw=randread
--name=256KQD32read --bs=256k --iodepth=32 --rw=randread
--name=512KQD32read --bs=512k --iodepth=32 --rw=randread
--name=4Kread --bs=4k --rw=read
--name=8Kread --bs=8k --rw=read
--name=16Kread --bs=16k --rw=read
--name=32Kread --bs=32k --rw=read
--name=64Kread --bs=64k --rw=read
--name=128Kread --bs=128k --rw=read
--name=256Kread --bs=256k --rw=read
--name=512Kread --bs=512k --rw=read
--name=Seqread --bs=1m --rw=read
--name=Longread --bs=8m --rw=read
--name=Longwrite --bs=8m --rw=write
--name=Seqwrite --bs=1m --rw=write
--name=512Kwrite --bs=512k --rw=write
--name=256write --bs=256k --rw=write
--name=128write --bs=128k --rw=write
--name=64write --bs=64k --rw=write
--name=32write --bs=32k --rw=write
--name=16write --bs=16k --rw=write
--name=8write --bs=8k --rw=write
--name=4write --bs=4k --rw=write
--name=512KQD32write --bs=512k --iodepth=32 --rw=randwrite
--name=256KQD32write --bs=256k --iodepth=32 --rw=randwrite
--name=128KQD32write --bs=128k --iodepth=32 --rw=randwrite
--name=64KQD32write --bs=64k --iodepth=32 --rw=randwrite
--name=32KQD32write --bs=32k --iodepth=32 --rw=randwrite
--name=16KQD32write --bs=16k --iodepth=32 --rw=randwrite
--name=8KQD32write --bs=8k --iodepth=32 --rw=randwrite
--name=4kQD32write --bs=4k --iodepth=32 --rw=randwrite
| grep -E 'read|write|test' | grep -v ioengine

بحرص! الموارد!سيُجري هذا الرمز 36 اختبارًا مختلفًا، كل منها لمدة 4 ثوانٍ. نصف الاختبارات مخصصة للتسجيل. يمكنك تسجيل الكثير على NVMe في 4 ثوانٍ. تصل إلى 3 جيجا بايت في الثانية. لذلك، يمكن لكل جولة من اختبارات الكتابة أن تستهلك ما يصل إلى 216 جيجابايت من موارد SSD منك.

القراءة والكتابة مختلطة؟نعم. من المنطقي إجراء اختبارات القراءة والكتابة بشكل منفصل. علاوة على ذلك، من المنطقي التأكد من مزامنة جميع ذاكرات التخزين المؤقت بحيث لا تؤثر الكتابة التي تم إجراؤها مسبقًا على القراءة.

ستختلف النتائج بشكل كبير أثناء الإطلاق الأول والإصدارات اللاحقة حيث تمتلئ ذاكرة التخزين المؤقت ووحدة التخزين الرفيعة، وأيضًا اعتمادًا على ما إذا كان النظام قد تمكن من مزامنة ذاكرات التخزين المؤقت التي تم ملؤها أثناء الإطلاق الأخير.

من بين أمور أخرى، أوصي بقياس السرعة على حجم رقيق ممتلئ بالفعل تم التقاط لقطة منه للتو. أتيحت للمؤلف الفرصة لملاحظة مدى تسارع عمليات الكتابة العشوائية بشكل حاد فور إنشاء اللقطة الأولى، خاصة عندما لا تكون ذاكرة التخزين المؤقت ممتلئة بالكامل بعد. يحدث هذا بسبب دلالات الكتابة عند النسخ، ومحاذاة ذاكرة التخزين المؤقت وكتل الحجم الرفيعة، وحقيقة أن الكتابة العشوائية إلى RAID 6 تتحول إلى قراءة عشوائية من RAID 6 متبوعة بالكتابة إلى ذاكرة التخزين المؤقت. في التكوين الخاص بنا، تكون القراءة العشوائية من RAID 6 أسرع بما يصل إلى 6 مرات (عدد محركات أقراص SATA SSD في المصفوفة) من الكتابة. لأن يتم تخصيص كتل CoW بالتتابع من مجموعة رقيقة، ثم يتحول التسجيل، في الغالب، إلى تسلسلي أيضًا.

كل من هذه الميزات يمكن استخدامها لصالحك.

ذاكرة التخزين المؤقت لقطات "متماسكة".

لتقليل مخاطر فقدان البيانات في حالة تلف/فقدان ذاكرة التخزين المؤقت، يقترح المؤلف تقديم ممارسة اللقطات الدوارة لضمان سلامتها في هذه الحالة.

أولاً، نظرًا لوجود بيانات التعريف ذات الحجم الصغير على جهاز غير مخبأ، ستكون البيانات التعريفية متسقة وسيتم عزل الخسائر المحتملة داخل كتل البيانات.

تضمن دورة تدوير اللقطات التالية سلامة البيانات الموجودة داخل اللقطات في حالة فقدان ذاكرة التخزين المؤقت:

  1. لكل مجلد رفيع يحمل الاسم <name>، قم بإنشاء لقطة بالاسم <name>.cached
  2. لنقم بتعيين حد الترحيل على قيمة عالية معقولة: #lvchange --quiet --cachesettings "migration_threshold=16384" cache/cachedata
  3. في الحلقة نتحقق من عدد الكتل القذرة في ذاكرة التخزين المؤقت: #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' حتى نحصل على الصفر. إذا كان الصفر مفقودًا لفترة طويلة جدًا، فيمكن إنشاؤه عن طريق تحويل ذاكرة التخزين المؤقت مؤقتًا إلى وضع الكتابة. ومع ذلك، مع الأخذ في الاعتبار خصائص السرعة لمصفوفات SATA وNVMe SSD الخاصة بنا، بالإضافة إلى مورد TBW الخاص بها، فستتمكن إما من التقاط اللحظة بسرعة دون تغيير وضع ذاكرة التخزين المؤقت، أو سوف يستهلك جهازك مورده بالكامل بالكامل بضعة ايام. نظرًا لقيود الموارد، فإن النظام، من حيث المبدأ، غير قادر على أن يكون أقل من 100% من حمل الكتابة طوال الوقت. سوف تقوم محركات أقراص NVMe SSD الخاصة بنا التي تقل عن 100% من حمل الكتابة باستنفاذ المورد بالكامل أيام 3-4. سوف تدوم محركات أقراص SATA SSD مرتين فقط. لذلك، سنفترض أن معظم العبء يذهب إلى القراءة، وأن لدينا دفقات قصيرة المدى نسبيًا من النشاط العالي للغاية مقترنًا بحمل منخفض في المتوسط ​​للكتابة.
  4. بمجرد حصولنا على (أو إنشاء) صفر، نقوم بإعادة تسمية <name>.cached إلى <name>.committed. تم حذف <name>.commited القديم.
  5. اختياريًا، إذا كانت ذاكرة التخزين المؤقت ممتلئة بنسبة 100%، فيمكن إعادة إنشائها بواسطة برنامج نصي، وبالتالي مسحها. مع ذاكرة تخزين مؤقت نصف فارغة، يعمل النظام بشكل أسرع بكثير عند الكتابة.
  6. اضبط حد الترحيل على الصفر: #lvchange --quiet --cachesettings "migration_threshold=0" cache/cachedata سيؤدي هذا إلى منع ذاكرة التخزين المؤقت مؤقتًا من المزامنة مع الوسائط الرئيسية.
  7. ننتظر حتى تتراكم الكثير من التغييرات في ذاكرة التخزين المؤقت #lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}' أو سوف ينفجر الموقت.
  8. نكرر مرة أخرى.

لماذا الصعوبات مع عتبة الهجرة...؟الشيء هو أنه في الممارسة العملية، فإن التسجيل "العشوائي" ليس في الواقع عشوائيًا تمامًا. إذا كتبنا شيئًا ما إلى قطاع يبلغ حجمه 4 كيلو بايت، فهناك احتمال كبير أنه سيتم إنشاء سجل في الدقائق القليلة القادمة لنفس القطاع أو أحد القطاعات المجاورة (+- 32 كيلو بايت).

من خلال تعيين حد الترحيل على الصفر، نقوم بتأجيل مزامنة الكتابة على محرك أقراص SATA SSD وتجميع العديد من التغييرات على كتلة واحدة بحجم 64 كيلو بايت في ذاكرة التخزين المؤقت. وهذا يوفر بشكل كبير موارد SATA SSD.

أين الكود..؟لسوء الحظ، يعتبر المؤلف نفسه غير مؤهل بما فيه الكفاية في تطوير نصوص الباش، لأنه علم نفسه بنفسه بنسبة 100% ويمارس التطوير القائم على "جوجل"، لذلك فهو يعتقد أن الكود الرهيب الذي يخرج من يديه لا ينبغي أن يستخدمه أي شخص آخر.

أعتقد أن المحترفين في هذا المجال سيكونون قادرين على تصوير كل المنطق الموصوف أعلاه بشكل مستقل، إذا لزم الأمر، وربما حتى تصميمه بشكل جميل كخدمة Systemd، كما حاول المؤلف القيام به.

سيسمح لنا نظام تدوير اللقطة البسيط هذا ليس فقط بالحصول على لقطة واحدة متزامنة بالكامل بشكل مستمر على SATA SSD فحسب، بل سيسمح لنا أيضًا، باستخدام الأداة المساعدة Thin_delta، بمعرفة الكتل التي تم تغييرها بعد إنشائها، وبالتالي تحديد موقع الضرر على المجلدات الرئيسية، وتبسيط عملية الاسترداد إلى حد كبير.

القطع/التجاهل في libvirt/KVM

لأن سيتم استخدام مساحة تخزين البيانات لتشغيل KVM libvirt، لذا سيكون من الجيد تعليم الأجهزة الافتراضية الخاصة بنا ليس فقط شغل المساحة الحرة، ولكن أيضًا تحرير ما لم تعد هناك حاجة إليه.

يتم ذلك عن طريق محاكاة دعم TRIM/DISCARD على الأقراص الافتراضية. للقيام بذلك، تحتاج إلى تغيير نوع وحدة التحكم إلى Virtio-SCSI وتحرير ملف XML.

#virsh edit vmname
<disk type='block' device='disk'>
<driver name='qemu' type='raw' cache='writethrough' io='threads' discard='unmap'/>
<source dev='/dev/images/vmname'/>
<backingStore/>
<target dev='sda' bus='scsi'/>
<alias name='scsi0-0-0-0'/>
<address type='drive' controller='0' bus='0' target='0' unit='0'/>
</disk>

<controller type='scsi' index='0' model='virtio-scsi'>
<alias name='scsi0'/>
<address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
</controller>

تتم معالجة عمليات التجاهل هذه من أنظمة تشغيل الضيف بشكل صحيح بواسطة LVM، ويتم تحرير الكتل بشكل صحيح في ذاكرة التخزين المؤقت وفي المجموعة الرقيقة. في حالتنا، يحدث هذا بشكل رئيسي بطريقة متأخرة، عند حذف اللقطة التالية.

النسخ الاحتياطي BTRFS

استخدم البرامج النصية الجاهزة مع أقصى الحذر و على مسؤوليتك الخاصة. كتب المؤلف هذا الرمز بنفسه وحصريًا لنفسه. أنا متأكد من أن العديد من مستخدمي Linux ذوي الخبرة لديهم أدوات مماثلة، وليس هناك حاجة لنسخ أدوات شخص آخر.

لنقم بإنشاء وحدة تخزين على جهاز النسخ الاحتياطي:

#lvcreate -L 256G --name backup backup

لنقم بتنسيقه في BTRFS:

#mkfs.btrfs /dev/backup/backup

لنقم بإنشاء نقاط تحميل وتركيب الأقسام الفرعية الجذرية لنظام الملفات:

#mkdir /backup
#mkdir /backup/btrfs
#mkdir /backup/btrfs/root
#mkdir /backup/btrfs/back
#ln -s /boot /backup/btrfs
# cat >>/etc/fstab << EOF

/dev/mapper/root-root /backup/btrfs/root btrfs defaults,space_cache,noatime,nodiratime 0 2
/dev/mapper/backup-backup /backup/btrfs/back btrfs defaults,space_cache,noatime,nodiratime 0 2
EOF
#mount -a
#update-initramfs -u
#update-grub

لنقم بإنشاء أدلة للنسخ الاحتياطية:

#mkdir /backup/btrfs/back/remote
#mkdir /backup/btrfs/back/remote/root
#mkdir /backup/btrfs/back/remote/boot

لنقم بإنشاء دليل للنصوص البرمجية الاحتياطية:

#mkdir /root/btrfs-backup

لننسخ البرنامج النصي:

الكثير من رموز باش المخيفة. استخدام على مسؤوليتك الخاصة. لا تكتب رسائل غاضبة للكاتب...#cat >/root/btrfs-backup/btrfs-backup.sh << EOF
#!/bin/bash
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

SCRIPT_FILE="$(realpath $0)"
SCRIPT_DIR="$(dirname $SCRIPT_FILE)"
SCRIPT_NAME="$(basename -s .sh $SCRIPT_FILE)"

LOCK_FILE="/dev/shm/$SCRIPT_NAME.lock"
DATE_PREFIX='%Y-%m-%d'
DATE_FORMAT=$DATE_PREFIX'-%H-%M-%S'
DATE_REGEX='[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]'
BASE_SUFFIX=".@base"
PEND_SUFFIX=".@pend"
SNAP_SUFFIX=".@snap"
MOUNTS="/backup/btrfs/"
BACKUPS="/backup/btrfs/back/remote/"

function terminate ()
{
echo "$1" >&2
exit 1
}

function wait_lock()
{
flock 98
}

function wait_lock_or_terminate()
{
echo "Wating for lock..."
wait_lock || terminate "Failed to get lock. Exiting..."
echo "Got lock..."
}

function suffix()
{
FORMATTED_DATE=$(date +"$DATE_FORMAT")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}

function filter()
{
FORMATTED_DATE=$(date --date="$1" +"$DATE_PREFIX")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}

function backup()
{
SOURCE_PATH="$MOUNTS$1"
TARGET_PATH="$BACKUPS$1"
SOURCE_BASE_PATH="$MOUNTS$1$BASE_SUFFIX"
TARGET_BASE_PATH="$BACKUPS$1$BASE_SUFFIX"
TARGET_BASE_DIR="$(dirname $TARGET_BASE_PATH)"
SOURCE_PEND_PATH="$MOUNTS$1$PEND_SUFFIX"
TARGET_PEND_PATH="$BACKUPS$1$PEND_SUFFIX"
if [ -d "$SOURCE_BASE_PATH" ] then
echo "$SOURCE_BASE_PATH found"
else
echo "$SOURCE_BASE_PATH File not found creating snapshot of $SOURCE_PATH to $SOURCE_BASE_PATH"
btrfs subvolume snapshot -r $SOURCE_PATH $SOURCE_BASE_PATH
sync
if [ -d "$TARGET_BASE_PATH" ] then
echo "$TARGET_BASE_PATH found out of sync with source... removing..."
btrfs subvolume delete -c $TARGET_BASE_PATH
sync
fi
fi
if [ -d "$TARGET_BASE_PATH" ] then
echo "$TARGET_BASE_PATH found"
else
echo "$TARGET_BASE_PATH not found. Synching to $TARGET_BASE_DIR"
btrfs send $SOURCE_BASE_PATH | btrfs receive $TARGET_BASE_DIR
sync
fi
if [ -d "$SOURCE_PEND_PATH" ] then
echo "$SOURCE_PEND_PATH found removing..."
btrfs subvolume delete -c $SOURCE_PEND_PATH
sync
fi
btrfs subvolume snapshot -r $SOURCE_PATH $SOURCE_PEND_PATH
sync
if [ -d "$TARGET_PEND_PATH" ] then
echo "$TARGET_PEND_PATH found removing..."
btrfs subvolume delete -c $TARGET_PEND_PATH
sync
fi
echo "Sending $SOURCE_PEND_PATH to $TARGET_PEND_PATH"
btrfs send -p $SOURCE_BASE_PATH $SOURCE_PEND_PATH | btrfs receive $TARGET_BASE_DIR
sync
TARGET_DATE_SUFFIX=$(suffix)
btrfs subvolume snapshot -r $TARGET_PEND_PATH "$TARGET_PATH$TARGET_DATE_SUFFIX"
sync
btrfs subvolume delete -c $SOURCE_BASE_PATH
sync
btrfs subvolume delete -c $TARGET_BASE_PATH
sync
mv $SOURCE_PEND_PATH $SOURCE_BASE_PATH
mv $TARGET_PEND_PATH $TARGET_BASE_PATH
sync
}

function list()
{
LIST_TARGET_BASE_PATH="$BACKUPS$1$BASE_SUFFIX"
LIST_TARGET_BASE_DIR="$(dirname $LIST_TARGET_BASE_PATH)"
LIST_TARGET_BASE_NAME="$(basename -s .$BASE_SUFFIX $LIST_TARGET_BASE_PATH)"
find "$LIST_TARGET_BASE_DIR" -maxdepth 1 -mindepth 1 -type d -printf "%fn" | grep "${LIST_TARGET_BASE_NAME/$BASE_SUFFIX/$SNAP_SUFFIX}.$DATE_REGEX"
}

function remove()
{
REMOVE_TARGET_BASE_PATH="$BACKUPS$1$BASE_SUFFIX"
REMOVE_TARGET_BASE_DIR="$(dirname $REMOVE_TARGET_BASE_PATH)"
btrfs subvolume delete -c $REMOVE_TARGET_BASE_DIR/$2
sync
}

function removeall()
{
DATE_OFFSET="$2"
FILTER="$(filter "$DATE_OFFSET")"
while read -r SNAPSHOT ; do
remove "$1" "$SNAPSHOT"
done < <(list "$1" | grep "$FILTER")

}

(
COMMAND="$1"
shift

case "$COMMAND" in
"--help")
echo "Help"
;;
"suffix")
suffix
;;
"filter")
filter "$1"
;;
"backup")
wait_lock_or_terminate
backup "$1"
;;
"list")
list "$1"
;;
"remove")
wait_lock_or_terminate
remove "$1" "$2"
;;
"removeall")
wait_lock_or_terminate
removeall "$1" "$2"
;;
*)
echo "None.."
;;
esac
) 98>$LOCK_FILE

EOF

ماذا يفعل حتى..؟يحتوي على مجموعة من الأوامر البسيطة لإنشاء لقطات BTRFS ونسخها إلى FS آخر باستخدام إرسال/استقبال BTRFS.

يمكن أن يكون الإطلاق الأول طويلاً نسبيًا، لأن... في البداية، سيتم نسخ كافة البيانات. عمليات الإطلاق الأخرى ستكون سريعة جدًا، لأن... سيتم نسخ التغييرات فقط.

نص آخر سنضعه في cron:

المزيد من كود باش#cat >/root/btrfs-backup/cron-daily.sh << EOF
#!/bin/bash
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

SCRIPT_FILE="$(realpath $0)"
SCRIPT_DIR="$(dirname $SCRIPT_FILE)"
SCRIPT_NAME="$(basename -s .sh $SCRIPT_FILE)"

BACKUP_SCRIPT="$SCRIPT_DIR/btrfs-backup.sh"
RETENTION="-60 day"
$BACKUP_SCRIPT backup root/@
$BACKUP_SCRIPT removeall root/@ "$RETENTION"
$BACKUP_SCRIPT backup root/@home
$BACKUP_SCRIPT removeall root/@home "$RETENTION"
$BACKUP_SCRIPT backup boot/
$BACKUP_SCRIPT removeall boot/ "$RETENTION"
EOF

ماذا تعمل، أو ماذا تفعل..؟يقوم بإنشاء ومزامنة لقطات متزايدة لوحدات تخزين BTRFS المدرجة على نسخة FS الاحتياطية. وبعد ذلك، يقوم بحذف جميع الصور التي تم إنشاؤها قبل 60 يومًا. بعد الإطلاق، ستظهر لقطات مؤرخة للمجلدات المدرجة في الدلائل الفرعية /backup/btrfs/back/remote/.

دعونا نعطي حقوق تنفيذ التعليمات البرمجية:

#chmod +x /root/btrfs-backup/cron-daily.sh
#chmod +x /root/btrfs-backup/btrfs-backup.sh

دعنا نتحقق منه ونضعه في ملف cron:

#/usr/bin/nice -n 19 /usr/bin/ionice -c 3 /root/btrfs-backup/cron-daily.sh 2>&1 | /usr/bin/logger -t btrfs-backup
#cat /var/log/syslog | grep btrfs-backup
#crontab -e
0 2 * * * /usr/bin/nice -n 19 /usr/bin/ionice -c 3 /root/btrfs-backup/cron-daily.sh 2>&1 | /usr/bin/logger -t btrfs-backup

نسخة احتياطية رفيعة من LVM

لنقم بإنشاء تجمع رفيع على جهاز النسخ الاحتياطي:

#lvcreate -L 274877906944B --poolmetadataspare y --poolmetadatasize 4294967296B --chunksize 64k -Z y -T backup/thin-pool

لنقم بتثبيت ddrescue، لأن... ستستخدم البرامج النصية هذه الأداة:

#apt-get install gddrescue

لنقم بإنشاء دليل للنصوص البرمجية:

#mkdir /root/lvm-thin-backup

دعونا نسخ البرامج النصية:

الكثير من الشجار في الداخل...#cat >/root/lvm-thin-backup/lvm-thin-backup.sh << EOF
#!/bin/bash
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

SCRIPT_FILE="$(realpath $0)"
SCRIPT_DIR="$(dirname $SCRIPT_FILE)"
SCRIPT_NAME="$(basename -s .sh $SCRIPT_FILE)"

LOCK_FILE="/dev/shm/$SCRIPT_NAME.lock"
DATE_PREFIX='%Y-%m-%d'
DATE_FORMAT=$DATE_PREFIX'-%H-%M-%S'
DATE_REGEX='[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]'
BASE_SUFFIX=".base"
PEND_SUFFIX=".pend"
SNAP_SUFFIX=".snap"
BACKUPS="backup"
BACKUPS_POOL="thin-pool"

export LVM_SUPPRESS_FD_WARNINGS=1

function terminate ()
{
echo "$1" >&2
exit 1
}

function wait_lock()
{
flock 98
}

function wait_lock_or_terminate()
{
echo "Wating for lock..."
wait_lock || terminate "Failed to get lock. Exiting..."
echo "Got lock..."
}

function suffix()
{
FORMATTED_DATE=$(date +"$DATE_FORMAT")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}

function filter()
{
FORMATTED_DATE=$(date --date="$1" +"$DATE_PREFIX")
echo "$SNAP_SUFFIX.$FORMATTED_DATE"
}

function read_thin_id {
lvs --rows --reportformat basic --quiet -othin_id "$1/$2" | awk '{print $2}'
}

function read_pool_lv {
lvs --rows --reportformat basic --quiet -opool_lv "$1/$2" | awk '{print $2}'
}

function read_lv_dm_path {
lvs --rows --reportformat basic --quiet -olv_dm_path "$1/$2" | awk '{print $2}'
}

function read_lv_active {
lvs --rows --reportformat basic --quiet -olv_active "$1/$2" | awk '{print $2}'
}

function read_lv_chunk_size {
lvs --rows --reportformat basic --quiet --units b --nosuffix -ochunk_size "$1/$2" | awk '{print $2}'
}

function read_lv_size {
lvs --rows --reportformat basic --quiet --units b --nosuffix -olv_size "$1/$2" | awk '{print $2}'
}

function activate_volume {
lvchange -ay -Ky "$1/$2"
}

function deactivate_volume {
lvchange -an "$1/$2"
}

function read_thin_metadata_snap {
dmsetup status "$1" | awk '{print $7}'
}

function thindiff()
{
DIFF_VG="$1"
DIFF_SOURCE="$2"
DIFF_TARGET="$3"
DIFF_SOURCE_POOL=$(read_pool_lv $DIFF_VG $DIFF_SOURCE)
DIFF_TARGET_POOL=$(read_pool_lv $DIFF_VG $DIFF_TARGET)

if [ "$DIFF_SOURCE_POOL" == "" ] then
(>&2 echo "Source LV is not thin.")
exit 1
fi

if [ "$DIFF_TARGET_POOL" == "" ] then
(>&2 echo "Target LV is not thin.")
exit 1
fi

if [ "$DIFF_SOURCE_POOL" != "$DIFF_TARGET_POOL" ] then
(>&2 echo "Source and target LVs belong to different thin pools.")
exit 1
fi

DIFF_POOL_PATH=$(read_lv_dm_path $DIFF_VG $DIFF_SOURCE_POOL)
DIFF_SOURCE_ID=$(read_thin_id $DIFF_VG $DIFF_SOURCE)
DIFF_TARGET_ID=$(read_thin_id $DIFF_VG $DIFF_TARGET)
DIFF_POOL_PATH_TPOOL="$DIFF_POOL_PATH-tpool"
DIFF_POOL_PATH_TMETA="$DIFF_POOL_PATH"_tmeta
DIFF_POOL_METADATA_SNAP=$(read_thin_metadata_snap $DIFF_POOL_PATH_TPOOL)

if [ "$DIFF_POOL_METADATA_SNAP" != "-" ] then
(>&2 echo "Thin pool metadata snapshot already exist. Assuming stale one. Will release metadata snapshot in 5 seconds.")
sleep 5
dmsetup message $DIFF_POOL_PATH_TPOOL 0 release_metadata_snap
fi

dmsetup message $DIFF_POOL_PATH_TPOOL 0 reserve_metadata_snap
DIFF_POOL_METADATA_SNAP=$(read_thin_metadata_snap $DIFF_POOL_PATH_TPOOL)

if [ "$DIFF_POOL_METADATA_SNAP" == "-" ] then
(>&2 echo "Failed to create thin pool metadata snapshot.")
exit 1
fi

#We keep output in variable because metadata snapshot need to be released early.
DIFF_DATA=$(thin_delta -m$DIFF_POOL_METADATA_SNAP --snap1 $DIFF_SOURCE_ID --snap2 $DIFF_TARGET_ID $DIFF_POOL_PATH_TMETA)

dmsetup message $DIFF_POOL_PATH_TPOOL 0 release_metadata_snap

echo $"$DIFF_DATA" | grep -E 'different|left_only|right_only' | sed 's/</"/g' | sed 's/ /"/g' | awk -F'"' '{print $6 "t" $8 "t" $11}' | sed 's/different/copy/g' | sed 's/left_only/copy/g' | sed 's/right_only/discard/g'

}

function thinsync()
{
SYNC_VG="$1"
SYNC_PEND="$2"
SYNC_BASE="$3"
SYNC_TARGET="$4"
SYNC_PEND_POOL=$(read_pool_lv $SYNC_VG $SYNC_PEND)
SYNC_BLOCK_SIZE=$(read_lv_chunk_size $SYNC_VG $SYNC_PEND_POOL)
SYNC_PEND_PATH=$(read_lv_dm_path $SYNC_VG $SYNC_PEND)

activate_volume $SYNC_VG $SYNC_PEND

while read -r SYNC_ACTION SYNC_OFFSET SYNC_LENGTH ; do
SYNC_OFFSET_BYTES=$((SYNC_OFFSET * SYNC_BLOCK_SIZE))
SYNC_LENGTH_BYTES=$((SYNC_LENGTH * SYNC_BLOCK_SIZE))
if [ "$SYNC_ACTION" == "copy" ] then
ddrescue --quiet --force --input-position=$SYNC_OFFSET_BYTES --output-position=$SYNC_OFFSET_BYTES --size=$SYNC_LENGTH_BYTES "$SYNC_PEND_PATH" "$SYNC_TARGET"
fi

if [ "$SYNC_ACTION" == "discard" ] then
blkdiscard -o $SYNC_OFFSET_BYTES -l $SYNC_LENGTH_BYTES "$SYNC_TARGET"
fi
done < <(thindiff "$SYNC_VG" "$SYNC_PEND" "$SYNC_BASE")
}

function discard_volume()
{
DISCARD_VG="$1"
DISCARD_LV="$2"
DISCARD_LV_PATH=$(read_lv_dm_path "$DISCARD_VG" "$DISCARD_LV")
if [ "$DISCARD_LV_PATH" != "" ] then
echo "$DISCARD_LV_PATH found"
else
echo "$DISCARD_LV not found in $DISCARD_VG"
exit 1
fi
DISCARD_LV_POOL=$(read_pool_lv $DISCARD_VG $DISCARD_LV)
DISCARD_LV_SIZE=$(read_lv_size "$DISCARD_VG" "$DISCARD_LV")
lvremove -y --quiet "$DISCARD_LV_PATH" || exit 1
lvcreate --thin-pool "$DISCARD_LV_POOL" -V "$DISCARD_LV_SIZE"B --name "$DISCARD_LV" "$DISCARD_VG" || exit 1
}

function backup()
{
SOURCE_VG="$1"
SOURCE_LV="$2"
TARGET_VG="$BACKUPS"
TARGET_LV="$SOURCE_VG-$SOURCE_LV"
SOURCE_BASE_LV="$SOURCE_LV$BASE_SUFFIX"
TARGET_BASE_LV="$TARGET_LV$BASE_SUFFIX"
SOURCE_PEND_LV="$SOURCE_LV$PEND_SUFFIX"
TARGET_PEND_LV="$TARGET_LV$PEND_SUFFIX"
SOURCE_BASE_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_BASE_LV")
SOURCE_PEND_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_PEND_LV")
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
TARGET_PEND_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_PEND_LV")

if [ "$SOURCE_BASE_LV_PATH" != "" ] then
echo "$SOURCE_BASE_LV_PATH found"
else
echo "Source base not found creating snapshot of $SOURCE_VG/$SOURCE_LV to $SOURCE_VG/$SOURCE_BASE_LV"
lvcreate --quiet --snapshot --name "$SOURCE_BASE_LV" "$SOURCE_VG/$SOURCE_LV" || exit 1
SOURCE_BASE_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_BASE_LV")
activate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
echo "Discarding $SOURCE_BASE_LV_PATH as we need to bootstrap."
SOURCE_BASE_POOL=$(read_pool_lv $SOURCE_VG $SOURCE_BASE_LV)
SOURCE_BASE_CHUNK_SIZE=$(read_lv_chunk_size $SOURCE_VG $SOURCE_BASE_POOL)
discard_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
sync
if [ "$TARGET_BASE_LV_PATH" != "" ] then
echo "$TARGET_BASE_LV_PATH found out of sync with source... removing..."
lvremove -y --quiet $TARGET_BASE_LV_PATH || exit 1
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
sync
fi
fi
SOURCE_BASE_SIZE=$(read_lv_size "$SOURCE_VG" "$SOURCE_BASE_LV")
if [ "$TARGET_BASE_LV_PATH" != "" ] then
echo "$TARGET_BASE_LV_PATH found"
else
echo "$TARGET_VG/$TARGET_LV not found. Creating empty volume."
lvcreate --thin-pool "$BACKUPS_POOL" -V "$SOURCE_BASE_SIZE"B --name "$TARGET_BASE_LV" "$TARGET_VG" || exit 1
echo "Have to rebootstrap. Discarding source at $SOURCE_BASE_LV_PATH"
activate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
SOURCE_BASE_POOL=$(read_pool_lv $SOURCE_VG $SOURCE_BASE_LV)
SOURCE_BASE_CHUNK_SIZE=$(read_lv_chunk_size $SOURCE_VG $SOURCE_BASE_POOL)
discard_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
TARGET_BASE_POOL=$(read_pool_lv $TARGET_VG $TARGET_BASE_LV)
TARGET_BASE_CHUNK_SIZE=$(read_lv_chunk_size $TARGET_VG $TARGET_BASE_POOL)
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
echo "Discarding target at $TARGET_BASE_LV_PATH"
discard_volume "$TARGET_VG" "$TARGET_BASE_LV"
sync
fi
if [ "$SOURCE_PEND_LV_PATH" != "" ] then
echo "$SOURCE_PEND_LV_PATH found removing..."
lvremove -y --quiet "$SOURCE_PEND_LV_PATH" || exit 1
sync
fi
lvcreate --quiet --snapshot --name "$SOURCE_PEND_LV" "$SOURCE_VG/$SOURCE_LV" || exit 1
SOURCE_PEND_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_PEND_LV")
sync
if [ "$TARGET_PEND_LV_PATH" != "" ] then
echo "$TARGET_PEND_LV_PATH found removing..."
lvremove -y --quiet $TARGET_PEND_LV_PATH
sync
fi
lvcreate --quiet --snapshot --name "$TARGET_PEND_LV" "$TARGET_VG/$TARGET_BASE_LV" || exit 1
TARGET_PEND_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_PEND_LV")
SOURCE_PEND_LV_SIZE=$(read_lv_size "$SOURCE_VG" "$SOURCE_PEND_LV")
lvresize -L "$SOURCE_PEND_LV_SIZE"B "$TARGET_PEND_LV_PATH"
activate_volume "$TARGET_VG" "$TARGET_PEND_LV"
echo "Synching $SOURCE_PEND_LV_PATH to $TARGET_PEND_LV_PATH"
thinsync "$SOURCE_VG" "$SOURCE_PEND_LV" "$SOURCE_BASE_LV" "$TARGET_PEND_LV_PATH" || exit 1
sync

TARGET_DATE_SUFFIX=$(suffix)
lvcreate --quiet --snapshot --name "$TARGET_LV$TARGET_DATE_SUFFIX" "$TARGET_VG/$TARGET_PEND_LV" || exit 1
sync
lvremove --quiet -y "$SOURCE_BASE_LV_PATH" || exit 1
sync
lvremove --quiet -y "$TARGET_BASE_LV_PATH" || exit 1
sync
lvrename -y "$SOURCE_VG/$SOURCE_PEND_LV" "$SOURCE_BASE_LV" || exit 1
lvrename -y "$TARGET_VG/$TARGET_PEND_LV" "$TARGET_BASE_LV" || exit 1
sync
deactivate_volume "$TARGET_VG" "$TARGET_BASE_LV"
deactivate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
}

function verify()
{
SOURCE_VG="$1"
SOURCE_LV="$2"
TARGET_VG="$BACKUPS"
TARGET_LV="$SOURCE_VG-$SOURCE_LV"
SOURCE_BASE_LV="$SOURCE_LV$BASE_SUFFIX"
TARGET_BASE_LV="$TARGET_LV$BASE_SUFFIX"
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
SOURCE_BASE_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_BASE_LV")

if [ "$SOURCE_BASE_LV_PATH" != "" ] then
echo "$SOURCE_BASE_LV_PATH found"
else
echo "$SOURCE_BASE_LV_PATH not found"
exit 1
fi
if [ "$TARGET_BASE_LV_PATH" != "" ] then
echo "$TARGET_BASE_LV_PATH found"
else
echo "$TARGET_BASE_LV_PATH not found"
exit 1
fi
activate_volume "$TARGET_VG" "$TARGET_BASE_LV"
activate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
echo Comparing "$SOURCE_BASE_LV_PATH" with "$TARGET_BASE_LV_PATH"
cmp "$SOURCE_BASE_LV_PATH" "$TARGET_BASE_LV_PATH"
echo Done...
deactivate_volume "$TARGET_VG" "$TARGET_BASE_LV"
deactivate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
}

function resync()
{
SOURCE_VG="$1"
SOURCE_LV="$2"
TARGET_VG="$BACKUPS"
TARGET_LV="$SOURCE_VG-$SOURCE_LV"
SOURCE_BASE_LV="$SOURCE_LV$BASE_SUFFIX"
TARGET_BASE_LV="$TARGET_LV$BASE_SUFFIX"
TARGET_BASE_LV_PATH=$(read_lv_dm_path "$TARGET_VG" "$TARGET_BASE_LV")
SOURCE_BASE_LV_PATH=$(read_lv_dm_path "$SOURCE_VG" "$SOURCE_BASE_LV")

if [ "$SOURCE_BASE_LV_PATH" != "" ] then
echo "$SOURCE_BASE_LV_PATH found"
else
echo "$SOURCE_BASE_LV_PATH not found"
exit 1
fi
if [ "$TARGET_BASE_LV_PATH" != "" ] then
echo "$TARGET_BASE_LV_PATH found"
else
echo "$TARGET_BASE_LV_PATH not found"
exit 1
fi
activate_volume "$TARGET_VG" "$TARGET_BASE_LV"
activate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
SOURCE_BASE_POOL=$(read_pool_lv $SOURCE_VG $SOURCE_BASE_LV)
SYNC_BLOCK_SIZE=$(read_lv_chunk_size $SOURCE_VG $SOURCE_BASE_POOL)

echo Syncronizing "$SOURCE_BASE_LV_PATH" to "$TARGET_BASE_LV_PATH"

CMP_OFFSET=0
while [[ "$CMP_OFFSET" != "" ]] ; do
CMP_MISMATCH=$(cmp -i "$CMP_OFFSET" "$SOURCE_BASE_LV_PATH" "$TARGET_BASE_LV_PATH" | grep differ | awk '{print $5}' | sed 's/,//g' )
if [[ "$CMP_MISMATCH" != "" ]] ; then
CMP_OFFSET=$(( CMP_MISMATCH + CMP_OFFSET ))
SYNC_OFFSET_BYTES=$(( ( CMP_OFFSET / SYNC_BLOCK_SIZE ) * SYNC_BLOCK_SIZE ))
SYNC_LENGTH_BYTES=$(( SYNC_BLOCK_SIZE ))
echo "Synching $SYNC_LENGTH_BYTES bytes at $SYNC_OFFSET_BYTES from $SOURCE_BASE_LV_PATH to $TARGET_BASE_LV_PATH"
ddrescue --quiet --force --input-position=$SYNC_OFFSET_BYTES --output-position=$SYNC_OFFSET_BYTES --size=$SYNC_LENGTH_BYTES "$SOURCE_BASE_LV_PATH" "$TARGET_BASE_LV_PATH"
else
CMP_OFFSET=""
fi
done
echo Done...
deactivate_volume "$TARGET_VG" "$TARGET_BASE_LV"
deactivate_volume "$SOURCE_VG" "$SOURCE_BASE_LV"
}

function list()
{
LIST_SOURCE_VG="$1"
LIST_SOURCE_LV="$2"
LIST_TARGET_VG="$BACKUPS"
LIST_TARGET_LV="$LIST_SOURCE_VG-$LIST_SOURCE_LV"
LIST_TARGET_BASE_LV="$LIST_TARGET_LV$SNAP_SUFFIX"
lvs -olv_name | grep "$LIST_TARGET_BASE_LV.$DATE_REGEX"
}

function remove()
{
REMOVE_TARGET_VG="$BACKUPS"
REMOVE_TARGET_LV="$1"
lvremove -y "$REMOVE_TARGET_VG/$REMOVE_TARGET_LV"
sync
}

function removeall()
{
DATE_OFFSET="$3"
FILTER="$(filter "$DATE_OFFSET")"
while read -r SNAPSHOT ; do
remove "$SNAPSHOT"
done < <(list "$1" "$2" | grep "$FILTER")

}

(
COMMAND="$1"
shift

case "$COMMAND" in
"--help")
echo "Help"
;;
"suffix")
suffix
;;
"filter")
filter "$1"
;;
"backup")
wait_lock_or_terminate
backup "$1" "$2"
;;
"list")
list "$1" "$2"
;;
"thindiff")
thindiff "$1" "$2" "$3"
;;
"thinsync")
thinsync "$1" "$2" "$3" "$4"
;;
"verify")
wait_lock_or_terminate
verify "$1" "$2"
;;
"resync")
wait_lock_or_terminate
resync "$1" "$2"
;;
"remove")
wait_lock_or_terminate
remove "$1"
;;
"removeall")
wait_lock_or_terminate
removeall "$1" "$2" "$3"
;;
*)
echo "None.."
;;
esac
) 98>$LOCK_FILE

EOF

ماذا تعمل، أو ماذا تفعل...؟يحتوي على مجموعة من الأوامر لمعالجة اللقطات الرقيقة ومزامنة الفرق بين لقطتين رفيعتين تم استلامهما عبر Thin_delta إلى جهاز كتلة آخر باستخدام ddrescue وblkdiscard.

نص آخر سنضعه في cron:

المزيد من الباش#cat >/root/lvm-thin-backup/cron-daily.sh << EOF
#!/bin/bash
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

SCRIPT_FILE="$(realpath $0)"
SCRIPT_DIR="$(dirname $SCRIPT_FILE)"
SCRIPT_NAME="$(basename -s .sh $SCRIPT_FILE)"

BACKUP_SCRIPT="$SCRIPT_DIR/lvm-thin-backup.sh"
RETENTION="-60 days"

$BACKUP_SCRIPT backup images linux-dev
$BACKUP_SCRIPT backup images win8
$BACKUP_SCRIPT backup images win8-data
#etc

$BACKUP_SCRIPT removeall images linux-dev "$RETENTION"
$BACKUP_SCRIPT removeall images win8 "$RETENTION"
$BACKUP_SCRIPT removeall images win8-data "$RETENTION"
#etc

EOF

ماذا تعمل، أو ماذا تفعل...؟يستخدم البرنامج النصي السابق لإنشاء ومزامنة النسخ الاحتياطية لوحدات التخزين الرفيعة المدرجة. سيترك البرنامج النصي لقطات غير نشطة للمجلدات المدرجة، والتي تكون ضرورية لتتبع التغييرات منذ آخر مزامنة.

يجب تحرير هذا البرنامج النصي، مع تحديد قائمة المجلدات الرفيعة التي يجب عمل نسخ احتياطية لها. الأسماء المذكورة هي لأغراض توضيحية فقط. إذا كنت ترغب في ذلك، يمكنك كتابة برنامج نصي يقوم بمزامنة جميع وحدات التخزين.

دعونا نعطي الحقوق:

#chmod +x /root/lvm-thin-backup/cron-daily.sh
#chmod +x /root/lvm-thin-backup/lvm-thin-backup.sh

دعنا نتحقق منه ونضعه في ملف cron:

#/usr/bin/nice -n 19 /usr/bin/ionice -c 3 /root/lvm-thin-backup/cron-daily.sh 2>&1 | /usr/bin/logger -t lvm-thin-backup
#cat /var/log/syslog | grep lvm-thin-backup
#crontab -e
0 3 * * * /usr/bin/nice -n 19 /usr/bin/ionice -c 3 /root/lvm-thin-backup/cron-daily.sh 2>&1 | /usr/bin/logger -t lvm-thin-backup

الإطلاق الأول سيكون طويلاً، لأن... ستتم مزامنة المجلدات الرقيقة بالكامل عن طريق نسخ كل المساحة المستخدمة. بفضل البيانات الوصفية الرقيقة لـ LVM، نعرف أي الكتل قيد الاستخدام بالفعل، لذلك سيتم نسخ الكتل الرفيعة الحجم المستخدمة فعليًا فقط.

ستقوم عمليات التشغيل اللاحقة بنسخ البيانات بشكل تدريجي بفضل تتبع التغيير عبر البيانات الوصفية الرقيقة لـ LVM.

دعونا نرى ما حدث:

#time /root/btrfs-backup/cron-daily.sh
real 0m2,967s
user 0m0,225s
sys 0m0,353s

#time /root/lvm-thin-backup/cron-daily.sh
real 1m2,710s
user 0m12,721s
sys 0m6,671s

#ls -al /backup/btrfs/back/remote/*
/backup/btrfs/back/remote/boot:
total 0
drwxr-xr-x 1 root root 1260 мар 26 09:11 .
drwxr-xr-x 1 root root 16 мар 6 09:30 ..
drwxr-xr-x 1 root root 322 мар 26 02:00 .@base
drwxr-xr-x 1 root root 516 мар 6 09:39 [email protected]
drwxr-xr-x 1 root root 516 мар 6 09:39 [email protected]
...
/backup/btrfs/back/remote/root:
total 0
drwxr-xr-x 1 root root 2820 мар 26 09:11 .
drwxr-xr-x 1 root root 16 мар 6 09:30 ..
drwxr-xr-x 1 root root 240 мар 26 09:11 @.@base
drwxr-xr-x 1 root root 22 мар 26 09:11 @home.@base
drwxr-xr-x 1 root root 22 мар 6 09:39 @[email protected]
drwxr-xr-x 1 root root 22 мар 6 09:39 @[email protected]
...
drwxr-xr-x 1 root root 240 мар 6 09:39 @[email protected]
drwxr-xr-x 1 root root 240 мар 6 09:39 @[email protected]
...

#lvs -olv_name,lv_size images && lvs -olv_name,lv_size backup
LV LSize
linux-dev 128,00g
linux-dev.base 128,00g
thin-pool 1,38t
win8 128,00g
win8-data 2,00t
win8-data.base 2,00t
win8.base 128,00g
LV LSize
backup 256,00g
images-linux-dev.base 128,00g
images-linux-dev.snap.2020-03-08-10-09-11 128,00g
images-linux-dev.snap.2020-03-08-10-09-25 128,00g
...
images-win8-data.base 2,00t
images-win8-data.snap.2020-03-16-14-11-55 2,00t
images-win8-data.snap.2020-03-16-14-19-50 2,00t
...
images-win8.base 128,00g
images-win8.snap.2020-03-17-04-51-46 128,00g
images-win8.snap.2020-03-18-03-02-49 128,00g
...
thin-pool <2,09t

ما علاقة هذا بدمى التعشيش؟

على الأرجح، نظرًا لأن وحدات التخزين المنطقية LVM LV يمكن أن تكون وحدات تخزين مادية LVM PV لوحدات VG أخرى. يمكن أن يكون LVM متكررًا، مثل الدمى المتداخلة. وهذا يمنح LVM مرونة قصوى.

PS

في المقالة التالية، سنحاول استخدام العديد من أنظمة التخزين المحمولة/KVM المماثلة كأساس لإنشاء مجموعة تخزين موزعة جغرافيًا/VM مع تكرار في عدة قارات باستخدام أجهزة الكمبيوتر المكتبية المنزلية والإنترنت المنزلي وشبكات P2P.

المصدر: www.habr.com

إضافة تعليق