อะโลฮ่า ผู้คน! ฉันชื่อ Oleg Anastasyev ฉันทำงานที่ Odnoklassniki ในทีมแพลตฟอร์ม นอกจากฉันแล้ว ยังมีฮาร์ดแวร์อีกมากมายที่ทำงานใน Odnoklassniki เรามีศูนย์ข้อมูลสี่แห่งพร้อมชั้นวางประมาณ 500 ตู้พร้อมเซิร์ฟเวอร์มากกว่า 8 เครื่อง เมื่อถึงจุดหนึ่ง เราตระหนักว่าการเปิดตัวระบบการจัดการใหม่จะช่วยให้เราสามารถโหลดอุปกรณ์ได้อย่างมีประสิทธิภาพมากขึ้น อำนวยความสะดวกในการจัดการการเข้าถึง ทำให้การกระจายทรัพยากรคอมพิวเตอร์ (อีกครั้ง) เป็นแบบอัตโนมัติ เร่งความเร็วในการเปิดตัวบริการใหม่ และเร่งการตอบสนอง สู่อุบัติเหตุครั้งใหญ่
มันมาจากอะไร?
นอกจากฉันและฮาร์ดแวร์อีกจำนวนหนึ่งแล้ว ยังมีคนที่ทำงานกับฮาร์ดแวร์นี้อีกด้วย เช่น วิศวกรที่อยู่ในศูนย์ข้อมูลโดยตรง เครือข่ายที่ติดตั้งซอฟต์แวร์เครือข่าย ผู้ดูแลระบบหรือ SRE ที่ให้ความยืดหยุ่นของโครงสร้างพื้นฐาน และทีมพัฒนา แต่ละคนมีหน้าที่รับผิดชอบส่วนหนึ่งของฟังก์ชันของพอร์ทัล ซอฟต์แวร์ที่พวกเขาสร้างขึ้นทำงานในลักษณะนี้:
ได้รับคำขอของผู้ใช้ทั้งที่ด้านหน้าของพอร์ทัลหลัก
แต่ละบริการเหล่านี้มีการปรับใช้บนเครื่องหลายเครื่อง และแต่ละบริการมีนักพัฒนาที่รับผิดชอบซึ่งรับผิดชอบการทำงานของโมดูล การทำงาน และการพัฒนาเทคโนโลยี บริการทั้งหมดนี้ทำงานบนเซิร์ฟเวอร์ฮาร์ดแวร์ และจนกระทั่งเมื่อไม่นานมานี้ เราได้เปิดตัวงานเดียวต่อเซิร์ฟเวอร์ กล่าวคือ เป็นงานเฉพาะสำหรับงานเฉพาะ
ทำไมเป็นอย่างนั้น? วิธีนี้มีข้อดีหลายประการ:
- โล่งใจ การจัดการมวลชน. สมมติว่างานต้องใช้ไลบรารี การตั้งค่าบางอย่าง จากนั้นเซิร์ฟเวอร์จะถูกกำหนดให้กับกลุ่มใดกลุ่มหนึ่งโดยเฉพาะ โดยมีการอธิบายนโยบาย cfengine สำหรับกลุ่มนี้ (หรือได้มีการอธิบายไว้แล้ว) และการกำหนดค่านี้จะถูกเผยแพร่จากส่วนกลางและโดยอัตโนมัติไปยังเซิร์ฟเวอร์ทั้งหมดในกลุ่มนี้
- ตัวย่อ การวินิจฉัย. สมมติว่าคุณดูที่โหลดที่เพิ่มขึ้นบนโปรเซสเซอร์กลาง และตระหนักว่าโหลดนี้สามารถสร้างได้จากงานที่รันบนโปรเซสเซอร์ฮาร์ดแวร์นี้เท่านั้น การค้นหาคนที่จะตำหนิสิ้นสุดลงอย่างรวดเร็ว
- ตัวย่อ การตรวจสอบ. หากมีบางอย่างผิดปกติกับเซิร์ฟเวอร์ มอนิเตอร์จะรายงานสิ่งนั้น และคุณจะรู้แน่ชัดว่าใครถูกตำหนิ
บริการที่ประกอบด้วยแบบจำลองหลายตัวได้รับการจัดสรรเซิร์ฟเวอร์หลายเครื่อง - หนึ่งเครื่องสำหรับแต่ละเครื่อง จากนั้น ทรัพยากรคอมพิวเตอร์สำหรับบริการจะได้รับการจัดสรรอย่างง่ายดาย: จำนวนเซิร์ฟเวอร์ที่บริการมี จำนวนทรัพยากรสูงสุดที่สามารถใช้ได้ “ง่าย” ในที่นี้ไม่ได้หมายความว่าใช้งานง่าย แต่ในแง่ที่ว่าการจัดสรรทรัพยากรทำได้ด้วยตนเอง
แนวทางนี้ยังทำให้เราสามารถทำได้ การกำหนดค่าเหล็กพิเศษ สำหรับงานที่ทำงานบนเซิร์ฟเวอร์นี้ หากงานจัดเก็บข้อมูลจำนวนมาก เราจะใช้เซิร์ฟเวอร์ 4U พร้อมแชสซีที่มีดิสก์ 38 แผ่น หากงานนี้เป็นงานที่ใช้การคำนวณเพียงอย่างเดียว เราก็สามารถซื้อเซิร์ฟเวอร์ 1U ที่ราคาถูกกว่าได้ นี่คือประสิทธิภาพในการคำนวณ เหนือสิ่งอื่นใด วิธีการนี้ช่วยให้เราใช้เครื่องน้อยลงสี่เท่าและมีภาระเทียบเท่ากับโซเชียลเน็ตเวิร์กที่เป็นมิตรเพียงเครือข่ายเดียว
ประสิทธิภาพในการใช้ทรัพยากรคอมพิวเตอร์ควรรับประกันประสิทธิภาพทางเศรษฐกิจ หากเราดำเนินการจากสมมติฐานที่ว่าสิ่งที่แพงที่สุดคือเซิร์ฟเวอร์ เป็นเวลานานแล้วที่ฮาร์ดแวร์มีราคาแพงที่สุด และเราพยายามอย่างมากในการลดราคาของฮาร์ดแวร์ โดยมาพร้อมกับอัลกอริธึมความทนทานต่อข้อผิดพลาดเพื่อลดข้อกำหนดด้านความน่าเชื่อถือของฮาร์ดแวร์ และวันนี้เรามาถึงขั้นที่ราคาของเซิร์ฟเวอร์หยุดชี้ขาดแล้ว หากคุณไม่พิจารณาสิ่งแปลกใหม่ล่าสุดการกำหนดค่าเฉพาะของเซิร์ฟเวอร์ในแร็คก็ไม่สำคัญ ตอนนี้เรามีปัญหาอื่น - ราคาของพื้นที่ที่เซิร์ฟเวอร์ครอบครองในศูนย์ข้อมูลนั่นคือพื้นที่ในชั้นวาง
เมื่อตระหนักว่าเป็นกรณีนี้ เราจึงตัดสินใจคำนวณว่าเราใช้ชั้นวางได้อย่างมีประสิทธิภาพเพียงใด
เราเอาราคาของเซิร์ฟเวอร์ที่ทรงพลังที่สุดมาจากราคาที่สมเหตุสมผลในเชิงเศรษฐกิจ โดยคำนวณจำนวนเซิร์ฟเวอร์ดังกล่าวที่เราสามารถวางบนชั้นวางได้ จำนวนงานที่เราจะรันบนเซิร์ฟเวอร์เหล่านั้นโดยอิงจากรุ่นเก่า “หนึ่งเซิร์ฟเวอร์ = หนึ่งงาน” และจำนวนดังกล่าว งานสามารถใช้อุปกรณ์ได้ พวกเขานับแล้วน้ำตาไหล ปรากฎว่าประสิทธิภาพในการใช้ชั้นวางของเราอยู่ที่ประมาณ 11% ข้อสรุปที่ชัดเจนคือ เราจำเป็นต้องเพิ่มประสิทธิภาพการใช้ศูนย์ข้อมูล ดูเหมือนว่าวิธีแก้ปัญหาจะชัดเจน: คุณต้องรันงานหลายอย่างบนเซิร์ฟเวอร์เครื่องเดียวในคราวเดียว แต่นี่คือจุดเริ่มต้นของความยากลำบาก
การกำหนดค่าจำนวนมากมีความซับซ้อนมากขึ้นอย่างมาก - ขณะนี้เป็นไปไม่ได้ที่จะกำหนดกลุ่มใดกลุ่มหนึ่งให้กับเซิร์ฟเวอร์ ท้ายที่สุดแล้ว ตอนนี้สามารถเปิดใช้งานงานหลายคำสั่งที่แตกต่างกันได้บนเซิร์ฟเวอร์เดียว นอกจากนี้ การกำหนดค่าอาจขัดแย้งกันสำหรับแอปพลิเคชันที่แตกต่างกัน การวินิจฉัยยังมีความซับซ้อนมากขึ้นอีกด้วย หากคุณเห็นว่ามีการใช้ CPU หรือดิสก์เพิ่มขึ้นบนเซิร์ฟเวอร์ คุณจะไม่รู้ว่างานใดที่สร้างปัญหา
แต่สิ่งสำคัญคือไม่มีการแยกระหว่างงานที่ทำงานบนเครื่องเดียวกัน ตัวอย่างเช่น นี่คือกราฟของเวลาตอบสนองโดยเฉลี่ยของงานเซิร์ฟเวอร์ก่อนและหลังการเปิดตัวแอปพลิเคชันการคำนวณอื่นบนเซิร์ฟเวอร์เดียวกัน โดยไม่เกี่ยวข้องกับแอปพลิเคชันแรกเลย - เวลาตอบสนองของงานหลักเพิ่มขึ้นอย่างมีนัยสำคัญ
แน่นอนว่าคุณต้องรันงานทั้งในคอนเทนเนอร์หรือในเครื่องเสมือน เนื่องจากงานเกือบทั้งหมดของเราทำงานภายใต้ระบบปฏิบัติการเดียว (Linux) หรือได้รับการดัดแปลงมา เราจึงไม่จำเป็นต้องรองรับระบบปฏิบัติการที่แตกต่างกันมากมาย ดังนั้นจึงไม่จำเป็นต้องมีการจำลองเสมือน เนื่องจากมีค่าใช้จ่ายเพิ่มเติม จึงมีประสิทธิภาพน้อยกว่าการใช้คอนเทนเนอร์
ในการใช้งานคอนเทนเนอร์สำหรับการรันงานโดยตรงบนเซิร์ฟเวอร์ Docker เป็นตัวเลือกที่ดี: อิมเมจระบบไฟล์แก้ปัญหาด้วยการกำหนดค่าที่ขัดแย้งกันได้ดี ความจริงที่ว่ารูปภาพสามารถประกอบด้วยหลายเลเยอร์ช่วยให้เราสามารถลดปริมาณข้อมูลที่จำเป็นในการปรับใช้บนโครงสร้างพื้นฐานได้อย่างมาก โดยแยกส่วนทั่วไปออกเป็นชั้นฐานที่แยกจากกัน จากนั้นเลเยอร์พื้นฐาน (และมีขนาดใหญ่ที่สุด) จะถูกแคชอย่างรวดเร็วทั่วทั้งโครงสร้างพื้นฐานทั้งหมด และเพื่อส่งมอบแอปพลิเคชันและเวอร์ชันต่างๆ มากมาย จะต้องถ่ายโอนเพียงเลเยอร์ขนาดเล็กเท่านั้น
นอกจากนี้ รีจีสทรีสำเร็จรูปและการแท็กรูปภาพใน Docker ยังช่วยให้เรามีพื้นฐานสำเร็จรูปสำหรับการกำหนดเวอร์ชันและการส่งโค้ดไปยังการใช้งานจริง
เช่นเดียวกับเทคโนโลยีอื่นๆ ที่คล้ายคลึงกัน Docker ช่วยให้เราสามารถแยกคอนเทนเนอร์ออกจากกล่องได้ในระดับหนึ่ง ตัวอย่างเช่น การแยกหน่วยความจำ - แต่ละคอนเทนเนอร์ได้รับการจำกัดการใช้หน่วยความจำของเครื่อง ซึ่งเกินกว่านั้นจะไม่กิน คุณยังสามารถแยกคอนเทนเนอร์ตามการใช้งาน CPU ได้อีกด้วย อย่างไรก็ตาม สำหรับเรา ฉนวนมาตรฐานยังไม่เพียงพอ แต่เพิ่มเติมเกี่ยวกับที่ด้านล่าง
การเรียกใช้คอนเทนเนอร์บนเซิร์ฟเวอร์โดยตรงเป็นเพียงส่วนหนึ่งของปัญหาเท่านั้น อีกส่วนหนึ่งเกี่ยวข้องกับการโฮสต์คอนเทนเนอร์บนเซิร์ฟเวอร์ คุณต้องเข้าใจว่าสามารถวางคอนเทนเนอร์ใดบนเซิร์ฟเวอร์ใดได้ นี่ไม่ใช่เรื่องง่าย เพราะต้องวางคอนเทนเนอร์บนเซิร์ฟเวอร์ให้หนาแน่นที่สุดเท่าที่จะเป็นไปได้โดยไม่ลดความเร็วลง การจัดวางดังกล่าวอาจเป็นเรื่องยากจากมุมมองของความทนทานต่อข้อผิดพลาด บ่อยครั้งที่เราต้องการวางแบบจำลองของบริการเดียวกันไว้ในชั้นวางที่แตกต่างกันหรือแม้แต่ในห้องต่างๆ ของศูนย์ข้อมูล ดังนั้นหากชั้นวางหรือห้องหนึ่งล้มเหลว เราจะไม่สูญเสียแบบจำลองบริการทั้งหมดทันที
การกระจายคอนเทนเนอร์ด้วยตนเองไม่ใช่ตัวเลือกเมื่อคุณมีเซิร์ฟเวอร์ 8 ตัวและคอนเทนเนอร์ 8-16 ตัว
นอกจากนี้ เราต้องการให้นักพัฒนามีอิสระมากขึ้นในการจัดสรรทรัพยากร เพื่อให้พวกเขาสามารถโฮสต์บริการของตนในการผลิตได้ด้วยตนเอง โดยไม่ต้องได้รับความช่วยเหลือจากผู้ดูแลระบบ ในเวลาเดียวกัน เราต้องการรักษาการควบคุมเพื่อให้บริการเล็กๆ น้อยๆ บางอย่างไม่กินทรัพยากรทั้งหมดของศูนย์ข้อมูลของเรา
แน่นอนว่าเราต้องการเลเยอร์ควบคุมที่จะทำสิ่งนี้โดยอัตโนมัติ
ดังนั้นเราจึงได้ภาพที่เรียบง่ายและเข้าใจได้ซึ่งสถาปนิกทุกคนชื่นชอบ: สี่เหลี่ยมสามช่อง
one-cloud masters คือคลัสเตอร์เฟลโอเวอร์ที่รับผิดชอบในการจัดการระบบคลาวด์ นักพัฒนาส่งรายการไปยังต้นแบบซึ่งมีข้อมูลทั้งหมดที่จำเป็นในการโฮสต์บริการ ตามนั้น ต้นแบบจะออกคำสั่งให้กับมินเนี่ยนที่เลือก (เครื่องจักรที่ออกแบบมาเพื่อใช้งานคอนเทนเนอร์) มินเนี่ยนมีตัวแทนของเราซึ่งรับคำสั่ง ออกคำสั่งไปยัง Docker และ Docker กำหนดค่าเคอร์เนล linux เพื่อเรียกใช้คอนเทนเนอร์ที่เกี่ยวข้อง นอกเหนือจากการดำเนินการคำสั่งแล้ว เอเจนต์ยังรายงานต่อต้นแบบอย่างต่อเนื่องเกี่ยวกับการเปลี่ยนแปลงในสถานะของทั้งเครื่องมินเนี่ยนและคอนเทนเนอร์ที่ทำงานอยู่บนนั้น
การจัดสรรทรัพยากร
ตอนนี้เรามาดูปัญหาของการจัดสรรทรัพยากรที่ซับซ้อนมากขึ้นสำหรับมินเนี่ยนจำนวนมาก
ทรัพยากรการประมวลผลในระบบคลาวด์เดียวคือ:
- ปริมาณพลังงานของโปรเซสเซอร์ที่ใช้โดยงานเฉพาะ
- จำนวนหน่วยความจำที่พร้อมใช้งานสำหรับงาน
- การรับส่งข้อมูลเครือข่าย มินเนี่ยนแต่ละตัวมีอินเทอร์เฟซเครือข่ายเฉพาะที่มีแบนด์วิธจำกัด ดังนั้นจึงเป็นไปไม่ได้ที่จะกระจายงานโดยไม่คำนึงถึงปริมาณข้อมูลที่พวกมันส่งผ่านเครือข่าย
- ดิสก์ นอกจากนี้เห็นได้ชัดว่าเรายังจัดสรรประเภทของดิสก์ให้กับพื้นที่สำหรับงานเหล่านี้: HDD หรือ SSD ดิสก์สามารถรองรับคำขอจำนวนจำกัดต่อวินาที - IOPS ดังนั้น สำหรับงานที่สร้าง IOPS มากกว่าที่ดิสก์ตัวเดียวสามารถจัดการได้ เรายังจัดสรร "สปินเดิล" อีกด้วย ซึ่งก็คืออุปกรณ์ดิสก์ที่ต้องสงวนไว้สำหรับงานนั้นโดยเฉพาะ
จากนั้นสำหรับบริการบางอย่าง เช่น สำหรับแคชผู้ใช้ เราสามารถบันทึกทรัพยากรที่ใช้ในลักษณะนี้: คอร์โปรเซสเซอร์ 400 ตัว, หน่วยความจำ 2,5 TB, การรับส่งข้อมูล 50 Gbit/s ทั้งสองทิศทาง, พื้นที่ HDD 6 TB ที่อยู่บน 100 สปินเดิล หรือในรูปแบบที่คุ้นเคยกว่านี้:
alloc:
cpu: 400
mem: 2500
lan_in: 50g
lan_out: 50g
hdd:100x6T
ทรัพยากรบริการแคชผู้ใช้ใช้เพียงส่วนหนึ่งของทรัพยากรที่มีอยู่ทั้งหมดในโครงสร้างพื้นฐานการใช้งานจริง ดังนั้นฉันต้องการตรวจสอบให้แน่ใจว่าแคชผู้ใช้ไม่ได้ใช้ทรัพยากรมากกว่าที่จัดสรรไว้โดยฉับพลันเนื่องจากข้อผิดพลาดของตัวดำเนินการหรือไม่ นั่นคือเราต้องจำกัดทรัพยากร แต่เราจะผูกโควต้ากับอะไรได้บ้าง?
กลับไปที่ไดอะแกรมที่เรียบง่ายอย่างมากของการโต้ตอบของส่วนประกอบแล้ววาดใหม่พร้อมรายละเอียดเพิ่มเติม - เช่นนี้
สิ่งที่ดึงดูดสายตาของคุณ:
- ส่วนหน้าของเว็บและเพลงใช้คลัสเตอร์ที่แยกออกจากกันของแอปพลิเคชันเซิร์ฟเวอร์เดียวกัน
- เราสามารถแยกแยะความแตกต่างของเลเยอร์ลอจิคัลที่เป็นของคลัสเตอร์เหล่านี้ได้: ส่วนหน้า แคช การจัดเก็บข้อมูล และเลเยอร์การจัดการ
- ส่วนหน้ามีความหลากหลาย ประกอบด้วยระบบย่อยการทำงานที่แตกต่างกัน
- แคชยังสามารถกระจัดกระจายไปทั่วระบบย่อยที่มีข้อมูลแคชอยู่
มาวาดภาพใหม่อีกครั้ง:
บ้า! ใช่ เราเห็นลำดับชั้น! ซึ่งหมายความว่าคุณสามารถกระจายทรัพยากรเป็นชิ้นใหญ่ได้: มอบหมายนักพัฒนาที่รับผิดชอบให้กับโหนดของลำดับชั้นนี้ที่สอดคล้องกับระบบย่อยการทำงาน (เช่น "เพลง" ในภาพ) และแนบโควต้าในระดับเดียวกันของลำดับชั้น ลำดับชั้นนี้ยังช่วยให้เราจัดระเบียบบริการได้อย่างยืดหยุ่นมากขึ้นเพื่อความสะดวกในการจัดการ ตัวอย่างเช่น เราแบ่งเว็บทั้งหมด เนื่องจากนี่เป็นกลุ่มเซิร์ฟเวอร์ที่ใหญ่มาก ออกเป็นหลายกลุ่มเล็กๆ ดังที่แสดงในภาพเป็น group1, group2
ด้วยการลบบรรทัดพิเศษออก เราสามารถเขียนแต่ละโหนดของรูปภาพของเราให้อยู่ในรูปแบบที่เรียบยิ่งขึ้น: group1.web.front, api.music.front, ผู้ใช้ cache.cache.
นี่คือวิธีที่เรามาถึงแนวคิดของ "คิวแบบลำดับชั้น" มีชื่อเหมือน "group1.web.front" มีการกำหนดโควต้าสำหรับทรัพยากรและสิทธิ์ผู้ใช้ เราจะให้สิทธิ์แก่บุคคลจาก DevOps ในการส่งบริการไปยังคิว และพนักงานดังกล่าวสามารถเปิดบางสิ่งในคิวได้ และบุคคลจาก OpsDev จะมีสิทธิ์ของผู้ดูแลระบบ และตอนนี้เขาสามารถจัดการคิว มอบหมายผู้คนที่นั่นได้ ให้สิทธิ์แก่บุคคลเหล่านี้ ฯลฯ บริการที่ทำงานบนคิวนี้จะทำงานภายในโควต้าของคิว หากโควต้าการประมวลผลของคิวไม่เพียงพอที่จะดำเนินการบริการทั้งหมดในคราวเดียว พวกเขาจะถูกดำเนินการตามลำดับ ดังนั้นจึงสร้างคิวขึ้นมาเอง
มาดูบริการกันดีกว่า บริการมีชื่อที่มีคุณสมบัติครบถ้วน ซึ่งจะรวมชื่อของคิวไว้ด้วยเสมอ จากนั้นเว็บเซอร์วิสส่วนหน้าก็จะมีชื่อ ตกลง-web.group1.web.front. และบริการแอปพลิเคชันเซิร์ฟเวอร์ที่เข้าถึงจะถูกเรียก ตกลง-app.group1.web.front. แต่ละบริการจะมีรายการซึ่งระบุข้อมูลที่จำเป็นทั้งหมดสำหรับการวางตำแหน่งบนเครื่องเฉพาะ เช่น จำนวนทรัพยากรที่งานนี้ใช้ การกำหนดค่าใดที่จำเป็นสำหรับงานนี้ จำนวนแบบจำลองที่ควรมี คุณสมบัติในการจัดการความล้มเหลวของบริการนี้ และหลังจากวางบริการบนเครื่องโดยตรงแล้ว อินสแตนซ์ก็จะปรากฏขึ้น นอกจากนี้ยังมีการตั้งชื่ออย่างไม่คลุมเครือ - เป็นหมายเลขอินสแตนซ์และชื่อบริการ: 1.ok-web.group1.web.front, 2.ok-web.group1.web.front, …
สะดวกมาก: เมื่อดูเฉพาะชื่อคอนเทนเนอร์ที่ทำงานอยู่เราก็สามารถค้นพบอะไรมากมายได้ทันที
ตอนนี้เรามาดูกันว่าอินสแตนซ์เหล่านี้ทำงานอย่างไรจริงๆ: งาน
ชั้นเรียนแยกงาน
งานทั้งหมดใน OK (และอาจทุกที่) สามารถแบ่งออกเป็นกลุ่มได้:
- งานหน่วงเวลาสั้น - prod. สำหรับงานและบริการดังกล่าว ความล่าช้าในการตอบสนอง (เวลาแฝง) มีความสำคัญมาก โดยระบบจะประมวลผลคำขอแต่ละรายการได้เร็วเพียงใด ตัวอย่างของงาน: ส่วนหน้าของเว็บ แคช แอปพลิเคชันเซิร์ฟเวอร์ ที่เก็บข้อมูล OLTP ฯลฯ
- ปัญหาการคำนวณ - แบทช์. ในที่นี้ ความเร็วในการประมวลผลของแต่ละคำขอนั้นไม่สำคัญ สำหรับพวกเขา สิ่งสำคัญคือจำนวนการคำนวณที่งานนี้จะทำในช่วงเวลาหนึ่ง (ยาวนาน) (ปริมาณงาน) สิ่งเหล่านี้จะเป็นงานใดๆ ของ MapReduce, Hadoop, การเรียนรู้ของเครื่อง, สถิติ
- งานเบื้องหลัง - ไม่ได้ใช้งาน. สำหรับงานดังกล่าว เวลาแฝงหรือปริมาณงานไม่สำคัญมาก ซึ่งรวมถึงการทดสอบต่างๆ การโยกย้าย การคำนวณใหม่ และการแปลงข้อมูลจากรูปแบบหนึ่งไปยังอีกรูปแบบหนึ่ง ในอีกด้านหนึ่ง มันคล้ายกับการคำนวณ ในทางกลับกัน มันไม่สำคัญสำหรับเราว่ามันจะเสร็จเร็วแค่ไหน
มาดูกันว่างานดังกล่าวใช้ทรัพยากรอย่างไร เช่น โปรเซสเซอร์กลาง
งานล่าช้าระยะสั้น งานดังกล่าวจะมีรูปแบบการใช้ CPU คล้ายกับสิ่งนี้:
ได้รับการร้องขอจากผู้ใช้เพื่อการประมวลผล งานเริ่มใช้คอร์ CPU ที่มีอยู่ทั้งหมด ประมวลผล ส่งคืนการตอบกลับ รอคำขอถัดไป และหยุด คำขอถัดไปมาถึง - เราเลือกทุกสิ่งที่มีอยู่อีกครั้งคำนวณและรอคำขอถัดไป
เพื่อรับประกันเวลาแฝงขั้นต่ำสำหรับงานดังกล่าว เราต้องใช้ทรัพยากรสูงสุดที่ใช้และจองจำนวนคอร์ที่ต้องการบนมินเนี่ยน (เครื่องที่จะดำเนินงาน) จากนั้นสูตรการจองสำหรับปัญหาของเราจะเป็นดังนี้:
alloc: cpu = 4 (max)
และถ้าเรามีเครื่องมินเนียนที่มี 16 คอร์ เราก็สามารถวางงานดังกล่าวได้สี่งานพอดี เราทราบเป็นพิเศษว่าปริมาณการใช้โปรเซสเซอร์โดยเฉลี่ยของงานดังกล่าวมักจะต่ำมาก ซึ่งเห็นได้ชัด เนื่องจากงานส่วนใหญ่ต้องรอคำขอและไม่ทำอะไรเลย
งานการคำนวณ รูปแบบจะแตกต่างออกไปเล็กน้อย:
การใช้ทรัพยากร CPU โดยเฉลี่ยสำหรับงานดังกล่าวค่อนข้างสูง บ่อยครั้งที่เราต้องการให้งานการคำนวณเสร็จสิ้นภายในระยะเวลาที่กำหนด ดังนั้นเราจึงจำเป็นต้องจองจำนวนตัวประมวลผลขั้นต่ำที่จำเป็นเพื่อให้การคำนวณทั้งหมดเสร็จสิ้นภายในระยะเวลาที่ยอมรับได้ สูตรการจองจะมีลักษณะดังนี้:
alloc: cpu = [1,*)
“กรุณาวางมันลงบนมินเนี่ยนที่มีแกนว่างอย่างน้อยหนึ่งแกน และมากเท่าที่มี มันจะกลืนกินทุกสิ่ง”
ที่นี่ประสิทธิภาพการใช้งานดีกว่างานที่มีความล่าช้าเล็กน้อยอยู่แล้ว แต่ประโยชน์ที่ได้รับจะยิ่งใหญ่กว่ามากหากคุณรวมงานทั้งสองประเภทไว้ในเครื่องมินเนี่ยนเครื่องเดียวและกระจายทรัพยากรไปได้ทุกที่ เมื่องานที่มีการหน่วงเวลาสั้น ๆ ต้องใช้ตัวประมวลผล งานนั้นจะได้รับทันที และเมื่อทรัพยากรไม่จำเป็นอีกต่อไป งานเหล่านั้นจะถูกโอนไปยังงานคำนวณ เช่น บางอย่างเช่นนี้:
แต่จะทำอย่างไร?
ก่อนอื่น มาดู prod และการจัดสรรของมันกันก่อน: cpu = 4 เราต้องจองสี่คอร์ไว้ ในการรัน Docker สามารถทำได้สองวิธี:
- การใช้ตัวเลือก
--cpuset=1-4
กล่าวคือจัดสรรคอร์เฉพาะสี่คอร์บนเครื่องให้กับงาน - ที่จะใช้
--cpuquota=400_000 --cpuperiod=100_000
กำหนดโควต้าสำหรับเวลาประมวลผล เช่น ระบุว่าทุก ๆ 100 มิลลิวินาทีของเวลาจริงงานใช้เวลาประมวลผลไม่เกิน 400 มิลลิวินาที ได้รับสี่คอร์เดียวกัน
แต่วิธีการใดต่อไปนี้เหมาะสม?
cpuset ดูน่าสนใจทีเดียว งานนี้มีคอร์เฉพาะสี่คอร์ ซึ่งหมายความว่าแคชของโปรเซสเซอร์จะทำงานได้อย่างมีประสิทธิภาพมากที่สุดเท่าที่จะเป็นไปได้ สิ่งนี้ก็มีข้อเสียเช่นกัน: เราจะต้องทำหน้าที่กระจายการคำนวณไปยังคอร์ที่ไม่ได้โหลดของเครื่องแทนที่จะเป็นระบบปฏิบัติการ และนี่เป็นงานที่ค่อนข้างไม่สำคัญ โดยเฉพาะอย่างยิ่งถ้าเราพยายามวางงานแบบแบตช์บน เครื่องจักร. การทดสอบแสดงให้เห็นว่าตัวเลือกที่มีโควต้าเหมาะสมกว่าที่นี่: วิธีนี้ทำให้ระบบปฏิบัติการมีอิสระมากขึ้นในการเลือกคอร์เพื่อดำเนินงานในขณะปัจจุบันและกระจายเวลาโปรเซสเซอร์ได้อย่างมีประสิทธิภาพมากขึ้น
มาดูวิธีการจอง Docker ตามจำนวนคอร์ขั้นต่ำกัน โควต้าสำหรับงานแบตช์ไม่สามารถใช้งานได้อีกต่อไป เนื่องจากไม่จำเป็นต้องจำกัดจำนวนสูงสุด แค่รับประกันขั้นต่ำก็เพียงพอแล้ว และที่นี่ตัวเลือกก็เข้ากันได้ดี docker run --cpushares
.
เราตกลงกันว่าหากแบตช์ต้องมีการรับประกันอย่างน้อยหนึ่งคอร์ เราก็จะต้องระบุ --cpushares=1024
และหากมีอย่างน้อยสองคอร์ เราก็จะระบุ --cpushares=2048
. การแบ่งใช้ CPU จะไม่รบกวนการกระจายเวลาของโปรเซสเซอร์ แต่อย่างใดตราบเท่าที่มีเวลาเพียงพอ ดังนั้น หากปัจจุบัน prod ไม่ได้ใช้คอร์ทั้งสี่คอร์ ก็ไม่มีอะไรจำกัดงานแบบแบตช์ และพวกเขาสามารถใช้เวลาประมวลผลเพิ่มเติมได้ แต่ในสถานการณ์ที่โปรเซสเซอร์ขาดแคลน หากผลิตภัณฑ์ใช้คอร์ทั้งสี่คอร์และถึงโควต้าแล้ว เวลาโปรเซสเซอร์ที่เหลือจะถูกแบ่งตามสัดส่วนของ cpushares เช่น ในสถานการณ์ที่มีคอร์ว่างสามคอร์ หนึ่งจะเป็น มอบให้กับงานที่มี cpushares 1024 ตัว และอีกสองอันที่เหลือจะถูกมอบให้กับงานที่มี cpushares 2048 ตัว
แต่การใช้โควต้าและส่วนแบ่งไม่เพียงพอ เราจำเป็นต้องตรวจสอบให้แน่ใจว่างานที่มีความล่าช้าเล็กน้อยจะได้รับลำดับความสำคัญมากกว่างานแบตช์เมื่อจัดสรรเวลาประมวลผล หากไม่มีการจัดลำดับความสำคัญ งานแบบแบตช์จะใช้เวลาตัวประมวลผลทั้งหมด ณ เวลาที่ผลิตภัณฑ์ต้องการ ไม่มีตัวเลือกการจัดลำดับความสำคัญของคอนเทนเนอร์ในการรัน Docker แต่นโยบายตัวกำหนดเวลา CPU ของ Linux มีประโยชน์ คุณสามารถอ่านรายละเอียดเกี่ยวกับพวกเขาได้
- กำหนดการ_OTHER
ตามค่าเริ่มต้น กระบวนการของผู้ใช้ปกติทั้งหมดบนเครื่อง Linux จะได้รับ - กำหนด_ชุด
ออกแบบมาสำหรับกระบวนการที่ใช้ทรัพยากรมาก เมื่อวางงานบนตัวประมวลผล จะมีการแนะนำสิ่งที่เรียกว่าการลงโทษในการเปิดใช้งาน: งานดังกล่าวมีโอกาสน้อยที่จะได้รับทรัพยากรของตัวประมวลผล หากงานนั้นถูกใช้งานโดย SCHED_OTHER ในปัจจุบัน - กำหนดการ_IDLE
กระบวนการเบื้องหลังที่มีลำดับความสำคัญต่ำมาก แม้จะต่ำกว่า nice -19 ก็ตาม เราใช้ไลบรารีโอเพ่นซอร์สของเราหนึ่ง-นีโอ เพื่อกำหนดนโยบายที่จำเป็นเมื่อเริ่มคอนเทนเนอร์โดยการเรียก
one.nio.os.Proc.sched_setscheduler( pid, Proc.SCHED_IDLE )
แต่แม้ว่าคุณจะไม่ได้เขียนโปรแกรมใน Java คุณก็สามารถทำได้โดยใช้คำสั่ง chrt:
chrt -i 0 $pid
มาสรุประดับการแยกทั้งหมดของเราไว้ในตารางเดียวเพื่อความชัดเจน:
ชั้นฉนวน
ตัวอย่างการจัดสรร
ตัวเลือกการเรียกใช้นักเทียบท่า
sched_setscheduler chrt*
แยง
ซีพียู = 4
--cpuquota=400000
--cpuperiod=100000
กำหนดการ_OTHER
ชุด
ซีพียู = [1, *)
--cpushares=1024
กำหนด_ชุด
Idle
ซีพียู= [2, *)
--cpushares=2048
กำหนดการ_IDLE
*หากคุณทำ chrt จากภายในคอนเทนเนอร์ คุณอาจต้องใช้ความสามารถ sys_nice เนื่องจากโดยค่าเริ่มต้น Docker จะลบความสามารถนี้ออกเมื่อเริ่มต้นคอนเทนเนอร์
แต่งานไม่เพียงใช้ตัวประมวลผลเท่านั้น แต่ยังรวมถึงการรับส่งข้อมูลซึ่งส่งผลต่อเวลาแฝงของงานเครือข่ายมากกว่าการจัดสรรทรัพยากรตัวประมวลผลที่ไม่ถูกต้อง ดังนั้นเราจึงต้องการได้ภาพการจราจรที่เหมือนกันทุกประการ นั่นคือ เมื่องาน prod ส่งแพ็กเก็ตบางส่วนไปยังเครือข่าย เราจะจำกัดความเร็วสูงสุด (formula จัดสรร: lan=[*,500mbps) ) โดยผลิตภัณฑ์ใดที่สามารถทำได้ และสำหรับแบทช์ เรารับประกันเฉพาะปริมาณงานขั้นต่ำ แต่ไม่จำกัดปริมาณสูงสุด (สูตร จัดสรร: lan=[10Mbps,*) ) ในกรณีนี้ ปริมาณการใช้ผลิตภัณฑ์ควรได้รับลำดับความสำคัญมากกว่างานแบบแบตช์
ที่นี่ Docker ไม่มีพื้นฐานใด ๆ ที่เราสามารถใช้ได้ แต่มันมาเพื่อช่วยเหลือเรา
ที่นี่ 1:0 คือ "root qdisc" ของระเบียบวินัย hsfc 1:1 - คลาสย่อย hsfc ที่มีขีดจำกัดแบนด์วิดท์รวม 8 Gbit/s ซึ่งคลาสย่อยของคอนเทนเนอร์ทั้งหมดจะถูกวางไว้ใต้นั้น 1:2 - คลาสย่อย hsfc เป็นเรื่องปกติสำหรับงานแบทช์และงานที่ไม่ได้ใช้งานทั้งหมดที่มีขีดจำกัด "ไดนามิก" ซึ่งจะกล่าวถึงด้านล่าง คลาสย่อย hsfc ที่เหลือเป็นคลาสเฉพาะสำหรับการรันคอนเทนเนอร์ที่ใช้งานอยู่ในปัจจุบัน โดยมีขีดจำกัดที่สอดคล้องกับไฟล์ Manifest - 450 และ 400 Mbit/s แต่ละคลาส hsfc ถูกกำหนดคิว qdisc fq หรือ fq_codel ขึ้นอยู่กับเวอร์ชันเคอร์เนล Linux เพื่อหลีกเลี่ยงการสูญเสียแพ็กเก็ตระหว่างการรับส่งข้อมูลที่ระเบิด
โดยทั่วไปแล้ว ระเบียบวินัยของ tc ทำหน้าที่จัดลำดับความสำคัญเฉพาะการรับส่งข้อมูลขาออกเท่านั้น แต่เราต้องการจัดลำดับความสำคัญของการรับส่งข้อมูลขาเข้าด้วย เพราะท้ายที่สุดแล้ว งานแบบแบตช์บางงานสามารถเลือกช่องสัญญาณขาเข้าทั้งหมดได้อย่างง่ายดาย เช่น รับข้อมูลอินพุตจำนวนมากสำหรับทำแผนที่&ลด สำหรับสิ่งนี้เราใช้โมดูล
ในระหว่างการทดลอง เราพบว่า hsfc แสดงผลลัพธ์ที่ดีที่สุดเมื่อคลาส 1:2 ของการรับส่งข้อมูลแบบแบตช์/ไม่ได้ใช้งานแบบไม่มีลำดับความสำคัญถูกจำกัดบนเครื่องมินเนี่ยนให้ไม่เกินเลนว่างบางเลน มิฉะนั้น การรับส่งข้อมูลที่ไม่มีความสำคัญจะมีผลกระทบมากเกินไปต่อเวลาแฝงของงานที่ใช้งานจริง miniond จะกำหนดจำนวนแบนด์วิธที่ว่างในปัจจุบันทุกๆ วินาที โดยวัดปริมาณการใช้ข้อมูลโดยเฉลี่ยของงานผลิตภัณฑ์ทั้งหมดของ minion ที่กำหนด และลบออกจากแบนด์วิธอินเทอร์เฟซเครือข่าย โดยมีระยะขอบเล็กน้อยเช่น
แบนด์ถูกกำหนดอย่างเป็นอิสระสำหรับการรับส่งข้อมูลขาเข้าและขาออก และตามค่าใหม่ มิเนียนจะกำหนดค่าขีดจำกัดคลาสที่ไม่มีลำดับความสำคัญใหม่เป็น 1:2
ดังนั้นเราจึงใช้คลาสการแยกทั้งสามคลาส: prod, แบทช์ และไม่ได้ใช้งาน คลาสเหล่านี้มีอิทธิพลอย่างมากต่อลักษณะการปฏิบัติงานของงาน ดังนั้นเราจึงตัดสินใจวางคุณลักษณะนี้ไว้ที่ด้านบนสุดของลำดับชั้น เพื่อที่ว่าเมื่อดูชื่อของคิวลำดับชั้น จะชัดเจนทันทีว่าเรากำลังจัดการกับอะไร:
เพื่อนของเราทุกคน เว็บ и เพลง ส่วนหน้าจะถูกวางไว้ในลำดับชั้นภายใต้ผลิตภัณฑ์ ตัวอย่างเช่น ภายใต้แบทช์ มาวางบริการกันดีกว่า แคตตาล็อกเพลงซึ่งรวบรวมแคตตาล็อกแทร็กจากชุดไฟล์ MP3 ที่อัปโหลดไปยัง Odnoklassniki เป็นระยะ ตัวอย่างของบริการภายใต้การไม่ได้ใช้งานจะเป็นเช่น หม้อแปลงเพลงซึ่งจะทำให้ระดับเสียงเพลงเป็นปกติ
เมื่อลบบรรทัดพิเศษออกอีกครั้ง เราสามารถเขียนชื่อบริการของเราให้สวยงามขึ้นได้โดยการเพิ่มคลาสการแยกงานต่อท้ายชื่อบริการแบบเต็ม: เว็บ.front.prod, แค็ตตาล็อก.music.batch, หม้อแปลงไฟฟ้าเพลงไม่ได้ใช้งาน.
และตอนนี้ เมื่อดูที่ชื่อของบริการ เราไม่เพียงแต่เข้าใจถึงฟังก์ชันที่บริการนั้นทำงานเท่านั้น แต่ยังรวมถึงคลาสการแยก (isolation class) ซึ่งหมายถึงความวิพากษ์วิจารณ์ของบริการ เป็นต้น
ทุกอย่างยอดเยี่ยม แต่มีความจริงอันขมขื่นอยู่ข้อหนึ่ง เป็นไปไม่ได้ที่จะแยกงานที่ทำงานอยู่บนเครื่องเดียวออกไปโดยสิ้นเชิง
สิ่งที่เราจัดการเพื่อให้บรรลุผลสำเร็จ: หากแบทช์ใช้ปริมาณมาก เท่านั้น ทรัพยากรของ CPU ดังนั้นตัวกำหนดเวลา CPU ของ Linux ในตัวจึงทำงานได้ดีมากและแทบไม่มีผลกระทบต่องานจริงเลย แต่ถ้างานแบตช์นี้เริ่มทำงานกับหน่วยความจำอย่างแข็งขันก็แสดงว่าอิทธิพลซึ่งกันและกันก็ปรากฏขึ้นแล้ว สิ่งนี้เกิดขึ้นเนื่องจากงานผลิตภัณฑ์ถูก "ล้าง" ออกจากแคชหน่วยความจำของโปรเซสเซอร์ ส่งผลให้แคชพลาดเพิ่มขึ้น และโปรเซสเซอร์ประมวลผลงานผลิตภัณฑ์ช้าลง งานแบบกลุ่มดังกล่าวสามารถเพิ่มเวลาแฝงของคอนเทนเนอร์ผลิตภัณฑ์ทั่วไปของเราได้ 10%
การแยกการรับส่งข้อมูลทำได้ยากยิ่งขึ้นเนื่องจากการ์ดเครือข่ายสมัยใหม่มีคิวแพ็กเก็ตภายใน หากแพ็กเก็ตจากงานแบตช์ไปถึงที่นั่นก่อน ก็จะเป็นกลุ่มแรกที่ถูกส่งผ่านสายเคเบิล และไม่สามารถทำอะไรกับมันได้
นอกจากนี้ เรายังจัดการได้เพียงแก้ปัญหาการจัดลำดับความสำคัญของการรับส่งข้อมูล TCP เท่านั้น: วิธี hsfc ใช้ไม่ได้กับ UDP และแม้แต่ในกรณีของการรับส่งข้อมูล TCP หากงานแบตช์สร้างการรับส่งข้อมูลจำนวนมาก ก็จะทำให้งานล่าช้าเพิ่มขึ้นประมาณ 10%
ความอดทนต่อความผิดพลาด
เป้าหมายประการหนึ่งเมื่อพัฒนาระบบคลาวด์เดียวคือการปรับปรุงความทนทานต่อความเสียหายของ Odnoklassniki ดังนั้น ต่อไปฉันอยากจะพิจารณารายละเอียดเพิ่มเติมเกี่ยวกับสถานการณ์ที่เป็นไปได้ของความล้มเหลวและอุบัติเหตุ เริ่มจากสถานการณ์ง่ายๆ - คอนเทนเนอร์ล้มเหลว
ตัวคอนเทนเนอร์เองอาจล้มเหลวได้หลายวิธี นี่อาจเป็นการทดลอง ข้อบกพร่อง หรือข้อผิดพลาดบางอย่างในรายการ ซึ่งส่งผลให้งานผลิตภัณฑ์เริ่มใช้ทรัพยากรมากกว่าที่ระบุไว้ในรายการ เรามีกรณี: นักพัฒนาใช้อัลกอริธึมที่ซับซ้อนตัวหนึ่ง แก้ไขมันหลายครั้ง คิดมากจนเกินไป และสับสนมากจนในที่สุดปัญหาก็กลายเป็นวนซ้ำที่ไม่สำคัญมาก และเนื่องจากงาน prod มีลำดับความสำคัญสูงกว่างานอื่นๆ ทั้งหมดในมินเนี่ยนตัวเดียวกัน จึงเริ่มใช้ทรัพยากรตัวประมวลผลที่มีอยู่ทั้งหมด ในสถานการณ์นี้ การแยกหรือโควต้าเวลา CPU จะช่วยประหยัดเวลาได้ หากงานได้รับการจัดสรรโควต้า งานจะไม่กินมากขึ้น ดังนั้นงานแบทช์และงานการผลิตอื่นๆ ที่ทำงานบนเครื่องเดียวกันจึงไม่สังเกตเห็นอะไรเลย
ปัญหาที่สองที่เป็นไปได้คือตู้คอนเทนเนอร์ตกลงมา และนโยบายการรีสตาร์ทที่นี่ช่วยเราได้ ทุกคนรู้จักนโยบายเหล่านี้ Docker เองก็ทำหน้าที่ได้ดีมาก งาน prod เกือบทั้งหมดมีนโยบายการรีสตาร์ทเสมอ บางครั้งเราใช้ on_failure สำหรับงานแบบแบตช์หรือการดีบักคอนเทนเนอร์ผลิตภัณฑ์
คุณจะทำอย่างไรถ้ามินเนี่ยนทั้งตัวไม่พร้อมใช้งาน?
แน่นอนว่ารันคอนเทนเนอร์บนเครื่องอื่น ส่วนที่น่าสนใจคือสิ่งที่เกิดขึ้นกับที่อยู่ IP ที่กำหนดให้กับคอนเทนเนอร์
เราสามารถกำหนดที่อยู่ IP ให้กับคอนเทนเนอร์ได้เช่นเดียวกับเครื่องมินเนี่ยนที่คอนเทนเนอร์เหล่านี้ทำงาน จากนั้น เมื่อคอนเทนเนอร์ถูกเปิดใช้งานบนเครื่องอื่น ที่อยู่ IP จะเปลี่ยนไป และไคลเอนต์ทั้งหมดจะต้องเข้าใจว่าคอนเทนเนอร์ถูกย้ายแล้ว และตอนนี้พวกเขาจำเป็นต้องไปยังที่อยู่อื่น ซึ่งต้องใช้บริการ Service Discovery แยกต่างหาก
บริการ Discovery มีความสะดวก มีโซลูชั่นมากมายในตลาดที่มีระดับความทนทานต่อความเสียหายที่แตกต่างกันสำหรับการจัดการทะเบียนบริการ บ่อยครั้งที่โซลูชันดังกล่าวใช้ตรรกะโหลดบาลานเซอร์ จัดเก็บการกำหนดค่าเพิ่มเติมในรูปแบบของพื้นที่จัดเก็บ KV ฯลฯ
อย่างไรก็ตาม เราต้องการหลีกเลี่ยงความจำเป็นในการใช้รีจิสทรีแยกต่างหาก เนื่องจากนี่จะหมายถึงการแนะนำระบบที่สำคัญซึ่งบริการทั้งหมดใช้งานจริง ซึ่งหมายความว่านี่คือจุดที่มีโอกาสเกิดความล้มเหลว และคุณจำเป็นต้องเลือกหรือพัฒนาโซลูชันที่ทนทานต่อข้อผิดพลาดอย่างมาก ซึ่งเห็นได้ชัดว่าเป็นเรื่องยากมาก ใช้เวลานาน และมีราคาแพง
และข้อเสียเปรียบที่สำคัญอีกประการหนึ่ง: เพื่อให้โครงสร้างพื้นฐานเก่าของเราทำงานร่วมกับโครงสร้างพื้นฐานใหม่ได้ เราจะต้องเขียนงานทั้งหมดใหม่ทั้งหมดเพื่อใช้ระบบ Service Discovery บางประเภท มีงานมากมาย และในบางแห่งแทบจะเป็นไปไม่ได้เลยเมื่อพูดถึงอุปกรณ์ระดับต่ำที่ทำงานในระดับเคอร์เนลของ OS หรือทำงานกับฮาร์ดแวร์โดยตรง การใช้งานฟังก์ชันนี้โดยใช้รูปแบบโซลูชันที่กำหนดไว้ เช่น
ในคลาวด์เดียว IP จะติดตามคอนเทนเนอร์ กล่าวคือ แต่ละอินสแตนซ์งานจะมีที่อยู่ IP ของตัวเอง ที่อยู่นี้เป็น “คงที่”: ถูกกำหนดให้กับแต่ละอินสแตนซ์เมื่อมีการส่งบริการไปยังคลาวด์เป็นครั้งแรก หากบริการมีจำนวนอินสแตนซ์ที่แตกต่างกันตลอดอายุการใช้งาน ในท้ายที่สุด บริการนั้นจะถูกกำหนดที่อยู่ IP ให้มากเท่ากับจำนวนอินสแตนซ์สูงสุด
ต่อจากนั้น ที่อยู่เหล่านี้จะไม่เปลี่ยนแปลง: ได้รับการมอบหมายเพียงครั้งเดียวและยังคงมีอยู่ตลอดอายุของบริการในการผลิต ที่อยู่ IP ติดตามคอนเทนเนอร์ทั่วทั้งเครือข่าย หากคอนเทนเนอร์ถูกโอนไปยังมินเนี่ยนตัวอื่น ที่อยู่จะตามมา
ดังนั้นการแมปชื่อบริการกับรายการที่อยู่ IP จึงเปลี่ยนแปลงน้อยมาก หากคุณดูชื่อของอินสแตนซ์บริการที่เรากล่าวถึงในตอนต้นของบทความอีกครั้ง (1.ok-web.group1.web.front.prod, 2.ok-web.group1.web.front.prod, …) เราจะสังเกตเห็นว่ามีลักษณะคล้ายกับ FQDN ที่ใช้ใน DNS ถูกต้องแล้ว ในการแมปชื่อของอินสแตนซ์บริการกับที่อยู่ IP เราใช้โปรโตคอล DNS นอกจากนี้ DNS นี้ยังส่งคืนที่อยู่ IP ที่สงวนไว้ทั้งหมดของคอนเทนเนอร์ทั้งหมด - ทั้งที่ทำงานอยู่และหยุดทำงานแล้ว (สมมติว่ามีการใช้แบบจำลองสามรายการ และเรามีที่อยู่ห้ารายการที่สงวนไว้ที่นั่น - ทั้งห้ารายการจะถูกส่งคืน) เมื่อลูกค้าได้รับข้อมูลนี้แล้ว จะพยายามสร้างการเชื่อมต่อกับแบบจำลองทั้งห้า - และกำหนดแบบจำลองที่ใช้งานได้ ตัวเลือกในการระบุความพร้อมใช้งานนี้มีความน่าเชื่อถือมากกว่ามาก โดยไม่เกี่ยวข้องกับ DNS หรือ Service Discovery ซึ่งหมายความว่าไม่มีปัญหาที่ยากในการแก้ไขเพื่อให้มั่นใจถึงความเกี่ยวข้องของข้อมูลและความทนทานต่อข้อบกพร่องของระบบเหล่านี้ ยิ่งไปกว่านั้น ในบริการที่สำคัญซึ่งขึ้นอยู่กับการทำงานของพอร์ทัลทั้งหมด เราไม่สามารถใช้ DNS ได้เลย แต่เพียงป้อนที่อยู่ IP ลงในการกำหนดค่า
การใช้การถ่ายโอน IP ดังกล่าวหลังคอนเทนเนอร์อาจไม่ใช่เรื่องเล็กน้อย และเราจะดูวิธีการทำงานด้วยตัวอย่างต่อไปนี้:
สมมติว่าต้นแบบคลาวด์เดียวออกคำสั่งให้มินเนี่ยน M1 วิ่ง 1.ok-web.group1.web.front.prod พร้อมที่อยู่ 1.1.1.1 ใช้งานได้กับมินเนี่ยน
ตอนนี้ สมมติว่าการเชื่อมต่อระหว่าง one-cloud master และ M1 minion ขาดหายไป ต้นแบบคลาวด์เดียวจะดำเนินการตามสมมติฐานที่ว่า M1 ล้มเหลวโดยสิ้นเชิง นั่นคือมันจะออกคำสั่งให้สมุน M2 เปิดตัว web.group1.web.front.prod โดยมีที่อยู่เดียวกัน 1.1.1.1. ตอนนี้เรามีสองเส้นทางที่ขัดแย้งกันบนเครือข่ายสำหรับ 1.1.1.1: บน M1 และบน M2 เพื่อแก้ไขข้อขัดแย้งดังกล่าว เราใช้ Multi Exit Discriminator ซึ่งระบุไว้ในประกาศ BGP เป็นตัวเลขที่แสดงน้ำหนักของเส้นทางที่โฆษณา ในบรรดาเส้นทางที่ขัดแย้งกัน เส้นทางที่มีค่า MED ต่ำกว่าจะถูกเลือก ต้นแบบคลาวด์เดียวรองรับ MED เป็นส่วนสำคัญของที่อยู่ IP ของคอนเทนเนอร์ เป็นครั้งแรกที่มีการเขียนที่อยู่ด้วย MED ที่มีขนาดใหญ่เพียงพอ = 1 ในสถานการณ์ของการถ่ายโอนตู้คอนเทนเนอร์ฉุกเฉินดังกล่าว ต้นแบบจะลด MED และ M000 จะได้รับคำสั่งให้โฆษณาที่อยู่ 000 ด้วย MED = 2 อินสแตนซ์ที่ทำงานบน M1.1.1.1 จะยังคงอยู่ในกรณีนี้ไม่มีการเชื่อมต่อ และชะตากรรมต่อไปของเขาจะไม่สนใจเราเพียงเล็กน้อยจนกว่าการเชื่อมต่อกับปรมาจารย์จะกลับมาอีกครั้ง เมื่อเขาจะถูกหยุดเหมือนครั้งเก่า
อุบัติเหตุ
ระบบการจัดการศูนย์ข้อมูลทั้งหมดจะจัดการกับความล้มเหลวเล็กน้อยที่ยอมรับได้เสมอ คอนเทนเนอร์ล้นเป็นเรื่องปกติในเกือบทุกที่
มาดูวิธีที่เราจัดการกับเหตุฉุกเฉิน เช่น ไฟฟ้าขัดข้องในห้องศูนย์ข้อมูลตั้งแต่หนึ่งห้องขึ้นไป
อุบัติเหตุมีความหมายต่อระบบการจัดการศูนย์ข้อมูลอย่างไร ประการแรก นี่เป็นความล้มเหลวครั้งใหญ่ที่เกิดขึ้นครั้งเดียวในเครื่องจักรหลายเครื่อง และระบบควบคุมจำเป็นต้องย้ายคอนเทนเนอร์จำนวนมากในเวลาเดียวกัน แต่หากภัยพิบัติมีขนาดใหญ่มาก ก็อาจเกิดขึ้นได้ว่างานทั้งหมดไม่สามารถจัดสรรใหม่ให้กับมินเนี่ยนตัวอื่นได้ เนื่องจากความจุทรัพยากรของศูนย์ข้อมูลลดลงต่ำกว่า 100% ของโหลด
บ่อยครั้งที่อุบัติเหตุเกิดขึ้นพร้อมกับความล้มเหลวของชั้นควบคุม สิ่งนี้สามารถเกิดขึ้นได้เนื่องจากความล้มเหลวของอุปกรณ์ แต่บ่อยครั้งมากขึ้นเนื่องจากไม่ได้ทดสอบอุบัติเหตุและชั้นควบคุมเองก็ตกลงเนื่องจากภาระที่เพิ่มขึ้น
คุณสามารถทำอะไรเกี่ยวกับเรื่องทั้งหมดนี้?
การโยกย้ายจำนวนมากหมายความว่ามีกิจกรรม การโยกย้าย และการปรับใช้จำนวนมากที่เกิดขึ้นในโครงสร้างพื้นฐาน การย้ายข้อมูลแต่ละครั้งอาจต้องใช้เวลาพอสมควรในการส่งมอบและแกะคอนเทนเนอร์อิมเมจไปยังมินเนี่ยน เปิดใช้และเตรียมใช้งานคอนเทนเนอร์ ฯลฯ ดังนั้นจึงเป็นที่พึงปรารถนาที่งานที่สำคัญกว่าจะถูกเปิดตัวก่อนงานที่สำคัญน้อยกว่า
มาดูลำดับชั้นของบริการที่เราคุ้นเคยอีกครั้ง และลองตัดสินใจว่างานใดที่เราต้องการดำเนินการก่อน
แน่นอนว่านี่เป็นกระบวนการที่เกี่ยวข้องโดยตรงในการประมวลผลคำขอของผู้ใช้ เช่น ผลิตภัณฑ์ เราระบุสิ่งนี้ด้วย ลำดับความสำคัญของตำแหน่ง — หมายเลขที่สามารถกำหนดให้กับคิวได้ หากคิวมีลำดับความสำคัญสูงกว่า บริการจะถูกจัดไว้ก่อน
ในการผลิตเรากำหนดลำดับความสำคัญที่สูงกว่า 0; ในแบทช์ - ต่ำกว่าเล็กน้อย 100; เมื่อไม่ได้ใช้งาน - ต่ำกว่า 200 ลำดับความสำคัญจะถูกนำไปใช้ตามลำดับชั้น งานทั้งหมดที่ต่ำกว่าในลำดับชั้นจะมีลำดับความสำคัญที่สอดคล้องกัน หากเราต้องการให้แคชภายในผลิตภัณฑ์เปิดใช้งานก่อนส่วนหน้า เราจะกำหนดลำดับความสำคัญให้กับแคช = 0 และคิวย่อยส่วนหน้า = 1 ตัวอย่างเช่น หากเราต้องการให้พอร์ทัลหลักเปิดใช้งานจากส่วนหน้าก่อน และให้เฉพาะส่วนหน้าของเพลงเท่านั้น จากนั้นเราสามารถกำหนดลำดับความสำคัญที่ต่ำกว่าให้กับอันหลัง - 10
ปัญหาต่อไปคือขาดแคลนทรัพยากร ดังนั้น อุปกรณ์จำนวนมาก ทั้งห้องโถงของศูนย์ข้อมูลจึงล้มเหลว และเราเปิดตัวบริการมากมายอีกครั้งจนขณะนี้มีทรัพยากรไม่เพียงพอสำหรับทุกคน คุณต้องตัดสินใจว่าจะเสียสละงานใดเพื่อให้บริการบริการหลักที่สำคัญยังคงดำเนินต่อไป
ต่างจากลำดับความสำคัญของตำแหน่ง เราไม่สามารถเสียสละงานแบตช์ทั้งหมดโดยไม่เลือกปฏิบัติได้ บางส่วนมีความสำคัญสำหรับการดำเนินงานของพอร์ทัล ดังนั้นเราจึงได้เน้นแยกกัน ลำดับความสำคัญของการจอง งาน เมื่อวางแล้ว งานที่มีลำดับความสำคัญสูงกว่าสามารถยึดไว้ก่อนได้ เช่น หยุดงานที่มีลำดับความสำคัญต่ำกว่า หากไม่มีมินเนี่ยนที่ว่างอีกต่อไป ในกรณีนี้ งานที่มีลำดับความสำคัญต่ำอาจจะไม่มีการวางตำแหน่ง กล่าวคือ จะไม่มีมินเนี่ยนที่เหมาะสมอีกต่อไปและมีทรัพยากรว่างเพียงพอ
ในลำดับชั้นของเรา เป็นเรื่องง่ายมากที่จะระบุลำดับความสำคัญของการขอล่วงหน้า เช่น งาน prod และงานแบตช์จะยึดหรือหยุดงานที่ไม่ได้ใช้งาน แต่ไม่ใช่ซึ่งกันและกัน โดยการระบุลำดับความสำคัญสำหรับการไม่ได้ใช้งานเท่ากับ 200 เช่นเดียวกับในกรณีของลำดับความสำคัญของตำแหน่ง เรา สามารถใช้ลำดับชั้นของเราเพื่ออธิบายกฎที่ซับซ้อนมากขึ้นได้ ตัวอย่างเช่น สมมติว่าเราเสียสละฟังก์ชันเพลงหากเรามีทรัพยากรไม่เพียงพอสำหรับเว็บพอร์ทัลหลัก โดยตั้งค่าลำดับความสำคัญสำหรับโหนดที่เกี่ยวข้องให้ต่ำกว่า: 10
อุบัติเหตุ DC ทั้งหมด
เหตุใดศูนย์ข้อมูลทั้งหมดจึงอาจล้มเหลว องค์ประกอบ. เป็นโพสต์ที่ดี
และนี่คือสิ่งที่เราทำเพื่อป้องกันไม่ให้ใครทวีต #alive
กลยุทธ์แรกคือการแยกตัว อินสแตนซ์แบบคลาวด์เดียวแต่ละรายการจะถูกแยกออกและสามารถจัดการเครื่องในศูนย์ข้อมูลแห่งเดียวเท่านั้น กล่าวคือ การสูญเสียระบบคลาวด์เนื่องจากข้อบกพร่องหรือคำสั่งของผู้ปฏิบัติงานที่ไม่ถูกต้อง เท่ากับการสูญเสียศูนย์ข้อมูลเพียงแห่งเดียว เราพร้อมสำหรับสิ่งนี้: เรามีนโยบายการสำรองซึ่งการจำลองแอปพลิเคชันและข้อมูลจะอยู่ในศูนย์ข้อมูลทั้งหมด เราใช้ฐานข้อมูลที่ทนทานต่อข้อผิดพลาดและทดสอบความล้มเหลวเป็นระยะ
ตั้งแต่วันนี้ เรามีศูนย์ข้อมูลสี่แห่ง นั่นหมายความว่ามีอินสแตนซ์ระบบคลาวด์เดียวที่แยกจากกันสี่แห่ง
วิธีการนี้ไม่เพียงแต่ป้องกันความล้มเหลวทางกายภาพเท่านั้น แต่ยังสามารถป้องกันข้อผิดพลาดของผู้ปฏิบัติงานได้อีกด้วย
ปัจจัยมนุษย์สามารถทำอะไรได้อีก? เมื่อผู้ปฏิบัติงานออกคำสั่งแปลกๆ หรืออาจเป็นอันตรายแก่คลาวด์ จู่ๆ เขาอาจถูกขอให้แก้ไขปัญหาเล็กๆ น้อยๆ เพื่อดูว่าเขาคิดได้ดีเพียงใด ตัวอย่างเช่น หากนี่เป็นการหยุดจำนวนมากของเรพลิกาจำนวนมากหรือเป็นเพียงคำสั่งแปลก ๆ ให้ลดจำนวนเรพลิกาหรือเปลี่ยนชื่อของรูปภาพ และไม่ใช่แค่หมายเลขเวอร์ชันในรายการใหม่
ผลของการ
คุณสมบัติที่โดดเด่นของคลาวด์เดียว:
- รูปแบบการตั้งชื่อแบบลำดับชั้นและภาพสำหรับบริการและคอนเทนเนอร์ซึ่งช่วยให้คุณค้นหาได้อย่างรวดเร็วว่างานคืออะไร เกี่ยวข้องกับอะไร ทำงานอย่างไร และใครเป็นผู้รับผิดชอบ
- เราใช้ของเรา เทคนิคการรวมผลิตภัณฑ์และแบทช์งานกับมินเนี่ยนเพื่อปรับปรุงประสิทธิภาพของการแบ่งปันเครื่อง แทนที่จะใช้ cpuset เราใช้โควต้า CPU, การแชร์, นโยบายตัวกำหนดเวลา CPU และ Linux QoS
- ไม่สามารถแยกคอนเทนเนอร์ที่ทำงานบนเครื่องเดียวกันได้อย่างสมบูรณ์ แต่อิทธิพลซึ่งกันและกันยังคงอยู่ภายใน 20%
- การจัดบริการเป็นลำดับชั้นช่วยในการกู้คืนความเสียหายโดยอัตโนมัติ ลำดับความสำคัญของตำแหน่งและใบจอง.
คำถามที่พบบ่อย
ทำไมเราไม่ใช้วิธีแก้ปัญหาสำเร็จรูป?
- การแยกภารกิจในแต่ละคลาสต้องใช้ตรรกะที่แตกต่างกันเมื่อวางไว้บนมินเนี่ยน หากสามารถวางงาน prod ได้โดยเพียงแค่จองทรัพยากร ก็จะต้องวางงานแบบแบตช์และงานว่างเพื่อติดตามการใช้งานจริงของทรัพยากรบนเครื่อง minion
- ความจำเป็นในการคำนึงถึงทรัพยากรที่ใช้โดยงาน เช่น:
- แบนด์วิธเครือข่าย
- ประเภทและ "สปินเดิล" ของดิสก์
- ความจำเป็นในการระบุลำดับความสำคัญของบริการระหว่างการตอบสนองฉุกเฉิน สิทธิ์และโควต้าคำสั่งสำหรับทรัพยากร ซึ่งแก้ไขได้โดยใช้คิวแบบลำดับชั้นในระบบคลาวด์เดียว
- ความจำเป็นในการตั้งชื่อคอนเทนเนอร์โดยมนุษย์เพื่อลดเวลาตอบสนองต่ออุบัติเหตุและเหตุการณ์ต่างๆ
- ความเป็นไปไม่ได้ของการนำ Service Discovery ไปใช้อย่างแพร่หลายเพียงครั้งเดียว ความจำเป็นในการอยู่ร่วมกันเป็นเวลานานกับงานที่โฮสต์บนโฮสต์ฮาร์ดแวร์ - สิ่งที่แก้ไขได้ด้วยที่อยู่ IP "คงที่" ที่อยู่ถัดจากคอนเทนเนอร์ และด้วยเหตุนี้ ความจำเป็นในการบูรณาการที่เป็นเอกลักษณ์กับโครงสร้างพื้นฐานเครือข่ายขนาดใหญ่
ฟังก์ชันทั้งหมดเหล่านี้จำเป็นต้องมีการปรับเปลี่ยนโซลูชันที่มีอยู่อย่างมีนัยสำคัญเพื่อให้เหมาะกับเรา และเมื่อประเมินปริมาณงานแล้ว เราก็ตระหนักว่าเราสามารถพัฒนาโซลูชันของเราเองโดยมีค่าแรงเท่าเดิมโดยประมาณ แต่โซลูชันของคุณจะใช้งานและพัฒนาได้ง่ายกว่ามาก - ไม่มีนามธรรมที่ไม่จำเป็นซึ่งสนับสนุนฟังก์ชันการทำงานที่เราไม่ต้องการ
สำหรับผู้ที่อ่านบรรทัดสุดท้าย ขอขอบคุณสำหรับความอดทนและความสนใจของคุณ!
ที่มา: will.com