Moving to Golang and Serverless

Pongsakorn Teeraparpwong
Hato Hub
Published in
3 min readJul 10, 2019
image credit: blog.travelex.io

ในช่วง Q1-Q2 2019 ที่ผ่านมาเกิดการเปลี่ยนแปลงมากมายใน Indy Dish ทั้ง rebrand และเปลี่ยนรูปแบบอาหารใหม่ ทางด้าน Tech ก็เปลี่ยนเช่นกัน จากเดิมที่ใช้ React Native App และ Ruby on Rails ที่เขียนไว้ในบทความที่แล้ว เราได้ขึ้นระบบใหม่โดยใช้ Line Frontend Framework (LIFF) / Line Chatbot เป็น frontend ในการสั่งอาหาร online และมี Point-of-sale iPad app สำหรับให้พนักงานใช้ในร้าน ส่วน backend ก็เปลี่ยนมาใช้ Serverless AWS Lambda กับ Golang แทน

จริงๆ แล้วระบบเดิมก็ยัง run อยู่ แต่จะใช้สำหรับ Basil ซึ่งเป็น Brand อาหารคลีนของเรา ส่วน Brand Indy Dish ใหม่จะอยู่บนระบบใหม่ทั้งหมด ในบทความนี้จะ focus ไปที่ backend อันใหม่เป็นหลักโดยจะเล่าถึงที่มาและสาเหตุที่เปลี่ยน tech stack รวมทั้งสิ่งที่เราได้เรียนรู้ในเบื้องต้น

สำหรับคนที่เริ่มอยากใช้ Serverless ขอฝาก Workshop ที่เราสอนให้กับ Skooldio ชื่อ Building Serverless Applications With AWS Lambda จัดในวันเสาร์ที่ 30 พฤศจิกายน 2562 ซึ่งเป็นเป็นรุ่นที่ 2 แล้ว รับรองว่าได้ลงมือทำจริงและกลับไปใช้ต่อได้แน่นอน กดสมัครเลย! 😄

Why changing backend stack?

เหตุผลแรกเลยคือ business และ product ที่เปลี่ยนไป เดิมเราเป็น app รวมร้านอาหารคลีนที่สั่งล่วงหน้าเป็นรายสัปดาห์และมีเมนูเปลี่ยนไปทุกๆวัน ต่อมาเราอยากสร้าง Chatbot สำหรับ B2B ที่ให้พนักงานบริษัทสั่งได้ง่ายๆโดยการกดปุ่มเดียว สำหรับอาหาร Indy Dish แบบใหม่ที่เป็น B2C ก็เป็นแบบ on-demand กดสั่งแล้วได้เลย รวมถึงสามารถให้ customize เปลี่ยน base, protein, topping, sauce และอื่นๆได้ และยังเพิ่ม personalization เข้าไปด้วย การที่จะทำให้ระบบเดิมรองรับอาหารและวิธีสั่งทั้งแบบใหม่ด้วยค่อนข้างยากเพราะไม่ได้ design มาแบบนั้นแต่ต้น

เหตุผลในทางด้าน technical คือระบบ Ruby on Rails เดิมนั้น maintain ได้ยากมาก การแก้ไขโค้ดที่มีอยู่มีโอกาศเพิ่ม bug มากมายโดยไม่รู้ตัวและยังชอบมีปัญหาในด้าน memory ด้วย

ด้วยเหตุผลทั้งหมดนั้นเลยตัดสินใจขึ้น stack ใหม่แยกออกมาจากอันเดิม เริ่มกันจาก 0 เลยทีเดียว แต่ก็นำเอาสิ่งที่เรียนรู้จากระบบเดิมมา apply ใช้ได้เยอะมาก

Why Serverless and AWS Lambda?

Ruby on Rails เดิมนั้น run อยู่บน AWS Beanstalk ซึ่งช่วยทำให้การ deploy และ manage server ง่ายขึ้นมากพอสมควร แต่ยังไงก็ยังต้องมาปวดหัวกับการ scale เครื่องอยู่ดี โดยเฉพาะสำหรับ Indy Dish ที่ไม่ได้มี traffic เข้ามาเยอะมากและสม่ำเสมอขนาดนั้นทำให้มักจะเสียค่า resource ไปโดยเปล่าประโยชน์ ทั้งใน staging และ production

Serverless เหมาะสำหรับมาแก้ปัญหานี้มาก โดยความสุดยอดของ Serverless คือ

  • ไม่ต้อง manage server อีกต่อไป
  • High availability out of the box ไม่ต้องมาสร้าง redundancy เองและ Auto scale ตาม traffic ที่เข้ามาจริงๆ จะเพิ่ม CPU/Memory ก็ทำได้ง่ายๆ
  • ค่าใช้จ่ายลดลงมากเพราะคิดตามที่ใช้จริงไม่ต้องเสียเงินไปกับ resource ที่ไม่ถูก utilize โดยเปล่าประโยชน์ นอกจากนี้ยังมี free tier ต่อเดือนที่เยอะมาก (eg. 1M requests)
  • การแบ่ง service เป็น micro service ย่อยๆ ทำได้ง่ายกว่ามาก
  • การ manage resource และ Lambda function ทั้งหมดใช้ config file (aka infrastructure as code) ทำให้ลดข้อผิดพลาดและสามารถ version control ได้

สุดท้ายที่ใช้ AWS เพราะ tech stack ทั้งหมดเราอยู่บน AWS อยู่แล้ว และ AWS Lambda นั้น mature และ reliable ที่สุดเมื่อเทียบกับ cloud provider อื่นๆ

Why Golang?

สิ่งนึงที่เราให้ความสำคัญมากในการเขียน backend คือต้องใช้ภาษาที่ strongly-typed ก็เลยมี option เหลือไม่เยอะ ก็เลือกระหว่าง Java, Node.js + Typescript หรือ Golang จริงๆประสบการณ์ตัวเองเขียน Java มาตลอด แต่คราวนี้อยากลองอะไรใหม่ๆบ้าง เคยลอง Node.js + Typescript แล้วเจอปัญหามากมายและทุกอย่างเปลี่ยนแปลงตลอดเวลา คือมันก็ไม่ reliable หนะแหละ เลยมาลงเอยที่ Golang ซึ่งถึงแม้จะหา dev ยากกว่าแต่คาดว่าตอนนี้หลายๆคนคงอยากเรียนรู้ที่จะเขียน Golang เพิ่มขึ้น มีหลายๆส่วนที่ชอบมากๆใน Golang และมันก็เหมาะกับ serverless มากๆด้วย

  • Static binary & dependencies ทุกอย่างถูก compile เป็น file binary เดียวแล้วโยนขึ้นไป run บน Lambda function เลย แม้แต่ตัว runtime ก็อยู่ในนั้นด้วย ทำให้สามารถเปลี่ยน Go version ได้โดยไม่ต้องไปแก้ตัว Lambda เลย
  • Stability — Go version 1 ออกมา 7 ปีแล้ว (from 2012) และคงจะอยู่ต่อไปอีกหลายปี ทำให้ไม่ต้องมานั่ง update code ใหม่ตลอดเวลา
  • มันเร็วมากกก โดยเฉพาะถ้าเทียบกับ Rails และมี lightweight concurrency ที่ดีมาก ถึงแม้จะไม่ได้ใช้มันเยอะ
  • ตามจุดประสงค์ของภาษา: Go makes it easy to build simple, reliable, and efficient software. ภาษา Go เน้น integrity และความเรียบง่าย

Architecture

สำหรับ Backend นั้นจะใช้ GraphQL เป็นหลักซึ่ง run อยู่บน Gin framework อีกที ส่วน database ก็ยังใช้ PostgresQL เหมือนเดิม สิ่งที่ต้อง design ดีๆคือการแบ่ง service เพื่อให้เหมาะกับ Serverless architecture

จริงๆ Lambda function ที่ดีควรจะเป็น function เล็กๆและ self-contained ในตัวมันเอง แต่เราก็เอามันมา deploy API service เลยก็ได้เหมือนกัน ตราบใดที่มันไม่ใหญ่เกินไปและยัง execute ได้เร็วอยู่ ปัจจุบัน Indy Dish ไม่ได้มีระบบที่ใหญ่ขนาดที่ต้องแบ่งเป็น microservice เล็กๆมากๆ ซึ่งซับซ้อนเกินความจำเป็น ปัจจุบันเราจึงแบ่ง Lambda function ตาม client แต่ละประเภทเช่น Line chatbot, Line LIFF, Admin Website หรือ POS App

Code ทั้งหมดอยู่ใน repo เดียวกันและต่อมาที่ database ตัวเดียวกัน ซึ่งจริงๆก็ยังมีความเป็น Monilith อยู่ แต่ข้อดีของ Go คือสามารถสร้าง app มาหลายๆ app และ build binary แยกกันเพื่อเอาไป deploy ไปแต่ละ Lambda function ได้ โดยแต่ละ App ก็จะมีแค่ code และ dependency ของตัวเองที่ถูก build ไปด้วยกัน ทีนี้พอแยก Lambda function กันแล้วการ monitor และ manage ก็จะไม่ปนกันมั่ว

สุดท้ายคือใช้ Serverless Application Model (SAM) ของ AWS ซึ่งเป็น Infrastructure as code (or config) เพื่อช่วยในการ manage และ deploy Lambda functions ซึ่งจริงๆ SAM ก็เป็น extension ของ AWS CloudFormation นั่นเอง โดยระบบจะ provision resources ที่อยู่ใน config เช่น Lambda function และ API Gateway ให้เรา ตามหลักของ Lambda แล้ว เวลารับ request จากภายนอกก็ต้องมี API Gateway เป็นตัวคั่นก่อน ดังในรูป

Architecture of Indy Dish using Golang and Serverless AWS Lambda

ระบบของจริงจะมีความซับซ้อนกว่านี้และมีบาง function ที่รับ event มาจาก source อื่นเช่น S3 ซึ่งทำให้ระบบโดยรวมเป็น event-driven มากกว่าระบบแบบเดิม

How did it go so far?

ตอนนี้มีแค่ส่วน Line LIFF ที่ขึ้นไปบน production แล้ว ส่วนอื่นๆกำลังจะตามไป สิ่งที่เห็นได้ชัดเลยคือ

Pros

  • Cost — ที่ใช้มาตอนนี้ยังไม่เกิน Free Tier เลย พูดง่ายๆคือยังฟรีอยู่ ไม่ต้องเสียค่า EC2 และ Load balancer อีกต่อไป ยังไงคาดว่าคงจะไม่เกิน Free Tier ไปอีกนานแต่ต้องเสียค่า API Gateway ซึ่งเป็นเศษตังนิดหน่อย
  • Operational overhead ลดลงอย่างมาก การไม่ต้องมานั่ง manage/scale เครื่องเองนี่ทำให้ช่วยประหยัดเวลาและเอา focus ไปที่การเขียนแอพแทน

Challenges

  • Cold start — ปกติถ้าไม่มี request เข้ามานานๆ หรือมี traffic เข้ามาเยอะๆพร้อมๆกันตัว Lambda จะต้องไป start function ใหม่แต่ต้น ซึ่งใช้เวลาพอสมควร โดยเฉพาะระบบเราที่ต้องต่อ database connection ใหม่ (1–2s สำหรับ Golang ที่ใช้อยู่) แต่มีวิธีแก้คือสร้าง cron function เพื่อไป invoke function หลักอยู่เรื่อยๆทุกๆ 5–10 นาทีเพื่อคอยให้มัน warm ตลอดเวลาตามจำนวนที่เราต้องการ
  • SAM ในช่วงแรกยังขาด feature และตัวอย่างการใช้งานพอสมควร ทำให้การ setup ในตอนแรกติดๆขัดๆหลายส่วน แต่พอทำครั้งแรกเสร็จแล้วก็ง่ายละ จริงๆ framework อื่นอาจจะใช้ง่ายกว่า แต่เพื่อจะให้ support CloudFormation และสามารถเชื่อมกับ AWS service อื่นๆ สะดวกๆ แล้วคิดว่า SAM ยังเป็นตัวเลือกที่ดีที่สุดอยู่

Conclusion

ส่วนตัวชอบ combination ของ Golang + Serverless มาก ทำให้การเขียน backend มีความสุขขึ้นมากกว่าเดิมเยอะ ถ้าใครยังลังเลว่าจะใช้ serverless ดีมั้ย อยากเชียร์ให้มาลองใช้เลย เอามาทำเป็น web app หรือ API ก็ได้ สามารถ save เงินและเวลา dev ไปได้มากมายแน่นอน เมื่อ infra เรา stable เราก็สามารถเอาพลังไปทุ่มที่การพัฒนา product แทนสุดท้ายประโยชน์ก็ตกกับ customer เรานั่นเอง

ถ้าไม่รู้จะเริ่มยังไงก็มาสมัคร workshop ของเรากับ Skooldio ได้ ซึ่งจะสอนการ deploy และทำ CICD ด้วย คิดว่าเป็นส่วนที่สำคัญมากที่อาจจะเขียนเป็นบทความถัดไป

--

--

Pongsakorn Teeraparpwong
Hato Hub

CTO & Co-founder @ Indie Dish • AWS Certified • TEDx Volunteer || Ex-SDE @ Amazon.com • Chula Intania 87 • BCC 150