Photo by Abraham Wiebe, from Unsplash.com

Bulkhead เป็นเทคนิคการออกแบบที่เหมาะกับ Critical System คือ ถ้าล่มปุ๊บ คนในองค์กรจะไม่สามารถทำงานต่อได้เลย

ถ้าใครเปิดดิกดู ศัพท์นี้จริงๆใช้เรียกการออกแบบพาหนะครับ

Bulkhead (n.) a dividing wall or barrier between separate compartments inside a ship, aircraft, or other vehicle.

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

ยกตัวอย่างเช่น สมมติว่าเรือของเรามีจุดรั่วจุดหนึ่ง หากเราไม่ได้มีการแบ่งโซนเป็นส่วน น้ำจะกระจายไปทั่วท้องเรือ เรือก็จะจมแน่นอน

การใช้ Bulkhead คือการกั้นส่วนท้องเรือออกเป็นส่วนๆ หากน้ำรั่วขึ้นมา น้ำก็จะเข้ามาจนเต็มแค่ส่วนหนึ่งๆ โดยไม่หนักพอทำให้เรือล่มทั้งลำ (ถ้ายังนึกภาพไม่ออก ลองดูภาพในกูเกิ้ลครับ)

ปัญหาที่ใช้ Bulkhead ช่วยได้

ในบทความเรื่อง Throttling เราพูดถึงปัญหาที่บางเซอร์วิซส่ง Request จำนวนมาก มายังระบบ A ทำให้ระบบล่ม และระบบอื่นๆไม่สามารถติดต่อกับ A ไปด้วย

เซอร์วิซ A โดน B ถล่ม C เลยทำงานไม่ได้ไปด้วย
เซอร์วิซ A โดน B ถล่ม C เลยทำงานไม่ได้ไปด้วย

สมมติว่ามีเหตุการอันไม่คาดฝัน (อาจจะมีระบบส่ง Request เยอะมาก, DDoS Attack) ถ้าระบบ A ล่มไป ทั้ง B และ C ก็จะทำงานไม่ได้

คราวนี้ลองสมมติเพิ่มว่าระบบ C ของผมเป็น Critical System ในขณะที่ B ไม่ใช่

ตัวอย่างเช่น ผมกำลังออกแบบระบบให้สายการบินฮัสกี้แอร์ไลน์(นามสมมติ) ผมมีระบบ A คือระบบที่เก็บข้อมูลเที่ยวบินทั้งหมด

  • B เป็นระบบที่ใช้คำนวนค่าจ้างของนักบิน สจ๊วต และแอร์โฮสเตจ ซึ่งได้เงินแปรผันตามจำนวนและระยะทางของเที่ยวบิน โดยต้องดึงข้อมูลเที่ยวบินจาก A มาใช้ในการคำนวน
  • C เป็นระบบสำหรับใช้เช็คอินผู้โดยสาร ถูกใช้โดยพนักงานในสนามบิน (ตรงที่เราต้องเอากระเป๋าไปโหลด) หากไม่มีข้อมูลเที่ยวบินจาก A พนักงานในสนามบินจะเช็คอินไม่ได้

จากตัวอย่างนี้ ระบบ B สำคัญก็จริง แต่ต่อให้ทำงานไม่ได้ ก็ไม่ได้ทำให้ทั้งบริษัทหยุดชะงัก แต่หากระบบ C พัง โกลาหลแน่นอน เสียหายมหาศาลทั้งด้านค่าใช้จ่ายและหน้าตาของบริษัท

ดังนั้น ผมต้องการออกแบบระบบ A ให้แม้มีอะไรพังบ้าง ยังไง ก็ต้องให้ระบบ C ทำงานต่อได้

ระบบ C สำคัญมาก หากทำงานไม่ได้ จะกระทบทั้งบริษัท กรณีนี้เราจะออกแบบระบบ A ยังไง?
ระบบ C สำคัญมาก หากทำงานไม่ได้ จะกระทบทั้งบริษัท กรณีนี้เราจะออกแบบระบบ A ยังไง?

เทคนิคการออกแบบ Bulkhead

ในความเป็นจริง ผมไม่มีทางการันตีได้ 100% หรอก เพราะไม่รู้ว่าอะไรจะพังบ้าง

แต่ผมลดความเสี่ยงได้ ตามรูปข้างล่าง

ออกแบบให้ 7 เซอร์เวอร์ กับ 3 เซอร์เวอร์แยกออกจากกัน
ออกแบบให้ 7 เซอร์เวอร์ กับ 3 เซอร์เวอร์แยกออกจากกัน

ในรูปนี้ เราออกแบบเซอร์วิซ A ให้มีทั้งหมด 10 เซอร์เวอร์ แต่แยกจากกันเป็น 7:3

โดยระบบที่ไม่ได้ Critical (สีน้ำเงิน ได้แก่ B, D, E, F) จะติดต่อไปยัง A1 ในขณะที่ระบบที่สำคัญมากอย่าง C จะติดต่อไปที่ A2

วิธีนี้ช่วยการันตีได้ว่า หากระบบสีน้ำเงินส่ง Request จำนวนมากเข้ามา หรือทำการ DDoS ระบบสีน้ำเงิน ระบบ C จะไม่ได้รับผลกระทบเลย

อีกวิธีหนึ่ง คือให้ C มี Endpoint ของทั้ง A1 และ A2 โดยถ้า A2 เกิดฟลุ้คพังขึ้นมา C ก็ยังมี A1 ให้ใช้ แต่วิธีนี้ต้องระวังเรื่อง Internal State กับ Data Replication ให้ดีเพราะข้อมูลของ A1 กับ A2 อาจต่างกัน ทำให้เกิดบั้กแปลกๆได้

เพื่อความปลอดภัย ผมพยายามแยกส่วนให้ทั้งสองส่วนของ A ไม่เกี่ยวกันโดยสิ้นเชิง โดย

  1. A1 และ A2 ต่างก็มี Load Balancer ของตนเอง และอยู่ใน Physical Machine ที่แยกกัน (กรณีใช้ Virtual Machine) ถ้าเอาโหดๆหน่อย ผมจะจัดให้เซอร์เวอร์ 3 ตัว A2 ตั้งอยู่ใน Data Center มากกว่าหนึ่งที่ เผื่อที่นึงไฟไหม้
  2. A1 และ A2 มีระบบการ Deploy ที่แยกกัน (ใช้ Deployment Script เดียวกัน แต่เวลา Deploy ไม่ Deploy พร้อมกัน)

เวลาผมทำการ Deploy ผมจะทำการ Deploy ลง 7 เซอร์เวอร์ใน A1 ก่อน แล้วทำการเช็คว่า Metrics และค่าต่างๆปกติดี ระบบยังใช้งานได้ ก่อน Deploy ลงในอีก 3 เซอร์เวอร์ของ A2

ดังนั้น หากโค้ดมีความผิดพลาด ระบบ C จะไม่ได้รับผลกระทบ เพราะเราจะตรวจจับเจอก่อนเอาเข้า A2

ข้อเสียของ Bulkhead

ในโลกของการออกแบบระบบ แทบทุกอย่างจะเป็น Trade-off ไม่มีอะไรดีๆที่ได้มาฟรีๆครับ

  1. อัตรา Server Utilization ลดลง แทนที่เราจะสามารถกระจายโหลดในระบบทั้งหมดไปใน 10 เซอร์เวอร์เวลาที่ Peak เราจะต้องกระจายแยก 7:3 กัน สมมติว่าระบบ C จะเรียกใช้งาน A หนักๆช่วงตอนกลางวัน แต่ B จะเป็นช่วงกลางคืน หากแชร์โหลดบนระบบเดียวกัน เราอาจจะใช้เครื่องน้อยกว่า 10 เครื่องได้
  2. ความซับซ้อนในการ Maintenance เพิ่มขึ้น แม้ในรูปจะดูง่ายมาก แต่การ Maintenance ทั้งหมดจะต้องทำเพิ่มขึ้นเท่าตัว เช่น Deploy, LoadBalancer, Configuration, Logging ซึ่งหากไม่ได้มีสคริป Deployment Automatation ที่ดีๆอยู่ จะทำให้การจัดการยากขึ้น และมีโอกาสเกิด Human Error สูงกว่า

ในมุมมองของผม ค่าใช้จ่ายเรื่อง Hardware เดี๋ยวนี้ต่ำมาก เมื่อเทียบกับค่าจ้างโปรแกรมเมอร์ (ที่อเมริกาหรือยุโรป) ดังนั้น ข้อเสียหนักๆของ Bulkhead คือเรื่องความซับซ้อนมากกว่า

สรุป

ไอเดียหลักของ Bulkhead คือการแยกส่วน (Compartmentalisation) ในระดับของเซอร์เวอร์ให้แยกออกจากกันโดยสิ้นเชิง หากมีส่วนใดส่วนหนึ่งพัง อีกส่วนควรจะทำงานต่อได้

โดยหากระบบที่เรียกใช้บางตัวมีความสำคัญมาก เราสามารถกันส่วนหนึ่งๆให้ตัวนั้นเลย (ในกรณีของ C) ซึ่งจะช่วยการันตีว่า C จะไม่ได้รับผลกระทบจากพฤติกรรมของระบบอื่นๆ (B, D, E, F)

แน่นอน การออกแบบแบบนี้ จะทำให้อัตรา Server Utilization ต่ำลง ทำให้อาจต้องการเซอร์เวอร์มากขึ้น และการ Maintenance ก็จะมีรายละเอียดเยอะขึ้น จึงควรมีการทำ Deployment Automation ให้พร้อมก่อนแต่แรก