สำรองและกู้คืนข้อมูล
เว็บที่ใช้ TomeCMS เก็บเนื้อหาไว้สองที่ บทความ เพจ การตั้งค่า และข้อมูลการเข้าสู่ระบบอยู่ใน PostgreSQL ส่วนไฟล์ในคลังไฟล์ รวมถึงโลโก้ เป็น object อยู่ใน bucket ของมีเดีย การสำรองแค่ฐานข้อมูลจึงไม่ครบ npm run backup จะคัดลอกทั้งสองที่ในการรันครั้งเดียว โดยทำตอนที่แอปหยุดอยู่ ข้อมูลสองฝั่งจึงตรงกัน
ก่อนสำรองข้อมูลครั้งแรก
หัวข้อที่มีชื่อว่า “ก่อนสำรองข้อมูลครั้งแรก”คำสั่งสำรองข้อมูลและคำสั่งตรวจการกู้คืนรันบนเซิร์ฟเวอร์จากโฟลเดอร์โค้ดด้วย Node.js สคริปต์ deploy สร้างแอปไว้ใน Docker และไม่ได้ติดตั้งแพ็กเกจที่สองคำสั่งนี้ต้องใช้ลงในโฟลเดอร์โค้ด จึงต้องติดตั้งเองหนึ่งครั้ง
npm ciทั้งสองคำสั่งอ่าน .env.local จากโฟลเดอร์โค้ด คำสั่งสำรองข้อมูลเข้าถึง PostgreSQL ผ่าน Compose และเข้าถึง bucket ผ่าน S3_ENDPOINT ดังนั้นทั้งสอง service และ proxy ของ origin มีเดียต้องทำงานอยู่
สำรองข้อมูล
หัวข้อที่มีชื่อว่า “สำรองข้อมูล”หยุดแอปก่อน เพื่อไม่ให้มีอะไรเขียนข้อมูลระหว่างคัดลอก
docker compose -f compose.yaml --env-file .env.local stop appแล้วสำรองไปไว้ในโฟลเดอร์ที่อยู่นอกโฟลเดอร์โค้ด
npm run backup -- --offline --output-root "$HOME/tomecms-backups"ต้องใส่ --offline ทุกครั้ง เป็นการยืนยันว่าไม่มีอะไรเขียนข้อมูลอยู่ และคำสั่งจะไม่ยอมเริ่มถ้าคอนเทนเนอร์แอปยังทำงานอยู่ เมื่อเสร็จแล้วจะพิมพ์ว่าชุดสำรองอยู่ที่ไหนและมีข้อมูลเท่าไร
Backup complete: /home/deploy/tomecms-backups/tomecms-20260913T120000000ZDatabase records: 42; media objects: 118ตัวเลขแรกคือจำนวนบทความกับเพจรวมกัน เมื่อเสร็จแล้วให้เปิดแอปอีกครั้ง เว้นแต่คุณกำลังจะอัปเดตต่อ
docker compose -f compose.yaml --env-file .env.local start appถ้าใน bucket มี object ที่ TomeCMS ไม่ได้เป็นคนตั้งชื่อ คำสั่งจะหยุดพร้อมข้อความ The media bucket contains an unsupported object key. ให้ย้าย object เหล่านั้นออกจาก bucket แล้วรันใหม่
ในชุดสำรองมีอะไรบ้าง
หัวข้อที่มีชื่อว่า “ในชุดสำรองมีอะไรบ้าง”การรันแต่ละครั้งจะสร้างโฟลเดอร์ใหม่หนึ่งโฟลเดอร์ใต้ output root ตั้งชื่อตามเวลาที่เริ่มรันเป็น UTC เช่น tomecms-20260913T120000000Z ข้างในมี
| ไฟล์หรือโฟลเดอร์ | คืออะไร |
|---|---|
database.dump |
ฐานข้อมูล tomecms ทั้งหมด dump ด้วย pg_dump ในรูปแบบ custom โดยไม่มีข้อมูล owner และสิทธิ์ |
objects/ |
object ทุกตัวใน bucket วางไว้ที่ path เดียวกับ key ของมัน |
manifest.json |
เวอร์ชันของ TomeCMS ที่สร้างชุดสำรอง ที่อยู่ของเว็บ ชื่อฐานข้อมูลและชื่อ bucket จำนวนการตั้งค่า บทความ เพจ และรายการในคลังไฟล์ที่ฐานข้อมูลมีตอนนั้น checksum SHA-256 ของไฟล์ dump และ checksum, content type และขนาดของ object แต่ละตัว |
คำสั่งเขียน manifest.json เป็นไฟล์สุดท้าย ถ้าโฟลเดอร์ไหนไม่มีไฟล์นี้ แปลว่าการสำรองครั้งนั้นล้มเหลวหรือถูกขัดจังหวะ ห้ามนำไปกู้คืน
--database-only จะไม่สำรอง bucket โฟลเดอร์จะไม่มี objects/ และ manifest จะมี "scope": "database" โดยไม่มีรายการไฟล์ ตัวอัปเดตใช้แบบนี้ก่อนการอัปเดตที่ไม่มี migration ตั้งแต่ 1.3.0 เพราะการอัปเดตแบบนั้นไม่เปลี่ยนตารางและไม่ได้คาดว่าจะเขียนไฟล์ใหม่ ถ้าจะกู้คืนจากชุดแบบนี้ ให้กู้ dump แล้วปล่อย bucket ไว้อย่างเดิม คำสั่งตรวจชุดสำรองใช้ได้กับทั้งสองแบบ และบอกว่าตรวจแบบไหน
ชุดสำรองไม่ได้เก็บค่าลับใน .env.local ไว้เลย เว็บที่กู้คืนจากชุดสำรองต้องใช้ค่าลับชุดเดิม จึงต้องเก็บสำเนา .env.local ไว้ในที่ปลอดภัย แยกจากชุดสำรอง ตามที่อธิบายไว้ในหน้าการตั้งค่า แต่ไฟล์ dump ยังมีทุกอย่างในฐานข้อมูล รวมถึงข้อมูลการเข้าสู่ระบบ จึงต้องเก็บชุดสำรองให้มิดชิดเท่ากับไฟล์นั้น
ไฟล์ไปอยู่ที่ไหน
หัวข้อที่มีชื่อว่า “ไฟล์ไปอยู่ที่ไหน”--output-root ต้องเป็น absolute path ที่อยู่นอกโฟลเดอร์โค้ด คำสั่งไม่รับ path แบบ relative, path ในโฟลเดอร์โค้ด และ root ของระบบไฟล์ ถ้าโฟลเดอร์ยังไม่มี คำสั่งจะสร้างให้ โฟลเดอร์ในชุดสำรองตั้งสิทธิ์ให้เจ้าของเข้าถึงได้คนเดียว (0700) และไฟล์ก็เช่นกัน (0600)
ชุดสำรองที่อยู่บนดิสก์ของเซิร์ฟเวอร์เองจะหายไปพร้อมดิสก์นั้น จึงควรคัดลอกแต่ละชุดไปเก็บไว้ที่เครื่องอื่น และเพราะแต่ละชุดคัดลอกทั้งฐานข้อมูลและทุกไฟล์ใน bucket ซ้ำอีกรอบ ให้เผื่อพื้นที่ดิสก์ตามจำนวนชุดที่เก็บไว้บนเซิร์ฟเวอร์
ตรวจชุดสำรอง
หัวข้อที่มีชื่อว่า “ตรวจชุดสำรอง”ตรวจชุดสำรองทุกชุดก่อนนำไปใช้จริง คำสั่งตรวจการกู้คืนจะกู้ชุดสำรองลงใน Compose project แยกที่ใช้แล้วทิ้ง แล้วเทียบผลกับ manifest
npm run restore:check -- \ --backup /home/deploy/tomecms-backups/tomecms-20260913T120000000Z \ --project tomecms-restore-check-20260913--backup คือ absolute path ของชุดสำรอง --project คือชื่อ project ที่ใช้แล้วทิ้ง ต้องขึ้นต้นด้วย tomecms-restore-check- แล้วตามด้วยตัวพิมพ์เล็ก ตัวเลข หรือขีดกลางไม่เกิน 40 ตัว โดยขึ้นต้นและลงท้ายด้วยตัวอักษรหรือตัวเลข ใช้ชื่อใหม่ทุกครั้ง เพราะคำสั่งจะไม่ยอมใช้ project ที่มีคอนเทนเนอร์อยู่แล้ว และจะไม่แตะ project ของเว็บจริงเลย
คำสั่งทำงานตามลำดับนี้
- ตรวจไฟล์ dump และ object ทุกตัวกับ checksum ใน manifest
- เปิด PostgreSQL และ SeaweedFS ชุดของตัวเองที่พอร์ต
55432และ59000บน127.0.0.1ระหว่างที่คำสั่งทำงานต้องปล่อยสองพอร์ตนี้ให้ว่าง - กู้ไฟล์ dump ลงใน PostgreSQL ชุดนั้น
- ใส่ object ทุกตัวกลับเข้าไปพร้อม content type เอกสารแต่ละไฟล์จะได้ header
Content-Dispositionคืนมาด้วย ซึ่งเป็น header ที่กำหนดชื่อไฟล์ตอนดาวน์โหลด และกำหนดว่า PDF จะเปิดในเบราว์เซอร์หรือไม่ header นี้ติดอยู่กับ object ใน bucket และชุดสำรองไม่ได้เก็บไว้ คำสั่งจึงสร้างขึ้นใหม่จากแถวของเอกสารนั้นในmedia_items - เทียบจำนวนการตั้งค่า บทความ เพจ และรายการในคลังไฟล์ที่กู้คืนได้กับตัวเลขใน manifest และเทียบ object ที่กู้คืนได้กับรายการใน manifest
- ลบคอนเทนเนอร์และ volume ที่ใช้แล้วทิ้งออก ไม่ว่าการตรวจจะผ่านหรือไม่
ชุดสำรองที่ผ่านจะจบด้วย Restore verified in disposable project tomecms-restore-check-20260913. ถ้าไม่ผ่าน บรรทัดสุดท้ายจะขึ้นต้นด้วย Error: เช่น Database dump checksum does not match its manifest. หรือ Restored object inventory does not match the backup. อย่าใช้ชุดสำรองที่ตรวจไม่ผ่าน
กู้คืนเว็บ
หัวข้อที่มีชื่อว่า “กู้คืนเว็บ”ตอนนี้ TomeCMS ยังไม่มีคำสั่งที่กู้ชุดสำรองกลับเข้าเว็บจริง คำสั่งตรวจการกู้คืนแสดงให้เห็นทุกขั้น การกู้คืนเองต้องทำแบบเดียวกันกับ service ของเว็บจริง โดยหยุดแอปไว้ก่อน
- กู้
database.dumpลงใน PostgreSQL ของเว็บด้วยpg_restore - ใส่ทุกไฟล์ใต้
objects/กลับเข้า bucket ที่ key เดิม พร้อม content type ตามที่ manifest บันทึกไว้ - ใส่ header
Content-Dispositionให้เอกสารทุกไฟล์ โดยสร้างแบบเดียวกับที่contentDispositionในsrc/server/media/disposition.tsสร้างจากชื่อและชนิดของเอกสารในmedia_itemsถ้าไม่มี header นี้ เอกสารจะไม่ได้ดาวน์โหลดด้วยชื่อของมันเอง - เปิดแอปด้วย
.env.localชุดที่เว็บใช้อยู่ตอนสำรองข้อมูล
ลองกู้คืนบนเซิร์ฟเวอร์สำรองก่อนถึงวันที่ต้องใช้จริง และเก็บข้อมูลเดิมไว้จนกว่าเว็บที่กู้คืนแล้วจะใช้งานได้

