Progressive Delivery (การส่งมอบแบบ Progressive)

ตลอดทศวรรษที่ผ่านมา เราฉลาดขึ้นในการ deploy ซอฟต์แวร์ไปยังผู้ใช้ของเรา เทคนิคใหม่ ๆ ได้เกิดขึ้นซึ่งขับเคลื่อนโดย use case ที่แตกต่างกันหลายแบบและมาจากหลายส่วนของอุตสาหกรรม IT แต่ส่วนใหญ่ล้วนมุ่งเน้นที่การทำให้การ push ซอฟต์แวร์ใหม่ออกไปมีความเสี่ยงน้อยลงมาก และถ้าการ release ซอฟต์แวร์มีความเสี่ยงน้อยลง เราก็สามารถ release ซอฟต์แวร์บ่อยขึ้นได้

มีกิจกรรมมากมายที่เราทำก่อนส่งซอฟต์แวร์ของเราขึ้น live ที่สามารถช่วยเราจับปัญหาได้ก่อนที่มันจะกระทบผู้ใช้จริง Preproduction testing เป็นส่วนใหญ่ของเรื่องนี้ แม้ว่า อย่างที่เราจะพูดคุยกันใน Chapter 9 มันก็ทำได้เพียงเท่านี้

ในหนังสือของพวกเขา Accelerate 6 Nicole Forsgren, Jez Humble และ Gene Kim แสดงหลักฐานที่ชัดเจนจากงานวิจัยที่กว้างขวางว่าบริษัทที่มีประสิทธิภาพสูง deploy บ่อยกว่าคู่แข่งที่มีประสิทธิภาพต่ำกว่า และในขณะเดียวกันก็มี อัตราความล้มเหลวจากการเปลี่ยนแปลงที่ต่ำกว่ามาก

แนวคิดที่ว่า "ไปเร็ว ๆ และพังไปเลย" ดูเหมือนจะไม่ค่อยใช้ได้กับการ ship ซอฟต์แวร์—การ ship บ่อยและมีอัตราความล้มเหลวต่ำไปด้วยกันได้ และองค์กรที่ตระหนักถึงเรื่องนี้ได้เปลี่ยนวิธีคิดเกี่ยวกับการ release ซอฟต์แวร์

องค์กรเหล่านี้ใช้เทคนิคอย่าง feature toggle, canary release, parallel run และอื่น ๆ ซึ่งเราจะลงรายละเอียดในหัวข้อนี้ การเปลี่ยนวิธี คิดเกี่ยวกับ การ release functionality นี้อยู่ภายใต้ร่มของสิ่งที่เรียกว่า progressive delivery Functionality ถูก release ให้ผู้ใช้ในลักษณะที่ควบคุมได้ แทนที่จะเป็น deployment แบบ big-bang เราสามารถฉลาดเกี่ยวกับว่าใครเห็น functionality อะไร—ตัวอย่างเช่น โดยการ roll out ซอฟต์แวร์เวอร์ชันใหม่ให้กับผู้ใช้กลุ่มย่อยของเรา

โดยพื้นฐานแล้ว สิ่งที่เทคนิคทั้งหมดนี้มีร่วมกันในแก่นแท้คือการเปลี่ยนวิธีคิดง่าย ๆ เกี่ยวกับการ ship ซอฟต์แวร์ นั่นคือเราสามารถแยกแนวคิดของ deployment ออกจากแนวคิดของ release ได้

Separating Deployment from Release (การแยก Deployment ออกจาก Release)

Jez Humble ผู้ร่วมเขียนหนังสือ Continuous Delivery เสนอเหตุผลในการแยกแนวคิดทั้งสองนี้ออกจากกัน และเขา ทำให้มันเป็นหลักการหลักสำหรับการ release ซอฟต์แวร์ที่มีความเสี่ยงต่ำ :

Deployment คือสิ่งที่เกิดขึ้นเมื่อคุณติดตั้งซอฟต์แวร์เวอร์ชันหนึ่งลงใน environment หนึ่ง (มักหมายถึง production environment) Release คือเมื่อคุณทำให้ระบบหรือส่วนหนึ่งของมัน (ตัวอย่างเช่น feature) พร้อมใช้งานสำหรับผู้ใช้

Jez โต้แย้งว่าโดยการแยกแนวคิดทั้งสองนี้ออกจากกัน เราสามารถทำให้แน่ใจว่าซอฟต์แวร์ของเราทำงานได้ใน production setting โดยไม่มีความล้มเหลวถูกเห็นโดยผู้ใช้ของเรา Blue-green deployment เป็นหนึ่งในตัวอย่างที่ง่ายที่สุดของแนวคิดนี้ในการปฏิบัติจริง—คุณมีซอฟต์แวร์เวอร์ชันหนึ่งที่ live อยู่ (blue) แล้วคุณ deploy เวอร์ชันใหม่ควบคู่ไปกับเวอร์ชันเก่าใน production (green) คุณตรวจสอบให้แน่ใจว่าเวอร์ชันใหม่ทำงานได้ตามที่คาดไว้ และถ้ามันทำงานได้ คุณก็ redirect ลูกค้าไปเห็นซอฟต์แวร์เวอร์ชันใหม่ ถ้าคุณพบปัญหาก่อนการสลับนี้ ก็จะไม่มีลูกค้าคนไหนได้รับผลกระทบ

แม้ blue-green deployment จะเป็นหนึ่งในตัวอย่างที่ง่ายที่สุดของหลักการนี้ มีเทคนิคที่ซับซ้อนกว่ามากมายที่เราสามารถใช้ได้เมื่อเรารับเอาแนวคิดนี้มาใช้

On to Progressive Delivery (มาถึง Progressive Delivery)

James Governor ผู้ร่วมก่อตั้ง RedMonk บริษัทวิเคราะห์อุตสาหกรรมที่มุ่งเน้นนักพัฒนา เป็นคนแรกที่บัญญัติ คำว่า progressive delivery เพื่อครอบคลุมเทคนิคที่แตกต่างกันหลายแบบที่ถูกใช้ในพื้นที่นี้ เขาได้อธิบาย progressive delivery ต่อไปว่าเป็น "continuous delivery พร้อมการควบคุมที่ละเอียดเหนือ blast radius" —ดังนั้นมันจึงเป็นส่วนขยายของ continuous delivery แต่ยังเป็นเทคนิคที่ให้เรามีความสามารถในการควบคุมผลกระทบที่อาจเกิดขึ้นของ ซอฟต์แวร์ ที่เพิ่ง release ไปของเรา

ต่อยอดจากธีมนี้ Adam Zimman จาก LaunchDarkly อธิบาย ว่า progressive delivery ส่งผลต่อ "ธุรกิจ" อย่างไร จากมุมมองนั้น เราต้องการการเปลี่ยนวิธีคิดเกี่ยวกับว่า functionality ใหม่ ๆ ไปถึงลูกค้าของเราอย่างไร มันไม่ใช่การ rollout เดียวอีกต่อไป—มันสามารถเป็นกิจกรรมแบบเป็นเฟสได้ ที่สำคัญคือ progressive delivery สามารถให้อำนาจแก่ product owner โดย ตามที่ Adam พูดไว้ว่า "การมอบการควบคุม feature ให้กับเจ้าของที่รับผิดชอบผลลัพธ์อย่างใกล้ชิดที่สุด" เพื่อให้สิ่งนี้ทำงานได้ อย่างไรก็ตาม product owner ที่เป็นปัญหานั้นต้องเข้าใจกลไกของเทคนิค progressive delivery ที่ถูกใช้ ซึ่งบ่งบอกถึง product owner ที่มีความเข้าใจทางเทคนิคในระดับหนึ่ง หรือไม่ก็ต้องมีการสนับสนุนจากกลุ่มคนที่มีความเข้าใจเพียงพอ

เราได้พูดถึง blue-green deployment ไปแล้วในฐานะเทคนิค progressive delivery หนึ่งอย่าง มาดูกันอีกสองสามอย่างสั้น ๆ กัน

Feature Toggles (Feature Toggle)

ด้วย feature toggle (หรือที่รู้จักกันในชื่อ feature flag) เราซ่อน functionality ที่ deploy แล้วไว้หลัง toggle ที่สามารถใช้เพื่อเปิดหรือปิด functionality ได้ นี่มักถูกใช้เป็นส่วนหนึ่งของ trunk-based development ที่ functionality ที่ยังไม่เสร็จสามารถถูก check in และ deploy ได้แต่ยังคงถูกซ่อนจากผู้ใช้ปลายทาง แต่มันก็มีการใช้งานอื่น ๆ นอกเหนือจากนี้มากมาย สิ่งนี้อาจมีประโยชน์ในการเปิด feature ในเวลาที่กำหนด หรือปิด feature ที่กำลังก่อปัญหา

คุณยังสามารถใช้ feature toggle ในลักษณะที่ละเอียดกว่านี้ได้ อาจให้ flag มีสถานะที่แตกต่างกันขึ้นอยู่กับลักษณะของผู้ใช้ที่ทำ request ตัวอย่างเช่น คุณอาจมีกลุ่มลูกค้าที่เห็น feature ถูกเปิด (อาจเป็นกลุ่ม beta test) ในขณะที่คนส่วนใหญ่เห็น feature ถูกปิด—สิ่งนี้สามารถช่วยคุณ implement canary rollout ซึ่งเราจะพูดถึงถัดไป มี managed solution เต็มรูปแบบสำหรับการจัดการ feature toggle รวมถึง LaunchDarkly และ Split แม้ platform เหล่านี้จะน่าประทับใจ ผมคิดว่าคุณสามารถเริ่มต้นด้วยอะไรที่ง่ายกว่ามาก—แค่ configuration file ก็เพียงพอสำหรับการเริ่มต้น แล้วค่อยพิจารณาเทคโนโลยีเหล่านี้เมื่อคุณเริ่มผลักดันวิธีที่คุณต้องการใช้ toggle มากขึ้น

สำหรับการเจาะลึกลงไปในโลกของ feature toggle มากขึ้น ผมขอแนะนำอย่างจริงใจให้อ่านบทความของ Pete Hodgson เรื่อง "Feature Toggles (aka Feature Flags)" ซึ่งลงรายละเอียดมากมายเกี่ยวกับวิธี implement มันและวิธีต่าง ๆ มากมายที่มันสามารถถูกใช้ได้

Canary Release (Canary Release)

มนุษย์ผิดพลาดได้ แต่ถ้าจะทำให้พังจริง ๆ คุณต้องใช้คอมพิวเตอร์ 7

เราทุกคนล้วนทำผิดพลาด และคอมพิวเตอร์ช่วยให้เราทำผิดพลาดได้เร็วขึ้นและในขนาดที่ใหญ่กว่าที่เคยเป็นมา เมื่อความผิดพลาดเป็นสิ่งที่หลีกเลี่ยงไม่ได้ (และเชื่อผมเถอะ มันหลีกเลี่ยงไม่ได้จริง ๆ) มันจึงสมเหตุสมผลที่จะทำสิ่งที่ช่วยให้เราจำกัดผลกระทบของความผิดพลาดเหล่านี้ Canary release เป็นหนึ่งในเทคนิคเหล่านั้น

ตั้งชื่อตามนกคานารีที่ถูกนำเข้าไปในเหมืองเป็นระบบเตือนภัยล่วงหน้าเพื่อเตือนคนงานเหมืองถึงการมีอยู่ของก๊าซอันตราย ด้วย canary rollout แนวคิดคือลูกค้าส่วนย่อยที่จำกัดของเราจะเห็น functionality ใหม่ ถ้ามีปัญหากับ rollout นั้น มีเพียงส่วนนั้นของลูกค้าของเราเท่านั้นที่จะได้รับผลกระทบ ถ้า feature นั้นทำงานได้สำหรับกลุ่ม canary นั้น มันก็สามารถถูก roll out ไปยังลูกค้าของคุณมากขึ้นเรื่อย ๆ จนกว่าทุกคนจะเห็นเวอร์ชันใหม่

สำหรับสถาปัตยกรรม microservice toggle สามารถถูก configure ที่ระดับ microservice แต่ละตัว เปิด (หรือปิด) functionality สำหรับ request ที่มาจากภายนอกหรือจาก microservice อื่นไปยัง functionality นั้น อีกเทคนิคหนึ่งคือการมี microservice สองเวอร์ชันที่แตกต่างกันรันเคียงข้างกัน และใช้ toggle เพื่อ route ไปยังเวอร์ชันเก่าหรือเวอร์ชันใหม่ ตรงนี้ การ implement canary จะต้องอยู่ที่ไหนสักแห่งใน routing/networking path มากกว่าที่จะอยู่ใน microservice ตัวเดียว

ตอนที่ผมทำ canary release ครั้งแรก เราควบคุม rollout ด้วยมือ เราสามารถ configure เปอร์เซ็นต์ของ traffic ของเราที่เห็น functionality ใหม่ และตลอดช่วงเวลาหนึ่งสัปดาห์เราค่อย ๆ เพิ่มขึ้นจนกว่าทุกคนจะเห็น functionality ใหม่ ตลอดสัปดาห์นั้น เราจับตามอง error rate, bug report และอื่น ๆ ทุกวันนี้ เป็นเรื่องปกติมากขึ้นที่จะเห็นกระบวนการนี้ถูกจัดการในลักษณะอัตโนมัติ เครื่องมืออย่าง Spinnaker ตัวอย่างเช่น มีความสามารถในการเพิ่ม call โดยอัตโนมัติตาม metric เช่น การเพิ่มเปอร์เซ็นต์ของ call ไปยัง microservice เวอร์ชันใหม่ถ้า error rate อยู่ในระดับที่ยอมรับได้

Parallel Run (Parallel Run)

ด้วย canary release request สำหรับ functionality หนึ่งจะถูกให้บริการโดยเวอร์ชันเก่าหรือเวอร์ชันใหม่อย่างใดอย่างหนึ่งเท่านั้น นี่หมายความว่าเราไม่สามารถเปรียบเทียบว่า functionality ทั้งสองเวอร์ชันจะจัดการ request เดียวกันอย่างไร ซึ่งอาจสำคัญถ้าคุณต้องการให้แน่ใจว่า functionality ใหม่ทำงานในลักษณะเดียวกันทุกประการกับ functionality เวอร์ชันเก่า

ด้วย parallel run คุณทำสิ่งนั้นได้จริง—คุณรัน implementation ที่แตกต่างกันสองแบบของ functionality เดียวกันเคียงข้างกัน และส่ง request สำหรับ functionality นั้นไปยัง implementation ทั้งสองแบบ สำหรับสถาปัตยกรรม microservice แนวทางที่ชัดเจนที่สุดอาจเป็นการ dispatch service call ไปยัง service สองเวอร์ชันที่แตกต่างกันและเปรียบเทียบผลลัพธ์ อีกทางเลือกหนึ่งคือให้ implementation ทั้งสองของ functionality นั้นอยู่ร่วมกันภายใน service เดียวกัน ซึ่งมักทำให้การเปรียบเทียบง่ายขึ้น

เมื่อรัน implementation ทั้งสองแบบ มันสำคัญที่จะตระหนักว่าคุณอาจต้องการเพียงผลลัพธ์จาก invocation หนึ่งเท่านั้น Implementation หนึ่งถือเป็น source of truth—นี่คือ implementation ที่คุณเชื่อถือในปัจจุบัน และมักเป็น implementation ที่มีอยู่แล้ว ขึ้นอยู่กับลักษณะของ functionality ที่คุณกำลังเปรียบเทียบด้วย parallel run คุณอาจต้องพิจารณาความละเอียดอ่อนนี้อย่างรอบคอบ—คุณคงไม่อยากส่ง order update ที่เหมือนกันสองครั้งให้ลูกค้า หรือจ่าย invoice สองครั้งตัวอย่างเช่น!

ผมสำรวจ pattern ของ parallel run อย่างละเอียดมากขึ้นใน Chapter 3 ของหนังสือของผม Monolith to Microservices 8 ที่นั่นผมสำรวจการใช้มันในการช่วยย้าย functionality จากระบบ monolithic ไปสู่สถาปัตยกรรม microservice ที่ซึ่งเราต้องการให้แน่ใจว่า microservice ใหม่ของเราทำงานในลักษณะเดียวกันกับ functionality ของ monolith เดิม ในอีกบริบทหนึ่ง GitHub ใช้ pattern นี้เมื่อ rework ส่วนหลักของ codebase ของพวกเขา และได้ release เครื่องมือ open source Scientist เพื่อช่วยในกระบวนการนี้ ตรงนี้ parallel run ถูกทำภายใน process เดียว โดย Scientist ช่วยเปรียบเทียบ invocation

Tip

ด้วย blue-green deployment, feature toggle, canary release และ parallel run เราเพิ่งจะแตะพื้นผิวของสาขา progressive delivery เท่านั้น แนวคิดเหล่านี้สามารถทำงานร่วมกันได้ดี (เราได้พูดถึงไปแล้วว่าคุณสามารถใช้ feature toggle เพื่อ implement canary rollout ได้อย่างไรตัวอย่างเช่น) แต่คุณคงอยากค่อย ๆ เริ่มต้น เริ่มแรก แค่จำไว้ว่าให้แยกแนวคิดทั้งสองของ deployment และ release ออกจากกัน ถัดไป เริ่มมองหาวิธีที่ช่วยให้คุณ deploy ซอฟต์แวร์ของคุณบ่อยขึ้น แต่ในลักษณะที่ปลอดภัย ทำงานร่วมกับ product owner หรือ business stakeholder อื่น ๆ ของคุณเพื่อทำความเข้าใจว่าเทคนิคเหล่านี้บางส่วนสามารถช่วยให้คุณไปได้เร็วขึ้น แต่ก็ช่วยลดความล้มเหลวได้เช่นกัน