บันทึก. แปล: ประวัติศาสตร์ที่เปิดหูเปิดตาของ Omio ซึ่งเป็นผู้รวบรวมการเดินทางในยุโรป นำผู้อ่านตั้งแต่ทฤษฎีพื้นฐานไปจนถึงความซับซ้อนในทางปฏิบัติที่น่าทึ่งของการกำหนดค่า Kubernetes ความคุ้นเคยกับกรณีดังกล่าวไม่เพียงแต่ช่วยเปิดโลกทัศน์ของคุณให้กว้างขึ้นเท่านั้น แต่ยังป้องกันปัญหาที่ไม่สำคัญอีกด้วย
คุณเคยประสบปัญหาแอปพลิเคชันค้าง หยุดตอบสนองต่อการตรวจสุขภาพ และคุณไม่สามารถทราบสาเหตุได้หรือไม่ คำอธิบายที่เป็นไปได้ประการหนึ่งเกี่ยวข้องกับขีดจำกัดโควต้าทรัพยากร CPU นี่คือสิ่งที่เราจะพูดถึงในบทความนี้
TL; DR:
เราขอแนะนำอย่างยิ่งให้ปิดใช้การจำกัด CPU ใน Kubernetes (หรือปิดใช้โควต้า CFS ใน Kubelet) หากคุณใช้เคอร์เนล Linux เวอร์ชันที่มีข้อบกพร่องโควต้า CFS ในแกนกลาง
ในโอมิโอะ โครงสร้างพื้นฐานทั้งหมดได้รับการจัดการโดย Kubernetes. ปริมาณงานแบบมีสถานะและไร้สถานะทั้งหมดของเราทำงานบน Kubernetes โดยเฉพาะ (เราใช้ Google Kubernetes Engine) ในช่วงหกเดือนที่ผ่านมา เราเริ่มสังเกตเห็นการชะลอตัวแบบสุ่ม แอปพลิเคชันค้างหรือหยุดตอบสนองต่อการตรวจสอบสภาพ ขาดการเชื่อมต่อกับเครือข่าย ฯลฯ พฤติกรรมนี้ทำให้เรางงงวยมาเป็นเวลานาน และในที่สุด เราก็ตัดสินใจที่จะแก้ไขปัญหานี้อย่างจริงจัง
สรุปบทความ:
- คำไม่กี่คำเกี่ยวกับคอนเทนเนอร์และ Kubernetes
- วิธีการใช้งานคำขอและขีดจำกัดของ CPU
- ขีดจำกัดของ CPU ทำงานอย่างไรในสภาพแวดล้อมแบบมัลติคอร์
- วิธีติดตามการควบคุมปริมาณ CPU
- การแก้ปัญหาและความแตกต่าง
คำไม่กี่คำเกี่ยวกับคอนเทนเนอร์และ Kubernetes
Kubernetes ถือเป็นมาตรฐานสมัยใหม่ในโลกโครงสร้างพื้นฐาน หน้าที่หลักคือการจัดการคอนเทนเนอร์
ตู้คอนเทนเนอร์
ในอดีต เราต้องสร้างอาร์ติแฟกต์ เช่น Java JARs/WARs, Python Eggs หรือโปรแกรมปฏิบัติการเพื่อให้ทำงานบนเซิร์ฟเวอร์ อย่างไรก็ตาม เพื่อให้ทำงานได้ จำเป็นต้องมีการทำงานเพิ่มเติม เช่น การติดตั้งสภาพแวดล้อมรันไทม์ (Java/Python) วางไฟล์ที่จำเป็นในตำแหน่งที่ถูกต้อง ตรวจสอบให้แน่ใจว่าเข้ากันได้กับระบบปฏิบัติการเวอร์ชันเฉพาะ เป็นต้น กล่าวอีกนัยหนึ่ง จะต้องให้ความสนใจอย่างระมัดระวังต่อการจัดการการกำหนดค่า (ซึ่งมักเป็นสาเหตุให้เกิดความขัดแย้งระหว่างนักพัฒนาและผู้ดูแลระบบ)
คอนเทนเนอร์เปลี่ยนทุกอย่าง ตอนนี้สิ่งประดิษฐ์เป็นอิมเมจคอนเทนเนอร์ มันสามารถแสดงเป็นไฟล์ปฏิบัติการแบบขยายที่มีไม่เพียงแต่โปรแกรมเท่านั้น แต่ยังรวมถึงสภาพแวดล้อมการดำเนินการเต็มรูปแบบ (Java/Python/...) เช่นเดียวกับไฟล์/แพ็คเกจที่จำเป็น ที่ติดตั้งไว้ล่วงหน้าและพร้อมที่จะ วิ่ง. คุณสามารถปรับใช้และรันคอนเทนเนอร์บนเซิร์ฟเวอร์ที่แตกต่างกันได้โดยไม่ต้องมีขั้นตอนเพิ่มเติม
นอกจากนี้ คอนเทนเนอร์ยังทำงานในสภาพแวดล้อมแซนด์บ็อกซ์ของตัวเองอีกด้วย พวกเขามีอะแดปเตอร์เครือข่ายเสมือนของตัวเอง ระบบไฟล์ของตัวเองที่มีการเข้าถึงที่จำกัด ลำดับชั้นของกระบวนการของตัวเอง ข้อ จำกัด ของตัวเองบน CPU และหน่วยความจำ ฯลฯ ทั้งหมดนี้เกิดขึ้นได้ด้วยระบบย่อยพิเศษของเคอร์เนล Linux - เนมสเปซ
Kubernetes
ตามที่ระบุไว้ก่อนหน้านี้ Kubernetes เป็นผู้ควบคุมคอนเทนเนอร์ มันทำงานดังนี้: คุณให้เครื่องจักรจำนวนหนึ่งกับมัน แล้วพูดว่า: “เฮ้ Kubernetes มาเปิดใช้คอนเทนเนอร์ของฉันสิบอินสแตนซ์ที่มีโปรเซสเซอร์ 2 ตัวและหน่วยความจำ 3 GB ต่อตัวแล้วปล่อยให้พวกมันทำงานต่อไป!” Kubernetes จะจัดการส่วนที่เหลือเอง มันจะค้นหาความจุฟรี เปิดใช้คอนเทนเนอร์และรีสตาร์ทหากจำเป็น ออกการอัปเดตเมื่อเปลี่ยนเวอร์ชัน ฯลฯ โดยพื้นฐานแล้ว Kubernetes ช่วยให้คุณสามารถแยกส่วนประกอบฮาร์ดแวร์ออกไป และสร้างระบบที่หลากหลายที่เหมาะสำหรับการปรับใช้และรันแอปพลิเคชัน
Kubernetes จากมุมมองของคนธรรมดา
คำขอและขีดจำกัดใน Kubernetes คืออะไร
โอเค เราได้ครอบคลุมคอนเทนเนอร์และ Kubernetes แล้ว เรายังทราบด้วยว่าตู้คอนเทนเนอร์หลายตู้สามารถอยู่ในเครื่องเดียวกันได้
สามารถเปรียบเทียบกับอพาร์ทเมนต์ส่วนกลางได้ มีผู้เช่าพื้นที่กว้างขวาง (เครื่องจักร/ยูนิต) แก่ผู้เช่า (ตู้คอนเทนเนอร์) หลายราย Kubernetes ทำหน้าที่เป็นนายหน้า คำถามเกิดขึ้นจะป้องกันไม่ให้ผู้เช่าขัดแย้งกันได้อย่างไร? จะเกิดอะไรขึ้นถ้าหนึ่งในนั้นตัดสินใจยืมห้องน้ำครึ่งวัน?
นี่คือจุดที่คำขอและขีดจำกัดเข้ามามีบทบาท ซีพียู ขอร้อง จำเป็นสำหรับวัตถุประสงค์ในการวางแผนเท่านั้น นี่คือสิ่งที่คล้ายกับ "รายการสินค้าที่ต้องการ" ของคอนเทนเนอร์ และใช้เพื่อเลือกโหนดที่เหมาะสมที่สุด ในขณะเดียวกันก็ซีพียู ลิมิตสวิตช์ สามารถเทียบได้กับสัญญาเช่า - ทันทีที่เราเลือกยูนิตสำหรับตู้คอนเทนเนอร์ ไม่ได้ ก้าวข้ามขีดจำกัดที่ตั้งไว้ และนี่คือที่มาของปัญหา...
วิธีนำคำขอและขีดจำกัดไปใช้ใน Kubernetes
Kubernetes ใช้กลไกการควบคุมปริมาณ (ข้ามรอบสัญญาณนาฬิกา) ที่สร้างไว้ในเคอร์เนลเพื่อใช้ขีดจำกัดของ CPU หากแอปพลิเคชันเกินขีดจำกัด การควบคุมปริมาณจะถูกเปิดใช้งาน (เช่น ได้รับรอบ CPU น้อยลง) คำขอและขีดจำกัดของหน่วยความจำได้รับการจัดระเบียบแตกต่างกัน ดังนั้นจึงตรวจพบได้ง่ายกว่า ในการดำเนินการนี้ เพียงตรวจสอบสถานะการรีสตาร์ทครั้งล่าสุดของพ็อด: ไม่ว่าจะเป็น "OOMKilled" หรือไม่ การควบคุมปริมาณ CPU ไม่ใช่เรื่องง่าย เนื่องจาก K8 ทำให้การวัดใช้งานได้ตามการใช้งานเท่านั้น ไม่ใช่ตามกลุ่ม c
คำขอซีพียู
วิธีดำเนินการคำขอ CPU
เพื่อความง่าย ลองดูที่กระบวนการโดยใช้เครื่องที่มี CPU 4 คอร์เป็นตัวอย่าง
K8 ใช้กลไกกลุ่มควบคุม (cgroups) เพื่อควบคุมการจัดสรรทรัพยากร (หน่วยความจำและโปรเซสเซอร์) มีโมเดลแบบลำดับชั้น: รายการย่อยสืบทอดขีดจำกัดของกลุ่มหลัก รายละเอียดการแจกจ่ายจะถูกจัดเก็บไว้ในระบบไฟล์เสมือน (/sys/fs/cgroup
). ในกรณีของโปรเซสเซอร์นี่คือ /sys/fs/cgroup/cpu,cpuacct/*
.
K8s ใช้ไฟล์ cpu.share
เพื่อจัดสรรทรัพยากรโปรเซสเซอร์ ในกรณีของเรา root cgroup ได้รับการแชร์ทรัพยากร CPU 4096 ครั้ง - 100% ของกำลังประมวลผลที่มีอยู่ (1 คอร์ = 1024 ซึ่งเป็นค่าคงที่) กลุ่มรากจะกระจายทรัพยากรตามสัดส่วนโดยขึ้นอยู่กับส่วนแบ่งของผู้สืบทอดที่ลงทะเบียน cpu.share
และพวกเขาก็ทำเช่นเดียวกันกับลูกหลานของพวกเขา ฯลฯ บนโหนด Kubernetes ทั่วไป cgroup รูทมีลูกสามคน: system.slice
, user.slice
и kubepods
. สองกลุ่มย่อยแรกใช้เพื่อกระจายทรัพยากรระหว่างโหลดระบบที่สำคัญและโปรแกรมผู้ใช้ภายนอก K8 อันสุดท้าย - kubepods
— สร้างโดย Kubernetes เพื่อกระจายทรัพยากรระหว่างพ็อด
แผนภาพด้านบนแสดงให้เห็นว่ากลุ่มย่อยที่หนึ่งและสองได้รับแต่ละกลุ่ม 1024 แบ่งปันโดยจัดสรรกลุ่มย่อย kuberpod 4096 หุ้น เป็นไปได้อย่างไร: กลุ่มรูทสามารถเข้าถึงได้เท่านั้น 4096 หุ้นและผลรวมของหุ้นของลูกหลานของเธอเกินจำนวนนี้อย่างมีนัยสำคัญ (6144)? ประเด็นก็คือค่านี้สมเหตุสมผล ดังนั้น Linux scheduler (CFS) จึงใช้เพื่อจัดสรรทรัพยากร CPU ตามสัดส่วน ในกรณีของเรา สองกลุ่มแรกจะได้รับ 680 หุ้นจริง (16,6% ของ 4096) และ kubepod รับส่วนที่เหลือ 2736 หุ้น ในกรณีที่ระบบหยุดทำงาน สองกลุ่มแรกจะไม่ใช้ทรัพยากรที่จัดสรร
โชคดีที่ตัวกำหนดเวลามีกลไกเพื่อหลีกเลี่ยงการสิ้นเปลืองทรัพยากร CPU ที่ไม่ได้ใช้ โดยจะถ่ายโอนความจุ “ที่ไม่ได้ใช้งาน” ไปยังพูลส่วนกลาง ซึ่งจะถูกกระจายไปยังกลุ่มที่ต้องการพลังการประมวลผลเพิ่มเติม (การถ่ายโอนจะเกิดขึ้นเป็นชุดเพื่อหลีกเลี่ยงการสูญเสียการปัดเศษ) วิธีการที่คล้ายกันนี้ใช้กับทายาทของทายาททั้งหมด
กลไกนี้ช่วยให้มั่นใจได้ถึงการกระจายพลังของโปรเซสเซอร์อย่างยุติธรรม และรับประกันว่าจะไม่มีใครประมวลผล "ขโมย" ทรัพยากรจากผู้อื่น
ขีด จำกัด ของ CPU
แม้ว่าการกำหนดค่าขีดจำกัดและคำขอใน K8 จะดูคล้ายกัน แต่การใช้งานก็แตกต่างอย่างสิ้นเชิง: สิ่งนี้ ทำให้เข้าใจผิดมากที่สุด และส่วนที่เป็นเอกสารน้อยที่สุด
K8s มีส่วนร่วม cfs_period_us
и cfs_quota_us
ในไดเร็กทอรี cgroup (ไฟล์ก็อยู่ที่นั่นด้วย cpu.share
).
แตกต่าง cpu.share
โควต้าจะขึ้นอยู่กับ ช่วงเวลาและไม่ขึ้นอยู่กับพลังของโปรเซสเซอร์ที่มีอยู่ cfs_period_us
ระบุระยะเวลาของช่วงเวลา (ยุค) - จะเป็น 100000 μs (100 ms) เสมอ มีตัวเลือกในการเปลี่ยนค่านี้ใน K8s แต่ตอนนี้ใช้ได้เฉพาะในอัลฟ่าเท่านั้น ตัวกำหนดเวลาใช้ยุคเพื่อรีสตาร์ทโควต้าที่ใช้แล้ว ไฟล์ที่สอง cfs_quota_us
ระบุเวลาที่มีอยู่ (โควต้า) ในแต่ละยุค โปรดทราบว่ามีการระบุเป็นไมโครวินาทีด้วย โควต้าอาจเกินความยาวของยุค กล่าวอีกนัยหนึ่ง อาจมากกว่า 100 มิลลิวินาที
ลองดูสองสถานการณ์บนเครื่อง 16 คอร์ (คอมพิวเตอร์ประเภททั่วไปที่เรามีที่ Omio):
สถานการณ์ที่ 1: 2 เธรดและขีดจำกัด 200 ms ไม่มีการควบคุมปริมาณ
สถานการณ์ที่ 2: 10 เธรดและขีดจำกัด 200 มิลลิวินาที การควบคุมปริมาณเริ่มต้นหลังจาก 20 ms การเข้าถึงทรัพยากรตัวประมวลผลจะกลับมาทำงานต่อหลังจากผ่านไปอีก 80 ms
สมมติว่าคุณตั้งค่าขีดจำกัด CPU เป็น 2 เมล็ด; Kubernetes จะแปลค่านี้เป็น 200 ms ซึ่งหมายความว่าคอนเทนเนอร์สามารถใช้เวลา CPU สูงสุด 200ms โดยไม่ต้องควบคุมปริมาณ
และนี่คือจุดเริ่มต้นของความสนุก ตามที่กล่าวไว้ข้างต้น โควต้าที่มีอยู่คือ 200 ms หากคุณทำงานคู่กัน สิบ เธรดบนเครื่อง 12 คอร์ (ดูภาพประกอบสำหรับสถานการณ์ที่ 2) ในขณะที่พ็อดอื่นๆ ทั้งหมดไม่ได้ใช้งาน โควต้าจะหมดในเวลาเพียง 20 มิลลิวินาที (เนื่องจาก 10 * 20 มิลลิวินาที = 200 มิลลิวินาที) และเธรดทั้งหมดของพ็อดนี้จะหยุดทำงาน » (เค้น) ในอีก 80 มิลลิวินาทีข้างหน้า ที่กล่าวไปแล้ว
จะประเมินการควบคุมปริมาณในพ็อดได้อย่างไร
เพียงเข้าสู่ระบบพ็อดและดำเนินการ cat /sys/fs/cgroup/cpu/cpu.stat
.
-
nr_periods
— จำนวนงวดตัวกำหนดตารางเวลาทั้งหมด -
nr_throttled
— จำนวนช่วงเวลาที่จำกัดในองค์ประกอบnr_periods
; -
throttled_time
— เวลาควบคุมปริมาณสะสมในหน่วยนาโนวินาที
เกิดอะไรขึ้นจริงๆ?
เป็นผลให้เราได้รับการควบคุมปริมาณสูงในทุกแอปพลิเคชัน บางครั้งเขาก็เข้า หนึ่งครั้งครึ่ง แกร่งเกินคาด!
สิ่งนี้นำไปสู่ข้อผิดพลาดต่างๆ - ความล้มเหลวในการตรวจสอบความพร้อม, การค้างของคอนเทนเนอร์, การเชื่อมต่อเครือข่ายขาด, การหมดเวลาภายในการโทรบริการ ซึ่งท้ายที่สุดแล้วส่งผลให้เวลาแฝงเพิ่มขึ้นและอัตราข้อผิดพลาดสูงขึ้น
การตัดสินใจและผลที่ตามมา
ทุกอย่างเรียบง่ายที่นี่ เราละทิ้งขีดจำกัดของ CPU และเริ่มอัปเดตเคอร์เนลระบบปฏิบัติการในคลัสเตอร์ให้เป็นเวอร์ชันล่าสุด ซึ่งข้อบกพร่องได้รับการแก้ไขแล้ว จำนวนข้อผิดพลาด (HTTP 5xx) ในบริการของเราลดลงอย่างมากทันที:
ข้อผิดพลาด HTTP 5xx
ข้อผิดพลาด HTTP 5xx สำหรับบริการที่สำคัญรายการหนึ่ง
เวลาตอบสนอง p95
เวลาแฝงของคำขอบริการที่สำคัญ เปอร์เซ็นไทล์ที่ 95
ต้นทุนการดำเนินงาน
จำนวนชั่วโมงที่ใช้อินสแตนซ์
จับคืออะไร?
ตามที่ระบุไว้ในตอนต้นของบทความ:
สามารถเปรียบเทียบได้กับอพาร์ทเมนต์ส่วนกลาง... Kubernetes ทำหน้าที่เป็นนายหน้า แต่จะป้องกันไม่ให้ผู้เช่าขัดแย้งกันได้อย่างไร? จะเกิดอะไรขึ้นถ้าหนึ่งในนั้นตัดสินใจยืมห้องน้ำครึ่งวัน?
นี่คือสิ่งที่จับได้ คอนเทนเนอร์ที่ไม่ระมัดระวังเพียงตัวเดียวอาจกินทรัพยากร CPU ที่มีอยู่ทั้งหมดบนเครื่องได้ หากคุณมีสแต็กแอปพลิเคชันอัจฉริยะ (เช่น JVM, Go, Node VM ได้รับการกำหนดค่าอย่างเหมาะสม) ก็ไม่ใช่ปัญหา: คุณสามารถทำงานในสภาวะดังกล่าวได้เป็นเวลานาน แต่หากแอปพลิเคชันได้รับการปรับให้เหมาะสมไม่ดีหรือไม่ได้รับการปรับให้เหมาะสมเลย (FROM java:latest
) สถานการณ์อาจไม่สามารถควบคุมได้ ที่ Omio เรามี Dockerfiles พื้นฐานแบบอัตโนมัติพร้อมการตั้งค่าเริ่มต้นที่เพียงพอสำหรับสแต็กภาษาหลัก ดังนั้นจึงไม่มีปัญหานี้
เราขอแนะนำให้ติดตามเมตริก
การอ้างอิง
นี่คือเรื่องราวของเรา เนื้อหาต่อไปนี้ช่วยให้เข้าใจสิ่งที่เกิดขึ้นได้อย่างมาก:
-
kernel.org → ตัวกำหนดเวลา CFS ; -
kernel.org → การควบคุมแบนด์วิธ CFS ; -
ทำความเข้าใจกับ Linux Container Scheduling ; -
ทุกสิ่งที่คุณต้องรู้เกี่ยวกับคอนเทนเนอร์ Linux ตอนที่ XNUMX: กลุ่มควบคุม Linux และการแยกกระบวนการ ; -
เรื่องราวความล้มเหลวของ Kubernetes - มองหา "การควบคุมปริมาณ CPU"
รายงานข้อผิดพลาดของ Kubernetes:
-
#51135: หลีกเลี่ยงการตั้งขีดจำกัด CPU สำหรับพ็อดที่รับประกัน ; -
#67577: โควต้า CFS อาจทำให้เกิดการควบคุมปริมาณโดยไม่จำเป็น ; -
CFS ที่ก้าวร้าวมากเกินไป .
คุณเคยพบปัญหาที่คล้ายกันในการปฏิบัติงานของคุณหรือมีประสบการณ์ที่เกี่ยวข้องกับการควบคุมปริมาณในสภาพแวดล้อมการผลิตแบบคอนเทนเนอร์หรือไม่? แบ่งปันเรื่องราวของคุณในความคิดเห็น!
ปล.จากผู้แปล
อ่านเพิ่มเติมในบล็อกของเรา:
- «
การปรับขนาดอัตโนมัติและการจัดการทรัพยากรใน Kubernetes (ภาพรวมและรายงานวิดีโอ) "; - «
CPU Manager ทำงานอย่างไรใน Kubernetes "; - «
จะเกิดอะไรขึ้นใน Kubernetes เมื่อคุณเรียกใช้ kubectl run ส่วนที่ 2 '
ที่มา: will.com