
การ Deploy ขึ้น prod ทุกครั้งที่มีการเปลี่ยนแปลงอาจน่ากลัว แต่การไม่ Deploy ขึ้น prod โดยตรงน่ะน่ากลัวกว่า
นี่คือกระบวนการที่ผมใช้ที่ Amazon ในการนำทีมที่ต้อง Deploy งานให้กับลูกค้าหลายร้อยล้านคน และจากการทำงานเป็นที่ปรึกษา ผมได้นำทีมวิศวกรจากที่เคย Deploy แบบมีรอบ Release ทุกสองสัปดาห์ ไปสู่การ Ship งานทุกครั้งที่มีการ Merge
เริ่มจากสิ่งที่เห็นได้ชัดก่อน:
คุณจะทำให้เกิด Outage ขึ้นแน่นอน มันไม่ใช่คำถามว่าจะเกิดไหม แต่เป็นคำถามว่าเมื่อไหร่
ไม่ว่าจะทำ Unit Testing, Integration Testing, Dogfooding, End-to-End อะไรก็ตาม หรือแม้แต่เซ่นไหว้เทพเจ้าแห่งการ Deploy ก็ไม่สามารถจับบั๊กได้ทุกตัว
การทดสอบที่คุณทำกับฟีเจอร์ของคุณเมื่อสัปดาห์ก่อน ไม่ได้ถูกทดสอบร่วมกับการเปลี่ยนแปลงล่าสุดของเพื่อนร่วมทีม
การทดสอบของคุณทำกับเวอร์ชัน Pre-prod ของ Service ของเพื่อนร่วมทีม ซึ่งตอนนี้เปลี่ยนไปแล้ว และมีการเปลี่ยนแปลงที่ไม่เข้ากับเวอร์ชันเก่า (Backwards Incompatible)
ยิ่งคุณรอนานเท่าไหร่ การเปลี่ยนแปลงก็ยิ่งสะสมใน Release มากขึ้นเท่านั้น หากคุณต้อง Rollback คุณก็จะต้อง Rollback การเปลี่ยนแปลงของสองสัปดาห์ แทนที่จะเป็นแค่ 1-2 ชั่วโมง
ถ้าเรายอมรับว่า Outage เป็นสิ่งที่เลี่ยงไม่ได้ การทุ่มทรัพยากรมหาศาลเพื่อทำ QA ให้กับ Release ก็ดูจะไม่สมเหตุสมผลเท่าไหร่ เราควรโฟกัสทรัพยากรไปที่การ Monitoring และสังเกตการณ์ Release และเตรียมพร้อมรับมือกับ Outage เมื่อมันเกิดขึ้น
ต่อไปเรามาดูวิธีไปถึงจุดนั้นกัน:
ข้อกำหนดเบื้องต้น:
CI/CD
การทดสอบสำคัญน้อยกว่าที่คุณคิดมาก การทดสอบพิสูจน์ไม่ได้ว่าการเปลี่ยนแปลงของคุณปลอดภัยใน Production ไม่มีอะไรพิสูจน์ได้เลย สิ่งที่การทดสอบทำคือทำให้ความล้มเหลวมีต้นทุนถูก บั๊กที่เจอใน CI เสียเวลาแค่นาที แต่บั๊กที่เจอใน Production ทำให้คุณต้องเสียเวลาช่วงเย็นไปกับการ Rollback
ดังนั้น จงรันชุดทดสอบทั้งหมดในทุก ๆ Merge หรืออย่างน้อยก็เป็นส่วนหนึ่งของ Pipeline: Unit, Integration, End-to-End Tests ยิ่งบั๊กเดินทางไปไกลใน Pipeline เท่าไหร่ ค่าใช้จ่ายในการแก้ก็ยิ่งสูงเท่านั้น
Monitoring/Observability
หัวใจของเรื่องนี้คือการจับ Regression ให้ได้เร็วที่สุด เพื่อให้ได้แบบนั้น คุณต้องมี Monitoring ที่ยอดเยี่ยม ซึ่งมีหน้าตาดังนี้:
- Metrics: errors, latency, availability
- Logs พร้อม Correlation IDs
- Alarms สำหรับ Sev-3 และ Sev-2 (Paging) ที่ต่อกับสองข้อด้านบน
การปรับ Threshold ของ Alarm นั้นมีทั้งศิลปะและวิทยาศาสตร์อยู่พอสมควร มันคือสมดุลระหว่างความไวของ Alarm กับความเร็วในการตอบสนองต่อ Incident จริง เป้าหมายของเวลาตั้งแต่เกิด Sev-2 ไปจนถึงการแจ้งเตือนควรอยู่ที่ 5-10 นาที
ในช่วงแรก คุณอาจตั้งค่าได้ผิดและไวเกินไป น่าเสียดายที่เรื่องนี้ต้องเรียนรู้จากการลองผิดลองถูกเป็นหลัก ดังนั้นคุณอาจต้องตื่นตอนตีสองอยู่สองสามครั้งในช่วงแรก
Feature Flags
สำหรับการเปลี่ยนแปลงใด ๆ ที่มีความเสี่ยง คุณควร Ship มันไว้หลัง Feature Flag / Remote Config Feature Flag จะช่วยให้คุณ Rollback หรือปิดการเปลี่ยนแปลงใด ๆ ได้ภายในไม่กี่นาที แทนที่จะต้อง Rollback ทั้ง Deployment ทั้งหมด ยิ่งไปกว่านั้น ถ้า Service ของ Feature Flag รองรับ (ซึ่งควรจะรองรับ) คุณสามารถค่อย ๆ เปิดฟีเจอร์เป็นเปอร์เซ็นต์หรือตามกลุ่มผู้ใช้ ก็จะลดผลกระทบจากการเปลี่ยนแปลงที่แย่ได้อีก
สิ่งนี้ทำให้เราแยกการ Deploy โค้ดออกจากการเปิดใช้โค้ดออกจากกันได้ ดูเล็กน้อยแต่มันเปลี่ยนเกมในการลดความเสี่ยง
หมายเหตุ: คุณต้องมีกระบวนการในการเก็บกวาดสิ่งเหล่านี้ วิธีที่ดีที่สุดคือสร้าง Ticket สำหรับลบ Flag ทุกอันที่สร้าง ถ้าไม่ทำ เมื่อ Service ของ Feature Flag ล่ม (และมันจะล่มแน่นอน) คุณจะเจอ Regression ครั้งใหญ่ ถามผมได้ว่าผมรู้ได้ยังไง
การ Rollback อัตโนมัติ (Deploy time circuit breaker)
Deploy Time Circuit Breaker คือฟังก์ชันที่ให้คุณ Rollback Deployment ได้ ถ้าคุณเห็นจำนวนหรือเปอร์เซ็นต์ของ Errors ขณะที่กำลังค่อย ๆ ปล่อยขึ้นไปทั่วทั้ง Fleet ผู้ให้บริการคลาวด์ส่วนใหญ่มีฟีเจอร์นี้ให้แล้ว โดยแค่ติ๊กช่องเดียว
การเปลี่ยนแปลงแบบ Backwards Compatible
คุณควรทำสิ่งนี้อยู่แล้ว แต่การ Deploy ทุก Commit จะบังคับให้ต้องทำจริง ๆ ในระหว่าง Rolling Deployment คุณจะมีเวอร์ชันเก่าและเวอร์ชันใหม่รันพร้อมกัน การเปลี่ยนแปลงทุกครั้งต้องทำงานร่วมกับเวอร์ชันก่อนหน้าได้ เทคนิคการ Deploy ตอนเที่ยงคืนเพื่อเลี่ยงปัญหานี้ใช้ไม่ได้อีกต่อไป
กลยุทธ์การ Deploy
ทีนี้ เมื่อมีสิ่งเหล่านั้นพร้อมแล้ว มาดูกลยุทธ์การ Deploy แบบต่าง ๆ ที่จะช่วยลดความเสี่ยงเวลาที่คุณปล่อยงานของคุณกัน
One box (canary)
การ Deploy แบบ One Box จะปล่อยการเปลี่ยนแปลงของคุณไปที่เครื่องเดียวใน Fleet ใหญ่ ทำให้ผลกระทบจากการเปลี่ยนแปลงที่แย่จำกัดอยู่ที่เครื่องเดียวเท่านั้น
คุณ Deploy แล้วปล่อยให้มันทำงานอยู่สักระยะ รับ Traffic สักส่วนเล็ก ๆ จาก Traffic ทั้งหมด คุณต้องตั้งค่า Monitoring และ Alerting ไว้ที่เครื่องนี้ ถ้ามีอะไรพังจะได้แจ้งเตือน
Rolling Deployments
Rolling Deployment ช่วยให้คุณปล่อยงานแบบค่อยเป็นค่อยไปตามเปอร์เซ็นต์ เมื่อเวลาผ่านไป ถ้าเกิด Error ขนาดร้ายแรง คุณจะจับมันได้ก่อนที่มันจะกระทบเครื่องทั้งหมด แล้วค่อยเริ่ม Rollback เครื่องเหล่านั้น
Regional Rollout
เมื่อบริษัทโตขึ้น คุณก็จะมีการ Deploy หลายภูมิภาค แทนที่จะ Deploy ไปทุกภูมิภาคพร้อมกัน คุณสามารถ Deploy ไปภูมิภาคเดียวก่อน (โดยทั่วไปคือภูมิภาคที่มี Traffic ต่ำสุด)
กรณีที่ใช้แนวทางนี้ไม่ได้
App store
การปล่อยแอปมือถือไม่ค่อยเข้ากับแนวทางนี้เท่าไหร่ เพราะคิวตรวจสอบของ App Store จะจำกัดจังหวะการ Deploy ของคุณ และต้องใช้กลยุทธ์ที่แตกต่างออกไป
สภาพแวดล้อมที่ต้องผ่านการรับรอง
อุปกรณ์การแพทย์, ระบบ Avionics, ระบบควบคุมอุตสาหกรรม ฯลฯ คุณไม่สามารถ Continuous Deploy ได้ ถ้าต้องให้หน่วยงานกำกับดูแลรับรอง Build
On-prem / Self-hosted
คุณไม่สามารถควบคุมการอัปเกรดฝั่งนั้นได้ คุณยังคงทำ Continuous Deploy กับทุกอย่างที่คุณดูแลได้ แต่คุณยังต้องทำเวอร์ชันให้กับการเปลี่ยนแปลงทุกครั้ง และลูกค้าจะเป็นคนกำหนดว่าจะอัปเกรดเมื่อไหร่
จะเริ่มจากตรงไหน
อย่าทำทั้งหมดนี้พร้อมกัน ลำดับสำคัญ:
- ทำให้ CI เขียวและเร็ว ควรอยู่ภายใต้ 15 นาที
- ตั้ง Metrics และ Alarms สำหรับ Error Rate, Latency และ Availability นี่คือส่วนที่สำคัญที่สุดของงานนี้
- ใส่การเปลี่ยนแปลงที่มีความเสี่ยงไว้หลัง Flag
- เพิ่ม One-Box + การ Rollback อัตโนมัติ
- ลบปฏิทิน Release ทิ้ง
- หาประโยชน์ใหม่ให้กับเวลาว่างทั้งหมดของคุณ ตอนนี้คุณไม่ต้องจัดตาราง Release แล้ว
ทีมส่วนใหญ่ที่ผมทำงานด้วยใช้เวลาประมาณหนึ่งไตรมาสเพื่อทำสิ่งเหล่านี้ให้ครบ เครื่องมือต่าง ๆ เป็นเรื่องง่าย ส่วนกระบวนการขององค์กรและการทำลายภาพลวงตาว่าการ Release ตามตารางเวลานั้นปลอดภัยต่างหากที่เป็นเรื่องยาก
ถ้าทีมของคุณยังใช้ปฏิทิน Release อยู่และอยากเลิกใช้ นั่นคืองานที่ผมทำ DM มาได้เลย





