บันทึกการย้าย TakeMeTour จาก AWS ไปยัง GCP และ Kubernetes

Panjamapong Sermsawatsri
TakeMeTour Engineering
3 min readSep 29, 2017

หลังจากปลุกปล้ำกับ Kubernetes (อ่านว่าคูเบอร์เนทีส) อยู่เป็นเดือน ก็ได้ฤกษ์ย้ายระบบ TakeMeTour ไปยัง Kubernetes บน GCP เรียบร้อยแล้ว โดยเบื้องหลังนั้นมีสิ่งที่น่าสนใจอยู่ จึงอยากจะมาแชร์กันครับ

Stack ของ TakeMeTour เป็นอย่างไร

เมื่อก่อน (อาทิตย์ที่แล้ว) ระบบของ TakeMeTour อยู่บน Docker Swarm บน AWS ซึ่งมีระบบต่าง ๆ รันอยู่ดังนี้

  • Frontend process สำหรับทำ server-side rendering และส่ง static files
  • API Services โดยแบ่งเป็น Micro-services
  • MongoDB เป็น Database หลัก
  • Redis สำหรับทำ caching, rate limiting
  • Memcached สำหรับทำ static html caching
  • MySQL สำหรับ Wordpress, Drone CI, และ Data Analytics
  • ElasticSearch สำหรับการ search ต่าง ๆ
  • On-the-fly image resizer
  • Nginx สำหรับการ route request ตามแต่ละ hostname

ทั้งหมดนี้มี environment production และ staging อย่างละชุด

หน้าตา Stack ปัจจุบัน

ทำไมต้องย้ายไป Kubernetes

จริง ๆ ได้ลอง Container Orchestration Tool มาหลายตัว ตั้งแต่ Rancher ซึ่งในขณะนั้นก็ไม่ค่อย work เลยย้ายมา Docker Swarm เพราะใช้งานง่ายมาก แต่แล้วก็เจอปัญหาอยู่ดี เป็นปัญหาที่บางที container ก็ตายไปเองส่งผลให้ระบบล่มไปช่วงเวลาหนึ่ง ซึ่งตอนนี้ก็ยังหาสาเหตุของปัญหาไม่ได้ซักดี

ก่อนหน้านี้ก็เคย research ตัว Kubernetes มาก่อนแล้ว แต่พบว่าติดตั้งบน AWS ค่อนข้างลำบาก แถมต้องเสีย instance ไป 1 ตัวในการเป็น master node จึงไปเลือก Docker Swarm ที่ใช้งานง่ายกว่าแทน

อีกประเด็นนึงคือ Declarative syntax อันนี้สำคัญมาก ใครที่เล่น React ก็จะชื่นชอบเป็นพิเศษ เหมือนกับใน React ที่เป็น

fn(state) -> Component

ส่วนของ Kubernetes ผมมองว่าเป็น

k8s(DesiredState, CurrentState) -> Actions

เรามีหน้าที่แค่กำหนด Desired State หรือปลายทางที่เราต้องการให้ระบบเป็น แล้ว Kubernetes จะทำยังไงก็ได้ ให้ไปถึงปลายทางนั้นให้ได้ เช่น เขียนบอกไว้ว่าต้องการเอา MongoDB ไปวางที่ Instance ไหนก็ได้ที่มี Memory เยอะหน่อย แต่ว่าต้องมี replica set 3 ชุดนะ แต่ละชุดต้องอยู่แยกเครื่องกันด้วย ประมาณนี้ ตัว Kubernetes ก็จะจัดแจงให้เสร็จสรรพ

ทำไมต้องเป็น Google Cloud Platform

เพราะว่า Kubernetes ทำงานบน GCP ได้อย่างเนียนมาก ๆ อย่างแรกคือไม่ต้องเปิด เครื่อง master node ทาง GCP จะจัดการให้หมดเลย (ฟรีด้วย)

ถัดมาคือการ integrate ระหว่าง service ของ GCP กับ Kubernetes ทำได้อย่างดี สามารถสร้าง Load Balancer, Disk Storage, Secret ได้ผ่าน config ของ Kubernetes เลย ตัว Kubernetes ก็จะไปยิง API เพื่อสร้าง Resource เหล่านั้นให้เราเอง

อีกเรื่องหนึ่งที่อยากจะเน้นเลยก็คือ Logging System โดยตัว Stackdriver เนี่ย ดีมาก ถึงมากที่สุด เพราะมันรวบรวม Log จากทุก Container เข้ามาไว้ที่เดียว เหมาะกับการ Debug มากมาย แถม Search แบบ full-text และเลือกช่วงเวลาได้อีกต่างหาก

ขั้นตอนการย้ายระบบ

(เนื้อหาด้านล่าง จะเหมาะสำหรับผู้ที่มี Kubernetes มาบ้างแล้วนะครับผม)

Infra-as-a-Code

เนื่องด้วย Kubernetes เป็น declarative syntax เราก็จัดแจงสร้าง Repository ขึ้นมาเลย บางคนคงด้านในก็น่าจะเป็นไฟล์ yaml น่ะสิ

เกือบถูกครับ แต่เนื่องจากเรามีหลาย service และมี production, staging environment เราจึงต้องเขียน “ตัวสร้าง” yaml ขึ้นมาอีกทีหนึ่ง ซึ่งผมเลือกใช้ template engine ที่ชื่อว่า Handlebars

และจะมีไฟล์ config.js ไว้สำหรับปรับค่าต่าง ๆ ไม่ว่าจะเพิ่ม service หรือว่าเพิ่ม subdomain ก็จะสามารถทำได้ผ่านไฟล์นี้ไฟล์เดียว

วิธีใช้ก็แค่ build ตัว template ของเราด้วย config ที่เราตั้งไว้ จะออกมาเป็นไฟล์ yaml พร้อมให้เราสามาถนำไปขึ้นบน cluster ของเราได้แล้ว

การทำแบบนี้จะช่วยลดปัญหาที่เราต้องมานั่ง maintain ไฟล์ yaml ที่ซ้ำซ้อน และเสี่ยงต่อการผิดพลาดครับ

ปัญหาที่พบ

ควรแยกไฟล์ Service ออกจาก Deployment
ไม่รู้คนอื่นเป็นเหมือนผมหรือเปล่า คือชอบใส่ของที่ทำหน้าที่เดียวกับไว้ในไฟล์เดียวกัน แล้วคั่นด้วย --- แทน เพราะมันจะสะอาดและเป็นระเบียบดี

ข้อเสียคือเวลาเราอยากจะลบบาง pod ชั่วคราว เราก็มักจะใช้ kubectl delete -f user-system.yml ทำให้ทั้ง Deployment และ Service ถูกลบไป แต่เวลามันสร้างใหม่ มันก็จะได้ IP ใหม่ เพราะถือว่าเป็น Service ใหม่ ซึ่งถ้าเรามีระบบไหนที่ผูกไว้กับ Service เดิม เช่น nginx ก็จะพังทันที ต้องไป restart nginx ด้วย ดังนั้น Service ควรถูกแยกออกมา และถ้าไม่จำเป็นก็ไม่ต้องไปลบมัน หรือไม่ก็ลบเฉพาะ deployment แทน ไม่ต้องลบด้วย -f

ใช้เวลา Migrate นานกว่าที่คาด
ประเมินเวลา Migrate ไว้ที่ 30 นาที ถึง 1 ชั่วโมง และกำหนดเวลา Migrate ตอนตีหนึ่งแต่พอทำจริงแล้ว กว่าจะได้นอนก็ตีสี่นู่น แต่ปัญหาก็ยังไม่จบแค่นั้น…

กำหนด Memory รวมของ Cluster ให้น้อยเกินไป
หลังจาก Migrate แล้วเรายังพบการ Down ของ Server เป็นระยะ ๆ จึงต้องไปดูว่าเกิดจากอะไร พบว่าเกิดจาก Memory Usage ของระบบจะมีช่วง peak ไปจนทำให้เกินใน Node นั้น จึงต้องแก้ไขผ่านการตั้งค่า request memory ให้พอดี และเพิ่ม instance ไปด้วย เผื่อไว้ให้โล่ง ๆ หน่อย พอขยับตัวได้

ยังไม่เป็น Zero-downtime ที่แท้จริง
เนื่องจากระบบที่ TakeMeTour นั้นใช้ cycle การ deploy ที่ถี่มาก ๆ ประมาณวันละ 5–10 ครั้ง ทำให้เวลาที่ service จะต้องทำการ update เพื่อเปลี่ยน image นั้นจะมีช่วง downtime สั้น ๆ อยู่ ไปค้นหาดูพบ blog ของ RisingStack เลยนำไปแชร์ในกรุ๊ป Kubernetes User Group เพราะว่าเค้าใช้ท่าน่าสนใจดี

หลังจากที่นำไปโพส น้อง Manatsawin Hanmongkolchai ก็แชร์ Blog ของวงในที่ใช้ท่า preStop ซึ่งพอไปลองทำตามแล้วพบว่าได้ผลดีทีเดียว ต้องกราบขอบคุณมา ณ ที่นี้ด้วย

สำหรับตอนนี้ ผลเป็นที่น่าพอใจมาก สิ่งที่ต้องทำต่อไปก็คือ HA (Highly available) แนวทางก็คือแบบที่ Jitta ทำเลยก็คือยิงเครื่องให้ดับเล่น ๆ โดยที่ยังไม่ทำให้ระบบล่มนั่นเอง ตอนนี้ก็ค่อนข้างใกล้ความจริงแล้ว เหลือ Wordpress นี่แหละที่ยาก เพราะยังไม่เคยทำ Network FS มาก่อนเลย

สำหรับใครที่กำลังเลือก Container Orchestration Tool อยู่ ก็อยากจะขอแนะนำ Kubernetes นะครับ และถ้าจะให้ดี ก็ใช้คู่กับ GCP จะทำงานด้วยกันได้เนียนกริ๊บเลย ใครที่สนใจ Kubernetes หรือ Google Cloud Platform ก็สามารถเข้ามา Join ร่วมพูดคุยถามตอบกันได้ทั้งกรุ๊ป GDG Cloud Bangkok และ Kubernetes User Group Thailand นะครับผม

--

--