A Brief Introduction to Continuous Integration (แนะนำ Continuous Integration โดยสังเขป)
Continuous integration (CI) มีมาเป็นเวลาหลายปีแล้ว อย่างไรก็ตาม ยังคุ้มค่าที่จะใช้เวลาทบทวนพื้นฐานของมันสักหน่อย เพราะมีตัวเลือกที่แตกต่างกันให้พิจารณา โดยเฉพาะเมื่อเราคิดถึงการจับคู่ระหว่าง microservices, builds และ version control repositories
เป้าหมายหลักของ CI คือการทำให้ทุกคนซิงค์กันอยู่เสมอ ซึ่งเราทำได้ด้วยการตรวจสอบบ่อยๆ ว่าโค้ดที่เพิ่ง check in เข้ามานั้น integrate เข้ากับโค้ดเดิมได้อย่างถูกต้อง วิธีการคือ CI server จะตรวจจับว่ามีการ commit โค้ดเข้ามา แล้วดึงโค้ดนั้นออกมา และทำการตรวจสอบบางอย่าง เช่น ตรวจว่าโค้ด compile ผ่านและ test ผ่านหรือไม่ อย่างน้อยที่สุดเราคาดหวังว่าการ integrate นี้จะเกิดขึ้นทุกวัน แม้ว่าในทางปฏิบัติผมเคยทำงานกับหลายทีมที่นักพัฒนา integrate การเปลี่ยนแปลงของตัวเองมากกว่าหนึ่งครั้งต่อวันด้วยซ้ำ
ในกระบวนการนี้ เรามักจะสร้าง artifacts ที่ใช้สำหรับตรวจสอบเพิ่มเติม เช่น การ deploy service ที่รันอยู่จริงเพื่อรันเทสต์กับมัน (เราจะเจาะลึกเรื่องการทดสอบใน Chapter 9 ) โดยหลักการแล้ว เราต้องการ build artifacts เหล่านี้เพียงครั้งเดียวเท่านั้น แล้วนำมันไปใช้กับการ deploy ทุกครั้งของโค้ดเวอร์ชันนั้น ทั้งนี้เพื่อหลีกเลี่ยงการทำสิ่งเดิมซ้ำแล้วซ้ำเล่า และเพื่อยืนยันได้ว่า artifact ที่เรา deploy คือตัวเดียวกับที่เราทดสอบไปแล้ว เพื่อให้ artifacts เหล่านี้ถูกนำกลับมาใช้ซ้ำได้ เราจะเก็บมันไว้ใน repository สักแห่ง ไม่ว่าจะเป็น repository ที่มากับตัว CI tool เองหรือเป็นระบบแยกต่างหาก
เราจะมาดูบทบาทของ artifacts กันอย่างละเอียดในไม่ช้า และจะเจาะลึกเรื่องการทดสอบใน Chapter 9
CI มีประโยชน์หลายอย่าง เราได้ feedback อย่างรวดเร็วเกี่ยวกับคุณภาพของโค้ด ผ่านการใช้ static analysis และการทดสอบ CI ยังช่วยให้เราสามารถ automate การสร้าง binary artifacts ได้ด้วย โค้ดทั้งหมดที่จำเป็นสำหรับการ build artifact นั้นถูกเก็บไว้ใน version control เช่นกัน ทำให้เราสามารถสร้าง artifact ขึ้นมาใหม่ได้หากจำเป็น เรายังสามารถ trace ย้อนกลับจาก artifact ที่ deploy ไปแล้วกลับไปยังโค้ดต้นทางได้ และขึ้นอยู่กับความสามารถของ CI tool นั้นๆ เรายังสามารถดูได้ว่ามีการรันเทสต์อะไรกับโค้ดและ artifact บ้าง หากเรานำแนวคิด infrastructure as code มาใช้ เราก็สามารถเก็บโค้ดทั้งหมดที่จำเป็นสำหรับตั้งค่า infrastructure ของ microservice ไว้ใน version control คู่ไปกับโค้ดของ microservice เองได้ด้วย ซึ่งช่วยเพิ่มความโปร่งใสในการเปลี่ยนแปลง และทำให้การสร้าง build ซ้ำง่ายขึ้นไปอีก ด้วยเหตุผลเหล่านี้เอง CI จึงประสบความสำเร็จอย่างมาก
Are You Really Doing CI? (คุณกำลังทำ CI จริงๆ อยู่หรือเปล่า)
CI เป็นแนวปฏิบัติสำคัญที่ช่วยให้เราเปลี่ยนแปลงโค้ดได้อย่างรวดเร็วและง่ายดาย และถ้าไม่มี CI แล้ว การเดินทางสู่ microservices จะเต็มไปด้วยความยากลำบาก ผมเดาว่าคุณคงใช้ CI tool อยู่ในองค์กรของคุณแล้ว แต่นั่นอาจไม่ใช่สิ่งเดียวกับการทำ CI จริงๆ ผมเคยเห็นหลายคนสับสนระหว่างการติดตั้ง CI tool กับการทำ CI อย่างแท้จริง CI tool ที่ใช้อย่างถูกวิธีจะช่วยให้คุณทำ CI ได้ก็จริง แต่การใช้เครื่องมืออย่าง Jenkins, CircleCI, Travis หรือตัวเลือกอื่นๆ อีกมากมายไม่ได้รับประกันว่าคุณกำลังทำ CI ถูกต้องจริงๆ
แล้วจะรู้ได้อย่างไรว่าคุณกำลังปฏิบัติ CI จริงๆ ผมชอบคำถามสามข้อของ Jez Humble ที่เขาใช้ทดสอบว่าคนอื่นเข้าใจแก่นแท้ของ CI จริงหรือไม่ ลองถามตัวเองด้วยคำถามเดียวกันนี้ดูก็น่าสนใจ:
คุณ check in เข้า mainline อย่างน้อยวันละครั้งหรือไม่
คุณต้องมั่นใจว่าโค้ดของคุณ integrate ได้จริง ถ้าคุณไม่ check in โค้ดของคุณรวมกับการเปลี่ยนแปลงของคนอื่นบ่อยๆ สุดท้ายแล้วการ integrate ในอนาคตจะยากขึ้นเรื่อยๆ แม้ว่าคุณจะใช้ short-lived branches ในการจัดการการเปลี่ยนแปลง ก็ควร integrate เข้า mainline branch เดียวให้บ่อยที่สุดเท่าที่จะทำได้ อย่างน้อยวันละครั้ง
คุณมีชุดเทสต์สำหรับตรวจสอบการเปลี่ยนแปลงของคุณหรือไม่
ถ้าไม่มีเทสต์ เราจะรู้แค่ว่าการ integrate ผ่านในเชิงไวยากรณ์เท่านั้น แต่เราไม่รู้เลยว่าเราทำให้พฤติกรรมของระบบพังไปหรือเปล่า CI ที่ไม่มีการตรวจสอบว่าโค้ดของเราทำงานตามที่คาดหวังนั้นไม่ใช่ CI ที่แท้จริง
เมื่อ build พัง การแก้ไขมันคือสิ่งที่ทีมให้ความสำคัญเป็นอันดับหนึ่งหรือไม่
build ที่เป็นสีเขียวหมายความว่าการเปลี่ยนแปลงของเรา integrate เข้าไปได้อย่างปลอดภัย ส่วน build ที่เป็นสีแดงหมายความว่าการเปลี่ยนแปลงล่าสุดอาจ integrate ไม่สำเร็จ คุณต้องหยุด check in ทั้งหมดที่ไม่เกี่ยวข้องกับการแก้ build จนกว่าจะทำให้มันกลับมาผ่านอีกครั้ง ถ้าคุณปล่อยให้การเปลี่ยนแปลงกองพะเนินขึ้นเรื่อยๆ เวลาที่ใช้ในการแก้ build จะเพิ่มขึ้นอย่างมาก ผมเคยทำงานกับทีมที่ build พังอยู่นานหลายวัน จนต้องใช้ความพยายามอย่างมากในที่สุดเพื่อทำให้ build กลับมาผ่านอีกครั้ง
Branching Models (รูปแบบการทำ Branch)
มีหัวข้อไม่กี่หัวข้อเกี่ยวกับ build และ deployment ที่สร้างความขัดแย้งได้มากเท่ากับเรื่องการใช้ source code branching สำหรับการพัฒนาฟีเจอร์ การทำ branch ใน source code ช่วยให้การพัฒนาเกิดขึ้นแยกต่างหากโดยไม่รบกวนงานของคนอื่น เมื่อมองผิวเผิน การสร้าง source code branch สำหรับแต่ละฟีเจอร์ที่กำลังทำอยู่ หรือที่เรียกว่า feature branching ดูเหมือนจะเป็น แนวคิดที่มีประโยชน์
ปัญหาคือเมื่อคุณทำงานบน feature branch คุณจะไม่ได้ integrate การเปลี่ยนแปลงของคุณกับคนอื่นอย่างสม่ำเสมอ โดยพื้นฐานแล้วคุณกำลัง เลื่อน การ integrate ออกไป และเมื่อคุณตัดสินใจ integrate การเปลี่ยนแปลงของคุณกับคนอื่นในที่สุด คุณจะต้องเจอกับการ merge ที่ซับซ้อนกว่าเดิมมาก
แนวทางอื่น คือให้ทุกคน check in เข้า "trunk" เดียวกันของ source code เพื่อไม่ให้การเปลี่ยนแปลงกระทบคนอื่น จะใช้เทคนิคอย่าง feature flags เพื่อ "ซ่อน" งานที่ยังทำไม่เสร็จเอาไว้ เทคนิคที่ทุกคนทำงานบน trunk เดียวกันนี้เรียกว่า trunk-based development
การถกเถียงในหัวข้อนี้มีความละเอียดอ่อนอยู่มาก แต่มุมมองของผมคือประโยชน์ของการ integrate บ่อยๆ และการตรวจสอบความถูกต้องของการ integrate นั้น มีนัยสำคัญมากพอที่จะทำให้ trunk-based development เป็นรูปแบบการพัฒนาที่ผมชื่นชอบ ยิ่งไปกว่านั้น การทำ feature flags มักจะมีประโยชน์ในแง่ของ progressive delivery ซึ่งเป็นแนวคิดที่เราจะสำรวจกันใน Chapter 8