• Monzo ได้รับคุณค่ามากมายจากการรันบริการมากกว่า 2,800 รายการด้วยสถาปัตยกรรม MSA
  • แต่สถาปัตยกรรมลักษณะนี้ก็มาพร้อมความยากเช่นกัน หนึ่งในนั้นคือการเปลี่ยนแปลงไลบรารีในทุกบริการ
  • โดยทั่วไปแล้ว คุณต้องเลือก 2 จาก 3 อย่างระหว่างไลบรารีที่ทันสมัย เวอร์ชันไลบรารีที่สอดคล้องกัน และความพยายามในการอัปเกรดที่ต่ำ

กลยุทธ์การย้ายแบบรวมศูนย์

  • ที่ Monzo เชื่อว่าแนวทางการย้ายที่ขับเคลื่อนแบบรวมศูนย์สามารถทำให้บรรลุคุณสมบัติทั้งสามข้างต้นได้ในระดับสูง
  • แทนที่จะผลักภาระการย้ายไปให้เจ้าของบริการ Monzo เลือกให้ทีมเดียวเป็นผู้ขับเคลื่อนการย้าย
  • วิธีนี้ช่วยหลีกเลี่ยงภาระด้านการประสานงานที่สูง (ซึ่งนำไปสู่การย้ายที่ช้า) และความเสี่ยงที่โครงการจะหยุดชะงัก (ซึ่งนำไปสู่ความไม่สอดคล้องกัน)
  • เพื่อให้ทีมเดียวสามารถทำการย้ายให้เสร็จได้ภายในเวลาที่เหมาะสม Monzo พึ่งพาสิ่งต่อไปนี้อย่างมาก:
    • การเลือกเทคโนโลยีหลัก (เช่น ความสอดคล้องในระดับสูง, การใช้ monorepo)
    • การใช้ระบบอัตโนมัติในวงกว้าง (เช่น เครื่องมือดีพลอยบริการจำนวนมาก, การตรวจสอบ rollback อัตโนมัติ)

การย้ายจาก OpenTracing SDK ไปยัง OpenTelemetry SDK

  • Monzo เพิ่งนำกลยุทธ์นี้มาใช้จริงผ่านโครงการเปลี่ยนจาก OpenTracing SDK ไปเป็น OpenTelemetry SDK
  • ทุกบริการจะส่งออกข้อมูล tracing ไปยัง Jaeger ก่อนหน้านี้ทำผ่าน OpenTracing และ Jaeger Go SDK
  • ตอนนี้ไลบรารีเหล่านี้เลิกใช้งานแล้ว และชุมชนได้รวมไปที่ OpenTelemetry แทน
  • เพื่อวางรากฐานสำหรับการปรับปรุงระบบ tracing ก่อนอื่น Monzo ต้องการแทนที่ไลบรารีที่เลิกใช้แล้วด้วย OpenTelemetry SDK

หลักการของการย้าย

  • การย้ายแบบรวมศูนย์ที่โปร่งใสต่อเจ้าของบริการ Monzo เลือกใช้กลยุทธ์ที่ทีมเดียวสามารถทำแบบรวมศูนย์ได้ เพื่อลดภาระการประสานงานและลดความเสี่ยงที่การย้ายจะหยุดกลางคัน
  • ไม่มี downtime การย้ายส่วนใหญ่เกี่ยวข้องกับบริการที่สำคัญต่อฟังก์ชันหลักของธนาคาร จึงไม่สามารถยอมรับ downtime ได้
  • roll forward แบบค่อยเป็นค่อยไป และ rollback ได้รวดเร็ว สำหรับการเปลี่ยนแปลงขนาดใหญ่ ต้องสามารถ roll forward แบบค่อยเป็นค่อยไปเพื่อลด blast radius เมื่อเกิดปัญหา แต่ในขณะเดียวกันก็ต้อง rollback ได้อย่างรวดเร็วหากจำเป็น
  • กฎ 80/20 สำหรับระบบอัตโนมัติ ในการย้ายขนาดใหญ่ โดยทั่วไปสัดส่วนสูงของการเปลี่ยนแปลงจะเข้ากับเทมเพลตร่วม ซึ่งสามารถทำให้เป็นอัตโนมัติได้ง่าย ส่วนกรณีใช้งานที่เฉพาะตัวกว่านั้น การทำอัตโนมัติจะให้ผลตอบแทนที่ลดลง และมักมีประสิทธิภาพกว่าหากจัดการเป็นกรณีไป เพื่อหลีกเลี่ยงเรื่องไม่คาดคิดและให้ติดตามความคืบหน้าได้ง่าย ควรจัดหมวดหมู่การเปลี่ยนแปลงที่จำเป็นล่วงหน้าเป็นสองกลุ่มนี้

กลยุทธ์การย้าย

  • ที่ Monzo มีชุดขั้นตอนการย้ายที่ใช้อย่างเป็นระบบ

1. การวางแผนและการทำให้สอดคล้องกัน

  • การย้ายมีความเสี่ยงไม่น้อย ไม่เพียงส่งผลต่อบริการจำนวนมาก แต่ทีมที่ดำเนินการย้ายอาจไม่มีบริบทมากนัก หรืออาจไม่มีเลย เกี่ยวกับบริการที่กำลังย้าย
  • แม้จะมุ่งสู่ความสอดคล้องของบริการ แต่ก็มีข้อยกเว้นเสมอ และ Monzo ต้องการจับความประหลาดใจเหล่านี้ให้ได้เร็วที่สุด
  • ดังนั้นจึงสำคัญมากที่กระบวนการวางแผนจะต้องโปร่งใส และวิศวกรทุกคนมีโอกาสมีส่วนร่วม
  • สำหรับสิ่งนี้ Monzo มีกระบวนการอยู่สองแบบ:
    • ข้อเสนอ: Monzo เขียนสิ่งนี้บ่อยมาก แทบทุกอย่างที่มีสาระสำคัญสุดท้ายจะถูกแชร์ในช่อง Slack เดียวที่ทุกคนในบริษัทสามารถแสดงความคิดเห็นได้
    • การทบทวนสถาปัตยกรรม: สำหรับการเปลี่ยนแปลงที่ใหญ่ที่สุด จะมีการประชุมทบทวนสถาปัตยกรรมแบบ synchronous เพื่อเจาะลึกบางประเด็นที่มีข้อถกเถียงมากกว่าหรือมีความเสี่ยงมากกว่า เป้าหมายไม่ใช่เพื่อขออนุมัติหรือลายเซ็น แต่เพื่อผลักดันสถานะของการออกแบบให้ก้าวหน้าอย่างมีนัยสำคัญและเร่งโครงการ

2. ครอบไลบรารีเดิม

  • แทนที่จะติดตั้งไลบรารีใหม่และอัปเดตโค้ดของบริการให้เรียกใช้ Monzo ตัดสินใจครอบไลบรารีเดิมก่อน

    1. สามารถดักการเรียกไปยังไลบรารีพื้นฐานและตัดสินใจได้จากการกำหนดค่าแบบไดนามิกว่าจะใช้ implementation ใด ซึ่งทำให้ roll forward และ rollback ได้ง่ายโดยไม่ต้องดีพลอยทุกบริการใหม่
    2. ไลบรารีใหม่มีชนิดข้อมูล/ฟังก์ชันที่แตกต่างอย่างมาก การอัปเดตทุก call site ต้องใช้ความพยายามมาก และในบางกรณีประโยชน์จาก API ใหม่ก็มีน้อย การครอบไลบรารีเดิมช่วยให้ในกรณีเหล่านี้ยังคงอินเทอร์เฟซที่คล้ายของเดิมไว้ ทำให้อัปเดต call site ได้ง่ายขึ้น
  • ประโยชน์อื่นของการครอบไลบรารี:

    • สามารถ instrument ด้วยไลบรารี telemetry ของตัวเองได้
    • สามารถให้อินเทอร์เฟซที่ชัดเจนและกำหนดทิศทางมากขึ้นได้

3. อัปเดต call site

  • การใช้งานไลบรารีนี้เข้ากับแพตเทิร์นร่วม:

    • มีฟังก์ชัน/ชนิดข้อมูลจำนวนไม่มากที่ถูกอ้างอิงหลายครั้งทั่วทั้งโค้ดเบส
    • จากนั้นจะมีหางยาวของฟังก์ชัน/ชนิดข้อมูลที่ถูกอ้างอิงเพียงไม่กี่แห่ง
  • Monzo จัดการแต่ละกรณีแตกต่างกัน:

    • สำหรับฟังก์ชัน/ชนิดข้อมูลส่วนน้อยที่ถูกอ้างอิงในหลายที่ จะทำให้เป็นอัตโนมัติให้มากที่สุด สำหรับไลบรารีนี้ Monzo พึ่งพา gopls และ gorename เป็นหลักเพื่อทำ automated refactoring
    • สำหรับหางยาวของฟังก์ชัน/ชนิดข้อมูลที่ถูกอ้างอิงเพียงไม่กี่แห่ง จะใช้แนวทางแบบ manual เป็นรายกรณี บางกรณีทำการย้ายด้วยมือ ในกรณีอื่นพบว่าสามารถทำสิ่งเดียวกันได้ด้วย API ที่มีอยู่เดิมมากกว่า จึงเปลี่ยนไปใช้ทางนั้น ซึ่งหมายความว่าไม่จำเป็นต้องรองรับเป็นกรณีพิเศษอีกต่อไป และยังมีข้อดีเพิ่มเติมคือช่วยให้ API ของไลบรารี wrapper มีขนาดเล็กและชัดเจน
  • นอกจากการครอบไลบรารีเดิมแล้ว Monzo ยังบล็อกไม่ให้เกิด dependency ใหม่กับไลบรารีเดิม โดยทำผ่านการเพิ่มการตรวจสอบใน CI ด้วย semgrep

4. ครอบไลบรารีใหม่

  • เมื่อครอบไลบรารีเดิมแล้ว ก็สามารถเริ่มเพิ่มไลบรารีใหม่ไว้หลังไลบรารี wrapper ได้
  • ในช่วงแรก implementation ใหม่จะถูกปิดไว้ผ่านการกำหนดค่า ซึ่งหมายความว่าไม่คาดว่าจะมีการเปลี่ยนแปลงพฤติกรรม และยังสามารถทยอย merge การเปลี่ยนแปลงเข้ากับ master branch ต่อไปได้

5. ดีพลอยบริการจำนวนมาก

  • ก่อนเริ่มเปิดใช้ implementation ใหม่ ต้องมั่นใจก่อนว่าทุกบริการที่กำลังรันอยู่รองรับ implementation ใหม่นี้ได้
  • สำหรับการเปลี่ยนแปลงไลบรารีประเภทอื่น อาจดีพลอยเพียงบางส่วนของบริการที่มีฟีเจอร์ใหม่ได้ในแต่ละครั้ง แต่สำหรับไลบรารี tracing หากมีบริการที่ถูกย้ายไปใช้ไลบรารีใหม่แล้ว บริการทั้งหมดที่มันอาจเรียกหาได้ในช่วงเปลี่ยนผ่านก็ต้องรองรับฟีเจอร์ใหม่นี้ด้วย
  • เพื่อจัดการการดีพลอยบริการจำนวนมาก Monzo ได้สร้างเครื่องมือดีพลอยแบบจำนวนมากที่สามารถ push การเปลี่ยนแปลงไลบรารีไปยังทุกบริการได้ด้วยงาน batch แบบ asynchronous
  • เพื่อลดผลกระทบจากการดีพลอยที่อาจผิดพลาด:
    • ใช้การตรวจสอบ rollback อัตโนมัติ
    • ดีพลอยบริการที่มีความสำคัญน้อยที่สุดก่อน ทุกบริการมีแท็ก "tier" และเครื่องมือดีพลอยแบบจำนวนมากจะใช้สิ่งนี้เพื่อจัดลำดับความสำคัญให้กับการดีพลอยที่มีความเสี่ยงต่ำที่สุด

6. ควบคุม rollout ผ่านการกำหนดค่า

  • ปัญหาของเครื่องมือดีพลอยแบบจำนวนมากคือค่อนข้างช้า สิ่งที่ Monzo อยากหลีกเลี่ยงที่สุดคือการดีพลอยทุกบริการไปแล้ว แต่กลับพบว่าไลบรารีใหม่มีปัญหาและไม่สามารถ rollback ได้อย่างรวดเร็ว
  • ดังนั้นแทนที่จะดีพลอยพร้อมเปิดใช้ implementation ใหม่ Monzo จึงดีพลอยความสามารถในการเปิดใช้ implementation ใหม่ผ่านระบบการกำหนดค่า
  • เมื่อเทียบกับการดีพลอยตามปกติ ข้อดีของการใช้ระบบการกำหนดค่าคือความเร็ว ทุกบริการจะรีเฟรชการกำหนดค่าทุก 60 วินาที ดังนั้นหากจำเป็นก็สามารถ rollback ได้อย่างรวดเร็ว
  • นอกจากนี้ยังให้การควบคุมมากขึ้นมากเกี่ยวกับช่วงเวลาที่ implementation ใหม่ถูกใช้งาน เช่น เปิดใช้เฉพาะกับชุดผู้ใช้บางกลุ่ม หรือกับเปอร์เซ็นต์แบบสุ่มของคำขอเท่านั้น
  • ในกรณีนี้ Monzo เลือก rollout เฉพาะกับ API endpoint ที่ทีมเป็นเจ้าของ และเปิดใช้ตามความน่าจะเป็นที่เพิ่มขึ้นทีละน้อย

7. เก็บงานให้เรียบร้อย

  • เมื่อเปลี่ยนไปใช้ implementation ใหม่ทั้งหมดแล้ว ก็ถึงเวลาของงานที่น่าพอใจคือการลบ implementation เดิมออกจากไลบรารี wrapper

พลังพิเศษของการย้ายระบบ

  • การย้ายแบบรวมศูนย์ลักษณะนี้เป็นไปได้ที่ Monzo เพราะการตัดสินใจเชิงเทคนิคพื้นฐานและเครื่องมือที่ลงทุนพัฒนาอย่างต่อเนื่อง
  • เทคโนโลยีที่สอดคล้องกัน: ทุกบริการเขียนด้วย Go และใช้เวอร์ชันเดียวกันของไลบรารีเดิม สิ่งนี้ทำให้การทำให้การเปลี่ยนแปลงเป็นอัตโนมัติง่ายขึ้นมาก ตัวอย่างเช่น ใช้เครื่องมือรีแฟกเตอร์เพียงตัวเดียวแทนที่จะต้องมีแยกตามภาษา
  • Monorepo: โค้ดของทุกบริการอยู่ใน monorepo เดียว ทำให้การรีแฟกเตอร์ขนาดใหญ่ทำได้ง่ายกว่ามากในคอมมิตเดียว และยังช่วยให้บังคับใช้การตรวจสอบใน CI กับการใช้ไลบรารีบางตัวได้ทั่วทั้งระบบ ช่วยรักษาความสอดคล้อง
  • การดีพลอยแบบจำนวนมาก: เมื่อมีองค์ประกอบที่ดีพลอยได้จำนวนมาก ก็จำเป็นต้องมีกระบวนการดีพลอยอัตโนมัติเพื่อ push การเปลี่ยนแปลงไลบรารี
  • บริการกำหนดค่าที่เบาและยืดหยุ่น: กระบวนการดีพลอยปลอดภัยแต่ช้า (ใช้เวลาหลายนาทีต่อการดีพลอย) จึงต้องมีกระบวนการที่เบาและยืดหยุ่นกว่าสำหรับเปิด/ปิดฟีเจอร์ใหม่อย่างรวดเร็วและทันทีในบริการจำนวนมาก

บทสรุป

  • ในอดีต Monzo เคยพยายามกระจายการย้ายออกไป แต่สิ่งนี้นำไปสู่การย้ายที่ไม่เสร็จสมบูรณ์และต้องใช้ความพยายามในการประสานงานจำนวนมากอย่างหลีกเลี่ยงไม่ได้
  • นี่จึงเป็นเหตุผลที่ Monzo ชอบการย้ายแบบรวมศูนย์อย่างมาก แม้ว่าทีมเดียวจะต้องแบกรับต้นทุนค่อนข้างสูง แต่โดยรวมใช้ความพยายามน้อยกว่าและมีโอกาสสูงกว่ามากที่จะรักษาความสอดคล้องได้
  • แนวทางนี้สร้างวงจรเชิงบวก:
    • ทีมที่ดำเนินการย้ายมีแรงจูงใจอย่างมากในการลงทุนกับเครื่องมือเพื่อทำให้การย้ายเป็นอัตโนมัติ
    • และยังช่วยรักษาความสอดคล้องทางเทคนิคไว้ด้วย (ซึ่งทำให้การสร้างเครื่องมือง่ายขึ้น)
    • แต่ก็ยังคงปฏิบัติจริงต่อระดับของระบบอัตโนมัติ โดยใช้กฎ 80/20
  • นอกเหนือจากเครื่องมือที่ Monzo ยังคงลงทุนแล้ว แนวทางนี้ยังเป็นไปได้เพราะการตัดสินใจเชิงเทคนิคหลักบางอย่างที่ทำไว้ตั้งแต่ต้น
    • โดยหลักคือการใช้ชุดเทคโนโลยีที่มีทิศทางชัดเจนและจำกัด

ยังไม่มีความคิดเห็น

ยังไม่มีความคิดเห็น