วันหนึ่งระหว่างการอัปเดตคลัสเตอร์ k8s ตามแผน เราพบว่า POD เกือบทั้งหมดของเรา (ประมาณ 500 จาก 1,000) ในโหนดใหม่ไม่สามารถเริ่มทำงานได้ และนาทีก็กลายเป็นชั่วโมงอย่างรวดเร็ว เราพยายามค้นหาสาเหตุที่แท้จริง แต่หลังจากผ่านไปสามชั่วโมง PODS ยังคงอยู่ในสถานะ ContainerCreating
โชคดีที่นี่ไม่ใช่สภาพแวดล้อมการผลิตและหน้าต่างการบำรุงรักษาถูกกำหนดไว้ในช่วงสุดสัปดาห์ เรามีเวลาตรวจสอบปัญหาโดยไม่มีแรงกดดันใดๆ
คุณควรเริ่มค้นหาสาเหตุที่แท้จริงจากที่ไหน คุณต้องการเรียนรู้เพิ่มเติมเกี่ยวกับวิธีแก้ปัญหาที่เราพบหรือไม่ เตรียมตัวให้พร้อมและสนุกไปกับมัน!
ปัญหาคือเรามีภาพ Docker จำนวนมากที่ต้องดึงและเริ่มต้นบนแต่ละโหนดในคลัสเตอร์ในเวลาเดียวกัน เนื่องจากการดึงภาพ Docker พร้อมกันหลายรายการบนโหนดเดียวอาจทำให้มีการใช้งานดิสก์สูงและเวลาเริ่มต้นระบบแบบเย็นยาวนานขึ้น
บางครั้งกระบวนการ CD อาจใช้เวลานานถึง 3 ชั่วโมงในการดึงภาพออกมา อย่างไรก็ตาม ในครั้งนี้ กระบวนการดังกล่าวเกิดการหยุดชะงัก เนื่องจากปริมาณ PODS ในระหว่างการอัปเกรด EKS (แบบอินไลน์ เมื่อเราแทนที่โหนดทั้งหมดในคลัสเตอร์) สูงเกินไป
แอปทั้งหมดของเราอยู่ใน k8s (ตาม EKS ) เพื่อประหยัดต้นทุนสำหรับสภาพแวดล้อม DEV เราจึงใช้อินสแตนซ์จุด
เราใช้อิมเมจ AmazonLinux2 สำหรับโหนด
เรามี ฟีเจอร์สาขา (FB) จำนวนมากในสภาพแวดล้อมการพัฒนาที่ปรับใช้กับคลัสเตอร์ Kubernetes ของเราอย่างต่อเนื่อง FB แต่ละรายการมีชุดแอปพลิเคชันของตัวเอง และแอปพลิเคชันแต่ละรายการมีชุดการอ้างอิงของตัวเอง (ภายในอิมเมจ)
ในโครงการของเรา มีแอปเกือบ 200 แอป และจำนวนนี้ยังคงเพิ่มขึ้นเรื่อยๆ แอปแต่ละตัวจะใช้หนึ่งใน 7 อิมเมจพื้นฐานของ Docker ที่มีขนาดประมาณ 2 GB ขนาดรวมสูงสุดของอิมเมจที่เก็บถาวร (ใน ECR ) อยู่ที่ประมาณ 3 GB
รูปภาพทั้งหมดถูกเก็บไว้ใน Amazon Elastic Container Registry (ECR)
เราใช้ประเภทปริมาณ gp3 EBS เริ่มต้นสำหรับโหนด
ระยะเวลาการเริ่มต้นแบบเย็นที่ขยายออก: การเริ่มพ็อดใหม่ด้วยภาพใหม่นั้นอาจใช้เวลานานกว่า 1 ชั่วโมง โดยเฉพาะอย่างยิ่งเมื่อมีการดึงภาพหลายภาพพร้อมกันบนโหนดเดียว
ข้อผิดพลาด ErrImagePull: เกิด ErrImagePull
บ่อยครั้ง หรือติดอยู่ในสถานะ ContainerCreating
บ่งชี้ถึงปัญหาในการดึงภาพ
การใช้ดิสก์สูง: การใช้ดิสก์ยังคงอยู่ที่เกือบ 100% ในระหว่างกระบวนการดึงภาพ ซึ่งเป็นผลมาจากการใช้ดิสก์ I/O จำนวนมากที่จำเป็นสำหรับการคลายการบีบอัด (เช่น “unpigz”)
ปัญหาชุด DaemonSet ของระบบ: ชุด DaemonSet ของระบบบางชุด (เช่น aws-node
หรือ ebs-csi-node
) ถูกย้ายไปยังสถานะ "ไม่พร้อม" เนื่องจากแรงกดดันของดิสก์ ซึ่งส่งผลกระทบต่อความพร้อมของโหนด
ไม่มีแคชรูปภาพบนโหนด: เนื่องจากเราใช้อินสแตนซ์จุด เราจึงไม่สามารถใช้ดิสก์ภายในเครื่องเพื่อแคชรูปภาพได้
ส่งผลให้การปรับใช้บนสาขาฟีเจอร์ต่างๆ หยุดชะงักเป็นจำนวนมาก โดยเฉพาะอย่างยิ่ง เนื่องจาก FB ที่แตกต่างกันมีชุดรูปภาพพื้นฐานที่แตกต่างกัน
หลังจากตรวจสอบอย่างรวดเร็ว เราพบว่าปัญหาหลักคือแรงกดดันของดิสก์บนโหนดโดยกระบวนการ unpigz
กระบวนการนี้รับผิดชอบในการคลายการบีบอัดอิมเมจของ Docker เราไม่ได้เปลี่ยนการตั้งค่าเริ่มต้นสำหรับประเภทโวลุ่ม EBS ของ gp3 เนื่องจากไม่เหมาะกับกรณีของเรา
ในขั้นตอนแรกเราตัดสินใจที่จะลดจำนวน POD บนโหนด
แนวคิดหลักของโซลูชันนี้คือการวอร์มอัปโหนดก่อนที่กระบวนการ CD จะเริ่มต้นโดยใช้ส่วนที่ใหญ่ที่สุดของอิมเมจ Docker (เลเยอร์การพึ่งพา JS) ซึ่งใช้เป็นอิมเมจรูทสำหรับแอปทั้งหมดของเรา เรามีอิมเมจรูทอย่างน้อย 7 ประเภทที่มีการพึ่งพา JS ซึ่งเกี่ยวข้องกับประเภทของแอป ดังนั้น มาวิเคราะห์การออกแบบ CI/CD ดั้งเดิมกัน
ใน CI/CD pipeline ของเรา เรามี 3 เสาหลัก:
ท่อ CI/CD ดั้งเดิม:
ในขั้นตอน Init
it: เตรียมสภาพแวดล้อม/ตัวแปร กำหนดชุดของอิมเมจที่จะสร้างใหม่ ฯลฯ...
ในขั้นตอน Build
: เราสร้างภาพและส่งไปยัง ECR
ในขั้นตอน Deploy
: เราจะปรับใช้รูปภาพไปยัง k8s (อัปเดตการปรับใช้ เป็นต้น...)
รายละเอียดเพิ่มเติมเกี่ยวกับการออกแบบ CICD ดั้งเดิม:
main
ในกระบวนการ CI เราจะวิเคราะห์ชุดรูปภาพที่มีการเปลี่ยนแปลงใน FB และสร้างใหม่เสมอ สาขา main
จะเสถียรเสมอ เนื่องจากคำจำกัดความควรมีรูปภาพพื้นฐานเวอร์ชันล่าสุดอยู่เสมอมีข้อกำหนดสำหรับกระบวนการอุ่นเครื่อง
บังคับ:
ContainerCreating
ดีใจที่มีการปรับปรุง:
หลังจากวิเคราะห์ข้อกำหนดและข้อจำกัดแล้ว เราตัดสินใจที่จะใช้กระบวนการวอร์มอัพที่จะอุ่นโหนดด้วยอิมเมจแคช JS พื้นฐาน กระบวนการนี้จะถูกเรียกใช้ก่อนที่กระบวนการ CD จะเริ่มต้น เพื่อให้แน่ใจว่าโหนดพร้อมสำหรับการใช้งาน FB และเรามีโอกาสสูงสุดที่จะเข้าถึงแคช
การปรับปรุงนี้เราแบ่งออกเป็นขั้นตอนใหญ่ๆ ดังนี้:
สร้าง ชุดโหนด (Virtual Node Group) ต่อแต่ละ FB
เพิ่ม รูปภาพพื้นฐานลงในสคริปต์ cloud-init สำหรับโหนดใหม่
เพิ่ม ขั้นตอนก่อนการปรับใช้เพื่อรัน DaemonSet พร้อมกับส่วน initContainers
เพื่อดาวน์โหลดภาพ Docker ที่จำเป็นไปยังโหนดก่อนที่กระบวนการ CD จะเริ่มต้น
สร้างชุดโหนดใหม่สำหรับแต่ละ FB ผ่านการเรียก API (ไปยังระบบปรับขนาดอัตโนมัติของบุคคลที่สาม) จากไปป์ไลน์ CI ของเรา
ปัญหาที่ได้รับการแก้ไข:
การแยก : FB แต่ละอันมีชุดโหนดของตัวเอง เพื่อให้แน่ใจว่าสภาพแวดล้อมจะไม่ได้รับผลกระทบจาก FB อื่นๆ
ความยืดหยุ่น : เราสามารถเปลี่ยนประเภทโหนดและอายุการใช้งานได้อย่างง่ายดาย
ประสิทธิภาพด้านต้นทุน : เราสามารถลบโหนดได้ทันทีหลังจากลบ FB
ความโปร่งใส : เราสามารถติดตามการใช้งานและประสิทธิภาพของโหนดได้อย่างง่ายดาย (แต่ละโหนดมีแท็กที่เกี่ยวข้องกับ FB)
การใช้งาน Spot Instance อย่างมีประสิทธิผล : Spot Instance เริ่มต้นด้วยอิมเมจพื้นฐานที่กำหนดไว้แล้ว ซึ่งหมายความว่า หลังจากที่โหนด Spot เริ่มทำงาน ก็มีอิมเมจพื้นฐานอยู่บนโหนดแล้ว (จากสาขาหลัก)
ดาวน์โหลดภาพฐาน JS ทั้งหมดจากสาขาหลักไปยังโหนดใหม่ ผ่านสคริปต์ cloud-init
ในขณะที่กำลังดาวน์โหลดรูปภาพในพื้นหลัง กระบวนการ CD สามารถสร้างรูปภาพใหม่ต่อไปได้โดยไม่มีปัญหาใดๆ นอกจากนี้ โหนดถัดไป (ซึ่งจะสร้างขึ้นโดยระบบปรับขนาดอัตโนมัติ) จากกลุ่มนี้จะถูกสร้างขึ้นโดยใช้ข้อมูล cloud-init
ที่อัปเดตแล้ว ซึ่งมีคำแนะนำในการดาวน์โหลดรูปภาพก่อนเริ่มต้นอยู่แล้ว
ปัญหาที่ได้รับการแก้ไข:
การแก้ไขปัญหา : แรงกดดันของดิสก์หายไปแล้ว เนื่องจากเราอัปเดตสคริปต์ cloud-init
โดยเพิ่มการดาวน์โหลดอิมเมจฐานจากสาขาหลัก วิธีนี้ช่วยให้เราเข้าถึงแคชได้เมื่อเริ่มต้น FB ครั้งแรก
การใช้งานอินสแตนซ์จุดอย่างมีประสิทธิภาพ : อินสแตนซ์จุดจะเริ่มต้นด้วยข้อมูลการเริ่มต้น cloud-init
ที่อัปเดต ซึ่งหมายความว่า หลังจากโหนดจุดเริ่มทำงานแล้ว จะมีอิมเมจพื้นฐานบนโหนด (จากสาขาหลัก) อยู่แล้ว
ประสิทธิภาพการทำงานที่ได้รับการปรับปรุง : กระบวนการ CD สามารถสร้างภาพใหม่ต่อไปได้โดยไม่มีปัญหาใดๆ
การดำเนินการนี้เพิ่มเวลา ~17 วินาที (การเรียก API) ให้กับไปป์ไลน์ CI/CD ของเรา
การดำเนินการนี้จะสมเหตุสมผลเฉพาะในครั้งแรกที่เราเริ่ม FB เท่านั้น ในครั้งถัดไป เราจะปรับใช้แอปของเรากับโหนดที่มีอยู่แล้ว ซึ่งมีอิมเมจพื้นฐานที่เราได้ส่งมอบไปแล้วในการปรับใช้ครั้งก่อน
เราจำเป็นต้องทำขั้นตอนนี้เนื่องจากรูปภาพ FB แตกต่างจากรูปภาพสาขาหลัก เราจำเป็นต้องดาวน์โหลดรูปภาพฐาน FB ไปยังโหนดก่อนที่กระบวนการ CD จะเริ่มต้น ซึ่งจะช่วยลดเวลาในการเริ่มต้นระบบแบบเย็นที่ยาวนานและการใช้ดิสก์สูงที่อาจเกิดขึ้นได้เมื่อดึงรูปภาพขนาดใหญ่หลายภาพพร้อมกัน
วัตถุประสงค์ของขั้นตอนก่อนการปรับใช้
ป้องกันแรงกดดันของดิสก์ : ดาวน์โหลดรูปภาพขนาดใหญ่ที่สุดของ Docker ตามลำดับ หลังจากขั้นตอน init-deploy แล้ว เราก็มีรูปภาพพื้นฐานบนโหนดแล้ว ซึ่งหมายความว่าเรามีโอกาสสูงที่จะเกิดแคชที่ได้รับผลกระทบ
ปรับปรุงประสิทธิภาพการใช้งาน : ช่วยให้แน่ใจว่าโหนดได้รับการอุ่นเครื่องล่วงหน้าด้วยอิมเมจ Docker ที่จำเป็น ซึ่งจะนำไปสู่เวลาในการเริ่มต้น POD ที่เร็วขึ้น (เกือบจะทันที)
เพิ่มเสถียรภาพ : ลดโอกาสที่จะพบข้อผิดพลาด ErrImagePull
/ ContainerCreating
และตรวจสอบให้แน่ใจว่าชุดเดมอนของระบบยังคงอยู่ในสถานะ "พร้อม"
ในขั้นตอนนี้ เราจะเพิ่มเวลาให้กับกระบวนการซีดี 10–15 นาที
รายละเอียดขั้นตอนก่อนการใช้งาน:
initContainers
initContainers
จะถูกดำเนินการก่อนที่คอนเทนเนอร์หลักจะเริ่มต้น เพื่อให้แน่ใจว่ามีการดาวน์โหลดรูปภาพที่จำเป็นก่อนที่คอนเทนเนอร์หลักจะเริ่มต้นการเปรียบเทียบขั้นตอนเดิมและขั้นตอนที่อัปเดตกับกระบวนการอุ่นล่วงหน้า
ขั้นตอน | ขั้นตอนการใช้งาน Init | ขั้นตอนก่อนการใช้งาน | การใช้งาน | เวลารวม | ต่าง |
---|---|---|---|---|---|
โดยไม่ต้องอุ่นเครื่อง | 0 | 0 | 11นาที 21วินาที | 11นาที 21วินาที | 0 |
ด้วยการอุ่นเครื่องล่วงหน้า | 8 วินาที | 58 วินาที | 25 วินาที | 1นาที 31วินาที | -9นาที50วินาที |
สิ่งสำคัญคือเวลา "Deploy" เปลี่ยนไป (จากคำสั่ง Apply แรกไปจนถึงสถานะ Running ของพ็อด) จาก 11 นาที 21 วินาที เป็น 25 วินาที เวลารวมเปลี่ยนจาก 11 นาที 21 วินาที เป็น 1 นาที 31 วินาที
ประเด็นสำคัญคือ หากไม่มีอิมเมจฐานจากสาขาหลัก เวลา "ปรับใช้" จะเท่ากับเวลาเดิมหรืออาจมากกว่าเล็กน้อย แต่ถึงอย่างไร เราก็ได้แก้ไขปัญหาความดันดิสก์และเวลาเริ่มต้นระบบแบบเย็นแล้ว
ปัญหาหลัก ContainerCreating
ได้รับการแก้ไขด้วยกระบวนการวอร์มอัพ ผลที่ได้คือ เราลดเวลาการเริ่มระบบแบบเย็นของ POD ได้อย่างมาก
แรงกดดันของดิสก์หายไปแล้ว เนื่องจากเรามีอิมเมจพื้นฐานบนโหนดแล้ว daemonSet ของระบบอยู่ในสถานะ "พร้อม" และ "มีสุขภาพดี" (เนื่องจากไม่มีแรงกดดันของดิสก์) และเราไม่พบข้อผิดพลาด ErrImagePull
ใดๆ ที่เกี่ยวข้องกับปัญหานี้
ป.ล.: ฉันอยากจะแสดงความชื่นชมต่อทีมงานด้านเทคนิคที่ยอดเยี่ยมของ Justt ( https://www.linkedin.com/company/justt-ai ) สำหรับการทำงานอย่างไม่รู้จักเหน็ดเหนื่อยและแนวทางที่สร้างสรรค์อย่างแท้จริงในการแก้ไขปัญหาต่างๆ ที่พวกเขาเผชิญ โดยเฉพาะอย่างยิ่ง ขอแสดงความชื่นชมต่อ Ronny Sharaby หัวหน้าที่ยอดเยี่ยมซึ่งรับผิดชอบต่องานอันยอดเยี่ยมที่ทีมงานกำลังทำอยู่ ฉันตั้งตารอที่จะเห็นตัวอย่างที่ยอดเยี่ยมมากขึ้นเรื่อยๆ ว่าความคิดสร้างสรรค์ของคุณส่งผลต่อผลิตภัณฑ์ของ Justt อย่างไร