Build Pipelines and Continuous Delivery (Build Pipeline และ Continuous Delivery)

ตั้งแต่ช่วงแรกๆ ของการทำ CI เพื่อนร่วมงานของผมที่ Thoughtworks ในตอนนั้นและตัวผมเองก็ตระหนักถึงคุณค่าของการมีหลาย stage ภายใน build บางครั้ง เทสต์เป็นกรณีที่พบเห็นได้บ่อยที่สุดในเรื่องนี้ ผมอาจมีเทสต์ที่รันเร็วและขอบเขตแคบจำนวนมาก และมีเทสต์ที่รันช้าและขอบเขตกว้างจำนวนน้อย ถ้าเรารันเทสต์ทั้งหมดพร้อมกัน และต้องรอให้เทสต์ขอบเขตกว้างที่ช้าทำงานเสร็จ เราอาจไม่ได้รับ feedback อย่างรวดเร็วเมื่อเทสต์ที่เร็วล้มเหลว และถ้าเทสต์ที่เร็วล้มเหลว ก็คงไม่มีประโยชน์อะไรที่จะรันเทสต์ที่ช้ากว่าต่อไปอยู่แล้ว! ทางแก้ปัญหานี้คือการมีหลาย stage ใน build ของเรา ซึ่งกลายเป็นสิ่งที่เรียกว่า build pipeline ดังนั้นเราจึงสามารถมี stage เฉพาะสำหรับเทสต์ที่เร็วทั้งหมด ซึ่งเรารันก่อน และถ้าผ่านทั้งหมด เราก็จะรัน stage แยกต่างหากสำหรับเทสต์ที่ช้ากว่าต่อไป

แนวคิด build pipeline นี้ให้วิธีที่ดีในการติดตามความคืบหน้าของซอฟต์แวร์ของเราขณะที่มันผ่านแต่ละ stage ไป ช่วยให้เรามองเห็น insight เกี่ยวกับคุณภาพของซอฟต์แวร์ เราสร้าง deployable artifact ซึ่งเป็นสิ่งที่จะถูก deploy ไปยัง production ในที่สุด และใช้ artifact นี้ตลอดทั้ง pipeline ในบริบทของเรา artifact นี้จะเกี่ยวข้องกับ microservice ที่เราต้องการ deploy ใน Figure 7-1 เราจะเห็นสิ่งนี้เกิดขึ้น artifact ตัวเดียวกันถูกใช้ในทุก stage ของ pipeline ซึ่งช่วยเพิ่มความมั่นใจมากขึ้นเรื่อยๆ ว่าซอฟต์แวร์จะทำงานได้ใน production

A simple release process for our Catalog service modeled as a build pipeline

Figure 7-1. กระบวนการ release อย่างง่ายสำหรับ Catalog service ของเรา จำลองในรูปแบบ build pipeline

Continuous delivery (CD) ต่อยอด จากแนวคิดนี้ และไปไกลกว่านั้นอีก ตามที่ระบุไว้ในหนังสือของ Jez Humble และ Dave Farley ที่ใช้ชื่อเดียวกัน 3 CD เป็นแนวทางที่เราได้รับ feedback อย่างต่อเนื่องเกี่ยวกับความพร้อมสำหรับ production ของทุกๆ check-in และยิ่งไปกว่านั้นยังถือว่าทุก check-in เป็น release candidate

การจะยอมรับแนวคิดนี้อย่างเต็มที่ เราต้องจำลองกระบวนการทั้งหมดที่เกี่ยวข้องกับการนำซอฟต์แวร์จาก check-in ไปสู่ production และเราต้องรู้ว่าซอฟต์แวร์เวอร์ชันใดๆ อยู่ ณ จุดไหนในแง่ของความพร้อมสำหรับการ release ใน CD เราทำสิ่งนี้โดยจำลองทุก stage ที่ซอฟต์แวร์ของเราต้องผ่าน ทั้งแบบ manual และแบบอัตโนมัติ ตัวอย่างที่ผมแสดงให้ดูสำหรับ Catalog service ใน Figure 7-1 ปัจจุบัน CI tool ส่วนใหญ่รองรับการกำหนดและแสดงภาพสถานะของ build pipeline แบบนี้ได้ในระดับหนึ่ง

ถ้า Catalog service เวอร์ชันใหม่ผ่านการตรวจสอบใดๆ ที่ทำใน stage หนึ่งของ pipeline มันก็จะไปยัง step ถัดไปได้ ถ้ามันไม่ผ่าน stage ใด CI tool ของเราจะบอกได้ว่า build ผ่าน stage ไหนไปแล้วบ้าง และเราสามารถมองเห็นได้ว่าอะไรที่ล้มเหลว ถ้าเราต้องแก้ไขอะไรบางอย่าง เราก็จะทำการเปลี่ยนแปลงแล้ว check in เข้าไป เพื่อให้ microservice เวอร์ชันใหม่พยายามผ่านทุก stage ก่อนที่จะพร้อมสำหรับการ deploy ใน Figure 7-2 เราจะเห็นตัวอย่างของสิ่งนี้: build-120 ล้มเหลวที่ fast test stage, build-121 ล้มเหลวที่ performance test แต่ build-122 ผ่านไปจนถึง production ได้สำเร็จ

Our Catalog microservice can only get deployed if it passes each step in our pipeline

Figure 7-2. Catalog microservice ของเราจะ deploy ได้ก็ต่อเมื่อผ่านทุก step ใน pipeline ของเราเท่านั้น
Continuous Delivery Versus Continuous Deployment (Continuous Delivery กับ Continuous Deployment)

ผม เคยเห็นความสับสนเกี่ยวกับคำว่า continuous delivery และ continuous deployment อยู่บ้างเป็นครั้งคราว อย่างที่เราคุยกันไปแล้วว่า continuous delivery คือแนวคิดที่ทุก check-in ถูกถือว่าเป็น release candidate และเราสามารถประเมินคุณภาพของแต่ละ release candidate เพื่อตัดสินใจว่าพร้อมสำหรับการ deploy หรือยัง ในขณะที่ continuous deployment นั้น check-in ทุกครั้งต้องผ่านการตรวจสอบด้วยกลไกอัตโนมัติ (เช่น เทสต์) และซอฟต์แวร์ใดๆ ที่ผ่านการตรวจสอบเหล่านี้จะถูก deploy โดยอัตโนมัติ โดยไม่ต้องมีคนเข้ามาเกี่ยวข้อง ดังนั้น continuous deployment จึงถือได้ว่าเป็นส่วนต่อขยายของ continuous delivery หากไม่มี continuous delivery คุณก็ทำ continuous deployment ไม่ได้ แต่คุณ สามารถ ทำ continuous delivery โดยไม่ต้อง ทำ continuous deployment ก็ได้

continuous deployment ไม่ได้เหมาะกับทุกคน หลายคนต้องการให้มนุษย์เข้ามาตัดสินใจว่าซอฟต์แวร์ควรถูก deploy หรือไม่ ซึ่งเป็นสิ่งที่เข้ากันได้อย่างสมบูรณ์กับ continuous delivery อย่างไรก็ตาม การนำ continuous delivery มาใช้ก็หมายถึงการมุ่งเน้นอย่างต่อเนื่องในการปรับปรุงเส้นทางสู่ production ให้ดีขึ้น การมองเห็นที่เพิ่มขึ้นทำให้ง่ายขึ้นที่จะเห็นว่าควรปรับปรุงตรงไหนบ้าง บ่อยครั้งที่การมีส่วนร่วมของมนุษย์ในกระบวนการหลัง check-in กลายเป็นคอขวดที่ต้องแก้ไข ลองดูตัวอย่างการเปลี่ยนจาก manual regression testing มาเป็น automated functional testing สิ ผลก็คือ เมื่อคุณ automate กระบวนการ build, deployment และ release มากขึ้นเรื่อยๆ คุณอาจพบว่าตัวเองกำลังเข้าใกล้ continuous deployment มากขึ้นเรื่อยๆ

Tooling (เครื่องมือ)

โดยหลักการแล้ว คุณ ต้องการเครื่องมือที่รองรับ continuous delivery เป็นแนวคิดหลักตั้งแต่ต้น ผมเคยเห็นหลายคนพยายาม hack และขยาย CI tool เพื่อให้มันทำ CD ได้ ซึ่งมักส่งผลให้ได้ระบบที่ซับซ้อนและใช้งานยากกว่าเครื่องมือที่สร้าง CD มาตั้งแต่แรกมาก เครื่องมือที่รองรับ CD อย่างเต็มรูปแบบจะช่วยให้คุณกำหนดและแสดงภาพ pipeline เหล่านี้ได้ จำลองเส้นทางทั้งหมดสู่ production สำหรับซอฟต์แวร์ของคุณ เมื่อโค้ดเวอร์ชันหนึ่งเคลื่อนผ่าน pipeline ถ้ามันผ่าน automated verification stage ใด stage หนึ่ง มันก็จะไปยัง stage ถัดไป

บาง stage อาจเป็นแบบ manual ตัวอย่างเช่น ถ้าเรามีกระบวนการ manual user acceptance testing (UAT) ผมควรจะใช้ CD tool ในการจำลองมันได้ ผมสามารถดู build ถัดไปที่พร้อม deploy เข้า UAT environment ของเรา แล้ว deploy มัน จากนั้นถ้ามันผ่านการตรวจสอบแบบ manual ของเรา ก็ทำเครื่องหมาย stage นั้นว่าสำเร็จ เพื่อให้มันไปยัง stage ถัดไปได้ ถ้า stage ถัดไปเป็นแบบอัตโนมัติ มันก็จะถูก trigger โดยอัตโนมัติทันที

Trade-Offs and Environments (การแลกเปลี่ยนและ Environment)

เมื่อเรา เคลื่อน artifact ของ microservice ผ่าน pipeline นี้ microservice ของเราจะถูก deploy เข้า environment ที่แตกต่างกัน environment แต่ละแบบมีจุดประสงค์ต่างกัน และอาจมีคุณลักษณะที่แตกต่างกันด้วย

การจัดโครงสร้าง pipeline และการหาว่าคุณต้องการ environment แบบไหนบ้างนั้น ในตัวมันเองก็เป็นการหาสมดุลอยู่แล้ว ในช่วงต้นของ pipeline เรากำลังมองหา feedback ที่รวดเร็วเกี่ยวกับความพร้อมของซอฟต์แวร์สำหรับ production เราต้องการให้นักพัฒนารู้โดยเร็วที่สุดถ้ามีปัญหาเกิดขึ้น ยิ่งเราได้รับ feedback เกี่ยวกับปัญหาเร็วเท่าไร ก็ยิ่งแก้ไขได้เร็วเท่านั้น เมื่อซอฟต์แวร์ของเราเข้าใกล้ production มากขึ้น เราต้องการความมั่นใจมากขึ้นว่าซอฟต์แวร์จะทำงานได้จริง ดังนั้นเราจึงจะ deploy เข้า environment ที่คล้าย production มากขึ้นเรื่อยๆ เราจะเห็นการแลกเปลี่ยนนี้ได้ใน Figure 7-3

Balancing a build pipeline for fast feedback and production-like execution environments

Figure 7-3. การสร้างสมดุลของ build pipeline ระหว่าง feedback ที่รวดเร็วกับ environment ที่ทำงานคล้าย production

คุณจะได้ feedback ที่เร็วที่สุดบน laptop สำหรับพัฒนาของคุณ แต่นั่นก็ห่างไกลจากการเป็น production มาก คุณอาจ roll out ทุก commit ไปยัง environment ที่จำลองมาจาก production environment จริงของคุณอย่างสมบูรณ์แบบ แต่นั่นก็มักจะใช้เวลานานขึ้นและมีค่าใช้จ่ายสูงขึ้น ดังนั้นการหาจุดสมดุลจึงเป็นสิ่งสำคัญ และการทบทวนการแลกเปลี่ยนระหว่าง feedback ที่รวดเร็วกับความต้องการ environment ที่คล้าย production อย่างต่อเนื่องนั้น ถือเป็นกิจกรรมที่สำคัญอย่างยิ่งที่ต้องทำต่อไปเรื่อยๆ

ความท้าทายในการสร้าง environment ที่คล้าย production ก็เป็นส่วนหนึ่งที่ทำให้คนหันมาทำการทดสอบใน production กันมากขึ้น รวมถึงเทคนิคอย่าง smoke testing และ parallel run เราจะกลับมาพูดถึงหัวข้อนี้อีกครั้งใน Chapter 8

Artifact Creation (การสร้าง Artifact)

เมื่อเรา ย้าย microservice ของเราเข้า environment ต่างๆ เราก็ต้องมีอะไรบางอย่างให้ deploy จริงๆ ปรากฏว่ามีตัวเลือกหลายแบบสำหรับประเภทของ deployment artifact ที่คุณสามารถใช้ได้ โดยทั่วไปแล้ว artifact แบบไหนที่คุณสร้างขึ้นมาจะขึ้นอยู่กับเทคโนโลยีที่คุณเลือกใช้สำหรับการ deploy เป็นหลัก เราจะเจาะลึกเรื่องนี้ในบทถัดไป แต่ผมอยากให้เคล็ดลับสำคัญบางอย่างก่อนว่าการสร้าง artifact ควรเข้ากับกระบวนการ build แบบ CI/CD ของคุณอย่างไร

เพื่อให้เรื่องง่ายขึ้น เราจะข้ามรายละเอียดว่า artifact ที่เรากำลังสร้างเป็นประเภทไหนแน่ชัดไปก่อน ให้ถือว่ามันเป็น deployable blob ก้อนเดียวไปพลางๆ ก่อน ทีนี้ มีกฎสำคัญสองข้อที่เราต้องคำนึงถึง ข้อแรก อย่างที่ผมพูดไปก่อนหน้านี้ เราควร build artifact เพียงครั้งเดียวเท่านั้น การ build สิ่งเดิมซ้ำแล้วซ้ำเล่าเป็นการเสียเวลาและไม่ดีต่อโลก และในทางทฤษฎีมันอาจก่อให้เกิดปัญหาได้ถ้า build configuration ไม่เหมือนกันทุกครั้งเป๊ะๆ ในบางภาษาโปรแกรมมิ่ง build flag ที่ต่างกันสามารถทำให้ซอฟต์แวร์ทำงานแตกต่างกันไปเลยก็ได้ ข้อสอง artifact ที่คุณตรวจสอบควรเป็น artifact ตัวเดียวกับที่คุณ deploy! ถ้าคุณ build microservice ทดสอบมัน บอกว่า "ใช่ มันทำงานได้" แล้วก็ build มันขึ้นมาใหม่อีกครั้งเพื่อ deploy เข้า production คุณจะรู้ได้อย่างไรว่าซอฟต์แวร์ที่คุณตรวจสอบไปนั้นเป็นตัวเดียวกับที่คุณ deploy จริงๆ

เมื่อรวมสองแนวคิดนี้เข้าด้วยกัน เราจะได้แนวทางที่ค่อนข้างเรียบง่าย นั่นคือ build deployable artifact ของคุณเพียงครั้งเดียวเท่านั้น และควรทำมันตั้งแต่ช่วงต้นๆ ของ pipeline ผมมักจะทำสิ่งนี้หลังจาก compile โค้ด (ถ้าจำเป็น) และรันเทสต์ที่เร็วเสร็จแล้ว เมื่อสร้างเสร็จ artifact นี้จะถูกเก็บไว้ใน repository ที่เหมาะสม ซึ่งอาจเป็นอะไรทำนอง Artifactory หรือ Nexus หรืออาจเป็น container registry ก็ได้ ประเภทของ deployment artifact ที่คุณเลือกมักจะกำหนดลักษณะของ artifact store ด้วย artifact ตัวเดียวกันนี้สามารถนำไปใช้ในทุก stage ถัดไปของ pipeline ได้ ไปจนถึงการ deploy เข้า production เลยทีเดียว ดังนั้นย้อนกลับไปดู pipeline ก่อนหน้านี้ของเรา เราจะเห็นใน Figure 7-4 ว่าเราสร้าง artifact สำหรับ Catalog service ของเราในช่วง stage แรกของ pipeline แล้วก็ deploy artifact ตัวเดียวกันคือ build-123 เป็นส่วนหนึ่งของ slow test, performance test และ production stage

The same artifact is deployed into each environment

Figure 7-4. artifact ตัวเดียวกันถูก deploy เข้าไปในทุก environment

ถ้า artifact ตัวเดียวกันจะถูกใช้ข้าม environment หลายแบบ การตั้งค่าที่แตกต่างกันไปในแต่ละ environment จำเป็นต้องถูกเก็บไว้นอก artifact เอง ตัวอย่างง่ายๆ ผมอาจต้องการตั้งค่า application log ให้บันทึกทุกอย่างตั้งแต่ระดับ DEBUG ขึ้นไปเมื่อรัน Slow Tests stage เพื่อให้ได้ข้อมูลมากขึ้นสำหรับวินิจฉัยว่าทำไมเทสต์ถึงล้มเหลว แต่ผมอาจตัดสินใจเปลี่ยนเป็น INFO เพื่อลดปริมาณ log สำหรับ Performance Tests และการ deploy เข้า Production