เมื่อต้นปีที่แล้ว Skooldio ปล่อย Product ใหม่ มาหนึ่งตัวหนึ่งนั่นคือ Skooldio Tutorials เป็น Platform รวมคลิปสั้นให้ความรู้แบบกระชับ เพื่อให้คนเข้ามาเรียนรู้ได้ไว ๆ ไม่ต้องใช้เวลามากเท่าเรียนแบบเต็มคอร์ส และยังใช้เป็นการ Audition ให้อาจารย์ที่จะมาสอนกับเรา ได้ลองอัดวิดิโอสอนก่อนจะทำเป็นคอร์สจริงอีกด้วย

ซึ่งทางฝั่ง Software Engineering ของเรา นอกจากจะทำ Product ให้ตอบโจทย์ในมุมของ User แล้ว เป้าหมายของเราอีกส่วนก็อยากลองสิ่งใหม่ ๆ (ณ ขณะนั้น) คือ Serverless ในรูปแบบของ Container คือ Cloud Run

Cloud Functions vs. Cloud Run

Serverless ของ Google Cloud มี 2 ตัวที่น่าสนใจ คือ Cloud Functions และ Cloud Run

Cloud Functions เราอาจจะเขียนโค้ดหรือฟังก์ชันการทำงาน ที่ Trigger จาก REST Method หรือมาจาก Event อื่น ๆ เช่น Pub/Sub แต่ก็จะมีข้อจำกัดว่าเราไม่สามารถลง Lib เสริมใน Environment ที่เราใช้ได้ และภาษาก็จะถูกจำกัด

ส่วน Cloud Run ให้อิสระเรามากขึ้น เพราะเราเอา Docker Container ไปรันโดยไม่จำเป็นต้องสร้าง Kubernetes Cluster พอเป็น Container แล้วหมายความว่าเรามีอิสระในการปรับแต่ง Environment ได้มากขึ้น แต่ก็จะมีข้อจำกัดบางอย่าง เช่น การต่อ MySQL อาจจะต้องต่อทาง Unix sockets แทน IP และ Container ของเรา อาจจะไม่ได้รันตลอดเวลา

ทางทีมเลือกใช้ Cloud Run สำหรับ Service หลัก ๆ ของ Tutorials เพราะเราไม่ต้องแปลงโค้ดให้ Support Cloud Functions และสามารถขึ้นโครงของโค้ดจากที่เรามีอยู่ได้เลย ซึ่งโค้ดของเราสามารถรันใน Docker Container ได้อยู่แล้ว ส่วนฟังก์ชันเสริมส่วนเล็ก ๆ ที่การทำงานชัดเจน จะใช้เป็น Cloud Functions หรือถ้ามีส่วนที่ต้องการรันเป็น schedule ก็จะใช้เป็น Cloud Scheduler แทน

Headless CMS

การสร้าง Content ใน Tutorials ทางทีมเลือกที่จะไม่ทำ Backoffice และ Backend Service ด้วยตัวเอง เพราะเราจะลองขึ้น Product แบบ MVP ก่อน ซึ่ง Process การทำงานของ user ทางหลังบ้านเราตกลงกันว่าจะใช้อะไรที่ง่าย ๆ

ซึ่งตัว CMS ปกติอาจจะใช้ WordPress ทำหน้าที่เป็น API แล้วมาเขียน Custom Frontend แต่ว่าก็จะมีข้อจำกัดเรื่องของรูปแบบ Content ที่อาจจะอิงกับ Post และ Page ส่วนหนึ่ง และความไม่สะดวกในการ Containerize ของมัน เราเลยหา Solution ปัจจุบัน ณ ขณะนั้นมา จึงเลือก Strapi มาเป็น Headless CMS

ข้อดีของการใช้ Strapi คือเราสามารถกำหนดรูปแบบของ Content ได้ง่ายผ่านทาง UI และสามารถ Custom เพิ่มเติม logic ต่าง ๆ ได้ด้วยการใช้ Javascript นอกจากนี้ก็มี GraphQL endpoint ที่เป็น API ที่ทางสคูลดิโอใช้งานเป็นหลัก

ข้อจำกัดของ Strapi คือ ตัว Database Connection ที่ Support อย่างเป็นทางการ ยังไม่มีตัวที่เป็น Serverless บนฝั่ง Google Cloud เราเลยต้องไปต่อกับ MySQL ของตัว Skooldio Platform แทน

ส่วนอื่นๆ

ฝั่งการพัฒนาส่วน Frontend นั้น อาจจะเรียกได้ว่าลอกแบบมาจาก Website skooldio.com คือใช้ Next.js กับ GraphQL ดังนั้นทีมที่ทำส่วนหน้าเว็บนั้นสามารถทำตามที่คุ้นเคยได้ ในขณะที่เราลองใช้ส่วนอื่น ๆ ที่เป็นของใหม่ไปด้วย

นอกจากนี้เรายังมีส่วนนับยอดวิวและยอดการกดไลก์ 2 ตัวนี้เราใช้ Cloud Function และ Firestore ในการเก็บค่า แต่เพื่อเพิ่มความเร็วในการดึงข้อมูล เราจะ Sync ข้อมูลจาก Firestore เก็บกลับเข้าไปที่ Strapi เป็น Snapshot เพื่อให้ดึงข้อมูลมาแสดงผลได้เร็ว และข้อมูลตรงนี้จะถูก Sync ขึ้นไปใน Excel ทำเป็น Dashboard ง่าย ๆ ให้ PM มาดูยอดได้รวดเร็ว

และเพื่อจะรวม Endpoint ต่าง ๆ มาไว้ที่เดียว เรามี Gateway Server ที่เป็น Apollo Server ใช้ Schema Stitching ทั้งดึง Schema จาก Strapi และเพื่อดึง Schema จาก Service ย่อย ๆ มาให้ Client ใช้ และเขียน Definition เองใน Server ในส่วนที่เราต้อง Custom บางอย่าง

CI เราใช้ Cloud Build เพื่อ Build Image และ Deploy ลงใน Dev Environment และใช้การ Tag เพื่อ Deploy ใน Production

หลังจากเขียนมาเยอะแล้ว มาดูแบบรูปภาพบ้างดีกว่า ซึ่ง Architecture ของระบบ Skooldio Tutorials ก็จะเป็นตามรูปนี้ครับ

What we learn?

หลังจากที่เราทำและปล่อยขึ้น Production ไปแล้ว ก็มีหลายเรื่องที่เราได้เรียนรู้จากโปรเจกต์นี้

  • เรายังไป Serverless ได้ไม่เต็มตัว เพราะยังติดเส้นของ Database อยู่ – บางครั้งก็มีเหตุการณ์ที่ไม่ดี เช่น โค้ดส่วนที่อัปเดทข้อมูล Snapshot ลง Strapi เขียนไว้ไม่ดีทำให้ Cloud Run Instance ของ Strapi ขึ้นมาเยอะมากจน Connection ของ MySQL รับไม่ไหว และ Restart ตัวเองไปเลย
  • Latency – ตอนแรกที่ Launch Skooldio Tutorials เราพบว่า Latency ของมันเยอะมาก ซึ่งเหตุผลตอนนั้นเป็นเพราะว่า Cloud Run Instance มีที่ใกล้สุดคือที่ไต้หวัน ต้นปีนี้เพิ่งมีที่สิงคโปร์ และพอเราย้ายจากไต้หวันมาสิงคโปร์ก็ทำให้ Latency ลดเยอะทีเดียว
  • ส่วนเรื่อง Cold Start ก็เป็นเรื่องปกติของฝั่ง Serverless – แต่ว่าการที่เราใช้ Architecture ปกติของ Skooldio มาใช้กับ Serverless ทำให้เกิด Hop ที่ไม่จำเป็นขึ้นมา (3 Service จะต้องขึ้นพร้อม ๆ กัน ถึงจะแสดงข้อมูลได้) ทำให้บางครั้งถ้า Cold Start ช้า หน้าเว็บก็จะโหลดไม่ขึ้น อันนี้เราใช้ Workaround ด้วยการไปปลุกมันบ่อย ๆ ผ่านทาง Uptime Monitoring ของ GCP’s Monitoring Console
  • Strapi ถ้าจะดึงข้อมูลท่ายาก ๆ ก็ต้อง Customize เอง – เช่นหน้า Browse วิดีโอ ที่ต้องสร้างเงื่อนไขสำหรับ Query Content ใน Category ต่าง ๆ ส่วนนี้ก็ต้องไปดึงที่ Database Connection Layer มาเขียน Query เอง
  • Roles and Permission (ในเวอร์ชันฟรี) ของ Strapi อาจจะไม่เหมาะกับ Corporate ใหญ่ ๆ ที่ต้องมีการ Customize สิทธิ์ของ Role ต่างๆ
  • ค่าใช้จ่ายของระบบ (ไม่รวม MySQL) ตกอยู่ที่ประมาณ 20–30 USD ต่อเดือน ซึ่งยังเป็นค่าใช้จ่ายที่สามารถ Optimize ได้อีก แต่ถ้าเราเอา Container พวกนี้ไปรันใน Kubernetes Cluster ก็น่าจะมีค่าใช้จ่ายมากกว่านี้เช่นกัน (ตัว Frontend ที่เป็น Next.js รันที่ 1 vCPU กับ RAM 512MB)

สรุป

Tutorials ใช้เวลาทำ Phase แรกแค่ประมาณเดือนเดียว (และหลังจากนั้นก็มีฟีเจอร์ต่าง ๆ เพิ่มมาอีก) นอกจากทีม Software Engineer ได้ลองเล่นกับ Tech ต่าง ๆ แล้ว เราก็นำผลของมันมาปรับใช้กับระบบบางส่วนด้วย

Cloud Run ตอนนี้เราใช้กับ Skooldio Platform ฝั่งหน้าบ้าน เพื่อ Offload การใช้งาน Resource จาก Kubernetes ที่ Service อื่นใช้ โดยที่ Cost เพิ่มไม่เยอะ และมีใช้งานกับ Product อื่น ๆ ของ Skooldio อีก

Strapi ก็มีใช้กับ Product หรือฟีเจอร์หนึ่ง ๆ เพื่อช่วยลดงานของทีม Backend ลง

ทั้งหมดนี้เป็นเพียงแค่หนึ่งใน Product หลาย ๆ ตัวที่ Skooldio ทำขึ้นมา และเราจะมาแนะนำ Product ของ Skooldio ตัวอื่น ๆ ใน Blog ต่อไปอีก รอติดตามกันนะครับ 🙂

Roparat Sukapirom
Head of Engineering | Skooldio

    Comments are closed.