ZFS สำหรับผู้เริ่มต้น
(ikrima.dev)- คู่มือเริ่มต้นที่สรุปตั้งแต่การ แก้ปัญหา, การจัดวางวอลุ่มใหม่ ไปจนถึงกลยุทธ์การสำรองข้อมูล โดยอ้างอิงจากกรณี ZFS ขัดข้องบน FreeNAS และอธิบายโครงสร้างกับคำสั่งที่จำเป็นสำหรับการใช้งาน ZFS ครั้งแรกอย่างต่อเนื่องในลำดับเดียว
- ZFS มอบทั้ง filesystem และ logical volume manager ในตัว และหลังจากเริ่มต้นที่ Sun Microsystems แล้ว ปัจจุบันบน Linux และ FreeBSD เป็นต้น มักใช้โค้ดของ OpenZFS
- โครงสร้างพื้นฐานแบ่งเป็น vdev ที่รวมดิสก์กายภาพเข้าด้วยกัน, pool ที่รวม vdev เหล่านั้น, dataset ที่ทำหน้าที่เป็น filesystem และ volume ที่อยู่ในรูปแบบ block device โดย RAIDZ-1/2/3 สามารถทนต่อดิสก์เสียได้ 1, 2, และ 3 ลูกตามลำดับ
- คำสั่งที่ใช้จริงในการทำงานมีศูนย์กลางอยู่ที่
zpool create/status/list/import/export/destroy/scrubและzfs create/mount/list/get/set/snapshot/diff/rollback/send/recv/destroyและเพื่อหลีกเลี่ยงปัญหาจากการเปลี่ยนชื่ออุปกรณ์ จึงนิยมใช้ device UUID - snapshot ใช้ปกป้องสถานะของข้อมูล ณ ช่วงเวลาหนึ่งได้อย่างเบา ๆ และสามารถทำสำเนาไปยัง pool หรือระบบอื่นด้วย
zfs send/recvได้ แต่ snapshot เพียงอย่างเดียวยังทดแทน backup หรือ DR ได้ยาก
แนวคิดพื้นฐานของ ZFS
- ZFS เป็นทั้ง local filesystem แบบเดียวกับ ext4, NTFS, exFAT และทำหน้าที่เป็น logical volume manager แบบเดียวกับ LVM บน Linux ไปพร้อมกัน
- ถูกสร้างโดย Sun Microsystems และซอร์สโค้ดของ ZFS เคยเผยแพร่ภายใต้ไลเซนส์โอเพนซอร์สจนกระทั่ง Oracle เข้าซื้อ Sun
- เนื่องจากโค้ดที่เปิดเผยไว้ถูกพอร์ตไปยังหลายระบบปฏิบัติการอยู่แล้ว จึงเกิดโครงการ OpenZFS ขึ้นภายหลัง และระบบตระกูล Unix ส่วนใหญ่ เช่น Linux และ FreeBSD ก็ใช้โค้ดชุดนี้
- เนื้อหานี้เรียบเรียงจากมุมมองของผู้เริ่มต้น เพื่อช่วยให้ผู้ที่เพิ่งรู้จัก ZFS เข้าใจโครงสร้างและคำสั่งสำหรับการดูแลระบบ
องค์ประกอบของ ZFS
- vdev ประกอบด้วย physical drive ตั้งแต่หนึ่งลูกขึ้นไป และอาจรวมไฟล์แทนฮาร์ดไดรฟ์ได้ด้วย
- สามารถจัดเป็นแบบ mirror หรือ RAIDZ ได้
- มี vdev อยู่ 7 ประเภท รวมถึงประเภทสำคัญอย่าง hot spare, L2ARC และ ZIL
- pool ประกอบด้วย vdev ตั้งแต่หนึ่งชุดขึ้นไป และโดยทั่วไปจะสร้าง volume หรือ dataset ภายใน pool
- ตอนสร้าง pool ด้วยคำสั่ง
zpoolจะต้องกำหนด vdev ไปพร้อมกัน - สามารถผสม vdev หลายประเภทเพื่อจัดระดับ RAIDZ ได้
- ตอนสร้าง pool ด้วยคำสั่ง
- dataset คือองค์ประกอบของ ZFS ที่เทียบได้กับ filesystem
- สามารถตั้งค่าการเข้าถึงของผู้ใช้, quota, การบีบอัด, snapshot และอื่น ๆ ได้
- volume คล้ายกับ dataset แต่ให้การแสดงผลในรูปแบบ block device
- รองรับเพียงบางความสามารถของ dataset
- มีประโยชน์เมื่อจะรัน filesystem อื่นบน ZFS หรือ export iSCSI extent
ประเภทของ RAIDZ
- Dynamic/Simple Stripe หรือ RAID0 กระจายข้อมูลโดยไม่มี parity และถ้าเสียอุปกรณ์ไปหนึ่งตัวจะสูญเสียข้อมูลทั้งหมด
- MIRROR หรือ RAID1 ทำการมิเรอร์ดิสก์ และมักใช้กับดิสก์ 2~4 ลูกหรือมากกว่านั้น
- RAIDZ-1 หรือ RAID5 กระจาย parity ไปพร้อมกับข้อมูล และสามารถทนต่อการสูญเสีย physical drive ได้ 1 ลูกก่อนที่ RAID จะพัง
- RAIDZ ต้องใช้ดิสก์อย่างน้อย 3 ลูก
- RAIDZ-2 หรือ RAID6 สามารถทนต่อการสูญเสีย physical drive ได้สูงสุด 2 ลูก
- RAIDZ-2 ต้องใช้ดิสก์อย่างน้อย 4 ลูก
- RAIDZ-3 สามารถทนต่อการสูญเสีย physical drive ได้สูงสุด 3 ลูก
- แม้จะต้องใช้ดิสก์อย่างน้อย 4 ลูก แต่โดยทั่วไปไม่แนะนำให้ใช้น้อยกว่า 5 ลูก
การสร้าง pool และตรวจสอบสถานะ
- ZFS pool สร้างด้วยรูปแบบ
zpool create [pool] [devices]- ตัวอย่าง pool แบบดิสก์เดี่ยว:
zpool create tank /dev/sdb - ตัวอย่าง stripe 3 ดิสก์:
zpool create tank /dev/sdb /dev/sdc /dev/sdd - ตัวอย่าง mirror 2 ดิสก์:
zpool create tank mirror sdb sdc - ตัวอย่าง RAIDZ pool:
zpool create tank raidz sdb sdc sdd - RAIDZ2/3 สามารถสร้างได้ด้วยรูปแบบ
zpool create [pool name] raidz[1,2,3] [devices]
- ตัวอย่าง pool แบบดิสก์เดี่ยว:
- ตอนสร้าง pool สามารถระบุ mount point เริ่มต้น ได้ด้วยแฟล็ก
-m- ตัวอย่าง:
zpool create tank -m /mnt/tank mirror sdb sdc
- ตัวอย่าง:
- ในตัวอย่างจะใช้ชื่ออุปกรณ์แบบ
/dev/sdxแต่เพื่อหลีกเลี่ยงปัญหาการบูตจากการเปลี่ยนชื่ออุปกรณ์ จึงนิยมใช้ device UUID zpool statusเป็นคำสั่งพื้นฐานสำหรับตรวจสอบสถานะของ poolstateแสดงว่า pool อยู่ในสถานะออนไลน์หรือไม่statusแสดงข้อมูลเพิ่มเติมเกี่ยวกับ poolactionแสดงงานถัดไปที่จำเป็นscanแสดงความคืบหน้าของ scrub หรือสถานะ scrub ล่าสุดerrorsแสดงว่ามีปัญหากับ pool หรือไม่
zpool listแสดงรายละเอียดอย่างขนาดของ pool, allocation, พื้นที่ว่าง, fragmentation, capacity, dedup และ healthzpool historyแสดงประวัติคำสั่งที่ใช้เปลี่ยนแปลงการตั้งค่าตั้งแต่สร้าง pool
การ import, export, ลบ pool และ scrub
- หลังสร้างแล้ว pool มักจะถูก import และ mount โดยอัตโนมัติ แต่ในงานแก้ปัญหาหรือหลัง reimage ระบบ อาจต้อง import ด้วยตนเอง
- หากรัน
zpool importโดยไม่ระบุชื่อ pool จะเห็นรายการ pool ที่สามารถ import ได้- ถ้าไม่มี pool ที่ import ได้ จะขึ้น
no pools available to import - ถ้าระบุชื่อ pool จะ import pool นั้น และคำสั่ง import จะทำการ mount pool ด้วย
zpool import -aจะนำเข้า pool ที่ import ได้ทั้งหมด
- ถ้าไม่มี pool ที่ import ได้ จะขึ้น
- หากใช้
-Rเช่นzpool import -R /mnt/tank2 tankจะ mount ไว้ที่ alternate root- ตำแหน่งนี้ไม่ใช่ mount path ของ pool เอง แต่เป็นโฟลเดอร์ root ทางเลือก
zpool export [pool name]เป็นคำสั่งตรงข้ามกับ import โดยจะพยายาม unmount filesystem ที่ mount อยู่ใน pool ก่อนแล้วจึง export- ถ้า unmount filesystem ไม่สำเร็จ สามารถบังคับ unmount ด้วย
-f - หากมี ZFS volume ที่กำลังใช้งานอยู่ ต่อให้ใช้
-fก็จะ export ไม่สำเร็จ
- ถ้า unmount filesystem ไม่สำเร็จ สามารถบังคับ unmount ด้วย
zpool destroyจะลบ pool พร้อม dataset หรือ volume ลูกทั้งหมด- เนื่องจากจะลบทั้งข้อมูลและ snapshot ทั้งหมดด้วย จึงต้องใช้ความระมัดระวัง
- ZFS scrub จะตรวจสอบความถูกต้องของข้อมูลโดยเทียบทุก block ใน pool กับ checksum ที่รู้จัก
- ใน vdev ที่มี parity จะใช้ข้อมูลจากดิสก์ปกติเพื่อกู้คืนข้อมูลที่เสียหาย
- เพื่อคงสภาพระบบให้ดี ควรรัน scrub ตามตารางเวลา
- เริ่ม:
zpool scrub [pool] - ตรวจสอบสถานะ: ดูส่วน
scanของzpool status - หยุด:
zpool scrub -s [pool]
การสร้าง dataset และ mount
zfs createเป็นคำสั่งสำหรับสร้าง filesystem หรือ volume ใหม่ และในที่นี้จะเน้นที่ dataset มากกว่า volumezfs create tank/dataset1จะสร้างdataset1ภายใต้tank- dataset
tankจะถูกสร้างให้อัตโนมัติตอนzpool create
- dataset
zfs create -pทำงานเหมือนmkdir -pโดยจะสร้าง dataset แม่ที่ยังไม่มีไปพร้อมกัน- ถ้า dataset แม่ไม่มีอยู่
zfs createแบบปกติจะล้มเหลว
- ถ้า dataset แม่ไม่มีอยู่
- หากรัน
zfs mountโดยไม่ใส่อาร์กิวเมนต์ จะเห็น ZFS filesystem ที่ mount อยู่ในปัจจุบันพร้อม mount point- เอาต์พุตนี้จะไม่รวม child dataset
zfs mount [pool|dataset]จะ mount filesystem ที่ระบุzfs mount -aจะ mount filesystem ทั้งหมด
- child dataset สามารถ mount ได้แม้ไม่มี parent dataset
- ในกรณีนั้น ระบบปฏิบัติการจะสร้าง path ที่จำเป็นใน filesystem ให้
- ภายหลังถ้าจะ mount parent dataset อาจเกิดข้อผิดพลาด
directory is not emptyเพราะมีไดเรกทอรีที่ถูกสร้างไว้แล้ว
zfs unmount [dataset]จะ unmount dataset ที่ระบุ
การดู dataset และตั้งค่า property
zfs list [dataset name]จะแสดงข้อมูลของ dataset ที่ระบุ- สามารถส่ง mount point แทนชื่อ dataset เป็นอาร์กิวเมนต์ได้เช่นกัน
- หากรัน
zfs listโดยไม่ระบุชื่อ dataset จะแสดง dataset ทั้งหมดในระบบแบบ recursive - เมื่อระบุชื่อ dataset สามารถใช้แฟล็ก
-rเพื่อแสดงข้อมูลภายใต้ dataset นั้นแบบ recursive ได้ - ZFS property ใช้ควบคุมพฤติกรรมของ filesystem, volume, snapshot และ clone
- อาจดูคล้าย mount option
zfs get all [dataset]จะแสดง property ทั้งหมดของ dataset- สามารถตรวจค่า property เฉพาะได้ เช่น
zfs get compression tank - การตั้งค่า property ใช้
zfs set- ตัวอย่าง:
zfs set compression=lz4 tank - หลังตั้งค่าแล้วสามารถตรวจสอบได้ด้วย
zfs get compression tankว่าค่าcompressionเปลี่ยนเป็นlz4
- ตัวอย่าง:
การทำงานและการใช้งานของ snapshot
- snapshot จะเก็บสถานะของ filesystem ณ ช่วงเวลาหนึ่ง แต่ไม่ได้คัดลอกไฟล์
- snapshot จะทำเครื่องหมายข้อมูลเดิมเป็นแบบ read-only และแม้ภายหลังจะมีข้อมูลใหม่ถูกเพิ่มเข้า filesystem ก็จะไม่กระทบ data block เดิมที่ snapshot ปกป้องไว้
- ลำดับตัวอย่างมีดังนี้
- สร้าง snapshot 1 จาก filesystem ที่มีข้อมูลเดิม Data A
- จากนั้นเพิ่ม Data B แล้วสร้าง snapshot 2
- จากนั้นเพิ่ม Data C
- snapshot 1 จะปกป้อง Data A
- snapshot 2 จะปกป้อง Data A และ Data B
- แม้ลบ snapshot 1 ไป Data A ก็ยังถูกปกป้องต่อโดย snapshot 2
- ปริมาณข้อมูลที่ snapshot ใช้มีน้อยมาก
- เพราะไม่ได้คัดลอกไฟล์ แต่เพียงบันทึก top-level metadata block ของ filesystem ว่าเป็นส่วนหนึ่งของ snapshot
- snapshot มีประโยชน์สำหรับการทดสอบพัฒนาซอฟต์แวร์หรือสร้าง failsafe ก่อนอัปเกรด
- ไม่ควรมองว่า snapshot เพียงอย่างเดียวเป็นโซลูชัน backup หรือ DR
คำสั่ง snapshot
- การสร้าง snapshot ใช้รูปแบบ
zfs snapshot [pool/dataset@snapshot_name]- ตัวอย่าง:
zfs snapshot tank/dataset1@snapshot1
- ตัวอย่าง:
- ดูรายการ snapshot ได้ด้วย
zfs list -t snapshot - ถ้ามี child dataset หลายตัว สามารถสร้าง snapshot ที่ top-level dataset หรือใช้แฟล็ก
-rเพื่อสร้าง snapshot แบบ recursive ได้- ตัวอย่าง snapshot ปกติ:
zfs snapshot tank@snapshot-master - ตัวอย่าง snapshot แบบ recursive:
zfs snapshot -r tank@recursive
- ตัวอย่าง snapshot ปกติ:
zfs diff [older snapshot] [newer snapshot]ใช้เปรียบเทียบความต่างระหว่าง snapshot- สามารถดูไฟล์ที่ถูกเพิ่มและ path ที่ถูกแก้ไขได้จากเอาต์พุต
- การกู้คืน snapshot ทำได้ด้วย
zfs rollback [pool/dataset@snapshot_name]- เมื่อ rollback แล้ว ไฟล์ที่สร้างหลัง snapshot จะถูกลบ
- snapshot ที่ใหม่กว่าก็จะถูกลบด้วย และในกรณีนี้อาจถูกขอให้ใช้ตัวเลือก
-r
ZFS send/recv และการทำ replication
- ZFS send เป็นความสามารถในการส่ง snapshot ออกไปเป็น data stream
- สามารถทำ replication ของ snapshot และ dataset ไปยังไฟล์, pool อื่น หรือระบบอื่นผ่าน SSH ได้
- ตัวอย่างนี้ใช้ pool สองชุดคือ
tankและbackup- dataset
tank/Moviesมีข้อมูลขนาด 1.50G - ก่อนส่ง จะสร้าง snapshot ด้วย
zfs snapshot tank/Movies@$(date '+%Y-%m-%d_%H-%M') - จากนั้นส่ง snapshot ไปยัง backup pool ด้วย
zfs send tank/Movies@2020-11-03_15-29 | zfs recv backup/Movies
- dataset
- หลังส่งเสร็จ จะเห็นทั้ง
backup/Moviesและtank/Moviesในzfs listและตรวจพบ snapshot ทั้งสองฝั่งในzfs list -t snapshot zfs sendเป็นฟีเจอร์ที่ควรศึกษาเพิ่มเติมทั้งในแง่ตัวเลือกและกรณีใช้งาน และเมื่อใช้ร่วมกับ RAIDZ และ snapshot ก็ช่วยให้ filesystem แข็งแกร่งขึ้นได้
การลบ dataset และ snapshot
- การลบ dataset ใช้รูปแบบ
zfs destroy [pool/dataset]- สามารถใช้แฟล็ก
-rได้เช่นกัน
- สามารถใช้แฟล็ก
- การลบ snapshot ก็ใช้
zfs destroy [pool/dataset@snapshot_name]- การลบ snapshot ก็สามารถใช้แฟล็ก
-rได้เช่นกัน
- การลบ snapshot ก็สามารถใช้แฟล็ก
- ZFS ยังมีความสามารถอีกมาก แต่เนื้อหานี้ใกล้เคียงกับจุดเริ่มต้นสำหรับทำความเข้าใจแนวคิดพื้นฐานและคำสั่งหลัก
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ตัวอย่างเช่น การสร้าง pool ก็ควรใช้ค่า default ที่สมเหตุสมผลไปเลย เช่น
ashift=12, การบีบอัดlz4,xattr=sa,acltype=posixacl,atime=offและสำหรับ encryption ก็อยากให้มีแค่เปิด/ปิด แทนที่จะมีตัวเลือกหลายอย่างอยากให้ช่วยสร้าง encryption key ให้ ตั้งค่า service ของ
systemdที่ถอดรหัส pool ตอนบูต และแนะนำการสำรอง key ด้วยzfs listควรแสดงว่า dataset ถูก mount อยู่หรือไม่, ถูกเข้ารหัสหรือไม่, และ key ถูกโหลดอยู่หรือไม่อยากให้เลิกใช้ recursive dataset และแยก pool กับ dataset ให้ชัดเจนด้วย
{pool}:{dataset}แทน{pool}/{dataset}ส่วนชื่อ pool หรือ snapshot ก็ไม่ต้องให้ตั้งเอง แต่ให้ตั้งกฎอย่าง{hostname}-[A-Z],{pool name}_{datetime created}และชื่อย่อแบบตัวเลขให้อัตโนมัติตอนสร้าง pool ไม่ควรต้องป้อน disk ID เอง และแม้จะสร้างด้วย
/dev/sda,/dev/sdbแล้วสลับลำดับ drive ภายหลังก็ไม่ควรสับสน โดยควรบันทึก metadata ไว้ใน diskความคืบหน้าควรแสดงด้วย
pvเสมอ และควรตั้งค่า scrub รายสัปดาห์ รวมถึง snapshot รายชั่วโมง/รายวัน/รายสัปดาห์/รายเดือนและการ cleanup ให้อัตโนมัติถ้าส่งข้อมูลไปยัง disk ที่ไม่มี pool ควรถามยืนยันก่อน แล้วสร้าง pool แบบ disk เดียวที่มีการตั้งค่าเหมือน pool ต้นทางให้ และอยากให้รวม
zpoolกับzfsเป็นคำสั่งเดียวเวลา send dataset ที่เข้ารหัสควรใช้
--rawให้อัตโนมัติ ค่า default ของการส่งควรเป็น--replicateและถ้าเป็นไปได้ก็ควรใช้-Iนอกจากนี้ ไม่ควรซ่อน filesystem ของ snapshot ไว้ในไดเรกทอรีลับ และควรมีวิธี mount และ browse snapshot dataset ได้ง่าย ๆashift=12หรือ syntax{pool}:{dataset}พอเข้าใจได้ แต่ดูเหมือนจะเปลี่ยนตอนนี้ยากไปแล้ว และบางข้อเสนออาจทำให้ use case ที่เราไม่รู้จักพังได้วิธีตั้งชื่อ pool ด้วย hostname ไม่เหมาะกับ SAN pool ที่อาจถูกนำเข้าได้จากหลาย host
ผมมองว่า scrub รายสัปดาห์, snapshot ตามรอบเวลา, และ cleanup ตามรอบเวลา เป็นงานที่ควรให้ scheduler ของระบบปฏิบัติการจัดการ การรวม
zpoolกับzfsก็อาจทำได้ แต่สุดท้ายคงกลายเป็นประมาณzfs -pool XXXX,zfs -volume XXXXซึ่งไม่รู้ว่าจำเป็นตรงไหนrecursive dataset มีกรณีที่มีประโยชน์จริง ๆ อยู่ ในทางกลับกัน ผมเห็นด้วยเต็มที่ว่า
zfs listควรแสดงว่า mount อยู่หรือไม่, เข้ารหัสหรือไม่, และโหลด key อยู่หรือไม่ส่วนที่บอกว่าไม่ต้องป้อน disk ID นั้นกำกวม จะระบุด้วย ID, WWN, label,
sdXหรือวิธีอื่น ๆ ก็ได้ แต่ไม่ว่าอย่างไรก็ต้องบอกอยู่ดีว่าจะใช้ disk ไหนฟีเจอร์ที่บันทึก metadata ลง disk เพื่อให้ยังหาเจอแม้สลับลำดับนั้นมีอยู่แล้ว ต่อให้ถอดเสียบ drive สลับไปสองสามตัวแล้ว import pool ก็ยังหาเจอ
ข้อเสนอบางอย่างเหมาะเป็นค่า default ได้ แต่บางข้อดูเหมือนไม่ได้พิจารณา use pattern และความต้องการนอกเหนือจากของตัวเองมากพอ ZFS รองรับผู้ใช้ที่กว้างกว่านั้นมาก
/dev/sda,/dev/sdbแล้วบอกว่า ZFS สับสนนั้น พูดตรง ๆ แล้วใกล้เคียงกับปัญหาของผู้ใช้ที่ใช้งานไม่ถูกยุค มากกว่าจะเป็น ปัญหาของ ZFSบน Linux มี reference แบบสมบูรณ์อย่าง
/dev/disk/by-id/ata-$manufactuer-$serial-$whateverมาค่อนข้างนานแล้ว เวลา create pool ควรใช้ path พวกนี้{pool}:{dataset}ถ้าไม่อยากตั้งชื่อ snapshot เอง เครื่องมือเล็ก ๆ ที่ผมทำไว้ https://github.com/rollcat/zfs-autosnap อาจเหมาะ
ใส่
zfs-autosnap snapไว้ใน cron แบบรายชั่วโมง และใส่zfs-autosnap gcแบบรายวัน ก็จะหมุนประวัติ snapshot ตาม retention policy ให้การใช้ wrapper ของคำสั่ง ZFS แบบง่าย ๆ ไม่ได้ยาก ดังนั้นจะเอา code ของผมไปทำเครื่องมือของตัวเองก็ได้
ที่ทำงานผมก็เขียน script หลายตัวสำหรับสร้าง ZFS array ให้ตรงกับรูปแบบ deployment ที่ตั้งใจไว้ในวันนั้น ๆ ภายในนั้นรวมถึงการสร้าง volume ที่เข้ารหัสด้วย LUKS เพื่อวาง zvol, การทำให้ naming convention เป็นมาตรฐาน, การตั้งค่า default อย่าง
ashift=12, การบีบอัดlz4นั่นเป็นเรื่องก่อนที่ ZFS จะมี native encryption อยู่มาก และวิธีปัจจุบันก็ยังไม่มีปัญหา เลยไม่ได้อัปเดต script ให้รองรับ ZFS encryption
ตอนนี้ผมจำ flag จำนวนมากไม่ได้แล้ว แต่ script ทำหน้าที่เป็นเอกสาร และคนอื่นในทีมแค่รัน
make-zfs-big-mirrorหรือmake-big-zfs-undundant-raid0ก็พอสักวันถ้ามีงาน provision ระบบเกิน 20 ครั้งต่อปี แม้แต่ส่วนนี้ก็อาจถูกทำให้เป็น automation ใน provisioning ได้
การไม่ให้ตั้งชื่อ pool นั้นไม่สมเหตุสมผล คุณไม่ได้สร้าง pool บ่อยขนาดนั้น แค่ตั้งชื่อไปก็พอ
ถ้าไม่อยากตั้งชื่อ snapshot ก็ใช้
httmกับzfs allowได้ เช่นhttm -S .จะสร้าง snapshot อย่างrpool/ROOT/ubuntu_tiebek@snap_2022-12-14-12:31:41_httmSnapFileMountzfsและzpoolต่างเป็น คำสั่ง Unix ที่ยอดเยี่ยม ซึ่งมีคำสั่งย่อยหลายตัว ผมคิดว่านักออกแบบ ZFS ตัดสินใจได้ฉลาดมากที่ไม่ทำคำสั่งจัดการแบบคำสั่งเดียวที่ซับซ้อนกว่าการ mount และ browse snapshot dataset อย่างชัดเจนก็ทำได้ง่ายด้วย
zfs mountเพียงแต่อยากให้เชื่อด้วยว่า virtual interface ที่เสถียรช่วยให้ค้นหา version ทั้งหมดของไฟล์ได้ง่าย และเรื่องนี้ทำใน btrfs ฯลฯ ยากกว่ามากhttmก็น่าดูไว้เช่นกัน[0]: https://kimono-koans.github.io/opinionated-guide/#dynamic-sn...
ยังมีสิ่งที่มีประโยชน์เพิ่มเติมใน ZFS อีก ควรรู้ความแตกต่างระหว่าง
zpool-attach(8)กับzpool-replace(8)และzfs list -t all -o spaceจะแสดงให้เห็นว่าพื้นที่ถูกใช้ไปที่ไหนสำหรับการปกป้องระบบปฏิบัติการก่อนการเปลี่ยนแปลงหรืออัปเกรดครั้งใหญ่ ZFS Boot Environments คือฟีเจอร์ที่ดีที่สุด ลิงก์ https://is.gd/BECTL อาจเป็นจุดเริ่มต้นที่มีประโยชน์
zpool history poolnameจะแสดงทั้งการตั้งค่าพูลและประวัติการเปลี่ยนแปลงทั้งหมด เช่น การเปลี่ยนแปลงอย่างzpool create,autotrim=on,atime=off,compression=zstd,recordsize=1mจะถูกบันทึกไว้ยังมีข้อมูลสำคัญที่คู่มือนี้ไม่ได้กล่าวถึงด้วย มิเรอร์แบบ 3-way แม้ดิสก์ 2 ใน 3 ลูกจะเสีย ข้อมูลก็ยังอยู่, มิเรอร์แบบ 4-way แม้ดิสก์ 3 ใน 4 ลูกจะเสียก็ยังอยู่ และมิเรอร์แบบ N-way แม้ดิสก์ N-1 จาก N ลูกจะเสีย ข้อมูลก็ยังคงถูกเก็บรักษาไว้
มีประโยชน์เมื่อข้อมูลสำคัญที่สุด แต่สล็อตหรือจำนวนดิสก์มีไม่มาก
ใช้งาน ฐานข้อมูล PostgreSQL ขนาดใหญ่ระดับหลาย TB บน ZFS มาหลายปีแล้ว ZFS ทำให้การสำรองข้อมูล การสร้างสภาพแวดล้อมทดสอบจากสแนปช็อตในอดีต และการประหยัดดิสก์ด้วยการบีบอัดในตัวทำได้ง่ายมาก
ถ้าสนใจ สามารถอ่านประสบการณ์ได้ที่ https://lackofimagination.org/2022/04/our-experience-with-po...
ตอนเริ่มใช้ ZFS สิ่งที่ช่วยได้มากที่สุดคือ ZFS Handbook ของ FreeBSD และบทความของ Aaron Toponce
[0] https://docs.freebsd.org/en/books/handbook/zfs/
1 https://pthree.org/2012/04/17/install-zfs-on-debian-gnulinux...
เคยติดตั้ง FreeBSD บน HP Microserver รุ่นเก่าที่มี ECC RAM 1GB ซึ่งหาได้จากของที่มีอยู่ และมีฮาร์ดดิสก์เก่า 500GB อีก 5 ลูก จึงตั้งค่าเป็นมิเรอร์ 5x โดยดูจาก FreeBSD Handbook ตอนนั้นใช้ FreeBSD เป็นครั้งแรก แต่ก็ทำได้ราบรื่นมาก
ช่วงหลังได้สร้างโครงสร้างพื้นฐานใหม่จำนวนมาก ซึ่งส่วนใหญ่เป็นเซิร์ฟเวอร์ LAMP จึงตัดสินใจวางทั้งหมดบน ZFS บน Linux เพื่อการจำลองสำรองข้อมูลที่มีประสิทธิภาพและการเข้ารหัส
ใช้ ZFS ร่วมกับ
rsyncสำหรับแบ็กอัปมานานแล้วจึงค่อนข้างคุ้นเคย และผลลัพธ์ก็ออกมาดี แต่พอพยายามทำให้ถูกต้องจริง ๆ กลับใช้เวลามากกว่าที่คาดไว้มาก โดยเฉพาะเรื่องฐานข้อมูลและการจำลองข้อมูล มีคำแนะนำผิด ๆ บนเว็บอยู่เยอะสำหรับฐานข้อมูล อย่างน้อยควรทำการปรับแต่งพื้นฐาน เช่น การจัดแนวขนาดบล็อก สำหรับ mariadb/InnoDB เอกสารของฝั่ง Let's Encrypt ที่ https://github.com/letsencrypt/openzfs-nvme-databases ดีเหนือกว่าอย่างชัดเจน เพราะอธิบายเหตุผลของทุกข้อและอ้างอิงแหล่งข้อมูลหลายแห่ง จึงมีคุณค่ามาก
ถ้าค้นเว็บต่อไป จะเจอคำแนะนำที่ขัดแย้งกัน เกร็ดเล่า และตำนานต่าง ๆ อย่างไม่รู้จบ พร้อมทฤษฎีที่ไม่สมบูรณ์และไม่มีหลักฐานรองรับ สุดท้ายต้องทดสอบเอง ทำความเข้าใจสิ่งที่กำลังปรับ และการตัดสินใจไม่ปรับบางอย่างก็เป็นเรื่องที่ยอมรับได้
สำหรับการจำลองข้อมูล แนะนำให้อ่านหน้า manual จริง ๆ ZFS มีเครื่องมือจำลองข้อมูลที่แข็งแกร่ง แต่มีความเอนกประสงค์มากจนให้ความรู้สึกเหมือนคำสั่ง plumbing ระดับล่างของ Git มันไม่ได้สมมติว่าจะใช้ผ่าน SSH จึงต้องต่อประกอบเอง และถ้าจะทำอัตโนมัติก็ต้องคิดถึงเงื่อนไขขอบเขตต่าง ๆ ด้วย ทำให้ตอนแรกดูน่ากลัว
ด้วยเหตุนี้ทุกคนจึงไปใช้เครื่องมืออย่าง
syncoidแต่สคริปต์จำลองข้อมูลสายsyncoidมีจุดที่แย่มากอยู่ คือไม่ใช้โหมดsend --replicationของ ZFS แต่ไปเขียนใหม่แบบไม่สมบูรณ์ด้วย Perl โดยอ้างว่ายืดหยุ่นกว่าตอนทดสอบครั้งแรก พอกู้คืนใหม่แล้วเห็นว่า encryption root ทั้งหมดพัง และคุณสมบัติของ dataset ไม่ได้ซิงก์อัตโนมัติทั้งหมด ก็แทบคลั่ง หากใช้เพียง ตัวเลือกการจำลองแบบ recursive ในตัว ZFS ก็จะจัดการเรื่องนี้ให้
ถ้าตั้งใจเขียนสคริปต์เองก็ไม่ยาก แค่ทำให้เรียบง่าย และอย่าใส่สิ่งไม่จำเป็นเต็ม pipeline แบบ
syncoidเมื่อทดสอบจริง สิ่งเหล่านั้นยังทำให้ช้าลงด้วย ดู progress ด้วยpvแล้วส่งต่อไปเลย ก็ทำงานได้เร็วสักวันอาจเผยแพร่สคริปต์จำลองข้อมูลของตัวเอง รู้สึกว่าแทบไม่มีสคริปต์อ้างอิงที่ดี ซึ่งครอบคลุมพื้นฐานแต่ไม่ไปเขียนการจำลองข้อมูลใหม่แบบเละเทะ
io_capacityและio_capacity_maxแต่เมื่อกดเข้าไปดูเอกสาร MySQL เพื่อดูบทบาทจริงของพารามิเตอร์แล้ว น่าเสียดายที่มันไม่ได้มีประโยชน์มากนักค่านี้ใช้ควบคุม I/O เบื้องหลัง เช่น การ merge change buffer และอาจแย่ง I/O ไปจากโปรเซสหลักที่ต้องทำงานจริง
จากประสบการณ์ที่เคยดูแล MySQL DB ที่ค่อนข้างยุ่งระดับ 120K QPS ไม่จำเป็นต้องไปแตะทั้งสองค่านี้เลย หากรู้สึกว่าจำเป็น ควรเริ่มจากการมอนิเตอร์เวลาที่ redo log ใช้จนเต็ม และสัดส่วน dirty page ใน buffer pool ก่อน น่าจะดีกว่าหากไปปรับพารามิเตอร์อื่น
[0] https://dev.mysql.com/doc/refman/8.0/en/innodb-parameters.ht...
ในบางระดับอาจมีประสิทธิภาพมากขึ้น แต่จากประสบการณ์แล้วทำให้ อัตราการบีบอัด ลดลงอย่างมาก
ผลข้างเคียงลำดับสองที่น่าสนใจคือ แม้จะขึ้นอยู่กับ workload แต่ถ้าคอขวดอยู่ที่แบนด์วิดท์ดิสก์ throughput อาจแย่ลงได้ เพราะการบีบอัดช่วยให้อ่าน/เขียนได้เร็วกว่าที่ดิสก์จริงรองรับ แต่ถ้าทำให้การบีบอัดแย่ลง ก็อาจส่งผลเสียต่อแบนด์วิดท์อ่าน/เขียน
ผมเริ่มใช้ ZFS บน Linux ตั้งแต่หลายปีก่อน และโดยรวมก็ราบรื่นดี
สิ่งเดียวที่ทำให้แปลกใจคือ ค่าเริ่มต้นของ volblocksize ค่อนข้างแย่สำหรับการตั้งค่า RAIDZ ส่วนใหญ่ ถ้าไม่อยากเสียพื้นที่ดิสก์ดิบไป 50% ต้องเพิ่มค่านี้
บทความที่เกี่ยวข้องมีดังนี้
https://jro.io/nas/#overhead
https://openzfs.github.io/openzfs-docs/Basic%20Concepts/RAID...
https://www.delphix.com/blog/zfs-raidz-stripe-width-or-how-i...
สุดท้ายถึงขั้นไปดูหนึ่งในสเปรดชีตเกี่ยวกับ ZFS
ZFS overhead calc.xlsx
https://docs.google.com/spreadsheets/d/1tf4qx1aMJp8Lo_R6gpT6...
RAID-Z parity cost
https://docs.google.com/spreadsheets/d/1pdu_X2tR4ztF6_HLtJ-D...
ตอนเด็กกว่านี้และยังโง่กว่านี้ ผมเคยอ่านบทความมากมายจากแฟนตัวยงที่เขียนว่า NAS OS โอเพนซอร์ส ซึ่งน่าจะเป็น FreeNAS กับ ZFS นั้นยอดเยี่ยมแค่ไหน ผมซื้อ HP microserver มือสองสเปกต่ำมากจาก eBay แล้วกระโจนเข้าไปทั้งที่ไม่ค่อยรู้ว่ากำลังทำอะไรอยู่
ผมถามคำถามไปสองสามข้อในฟอรัมชุมชน แต่คำตอบส่วนใหญ่คือ “อ่านเอกสารหรือยัง?” “RAM พอไหม?”
เอกสารที่ว่านั้นเป็นสไลด์ PowerPoint สไตล์อ่านยาก น้ำเสียงออกแนวเผยแพร่ศาสนานิด ๆ มีสมมติฐานเรื่องความรู้พื้นฐานเยอะ และไม่ได้อัปเดตเป็นประจำ ปริมาณ RAM ที่ต้องใช้ก็คลุมเครือ และเน้นแค่ว่าให้ใส่ให้มากที่สุดเท่าที่ทำได้
สุดท้ายผมก็เมินสัญญาณเตือนทั้งหมด ทั้งเรื่องเทคโนโลยี บรรยากาศที่อวยเกินจริง และระดับความรู้ของตัวเอง แล้วก็สูญเสียข้อมูลไปมาก ได้เรียนรู้อะไรเยอะเลย
ผมคิดว่า filesystem เสถียรขึ้นมากแล้ว และเอกสารก็ชัดเจนขึ้นด้วย
ถึงอย่างนั้น เพราะมัน ทรงพลังกว่าและขั้นสูงกว่า filesystem แบบ journaling ดั้งเดิมอย่าง ext3 มาก วิธีที่จะทำให้ตัวเองพลาดก็มีมากขึ้นตามไปด้วย
ขอทิ้งบางอย่างไว้สำหรับภายหลังอีกหน่อยคือ ความซ้ำซ้อนทั้งหมดของ ZFS อยู่ที่ชั้น vdev zpool ถูกสร้างจาก vdev อย่างน้อยหนึ่งตัว และไม่ว่าในกรณีใด ถ้าเสีย vdev ไปหนึ่งตัวใน zpool, zpool ก็จะพังถาวร
ในอดีต RAIDZ หรือก็คือ parity RAID ไม่สามารถขยายด้วยการเพิ่มดิสก์ได้ วิธีเดียวที่จะขยาย RAIDZ คือแทนที่ดิสก์แต่ละลูกในอาร์เรย์ทีละลูกด้วยดิสก์ที่ใหญ่กว่า แล้วภาวนาไม่ให้ดิสก์เสียระหว่าง rebuild
ถ้าตัดสินแบบสมัครเล่นมาก ๆ ผมจะพิจารณา RAIDZ เฉพาะกรณีที่มีดิสก์จำนวนมากอย่าง RAIDZ2 หรือ RAIDZ3 เท่านั้น ถ้า n<=6 และงบประมาณเอื้อ ผมจะใช้ mirror vdev หลายตัว ในสภาพแวดล้อม production ควรต้องไปค้นคว้าตัวชี้วัดประสิทธิภาพการอ่าน/เขียนของ RAID หลายแบบเพิ่มเติม
แต่จะแนะนำ RAIDZ1 ได้ก็ต่อเมื่อมีแบ็กอัปทั้งไซต์ และค่อนข้างเชื่อมั่นในความสามารถกับการมอนิเตอร์ของตัวเองเท่านั้น
กรณีของผมมีพูลไดรฟ์ 3x3 และส่ง snapshot ไปยังเป้าหมายแบ็กอัปที่อยู่ต่ำลงไปไม่กี่ U ในแร็ก เป้าหมายแบ็กอัปนั้นตื่นขึ้นมาทุกวัน มีพูลไดรฟ์ 3x4 และก็เป็น RAIDZ1 เช่นกัน
ถ้าไดรฟ์ใน NAS เสีย แผนคือจะเริ่มแบ็กอัปทันที รับ snapshot แล้วค่อยเปลี่ยนไดรฟ์ วิธีนี้จะลดโอกาสที่ข้อมูลจะหายจากไดรฟ์ลูกที่สองเสียระหว่าง resilvering ให้เหลือน้อยที่สุด
แน่นอนว่าข้อมูลที่สำคัญจริง ๆ ก็เก็บไว้นอกสถานที่ด้วย
ผมกำลังเจอ ปัญหา ZFS ที่ไม่เข้าใจ ใน zpool หนึ่ง
zpool statusแสดงรายการข้อผิดพลาดที่ตรวจพบ แต่ไม่ได้อยู่ในไฟล์ และมักปรากฏเฉพาะใน snapshot รวมถึงรายการเลขฐานสิบหกที่น่าจะเป็น snapshot ที่ถูกลบไปแล้วถ้าลบ snapshot ที่ถูกระบุว่ามีข้อผิดพลาด แล้วรัน
zpool scrubสองครั้ง ข้อผิดพลาดก็หายไป และ scrub ก็ไม่พบข้อผิดพลาดzpool statusไม่เคยแสดงข้อผิดพลาดในอุปกรณ์ใดเลยไฟล์ไม่มีข้อผิดพลาด อุปกรณ์ก็ไม่มีข้อผิดพลาด scrub ก็ตรวจไม่พบข้อผิดพลาด แต่ระหว่างใช้งานกลับมี “ข้อผิดพลาด” ใหม่วันละ 12 รายการโผล่ใน
zpool statusไม่รู้ว่าเรื่องแบบนี้เกิดขึ้นได้อย่างไรอันแรกดูแปลก ๆ แต่เป็นเว็บฟรอนต์เอนด์ปกติของ mailing list
ย้ายหรือแก้ไขไม่ได้ ลบได้อย่างเดียว ไม่รู้เลยว่าทำไมไฟล์พวกนี้ถึงเสียหาย โชคดีที่ทั้งหมดเป็น Linux ISO ขนาดใหญ่ เลยไม่ถึงกับร้ายแรง