روز بخیر.
من می خواهم تجربه عملی خود را از ساخت یک سیستم ذخیره سازی داده برای KVM با استفاده از md RAID + LVM با جامعه به اشتراک بگذارم.
این برنامه شامل موارد زیر خواهد بود:
- ساخت md RAID 1 از NVMe SSD.
- مونتاژ md RAID 6 از SATA SSD و درایوهای معمولی.
- ویژگی های عملیات TRIM/DISCARD در SSD RAID 1/6.
- ایجاد یک آرایه bootable md RAID 1/6 بر روی مجموعه ای از دیسک ها.
- نصب سیستم روی NVMe RAID 1 زمانی که پشتیبانی از NVMe در BIOS وجود ندارد.
- استفاده از کش LVM و LVM thin.
- استفاده از عکس های فوری BTRFS و ارسال/دریافت برای پشتیبان گیری.
- استفاده از عکسهای فوری نازک LVM و thin_delta برای پشتیبانگیری به سبک BTRFS.
اگر علاقه مند هستید، لطفا گربه را ببینید.
بیانیه
نویسنده هیچ گونه مسئولیتی در قبال عواقب استفاده یا عدم استفاده از مطالب/نمونه ها/کد/نکات/داده های این مقاله ندارد. با مطالعه یا استفاده از این مطالب به هر نحوی، مسئولیت تمامی عواقب این اقدامات را بر عهده می گیرید. پیامدهای احتمالی عبارتند از:
- SSD های NVMe سرخ شده.
- مصرف کامل منابع ضبط و خرابی درایوهای SSD.
- از دست دادن کامل تمام داده ها در همه درایوها، از جمله نسخه های پشتیبان.
- سخت افزار کامپیوتر معیوب.
- وقت، اعصاب و پول تلف شده.
- هرگونه عواقب دیگری که در بالا ذکر نشده است.
آهن
موجود عبارت بودند از:
مادربرد حدوداً در سال 2013 با چیپست Z87، کامل با Intel Core i7 / Haswell.
- پردازنده 4 هسته ای 8 رشته
- 32 گیگابایت رم DDR3
- 1 x 16 یا 2 x 8 PCIe 3.0
- 1 x 4 + 1 x 1 PCIe 2.0
- 6 x 6 GBps SATA 3 کانکتور
آداپتور SAS LSI SAS9211-8I به حالت IT / HBA فلش شد. سفتافزار فعالشده با RAID عمداً با سیستمافزار HBA جایگزین شده است تا:
- می توانید هر زمان که بخواهید این آداپتور را بیرون بیاورید و آن را با هر آداپتور دیگری که برخورد کردید جایگزین کنید.
- TRIM/Discard به طور معمول روی دیسک ها کار می کرد، زیرا... در سیستم عامل RAID این دستورات به هیچ وجه پشتیبانی نمی شوند و HBA، به طور کلی، اهمیتی نمی دهد که چه دستوراتی از طریق اتوبوس منتقل می شود.
هارد دیسک - 8 عدد HGST Travelstar 7K1000 با ظرفیت 1 ترابایت در فرم فاکتور 2.5 برای لپ تاپ ها. این درایوها قبلا در یک آرایه RAID 6 بودند. آنها همچنین در سیستم جدید کاربرد خواهند داشت. برای ذخیره نسخه های پشتیبان محلی
به علاوه اضافه شد:
6 عدد SATA SSD مدل Samsung 860 QVO 2TB. این SSD ها به حجم زیادی نیاز داشتند، وجود حافظه پنهان SLC، قابلیت اطمینان و قیمت پایین مورد نظر بود. پشتیبانی برای discard/zero مورد نیاز بود که توسط خط در dmesg بررسی میشود:
kernel: ata1.00: Enabling discard_zeroes_data
2 عدد NVMe SSD مدل Samsung SSD 970 EVO 500GB.
برای این SSD ها، سرعت خواندن/نوشتن تصادفی و ظرفیت منابع برای نیازهای شما مهم است. رادیاتور برای آنها لزوما. کاملا. در غیر این صورت، در اولین همگام سازی RAID، آنها را سرخ کنید تا ترد شوند.
آداپتور StarTech PEX8M2E2 برای 2 x NVMe SSD نصب شده در اسلات PCIe 3.0 8x. این، دوباره، فقط یک HBA است، اما برای NVMe. تفاوت آن با آداپتورهای ارزان قیمت در این است که به دلیل وجود سوئیچ داخلی PCIe به پشتیبانی از دوشاخه PCIe از مادربرد نیاز ندارد. حتی اگر یک اسلات x1 PCIe 1.0 باشد، حتی در قدیمی ترین سیستم با PCIe نیز کار خواهد کرد. طبیعتا با سرعت مناسب. هیچ RAID وجود ندارد. هیچ بایوس داخلی روی برد وجود ندارد. بنابراین، سیستم شما به طور جادویی بوت شدن با NVMe را نمیآموزد، چه رسد به NVMe RAID به لطف این دستگاه.
این قطعه صرفاً به دلیل وجود تنها یک 8x PCIe 3.0 رایگان در سیستم بود و در صورت وجود 2 اسلات رایگان، می توان آن را به راحتی با دو پنی PEX4M2E1 یا آنالوگ که در هر جایی با قیمت 600 خریداری کرد جایگزین کرد. روبل
رد انواع سختافزار یا RAIDهای داخلی چیپست/BIOS عمداً انجام شد تا بتوان با حفظ تمام دادهها، کل سیستم را به استثنای خود SSD/HDD جایگزین کرد. در حالت ایده آل، به طوری که می توانید حتی سیستم عامل نصب شده را هنگام انتقال به سخت افزار کاملاً جدید/متفاوت ذخیره کنید. نکته اصلی این است که پورت های SATA و PCIe وجود دارد. این مانند یک سی دی زنده یا فلش درایو قابل بوت است، فقط بسیار سریع و کمی حجیم.
شوخیدر غیر این صورت، می دانید چه اتفاقی می افتد - گاهی اوقات لازم است فوراً کل آرایه را با خود ببرید تا بردارید. اما من نمی خواهم اطلاعات را از دست بدهم. برای انجام این کار، تمام رسانه های ذکر شده به راحتی بر روی اسلایدها در 5.25 دهانه کیس استاندارد قرار می گیرند.
خوب، و البته، برای آزمایش روشهای مختلف کش کردن SSD در لینوکس.
حملات سخت افزاری خسته کننده هستند. آن را روشن کنید. یا کار می کند یا نمی کند. و با mdadm همیشه گزینه هایی وجود دارد.
نرم
قبلاً دبیان 8 جسی روی سخت افزار نصب شده بود که نزدیک به EOL است. RAID 6 از هارد دیسک های فوق الذکر جفت شده با LVM مونتاژ شد. ماشین های مجازی را در kvm/libvirt اجرا می کرد.
زیرا نویسنده تجربه مناسبی در ایجاد درایوهای فلش قابل بوت SATA/NVMe قابل حمل دارد و همچنین برای اینکه الگوی مناسب معمولی شکسته نشود، اوبونتو 18.04 به عنوان سیستم هدف انتخاب شده است که قبلاً به اندازه کافی تثبیت شده است، اما هنوز 3 سال از زمان آن باقی مانده است. پشتیبانی در آینده
سیستم ذکر شده شامل تمام درایورهای سخت افزاری است که ما به آنها نیاز داریم. ما به هیچ نرم افزار یا درایور شخص ثالث نیاز نداریم.
آماده شدن برای نصب
برای نصب سیستم به تصویر دسکتاپ اوبونتو نیاز داریم. سیستم سرور دارای نوعی نصب کننده قوی است که استقلال بیش از حد را نشان می دهد که با فشار دادن پارتیشن سیستم UEFI روی یکی از دیسک ها غیرفعال نمی شود و زیبایی را از بین می برد. بر این اساس، فقط در حالت UEFI نصب می شود. هیچ گزینه ای ارائه نمی دهد
ما از این راضی نیستیم.
چرا؟متأسفانه بوت UEFI به شدت با RAID نرم افزار بوت سازگاری ضعیفی دارد، زیرا... هیچ کس رزرو پارتیشن UEFI ESP را به ما پیشنهاد نمی کند. دستور العمل های آنلاین وجود دارد که پیشنهاد می کند پارتیشن ESP را در یک درایو فلش در یک پورت USB قرار دهید، اما این یک نقطه شکست است. دستور العمل هایی با استفاده از نرم افزار mdadm RAID 1 با ابرداده نسخه 0.9 وجود دارد که مانع از دیدن این پارتیشن توسط UEFI BIOS نمی شود، اما تا لحظه خوشی که بایوس یا سیستم عامل سخت افزاری دیگری چیزی را روی ESP می نویسد و فراموش می کند آن را با سایرین همگام کند، زنده می ماند. آینه.
علاوه بر این، بوت UEFI به NVRAM بستگی دارد، که همراه با دیسک ها به سیستم جدید منتقل نمی شود، زیرا بخشی از مادربرد است.
بنابراین، ما یک چرخ جدید را دوباره اختراع نمی کنیم. ما قبلاً یک دوچرخه پدربزرگ آماده و آزمایش شده در زمان داریم که اکنون به نام Legacy/BIOS boot نامیده می شود و نام افتخار CSM را در سیستم های سازگار با UEFI یدک می کشد. ما فقط آن را از قفسه خارج می کنیم، آن را روغن کاری می کنیم، لاستیک ها را پمپ می کنیم و با یک پارچه مرطوب پاک می کنیم.
نسخه دسکتاپ اوبونتو نیز نمی تواند با بوت لودر Legacy به درستی نصب شود، اما در اینجا، همانطور که می گویند، حداقل گزینه هایی وجود دارد.
و بنابراین، ما سخت افزار را جمع آوری می کنیم و سیستم را از درایو فلش قابل بوت شدن Ubuntu Live بارگذاری می کنیم. ما باید بسته ها را دانلود کنیم، بنابراین شبکه ای را راه اندازی می کنیم که برای شما کار می کند. اگر کار نمی کند، می توانید بسته های لازم را از قبل روی یک درایو فلش بارگذاری کنید.
وارد محیط دسکتاپ میشویم، شبیهساز ترمینال را راهاندازی میکنیم و ادامه میدهیم:
#sudo bash
چگونه…؟خط بالا محرک متعارف هولیوارها در مورد سودو است. ج بоفرصت های بزرگتر می آیند وоمسئولیت بیشتر سوال این است که آیا شما می توانید آن را به عهده بگیرید. بسیاری از مردم فکر می کنند که استفاده از سودو به این روش حداقل محتاطانه نیست. با این حال:
#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 برای / (root) استفاده کند، فراموش نمی کند که برای هر نرم افزار دیگری می توانید پارتیشن های جداگانه ای را در LVM ایجاد کنید و آنها را در دایرکتوری های لازم نصب کنید.
ما هیچ تصویری از ماشین های مجازی یا پایگاه داده را در این FS ذخیره نمی کنیم.
این FS فقط برای ایجاد عکس های فوری از سیستم بدون خاموش کردن آن استفاده می شود و سپس این عکس های فوری را با استفاده از ارسال/دریافت به دیسک پشتیبان منتقل می کند.
علاوه بر این، نویسنده عموماً ترجیح میدهد حداقل نرمافزار را مستقیماً روی سختافزار نگه دارد و همه نرمافزارهای دیگر را در ماشینهای مجازی با استفاده از مواردی مانند انتقال پردازندههای گرافیکی و کنترلکنندههای میزبان PCI-USB به KVM از طریق IOMMU اجرا کند.
تنها چیزهایی که روی سخت افزار باقی می ماند ذخیره سازی داده ها، مجازی سازی و پشتیبان گیری است.
اگر به ZFS بیشتر اعتماد دارید، در اصل، برای برنامه مشخص شده آنها قابل تعویض هستند.
با این حال، نویسنده عمداً ویژگیهای Mirroring/RAID داخلی و افزونگی که ZFS، BRTFS و LVM دارند را نادیده میگیرد.
به عنوان یک استدلال اضافی، BTRFS توانایی تبدیل نوشتن تصادفی به موارد متوالی را دارد، که تأثیر بسیار مثبتی بر سرعت همگامسازی عکسهای فوری/پشتیبانگیری روی HDD دارد.
بیایید همه دستگاه ها را دوباره اسکن کنیم:
#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
طرح دیسک
SSD NVMe
اما ما به هیچ وجه آنها را علامت گذاری نمی کنیم. با این حال، BIOS ما این درایوها را نمی بیند. بنابراین، آنها به طور کامل به نرم افزار RAID خواهند رفت. ما حتی بخش هایی را در آنجا ایجاد نمی کنیم. اگر میخواهید «کانن» یا «اصولاً» را دنبال کنید، یک پارتیشن بزرگ مانند یک HDD ایجاد کنید.
هارد SATA
در اینجا نیازی به اختراع چیز خاصی نیست. ما یک بخش برای همه چیز ایجاد خواهیم کرد. ما یک پارتیشن ایجاد خواهیم کرد زیرا 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 پارتیشن اول را در صورت قرار گرفتن در 2 ترابایت اول ببینند. نکته اصلی این است که پارتیشن بوت و پارتیشن bios_grub در این دیسک ها باید در ابتدا باشند. این حتی به شما امکان می دهد از درایوهای GPT Legacy/BIOS بوت شوید.
اما این مورد ما نیست.
در اینجا دو بخش ایجاد می کنیم. اندازه اولی 1 گیگابایت خواهد بود و برای RAID 1 /boot استفاده می شود.
مورد دوم برای RAID 6 استفاده می شود و تمام فضای خالی باقی مانده را به جز یک منطقه کوچک تخصیص نشده در انتهای درایو اشغال می کند.
این منطقه بدون علامت چیست؟طبق منابع موجود در شبکه، حافظه های SSD SATA ما دارای حافظه کش SLC قابل ارتقاء پویا با اندازه های 6 تا 78 گیگابایت هستند. به دلیل تفاوت بین "گیگابایت" و "گیبی بایت" در برگه اطلاعات درایو، 6 گیگابایت "رایگان" دریافت می کنیم. 72 گیگابایت باقی مانده از فضای بلااستفاده اختصاص داده شده است.
در اینجا لازم به ذکر است که ما کش SLC داریم و فضا در حالت MLC 4 بیتی اشغال شده است. که برای ما به طور موثر به این معنی است که به ازای هر 4 گیگابایت فضای آزاد، تنها 1 گیگابایت حافظه کش SLC دریافت می کنیم.
72 گیگابایت را در 4 ضرب کنید و 288 گیگابایت بدست آورید. این فضای خالی است که ما آن را علامت گذاری نمی کنیم تا به درایوها اجازه دهیم تا از کش SLC استفاده کامل کنند.
بنابراین، ما به طور موثر تا 312 گیگابایت کش SLC از مجموع شش درایو دریافت خواهیم کرد. از بین تمام درایوها، 2 عدد در RAID برای افزونگی استفاده خواهد شد.
این مقدار حافظه نهان به ما این امکان را می دهد که در زندگی واقعی به ندرت با موقعیتی مواجه شویم که یک نوشتن به حافظه پنهان نمی رود. این به خوبی غم انگیزترین اشکال حافظه QLC را جبران می کند - سرعت بسیار پایین نوشتن زمانی که داده ها با عبور از حافظه پنهان نوشته می شوند. اگر بارهای شما با این مطابقت ندارد، پس توصیه می کنم با در نظر گرفتن TBW از برگه داده، در مورد مدت زمان ماندگاری SSD خود در چنین بارگیری خوب فکر کنید.
#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
SSD NVMe
#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، باید آن را در پارامترهای ماژول هسته فعال کنید.
این کار فقط در صورتی انجام می شود که همه SSD های استفاده شده در آرایه های سطح 4/5/6 در این سیستم از discard_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 و ترفندهای thin-provisioning جبران خواهیم کرد.
ما هنوز DISCARD را برای RAID 6 فعال نکردهایم. بنابراین فعلاً این آرایه را "آغاز" نمیکنیم. ما این کار را بعداً پس از نصب سیستم عامل انجام خواهیم داد.
هارد SATA
#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 است.
با این حال، ما همچنان به این آرایه سریع برای نیازهای دیگر مانند swap، metadata و LVM-cache و LVM-thin metadata نیاز خواهیم داشت، بنابراین یک LVM VG در این آرایه ایجاد خواهیم کرد.
#pvcreate /dev/md0
#vgcreate root /dev/md0
بیایید یک پارتیشن برای فایل سیستم ریشه ایجاد کنیم.
#lvcreate -L 128G --name root root
بیایید یک پارتیشن برای تعویض با توجه به اندازه رم ایجاد کنیم.
#lvcreate -L 32G --name swap root
نصب سیستم عامل
در مجموع، ما همه چیز لازم برای نصب سیستم را داریم.
جادوگر نصب سیستم را از محیط Ubuntu Live اجرا کنید. نصب معمولی فقط در مرحله انتخاب دیسک برای نصب، باید موارد زیر را مشخص کنید:
- /dev/md1، - mount point /boot، FS - BTRFS
- /dev/root/root (معروف به /dev/mapper/root-root)، - mount point / (root)، FS - BTRFS
- /dev/root/swap (با نام مستعار /dev/mapper/root-swap)، - استفاده به عنوان پارتیشن مبادله
- بوت لودر را روی /dev/sda نصب کنید
هنگامی که BTRFS را به عنوان سیستم فایل ریشه انتخاب می کنید، نصب کننده به طور خودکار دو جلد BTRFS به نام های "@" برای / (root) و "@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 کافی تنظیم کنید. به نظر می رسد مقدار پیش فرض از زمان ایجاد لینوکس تغییر نکرده است و برای مدت طولانی کافی نبوده است.
- حداقل IO را برای مدت زمان بررسی/همگام سازی آرایه رزرو کنید. این برای جلوگیری از گیرکردن آرایه های شما در حالت همگام سازی ابدی تحت بار است.
- حداکثر IO را در هنگام بررسی/همگام سازی آرایه ها محدود کنید. این امر ضروری است تا همگامسازی/بررسی RAIDهای SSD درایوهای شما را کاملاً روشن نکند. این به ویژه برای NVMe صادق است. (در مورد رادیاتور یادته؟ شوخی نکردم.)
- دیسک ها را از توقف چرخش اسپیندل (HDD) از طریق APM منع کنید و مدت زمان خواب را برای کنترل کننده های دیسک روی 7 ساعت تنظیم کنید. اگر درایوهای شما قادر به انجام آن باشند، می توانید APM را به طور کامل غیرفعال کنید (-B 255). با مقدار پیش فرض، درایوها پس از پنج ثانیه متوقف می شوند. سپس سیستمعامل میخواهد حافظه پنهان دیسک را بازنشانی کند، دیسکها دوباره میچرخند و همه چیز دوباره شروع میشود. دیسک ها دارای حداکثر تعداد محدودی از چرخش دوک هستند. چنین چرخه پیش فرض ساده ای می تواند به راحتی دیسک های شما را در چند سال از بین ببرد. همه دیسکها از این مشکل رنج نمیبرند، اما دیسکهای ما لپتاپهایی هستند، با تنظیمات پیشفرض مناسب، که RAID را شبیه یک مینی MAID میکند.
- نصب Readahead روی دیسک (چرخش) 1 مگابایت - دو بلوک متوالی/تکه RAID 6
- Readahead را در خود آرایه ها غیرفعال کنید.
بیایید /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 ذخیره میشوند، با عبور از کش ( _corig). در این حالت، خود دستگاه ذخیره شده همچنان از طریق کش اسکن می شود (فقط ).
- دستگاههای حاوی فراداده حافظه پنهان LVM (cmeta)
- همه دستگاه ها در VG با تصاویر نام. در اینجا ما تصاویر دیسک ماشین های مجازی را خواهیم داشت و نمی خواهیم LVM روی هاست حجم های متعلق به سیستم عامل مهمان را فعال کند.
- همه دستگاهها در VG با نام پشتیبان. در اینجا ما یک نسخه پشتیبان از تصاویر ماشین مجازی خواهیم داشت.
- همه دستگاه هایی که نام آنها با "gpv" (حجم فیزیکی مهمان) ختم می شود
هنگام آزاد کردن فضای آزاد در LVM VG، پشتیبانی DISCARD را فعال کرده ایم. مراقب باش. این باعث می شود حذف LV ها در SSD بسیار زمان بر باشد. این به ویژه در مورد SSD RAID 6 صدق می کند. با این حال، طبق برنامه، ما از thin provisioning استفاده خواهیم کرد، بنابراین این به هیچ وجه مانع ما نخواهد شد.
بیایید تصویر initramfs را به روز کنیم:
#update-initramfs -u -k all
grub را نصب و پیکربندی کنید:
#apt-get install grub-pc
#apt-get purge os-prober
#dpkg-reconfigure grub-pc
کدام دیسک ها را باید انتخاب کنید؟همه کسانی که sd * هستند. سیستم باید بتواند از هر درایو SATA یا SSD فعال بوت شود.
چرا os-prober را اضافه کردند..؟برای استقلال بیش از حد و دست های بازیگوش.
اگر یکی از RAID ها در وضعیت تخریبی باشد، به درستی کار نمی کند. سعی می کند سیستم عامل را بر روی پارتیشن هایی که در ماشین های مجازی در حال اجرا بر روی این سخت افزار استفاده می شود، جستجو کند.
اگر به آن نیاز دارید، می توانید آن را ترک کنید، اما تمام موارد بالا را در نظر داشته باشید. توصیه می کنم به دنبال دستور العمل هایی برای خلاص شدن از شر دست های شیطان به صورت آنلاین باشید.
با این کار نصب اولیه را تکمیل کردیم. زمان راه اندازی مجدد در سیستم عامل تازه نصب شده فرا رسیده است. فراموش نکنید که CD/USB زنده قابل بوت را بردارید.
#exit
#reboot
هر یک از SSD های SATA را به عنوان دستگاه بوت انتخاب کنید.
LVM در SATA SSD
در این مرحله، ما قبلاً در سیستم عامل جدید بوت شده ایم، شبکه را پیکربندی کرده، apt، شبیه ساز ترمینال را باز کرده و راه اندازی کرده ایم:
#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 به نام root داریم. چرا همه چیز را به یک VG اضافه نمی کنید؟
اگر چندین PV در یک VG وجود دارد، برای اینکه VG به درستی فعال شود، همه PV ها باید (آنلاین) وجود داشته باشند. استثنا LVM RAID است که ما عمدا از آن استفاده نمی کنیم.
ما واقعاً می خواهیم که اگر در هر یک از آرایه های RAID 6 خرابی (از دست دادن اطلاعات خوانده شده) رخ داد، سیستم عامل به طور عادی بوت شود و به ما فرصت حل مشکل را بدهد.
برای انجام این کار، در اولین سطح انتزاع، هر نوع "رسانه" فیزیکی را در یک VG جداگانه جدا می کنیم.
از نظر علمی، آرایههای RAID مختلف به «حوزههای قابلیت اطمینان» متفاوتی تعلق دارند. شما نباید با جمع کردن آنها در یک VG یک نقطه مشترک دیگر از شکست برای آنها ایجاد کنید.
وجود LVM در سطح «سختافزار» به ما این امکان را میدهد که بهطور دلخواه قطعات آرایههای RAID مختلف را با ترکیب آنها به روشهای مختلف برش دهیم. به عنوان مثال - اجرا کنید در همان زمان bcache + LVM thin، bcache + BTRFS، LVM cache + LVM thin، یک پیکربندی پیچیده ZFS با حافظه نهان، یا هر ترکیب جهنمی دیگری برای امتحان و مقایسه همه آنها.
در سطح «سختافزار»، ما از هیچ چیز دیگری به جز حجمهای قدیمی «ضخیم» LVM استفاده نخواهیم کرد. استثنای این قانون ممکن است پارتیشن پشتیبان باشد.
فکر میکنم در این لحظه، بسیاری از خوانندگان قبلاً به چیزی در مورد عروسک لانه شک کرده بودند.
LVM در SATA HDD
#pvcreate /dev/md3
#vgcreate backup /dev/md3
دوباره وی جی جدید..؟ما واقعاً میخواهیم که اگر آرایه دیسکی که برای پشتیبانگیری از دادهها استفاده میکنیم خراب شود، سیستم عامل ما به طور معمول به کار خود ادامه دهد و در عین حال دسترسی به دادههای غیر پشتیبان را طبق معمول حفظ کند. بنابراین، برای جلوگیری از مشکلات فعال سازی VG، یک VG جداگانه ایجاد می کنیم.
راه اندازی حافظه نهان LVM
بیایید یک LV در NVMe RAID 1 ایجاد کنیم تا از آن به عنوان یک دستگاه کش استفاده کنیم.
#lvcreate -L 70871154688B --name cache root
چرا اینقدر کم است...؟واقعیت این است که SSD های NVMe ما یک کش SLC نیز دارند. 4 گیگابایت "رایگان" و 18 گیگابایت پویا به دلیل فضای خالی اشغال شده در MLC 3 بیتی. هنگامی که این کش تمام شد، حافظه های SSD NVMe خیلی سریع تر از 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 نسبت به، به عنوان مثال، bcache این است که می توان این کار را در لحظه انجام داد.
بیایید یک 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 مطابقت داشته باشد. علاوه بر این، هرچه اندازه کوچکتر باشد، پیکربندی در یک ضبط تصادفی بهتر انجام می شود.
64k حداقل اندازه بلوک مجاز برای LVM Thin است.
مواظب بازنویسی باشید..!آره. این نوع کش همگام سازی نوشتن را در دستگاه ذخیره شده به تعویق می اندازد. این بدان معنی است که اگر حافظه پنهان از بین برود، ممکن است داده های موجود در دستگاه ذخیره شده را از دست بدهید. بعداً نویسنده به شما خواهد گفت که علاوه بر 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 به سادگی کش را با دیسک همگام می کند، آن را حذف می کند و نام cachedata_corig را به cachedata تغییر می دهد.
راه اندازی LVM Thin
بیایید به طور تقریبی تخمین بزنیم که چه مقدار فضایی برای ابرداده نازک 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 log ها بازیابی می شوند.
علاوه بر این، اگر قبلاً یک عکس فوری از یک حجم نازک گرفته شده باشد و پس از آن حداقل یک بار حافظه پنهان به طور کامل همگام شود، به دلیل طراحی داخلی LVM Thin، یکپارچگی آن در صورت از بین رفتن حافظه پنهان تضمین می شود. .
بیایید یک 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 yعلاوه بر آنچه که این حالت در واقع برای آن در نظر گرفته شده است - برای جلوگیری از نشت داده های یک ماشین مجازی به ماشین مجازی دیگر در هنگام توزیع مجدد فضا - صفر کردن علاوه بر این برای افزایش سرعت نوشتن تصادفی در بلوک های کوچکتر از 64k استفاده می شود. هر نوشته ای کمتر از 64 کیلوبایت در ناحیه ای که قبلاً تخصیص داده نشده بود حجم نازک، 64 کیلوبایت در حافظه نهان تراز می شود. این اجازه می دهد تا عملیات به طور کامل از طریق حافظه پنهان، دور زدن دستگاه ذخیره شده انجام شود.
بیایید LV ها را به PV های مربوطه منتقل کنیم:
#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 ثانیه اجرا می شوند. نیمی از تست ها برای ضبط است. شما می توانید در 4 ثانیه تعداد زیادی را در NVMe ضبط کنید. حداکثر 3 گیگابایت در ثانیه. بنابراین، هر بار تست نوشتن می تواند تا 216 گیگابایت از منبع SSD را از شما بخورد.
خواندن و نوشتن ترکیب شده است؟آره. منطقی است که تست های خواندن و نوشتن را جداگانه اجرا کنید. علاوه بر این، منطقی است که اطمینان حاصل شود که تمام حافظه های پنهان همگام هستند به طوری که نوشتن قبلی بر روی خواندن تأثیر نمی گذارد.
نتایج در اولین راهاندازی و راهاندازیهای بعدی بسیار متفاوت خواهد بود، زیرا حافظه پنهان و حجم نازک پر میشود، و همچنین بسته به اینکه آیا سیستم توانسته است حافظههای پنهان پر شده در آخرین راهاندازی را همگامسازی کند یا خیر.
در میان چیزهای دیگر، من توصیه میکنم سرعت را روی یک حجم کاملاً نازک اندازهگیری کنید که از آن یک عکس فوری گرفته شده است. نویسنده این فرصت را داشت تا مشاهده کند که چگونه نوشتن های تصادفی بلافاصله پس از ایجاد اولین عکس فوری، به ویژه زمانی که کش هنوز کاملاً پر نشده است، به شدت شتاب می گیرند. این به دلیل معناشناسی نوشتن کپی روی نوشتن، تراز کردن حافظه پنهان و بلوک های حجم نازک و این واقعیت است که یک نوشتن تصادفی در RAID 6 به خواندن تصادفی از RAID 6 و به دنبال آن یک نوشتن در حافظه پنهان تبدیل می شود. در پیکربندی ما، خواندن تصادفی از RAID 6 تا 6 برابر (تعداد SSD های SATA در آرایه) سریعتر از نوشتن است. زیرا بلوکها برای CoW به ترتیب از یک استخر نازک تخصیص داده میشوند، سپس ضبط، در بیشتر موارد، نیز به ترتیب تبدیل میشود.
از هر دوی این ویژگی ها می توان به نفع خود استفاده کرد.
عکس های فوری "منسجم" را در حافظه پنهان ذخیره کنید
برای کاهش خطر از دست دادن داده ها در صورت آسیب/از بین رفتن حافظه پنهان، نویسنده پیشنهاد می کند تا عمل چرخش عکس های فوری را برای تضمین یکپارچگی آنها در این مورد معرفی کند.
اول، از آنجا که ابرداده با حجم کم در یک دستگاه ذخیره نشده قرار دارد، ابرداده سازگار خواهد بود و تلفات احتمالی در بلوک های داده ایزوله می شود.
چرخه چرخش عکس فوری زیر یکپارچگی داده های داخل عکس های فوری را در صورت از دست دادن حافظه پنهان تضمین می کند:
- برای هر حجم نازک با نام <name>، یک عکس فوری با نام <name>.cached ایجاد کنید.
- بیایید آستانه مهاجرت را روی یک مقدار معقول بالا تنظیم کنیم:
#lvchange --quiet --cachesettings "migration_threshold=16384" cache/cachedata
- در حلقه تعداد بلوک های کثیف در حافظه پنهان را بررسی می کنیم:
#lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}'
تا به صفر برسیم. اگر صفر برای مدت طولانی وجود نداشته باشد، می توان آن را با تغییر موقت حافظه پنهان به حالت نوشتن ایجاد کرد. با این حال، با در نظر گرفتن ویژگیهای سرعت آرایههای SATA و NVMe SSD ما، و همچنین منبع TBW آنها، میتوانید بدون تغییر حالت حافظه نهان، لحظهها را به سرعت ثبت کنید، یا اینکه سختافزار شما تمام منابع خود را در چند روز. به دلیل محدودیت منابع، سیستم، در اصل، نمی تواند همیشه زیر 100٪ بار نوشتن باشد. SSD های NVMe ما تحت بار نوشتن 100٪، منابع را به طور کامل تخلیه می کنند روز 3-4. حافظه های SATA SSD تنها دو برابر بیشتر عمر می کنند. بنابراین، فرض میکنیم که بیشتر بار به خواندن میرود، و ما انفجارهای نسبتاً کوتاهمدت فعالیت بسیار زیاد همراه با بار کم به طور متوسط برای نوشتن داریم. - به محض اینکه صفر را گرفتیم (یا ساختیم)، نام <name>.cached را به <name>.committed تغییر می دهیم. <name>.committed قدیمی حذف شده است.
- به صورت اختیاری، اگر حافظه نهان 100٪ پر باشد، می توان آن را با یک اسکریپت دوباره ایجاد کرد، بنابراین آن را پاک کرد. با کش نیمه خالی، سیستم هنگام نوشتن بسیار سریعتر کار می کند.
- آستانه مهاجرت را روی صفر قرار دهید:
#lvchange --quiet --cachesettings "migration_threshold=0" cache/cachedata
این به طور موقت از همگام سازی حافظه پنهان با رسانه اصلی جلوگیری می کند. - ما صبر می کنیم تا تغییرات زیادی در حافظه پنهان جمع شود
#lvs --rows --reportformat basic --quiet -ocache_dirty_blocks cache/cachedata | awk '{print $2}'
یا تایمر خاموش می شود. - دوباره تکرار می کنیم.
چرا مشکلات آستانه مهاجرت...؟نکته این است که در عمل واقعی، یک ضبط "تصادفی" در واقع کاملا تصادفی نیست. اگر چیزی را روی یک سکتور به اندازه 4 کیلوبایت بنویسیم، احتمال زیادی وجود دارد که در چند دقیقه آینده یک رکورد در همان یا یکی از سکتورهای همسایه (+- 32K) ایجاد شود.
با تنظیم آستانه مهاجرت روی صفر، همگام سازی نوشتن در SATA SSD را به تعویق می اندازیم و چندین تغییر را به یک بلوک 64K در حافظه پنهان جمع می کنیم. این به طور قابل توجهی در منابع SATA SSD صرفه جویی می کند.
کد کجاست..؟متأسفانه نویسنده خود را در توسعه اسکریپت های bash ناتوان می داند زیرا 100% خودآموخته است و توسعه مبتنی بر «گوگل» را تمرین می کند، بنابراین معتقد است که کد وحشتناکی که از دست او خارج می شود نباید توسط کسی استفاده شود. دیگر
من فکر می کنم که متخصصان در این زمینه می توانند به طور مستقل تمام منطق توصیف شده در بالا را در صورت لزوم به تصویر بکشند و حتی شاید به زیبایی آن را به عنوان یک سرویس سیستمی طراحی کنند، همانطور که نویسنده سعی کرد انجام دهد.
چنین طرح چرخش عکس فوری ساده به ما این امکان را می دهد که نه تنها به طور مداوم یک عکس فوری را به طور کامل روی SSD SATA همگام سازی کنیم، بلکه به ما این امکان را می دهد که با استفاده از ابزار thin_delta متوجه شویم که کدام بلوک ها پس از ایجاد آن تغییر کرده اند، و در نتیجه آسیب ها را بومی سازی کنیم. حجم اصلی، تا حد زیادی ساده بازیابی.
TRIM/DISCARD در 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>
چنین DISCARDهایی از سیستمعاملهای مهمان بهدرستی توسط LVM پردازش میشوند و بلوکها هم در حافظه پنهان و هم در استخر نازک به درستی آزاد میشوند. در مورد ما، این به طور عمده در هنگام حذف عکس فوری بعدی، با تاخیر اتفاق می افتد.
پشتیبان گیری BTRFS
استفاده از اسکریپت های آماده با مفرط احتیاط و با ریسک خود شخص. نویسنده این کد را خودش و منحصرا برای خودش نوشته است. من مطمئن هستم که بسیاری از کاربران با تجربه لینوکس ابزارهای مشابهی دارند و نیازی به کپی کردن ابزار دیگران نیست.
بیایید یک حجم در دستگاه پشتیبان ایجاد کنیم:
#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
بیایید اسکریپت را کپی کنیم:
بسیاری از کدهای ترسناک bash. با مسئولیت خود استفاده کنید. برای نویسنده نامه های عصبانی ننویسید...#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 قرار می دهیم:
چند کد bash بیشتر#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