Tutorial

บันทึกการ Upgrade Ghost 4.x เป็น 5.x เ_ย อะไรเนี่ย !!!!

By Arnon Puitrakul - 24 มิถุนายน 2022 - 1 min read min(s)

บันทึกการ Upgrade Ghost 4.x เป็น 5.x เ_ย อะไรเนี่ย !!!!

หลังจากตอนก่อน เราทำการ Upgrade MySQL จาก 5.7 เป็น 8 ไปแล้ว เพราะทั้ง Home Assistant และ Ghost CMS เอง ก็เลิก Support ไปแล้ว อันนั้นที่พัง ก็เป็นเพราะโง่ อ่านไม่หมดเองแหละ กลับไปย้อนอ่านได้ที่ ลิงค์นี้ แต่มาในตอนนี้เราจะมาดูต่อกันว่า ถ้าเราจะ Upgrade Ghost CMS ซึ่งก็คือเว็บที่ทุกคนกำลังอ่านอยู่ตอนนี้เราจะต้องทำอย่างไร และ เราเจออะไรบ้าง

วิธีการ Upgrade

ก่อนที่เราจะทำอะไรก็ตามกับระบบ แนะนำว่าควร Backup ก่อน ไม่งั้นชิบหายขึ้นมา คือแตกหมู่เลยนะ ไม่สนุกแน่นอน โดยวิธีการ Backup จริง ๆ ทำได้ง่ายมาก ๆ ถ้าเราสามารถเข้าถึง Command Line ได้ เราสามารถเรียก ghost backup ตอนที่เราอยู่ใน Directory ของ Ghost เราเอง มันจะคายออกมาเป็น Zip File ที่มีข้อมูลทุกอย่างในเว็บของเรา กันเหนียวไว้

หรือ ถ้าเราไม่สามารถเข้าถึง Command Line ได้ เราสามารถเข้าไปที่หน้า Settting ของเว็บ เลือก Labs แล้วเลือก Export your content อันนี้เราจะได้ Content ของเราที่เป็น JSON ออกมา จากนั้น ให้เราเอา Redirect และ Route กับ Theme ออกมาด้วย

หรือสำหรับใครที่ Inject Code ต่าง ๆ พวก Google Analytics และ Facebook Pixel เราก็ไปก๊อปมันออกมา ใส่ Text File ไว้ก่อนก็ได้ กันเหนียวเยอะดีกว่า เสียแล้วต้องมานั่งลงใหม่เสียเวลามาก ๆ

จริง ๆ แล้ววิธีการ Upgrade Ghost ที่เราลงผ่าน Docker Container นั้นง่ายมาก ๆ คือ ให้เราเข้าไป Pull Image Version ล่าสุดเข้ามา แล้วเราก็สามารถเอามา Replace แล้วเปิดใช้งานได้เลย อันนี้เรียกว่าเป็นเรื่องปดติที่เราทำกันอยู่แล้ว

ส่วนถ้าใครที่ลงไปใน Instance หรือ VM เราแนะนำให้ทำการ Update Minor Version ให้เป็น Version ล่าสุดก่อน ผ่านคำสั่ง ghost update v4 เพื่อความปลอดภัย เราอาจจะ Backup อีกรอบก็ได้

สุดท้าย รัน ghost update ก็เป็นอันเรียบร้อย มันจะทำการติดตั้ง Ghost Version ล่าสุดให้เราทันที และ ทำการ Migrate Database อะไรของมันเอง เราไม่ต้องทำอะไรมันเลย แล้วก็เป็นอันเสร็จการ Update Ghost CMS จะเห็นว่า อันที่เราติดตั้งลงไปใน Instance หรือ VM เลย เราว่าแอบยุ่งยากกว่า Docker เยอะพอสมควรเลย ดังนั้น ปกติ เราเลยแนะนำให้ทำบน Docker จะจัดการอะไรได้สะดวกกว่าเยอะ ยังไม่นับเรื่องการทำ Docker Network ด้านในด้วย ทำให้ปลอดภัยยิ่งขึ้นอีกหน่อย

ความชิบหาย มาเยือนแล้ว !

เราก็ทำตามขั้นตอนปกติเลย จน เราก็ Pull Image Version ล่าสุดเข้ามาในเครื่องของเราเอง แล้วก็ Backup อะไรเรียบร้อยหมดแล้ว ก็เลยทำการ Reset Container เพื่อให้มันไปเรียก Image ที่เราพึ่งโหลดเข้ามา

ตอนรันทุกอย่างดูปกติดีเลย คือ Image ที่เรารันเป็น Container ก็น่าจะเป็น Version ใหม่แน่ ๆ ละ ไม่น่าโหลดผิด เพราะใน Log มันมีการ Migrate พวก Database อยู่ น่าจะปลอดภัย

จนกระทั่ง เอ๋ เกิด Error ขึ้น เราเข้าเว็บ ทำอะไรไม่ได้เลย แล้ว Container ก็ Stop ไป มันบอกว่า Error occurred while executing the following migration เอาแล้วไง แปลว่า Migration ไม่ผ่านอะสิ ชิบหายจริง ๆ ละ เอาไงดี

เลยขึ้นไปอ่าน Log พยายามหาว่า มันแตกตรงไหน ทำให้ Migration Failed ได้ จนไปเจอว่า มันพยายามที่จะ Alter Table บนตาราง members_cancel_events ด้วยการเพิ่ม Foreign Key เข้าไป ทำให้ปัญหาแรกที่เราพอจะคิดได้คือ หรือว่า อยู่ ๆ พอเราเพิ่ม Foreign Key เข้าไปมันจะต่อไม่ได้ เพราะมีบาง Record ตรง Foreign Key มันไม่มี Integrity มันไม่สมบูรณ์เลยแตกเหรอ

จนเข้าไปดู เอ๊ะ แมร่งไม่มีข้อมูลนี่หว่า ชิบหายแล้วเ_ยอะไรเนี่ย !!! แน่นอนว่า เราเรียนวิทยาศาสตร์มา เราจะพึ่งไสยศาสตร์กัน ไม่ได้แก้อะไรเลย รันใหม่อาจจะได้ก็ได้ รันใหม่ ก็คือแตกอีก แล้วได้อีก Error มาแทน Migration lock was never released อ๋าาาาาา รู้เลยนะขร๊ะ ว่า รอบก่อนที่มันพยายามจะ Migrate พอแตกแล้ว มันไม่ยอมคลายล๊อค ได้เลย !!

งั้นเราไปคลาย Lock มันก่อน ไปเปิด Database มัน ก็พอดีเห็น Table ชื่อ migration_lock อื้ม... น่าสนใจ พอดีเห็น Flag locked เป็น 1 เดาว่า มันน่าจะ Lock ตรงนี้แหละ เลยปรับให้เป็น 0 กับ แก้ released_at เป็นเวลาปัจจุบันไป น่าจะรอด ซึ่งใช่ฮ่ะ รอด กลับมาที่ Error แรกเหมือนเดิม บ้าเอ้ยยยยยยย เ_ยอะไรเนี่ย

ตอนนั้นคือ เอาวะ in the worst case เราสามารถเปิด Ghost และ MySQL 8 Container ขึ้นมา แล้วก็เอาข้อมูลที่ Backup ใส่เข้าไปได้แหละ ไม่ยาก ๆๆๆๆๆ ส่วนรูปเราก็ก๊อปตามเอา

ปัญหาเกิดจาก Default Schema Collation บน MySQL 8

นั่งคิดอยู่นาน เลยนึกถึงตอนนั่งดู Youtube สักช่องนี่แหละนานมาก ๆ แล้วเรื่อง Changes บน MySQL 8 ทำให้นึกถึง Collation ขึ้นมา มันน่าจะทำให้แตกตอนเราจัดการ Foreign Key ได้ด้วยนี่หว่า

อย่างที่หลาย ๆ คนทราบดีว่า ตัว MySQL 5.7 หรือ 5 ทั้งหลายเลยเนี่ย มันใช้ utf8mb4 เป็น Default Collation ก็คือ เมื่อ เราสร้าง Schema ขึ้นมา มันก็จะเรียกใช้ utf8mb4 นั่นแหละ ถ้า งง ก็คือ มันเป็นวิธีการจัดเก็บตัวอักษรละกัน เมื่อก่อน เราก็ใช้ UTF-8 ปกตินี่แหละ แต่ utf8mb4 มันขยาย ตัวหนังสือที่เราเก็บได้กว้างขึ้น และ สามารถเก็บพวก Emoji ได้ด้วย ทำให้ใน บทความหน้าเว็บของเราสามารถเก็บพวก Emoji หรือภาษาแปลก ๆ ได้

แต่พอมาบน MySQL 8 มันทะลึ่งเปลี่ยน Default Collation เป็น utf8mb4_0900_ai_ci ซะงั้นนี่ จริง ๆ มันก็เหมือนเดิมแหละ แต่เพิ่มพวก Specification เข้าไปเอาเป็นว่า มันก็ใกล้ ๆ เพื่อนบ้านกันละกัน โอเคกลับมาที่ปัญหาของเรากัน

ปัญหาของเรามันเกิดเมื่อ Ghost พยายามที่จะ Migrate โดยการเพิ่ม Foreign Key เข้าไปแล้วเด้งตรงนั้นเลย เราเข้าใจว่า มันพยายาม Integrity Check แหละ แต่พอ Collation มันเปลี่ยนทำให้มันอ่านไม่ออก มันเลย งง ก็เลยแตกเลย งั้นเอาไงดี

ถ้า Collation มันไม่ได้ งั้นเราเปลี่ยนได้มั้ย ถ้าเราจำได้ เราสามารถเขียนคำสั่ง เพื่อเปลี่ยน Schema Collation ได้นิ ก็แค่ Alter แค่นั้นเองก็เลยลองรัน แล้วปลดล๊อค Migration Lock แล้วลองอีกครั้ง ปรากฏว่า แตกเหมือนเดิม อั้ยหยาาาาาาา เ_ยอะไรเนี่ย !

งั้นเอางี้ เพื่อตัดปัญหาแมร่งเลย เรา Drop Schema แล้วสร้างใหม่แมร่งเลยมั้ย มา ! ก่อนอื่น เราจะต้องทำการ Backup Database ออกมาก่อน เราก็ใช้ mysqldump ปกติเลย ถ้าใครที่ใช้ macOS เราก็สามารถไปโหลด MySQL Workbench มา แล้วในนั้นมันจะมี Binary ของ mysqldump ให้เราเรียกอยู่ เราก็จัดการเรียกแล้ว Backup ได้ทั้งก้อนเลย

จากนั้นก็ Drop Schema ออก แล้วลงอันใหม่ทับเข้าไป อะแหม่ คิดว่าเราจะทำแบบนั้นจริง ๆ เหรอ ง่ายไปป่ะ เผื่อเสียขึ้นมาทำไง ความเป็นจริง เราก็เปลี่ยนชื่อ Schema เดิมเป็น Backup ก่อน แล้วค่อยรัน Restore Backup เข้าไป แล้ว Start Ghost อีกรอบ ปรากฏว่า ผ่านโว้ยยยยย Migration Completed และ Web Server รันได้ปกติ เข้าไปดูข้อมูลก็อยู่ครบเหมือนเดิมเลย รอดตายหนึ่ง !

สรุป

เรื่องทั้งหมดเกิดจากการที่เรา Migrate Database จาก MySQL 5.7 เป็น MySQL 8 ที่ มันใช้ Default Collation ไม่เหมือนกัน เลยทำให้เวลาเราดึง Database มาใช้ เราอาจจะ Query ได้อะไรได้หมดเลยนะ แต่ถ้าเราเริ่มยุ่งกับพวก Index และ Keys ต่าง ๆ ก็คือเกมเลยทันทีเหมือนกับที่เราเอามาเล่าในบทความนี้ เราเดาว่า ถ้าเราจะไม่ Drop Schema เราอาจจะต้อง Drop Indexes ทั้งหมดเลย แล้วทำการเปลี่ยน Collation ราย Table แล้วก็เติม Index กลับเข้าไปน่าจะพอทำได้อยู่นะ สำหรับ Database ขนาดใหญ่มาก ๆ และ ไม่อยากสร้างใหม่ ก็ถือว่า ยุ่ง แต่ถ้าต้อง Migrate Data ขนาดใหญ่ไป เราว่า ก็อาจจะคุ้มกว่ามั้งนะ