Screen Shot 2557-05-30 at 12.16.46 AM

GopherCon  2014 เป็นงานสัมมนาเกี่ยวกับการพัฒนาด้วยภาษา Go โดยเฉพาะ
ซึ่งมีหัวข้อต่างๆ ที่น่าสนใจมาก
โดยหนึ่งในนั้นก็คือเรื่อง Best practice for Production Environment
เป็นการแบ่งประสบการณ์สำหรับการพัฒนาระบบด้วยภาษา Go
ดังนั้นมาดูกันว่าในการพูดครั้งนี้ ผู้พูดได้สรุปอะไรไว้ให้เราบ้าง
เพื่อให้เรานำไปประยุกต์ใช้ต่อไป

สามารถ Download slide ได้จากที่นี่

ผู้พูดนั้นทำงานอยู่ที่ Songcloud โดยที่บริษัทนี้ได้ทำ APIs ให้ผู้ใช้งานชนิดต่างๆ ใช้งาน
ทั้งจาก Web, Mobile ซึ่ง APIs เหล่านี้ถูกสร้างตามแนวคิด Service Oriented Architecture
และในปัจจุบัน APIs ถูกพัฒนาด้วยหลายหลายภาษา
และหนึ่งในนั้นก็คือภาษา Go

เมื่อนำ Go มาพัฒนาแล้ว จึงได้นำประสบการณ์ต่างๆ ที่พบเจอ
ตั้งแต่การพัฒนาไปจนถึงการติดตั้งขึ้น production server มาเล่าให้ฟัง

โดยเริ่มจาก Development environment

เครื่องพัฒนาทุกๆ เรื่องจะต้องมี GOPATH เดียวกัน หรือเหมือนกันนั่นเอง
ดังนั้นในการ clone sourcecode มาจาก repository จะทำได้ง่ายมาก
ตัวอย่างเช่น

Screen Shot 2557-05-30 at 12.25.48 AM

ส่วนในเรื่อง Editor ที่ใช้งานกันนั้นมีหลายหลาย เช่น Vim, sublimeและ Emacs
แต่ไม่มีใครใช้ IDE เลย

ประเด็นหลักของเรื่องนี้ คือ ทุกๆ คนภายในทีมพัฒนาต้องมี environment เหมือนกัน
เพื่อลดแนวปฏิบัติที่หลายหลาย

ต่อมาเรื่องโครงสร้างของ Repository หรือโครงสร้างsoucecode เป็นอย่างไร

เป็นโครงสร้างที่เรียบง่ายตามรูป

Screen Shot 2557-05-29 at 10.46.11 PM

แต่ถ้าต้องการให้มี package main หลายๆ ตัว หรือต้องการสร้าง binary มากกว่า 1 ตัว
จะทำการแยกออกเป็น package ดังรูป

Screen Shot 2557-05-29 at 10.50.11 PM

เรื่องของการจัดรูปแบบของ sourcecode

แน่นอนว่าจะต้องใช้ gofmt ใน Editor ของทุกๆ คน ใช้คา default ทั้งหมด
หรือบางคนอาจจะใช้ Goimports
ซึ่งจะถูกทำงานทุกๆ ครั้งที่ทำการบันทึกการเปลี่ยนแปลงของ sourcecode
นั่นหมายความว่า ถ้ารูปแบบของ code ไม่เป็นไปตามที่กำหนดหรือตกลงกัน
ก็จะไม่สามารถทำการ commit เข้ามายัง repository ได้นะ

แต่ถ้าใครอยากได้รูปแบบตามที่ Google ประกาศออกมาใน Code Review Comment
ก็สามารถนำไปใช้งานได้นะ

การ Configuration

ทางทีมพัฒนาพยายามที่จะส่ง configuration file ไปยังระบบที่พัฒนาด้วย Go
ทำให้ต้องส่วนการทำงานต่างๆ ขึ้นมา เช่น การอ่าน การ parse
แยกให้ได้ว่า ตอนนี้ทำงานอยู่บน environment อะไร จาก os.Getenv
แล้วทำการกำหนดค่าลงใน flag ซึ่งอยู่ใน package flag ของ Go

แต่เมื่อทำไปได้สักระยะหนึ่งพบว่า เราทำแบบนั้นไปทำไม เสียเวลาเปล่าๆ
กลับมาสู่วิธีที่เรียบง่ายที่สุดเลย คือการกำหนดค่าต่างๆ ลงไปใน flag เลยสิ
ตัวอย่างเช่น

Screen Shot 2557-05-29 at 11.02.24 PM

การทำ Logging และตัวชี้วัดต่างๆ

ทางทีมพัฒนาได้ลองใช้ logging framework ต่างๆ มากมาย
สำหรับการเก็บ logging ตาม level ต่างๆ และด้วยรูปแบบต่างๆ
และพบว่าเราเพียงใช้งานจาก package log ก็เพียงพอแล้ว
เพราะว่าต้องการเก็บเพียงข้อมูลการใช้งานต่างๆ เท่านั้นเอง

แต่ทุกอย่างจะถูกจัดเก็บไว้สักที่หนึ่ง ดังนั้นจึงต้องทำการพิจารณากันหน่อยว่าจะเลือกอะไร
โดยมีตัวชี้วัด คือ เวลาของ request และ response ในการทำงาน
QPS, runtime error, ขนาดของ queue เป็นต้น
ของที่เราจะเลือกมาใช้นั้นมีการทำงาน 2 รูปแบบ คือ

  1. Push คือการส่งข้อมูลต่างๆ ไปยังระบบที่เรากำหนดหรือรู้อยู่แล้ว เช่น Statd, Graphite และ AirBrake
  2. Pull คือการส่งข้อมูลต่างๆ ไปยังที่ที่หนึ่งที่เรารู้จัก และอนุญาติให้ระบบอื่นๆ สามารถนำไปใช้งานได้ เช่น expvar และ  Prometheus

ถ้าถามว่าทั้งสองรูปแบบ จะเลือกแบบไหน สามารถอธิบายได้ดังนี้

ในการเริ่มต้นใหม่ๆ แนะนำให้ใช้งานแบบ Push ก่อน
แต่ถ้าเริ่มมีจำนวนข้อมูลที่สูงขึ้นมากๆ  แล้วทำให้มีการใช้ทรัพยากรสูงขึ้นมากๆ
เช่นใช้งาน CPU, bandwidth  เป็นต้น
ดังนั้นจึงต้องเพิ่ม CPU เพื่อเครื่อง server ขยาย bandwidth ขึ้น

ถ้าเกิดเหตุการณ์ในลักษณะดังกล่าว แนะนำให้ลองเปลี่ยนมาใช้งานในกลุ่มของ Pull
ตัวที่แนะนำก็คือ expvar

การทดสอบ

ในการทดสอบทางทีมพัฒนาลองใช้ test framework ต่างๆ มากมาย
แต่ในทุกวันนี้ใช้จาก  package testing ร่วมกับการทดสอบแบบ data driven หรือ table driven
โดยใช้งานร่วมกับ reflect.DeepEqual
สำหรับการเปรียบเทียบข้อมูลที่คาดหวัง กับ ข้อมูลจริงที่ได้รับ
ทำให้การทดสอบมันเรียบง่ายมากๆ

แต่ package testing นั้นจะอยู่ในเฉพาะ unit test เท่านั้น
ถ้าต้องการทำ integration test ด้วยล่ะ จะต้องทำอย่างไร ?
มีแนวทางแนะนำคือ สร้าง file integration_test.go ขึ้นมา
แล้วกำหนด tag เป็น integration ผ่าน flag เพื่อใช้ในการทดสอบ
ตัวอย่างเช่น

Screen Shot 2557-05-29 at 11.41.11 PM

ส่วนเรื่องการ validate ต่างๆ นั้นใน Go ได้เตรียมเครื่องมือไว้ให้ใช้งานครบ เช่น

ดังนั้นสิ่งที่เกิดขึ้นในการทำงานเป็นดังนี้

  1. เมื่อทำการบันทึกไฟล์ จะเรียกใช้งาน go fmt หรือ goimports สำหรับจัดรูปแบบของ sourcecode
  2. เมื่อต้องการ build ระบบ จะเรียกใช้งาน go vet, golint และ go test
  3. เมื่อต้องการ deploy ระบบ จะเรียกใช้งาน go test -tags=integration

มาดูการจัดการพวก Dependency Library กันบ้าง

สิ่งที่แนะนำก็คือ ให้คิดก่อนว่าสิ่งที่ทำอยู่นั้นมันมีความสำคัญหรือไม่
ถ้าไม่ค่อยมีความสำคัญอะไร หรือไม่สนใจอะไรมากนัก  แนะนำให้ใช้ผ่านคำสั่ง go get นะ
และหวังว่าจะไม่มีอะไรผิดพลาดขึ้นมา !!

แต่ถ้าสิ่งที่ทำต้องการความถูกต้องหรือไม่ให้มีความผิดพลาดอะไรมากนัก
แนะนำให้ใช้การ copy library ที่เราต้องการมาในระบบเองเลย
โดย sourcecode จะอยู่ใน folder vendor
และเมื่อจะนำไปใช้งานจริงๆ แนะนำให้ทำการ build เป็น binary แล้วเก็บไว้ใน fodler _vendor
มี script การ build ดังนี้

Screen Shot 2557-05-29 at 11.53.47 PM

วิธีการนี้จะเรียบง่ายมากๆ แต่มักมีการพูดถึงวิธีการจัดการอื่นๆ กันมากมาย เช่น
การใช้ git submodule แต่ทางทีมพัฒนามองว่ามันยากต่อการจัดการ
ในปัจจุบันมีการพูดถึงและใช้งาน godep มากขึ้น
และแน่นอน ทีมงานก็กำลังศึกษาการใช้งานอยู่เช่นกัน

สุดท้ายคือการ Build และ Deploy ระบบงาน

การ build และ deploy นั้นส่วนใหญ่จะผูกติดไปกับ environment ของแต่ละที่อยู่แล้ว
ดังนั้นวิธีการที่ทาง Songcloud ทำนั้นอาจจะดีเฉพาะที่นี่ แต่สำหรับที่อื่นๆ อาจจะไม่ดีก็ได้
ดังนั้น ก่อนลอกไปใช้งาน กรุณาคิดก่อนนะครับ

ในขั้นตอนการ build จะใช้คำสั่ง go build  สำหรับการพัฒนา
แต่เมื่อต้องการ build เพื่อนำไปติดตั้ง จะใช้ Makefile
โดยใน Makefile จะมีหน้าตาประมาณนี้

Screen Shot 2557-05-30 at 12.03.19 AM

สำหรับการ Deploy นั้นจะมีอยู่ 2 กลุ่ม service คือ Stateless และ Stateful
ตัวอย่าง
Stateful เช่นการติดตั้ง Redis, MySQL ซึ่งเป็นส่วนที่ต้อง deploy ก่อนเสมอ

Stateless เช่นการขยายการรองรับของระบบ ด้วยการเพิ่ม router  จะมีรูปแบบของการ deploy
ซึ่งที่ SongCloud นั้น deploy ตามรูปแบบ 12-factor เป็นสิ่งที่น่าสนใจมาก แนะนำให้ลองศึกษาดูครับ

การ deploy แบบ Stateless นั้นจะเหมือนการใช้งาน Heroku มาก
ตัวอย่างเช่น

Screen Shot 2557-05-30 at 12.14.01 AM

โดยสรุปแล้ว จะเห็นได้ว่า

Go นั้นได้เตรียมของที่จำเป็นต่อการพัฒนา ทดสอบ และติดตั้งระบบงาน มาอย่างครบครัน
ดังนั้น การใช้งาน Go ให้เรียบง่ายที่สุด นั้นคือแนวทางปฏิบัติที่ดีที่สุด …