DevOps with love. Certified Kubernetes Administrator. I also enjoy riding a motorcycle.
Walkthroughs, tutorials, guides, and tips. This story will teach you how to do something new or how to do something better.
วันหนึ่งระหว่างการอัปเดตคลัสเตอร์ k8s ตามแผน เราพบว่า POD เกือบทั้งหมดของเรา (ประมาณ 500 จาก 1,000) ในโหนดใหม่ไม่สามารถเริ่มทำงานได้ และนาทีก็กลายเป็นชั่วโมงอย่างรวดเร็ว เราพยายามค้นหาสาเหตุที่แท้จริง แต่หลังจากผ่านไปสามชั่วโมง PODS ยังคงอยู่ในสถานะ ContainerCreating
Kubernetes ติดอยู่ใน 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 ดั้งเดิม
ท่อ 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 จะเริ่มต้น
CI/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 อย่างไร