ผลของการกำหนดค่า vm.overcommit_memory


41

เว็บเซิร์ฟเวอร์ VPS ของฉันทำงานบน CentOS 5.4 (เคอร์เนล Linux 2.6.16.33-xenU) ไม่สม่ำเสมอ (เช่นเดือนละครั้งให้หรือใช้เวลาสองสามสัปดาห์) จะไม่ตอบสนองเนื่องจากผู้โจมตีใช้งานได้การตรวจสอบเซิร์ฟเวอร์แสดงว่าไม่ ปกติหน่วยความจำไม่เพียงพอทุกครั้ง

ฉันได้อ่านบล็อกที่ชี้ไปที่หน้านี้ซึ่งกล่าวถึงการกำหนดค่าเคอร์เนลเพื่อจัดการ overcommit ที่ดีขึ้นโดยใช้การตั้งค่า sysctl ต่อไปนี้:

vm.overcommit_memory = 2
vm.overcommit_ratio = 80

ความเข้าใจของฉันเกี่ยวกับเรื่องนี้ (ซึ่งอาจผิด แต่ฉันไม่สามารถหาคำจำกัดความตามกฎหมายเพื่อชี้แจง) คือการป้องกันเคอร์เนลที่จัดสรรหน่วยความจำเกินกว่า swap + 80% ของหน่วยความจำกายภาพ

แต่ผมได้อ่านบางแหล่งข้อมูลอื่น ๆ บอกว่าการตั้งค่าเหล่านี้ไม่ได้เป็นความคิดที่ดี - แม้ว่านักวิจารณ์ของวิธีการนี้ดูเหมือนจะบอกว่า "ไม่ได้ทำสิ่งที่จะทำลายระบบของคุณแทนที่จะพยายาม kludge นี้" ในสมมติฐานว่า เป็นที่รู้จักกันเสมอ

ดังนั้นคำถามของฉันคืออะไรข้อดีและข้อเสียของวิธีการนี้ในบริบทของเว็บเซิร์ฟเวอร์ Apache2 โฮสติ้งประมาณ 10 ไซต์การจราจรต่ำ? ในกรณีเฉพาะของฉันเว็บเซิร์ฟเวอร์มี 512Mb RAM พร้อมพื้นที่สลับ 1024Mb ดูเหมือนว่าจะเพียงพอสำหรับเวลาส่วนใหญ่

คำตอบ:


32

การตั้งค่าovercommit_ratioเป็น 80 ไม่ใช่การกระทำที่ถูกต้อง การตั้งค่าเป็นอะไรที่น้อยกว่า 100 นั้นไม่ถูกต้องเกือบทุกครั้ง

เหตุผลนี้คือแอปพลิเคชัน linux จัดสรรมากกว่าที่พวกเขาต้องการจริงๆ สมมติว่าพวกเขาจัดสรร 8kb เพื่อเก็บสตริงอักขระคู่ นั่นคือหลาย KB ที่ไม่ได้ใช้อยู่ตรงนั้น แอปพลิเคชันทำสิ่งนี้มากมายและนี่คือสิ่งที่ overcommit ออกแบบมา

ดังนั้นโดยพื้นฐานแล้วด้วย overcommit ที่ 100 เคอร์เนลจะไม่อนุญาตให้แอปพลิเคชันจัดสรรหน่วยความจำได้มากกว่าที่คุณมี (swap + ram) ตั้งไว้ที่น้อยกว่า 100 หมายความว่าคุณจะไม่ใช้หน่วยความจำทั้งหมด หากคุณกำลังจะตั้งการตั้งค่านี้คุณควรตั้งค่าให้สูงกว่า 100 เนื่องจากสถานการณ์ที่กล่าวถึงก่อนหน้าซึ่งเป็นเรื่องปกติ

ทีนี้สำหรับปัญหาของคุณกับ OOM killer triggering การตั้งค่า overcommit ด้วยตนเองจะไม่สามารถแก้ไขได้ การตั้งค่าเริ่มต้น (การกำหนดฮิวริสติก) ค่อนข้างฉลาด

หากคุณต้องการดูว่านี่เป็นสาเหตุของปัญหาหรือไม่ให้ดูที่/proc/meminfoOOM killer ทำงาน หากคุณเห็นว่าCommitted_ASใกล้เคียงกับCommitLimitแต่freeยังคงแสดงหน่วยความจำว่างที่มีอยู่แล้วใช่คุณสามารถปรับเปลี่ยน overcommit สำหรับสถานการณ์ของคุณได้ด้วยตนเอง การตั้งค่านี้ต่ำเกินไปจะทำให้ OOM killer เริ่มต้นฆ่าแอปพลิเคชันเมื่อคุณยังมีหน่วยความจำว่างมากมาย การตั้งค่าสูงเกินไปอาจทำให้แอปพลิเคชันแบบสุ่มตายเมื่อพวกเขาพยายามใช้หน่วยความจำที่จัดสรร แต่ไม่สามารถใช้งานได้จริง (เมื่อหน่วยความจำทั้งหมดหมดจริง)


1
ขอบคุณ - ฉันกำลังลองสิ่งต่าง ๆ ด้วย overcommit_ratio ตั้งค่าเป็น 100 เพื่อดูว่าเกิดอะไรขึ้น ปัญหาหลักที่ฉันมีคือเมื่อ oom-killer เริ่มทำงานมันจะฆ่า sshd อย่างสม่ำเสมอทำให้ฉันไม่สามารถเข้าถึงเซิร์ฟเวอร์และดูว่าเกิดอะไรขึ้น ฉันเดาว่าสิ่งที่ฉันต้องการจริงๆคือหยุดการทำงานของ oom-killer และวิธีการบันทึกสิ่งที่จะเกิดขึ้นเมื่อมันทำงานได้ดังนั้นฉันจึงสามารถหาสาเหตุของปัญหาได้
dunxd

4
@dunxd คุณสามารถใช้/proc/<PID>/oom_score_adjเพื่อจุดประสงค์นี้ ตัวอย่างเช่นหากคุณตั้งค่า oom_score_adj เป็น -1000 สำหรับ sshd, oom killer จะไม่กำหนดเป้าหมาย sshd เมื่อต้องการฆ่าบางสิ่ง การหยุด oom killer ทั้งหมดนั้นไม่ใช่ความคิดที่ดีเพราะโปรแกรมของคุณจะไม่สามารถใช้หน่วยความจำแบบ malloc ได้และพวกเขาก็จะตายไป
Patrick

4
@dunxd การสืบทอด ให้สคริปต์เริ่มต้นของคุณติดตั้งเองและสิ่งใดที่สคริปต์เริ่มต้นจะสืบทอดมา
Patrick

4
ตัวอย่าง 4 KB ของคุณผิด หน่วยความจำเสมือนใช้กับหน้าและขนาด (เล็กที่สุด) ของหน้าภายใต้ Linux คือ 4 KB นั่นหมายถึงการจัดเก็บอักขระสองตัวต้องการการแมป 4 KB ที่ใดที่หนึ่งโดยไม่คำนึงถึงการตั้งค่า overcommitment ตัวอย่างที่เหมาะสมของหน่วยความจำเกินความมุ่งมั่นจะเป็นตัวอย่างที่คุณจัดสรร 10 KB และใช้ 4100 ไบต์แรกเท่านั้น นั่นหมายถึงหน้า 4 KB สองหน้าจำเป็นต้องจัดเก็บข้อมูลและไม่ได้ใช้หน้าพิเศษหนึ่งหน้า ระบบที่ไม่ใช้งานมากเกินไปจะมีหน้าสามที่พร้อมที่จะเก็บข้อมูลหากความต้องการมาถึงมากกว่าระบบที่ใช้งานจะไม่บังคับใช้
jlliagre

2
/ proc / self ชี้ไปที่กระบวนการปัจจุบันดังนั้น / proc / self / oom_score_adj สามารถใช้เพื่อเปลี่ยน oom_score_adj ของกระบวนการปัจจุบัน
r_2

23

ส่วนที่ 9.6 "Overcommit และ OOM" ในเอกสารที่ @dunxd กล่าวถึงเป็นภาพกราฟิกโดยเฉพาะอย่างยิ่งอันตรายของการอนุญาตให้มีคำสั่งเกินความจำเป็น อย่างไรก็ตามสิ่ง80ที่ฉันสนใจก็ดูน่าสนใจเช่นกัน

สิ่งที่ฉันพบคือการที่overcommit_ratioส่งผลกระทบต่อ RAM ทั้งหมดที่มีอยู่สำหรับกระบวนการทั้งหมด กระบวนการรูทไม่ได้รับการปฏิบัติแตกต่างจากกระบวนการผู้ใช้ทั่วไป

การตั้งค่าอัตราส่วนเป็น100หรือน้อยกว่านั้นควรให้ความหมายแบบคลาสสิกที่ค่าส่งคืนmalloc/sbrkมีความน่าเชื่อถือ การตั้งค่าอัตราส่วนต่ำกว่า100อาจเป็นวิธีสำรอง RAM เพิ่มเติมสำหรับกิจกรรมที่ไม่ได้ดำเนินการเช่นแคชเป็นต้น

ดังนั้นในคอมพิวเตอร์ของฉันที่มี RAM 24 GiB โดยมีการปิดการใช้งาน swap มีการใช้งาน 9 GiB พร้อมการtopแสดง

Mem:  24683652k total,  9207532k used, 15476120k free,    19668k buffers
Swap:        0k total,        0k used,        0k free,   241804k cached

ต่อไปนี้คือovercommit_ratioการตั้งค่าบางอย่างและจำนวนแรมที่โปรแกรม ram-Consumer ของฉันสามารถคว้าmallocได้

 50    ~680 MiB
 60   ~2900 MiB
 70   ~5200 MiB
100  ~12000 MiB

เรียกใช้หลายรายการพร้อมกันแม้ว่าจะเป็นผู้ใช้รูท แต่ก็ไม่ได้เปลี่ยนยอดรวมที่พวกเขาใช้ไปด้วยกัน เป็นเรื่องที่น่าสนใจที่ไม่สามารถบริโภค 3+ GiB ล่าสุดหรือมากกว่านั้น freeไม่ได้วางต่ำกว่าสิ่งที่แสดงให้เห็นที่นี่:

Mem:  24683652k total, 20968212k used,  3715440k free,    20828k buffers

การทดลองยุ่งเหยิง - อะไรก็ตามที่ใช้ malloc ในขณะที่ RAM ทั้งหมดที่ใช้งานมีแนวโน้มที่จะผิดพลาดเนื่องจากโปรแกรมเมอร์จำนวนมากกลัวการตรวจสอบความล้มเหลว malloc ใน C ห้องสมุดเก็บรวบรวมยอดนิยมบางแห่งไม่สนใจมันทั้งหมดและ C ++ และภาษาอื่น ๆ แย่ลง

การนำไปใช้งานในช่วงแรกของ RAM ในจินตนาการที่ฉันเห็นคือจัดการกับกรณีที่เฉพาะเจาะจงมากซึ่งกระบวนการขนาดใหญ่เดียว - กล่าวว่า 51% + ของหน่วยความจำที่มีอยู่ - จำเป็นต้องใช้fork()เพื่อexec()สนับสนุนโปรแกรมบางโปรแกรมซึ่งมักจะเล็กกว่ามาก OS ที่มีซีแมนทิกส์ copy-on-write จะอนุญาตfork()แต่ด้วยเงื่อนไขที่ว่าหากกระบวนการที่แยกจากกันพยายามแก้ไขหน้าหน่วยความจำมากเกินไป (แต่ละอันนั้นจะต้องถูกสร้างอินสแตนซ์เป็นหน้าใหม่โดยไม่ขึ้นอยู่กับกระบวนการขนาดใหญ่เริ่มต้น) มันจะจบลงด้วยการถูกฆ่าตาย กระบวนการผู้ปกครองตกอยู่ในอันตรายหากจัดสรรหน่วยความจำเพิ่มเติมและสามารถจัดการกับการเรียกใช้ในบางกรณีเพียงแค่รอสักครู่สำหรับกระบวนการอื่นที่จะตายแล้วดำเนินการต่อ กระบวนการลูกมักจะแทนที่ตัวเองด้วยโปรแกรม (มักจะเล็กกว่า) ผ่านทางexec() และจากนั้นก็เป็นอิสระจากเงื่อนไข

แนวคิด overcommit ของ Linux เป็นวิธีการที่ยอดเยี่ยมในการอนุญาตให้ทั้งสองfork()เกิดขึ้นรวมถึงการอนุญาตให้กระบวนการเดียวเพื่อสร้างภาพรวมที่ใหญ่โต เสียชีวิต OOM นักฆ่าที่เกิดจากการถ่ายทอดสดเกิดขึ้นแม้กับโปรแกรมที่ทำจัดสรรหน่วยความจำจับความรับผิดชอบ ฉันเกลียดการ overcommit ทั่วทั้งระบบโดยทั่วไปและโดยเฉพาะอย่างยิ่ง oom-killer - มันสนับสนุนแนวทางการดูแลหน่วยความจำที่ติดมารและอาจทำให้ห้องสมุดและผ่านพวกเขาทุกแอปที่ใช้พวกเขา

ฉันขอแนะนำให้ตั้งค่าอัตราส่วนเป็น 100 และมีพาร์ติชันสลับเช่นกันซึ่งโดยทั่วไปแล้วจะจบลงด้วยการใช้งานโดยกระบวนการขนาดใหญ่ - ซึ่งมักใช้เพียงเศษเสี้ยวเล็ก ๆ ของส่วนของตัวเองที่ถูกยัดเข้าไปใน swap แล้ว ปกป้องกระบวนการส่วนใหญ่จากความผิดพลาดของนักฆ่า OOM สิ่งนี้จะช่วยให้เว็บเซิร์ฟเวอร์ของคุณปลอดภัยจากการเสียชีวิตแบบสุ่มและหากมีการเขียนเพื่อจัดการอย่างmallocรับผิดชอบแม้จะปลอดภัยจากการฆ่าตัวเอง (แต่ไม่ต้องเดิมพันตอนหลัง)

นั่นหมายความว่าฉันกำลังใช้สิ่งนี้ใน /etc/sysctl.d/10-no-overcommit.conf

vm.overcommit_memory = 2
vm.overcommit_ratio = 100

และคุณอยากแนะนำให้รักษา vm.overcommit_memory เป็น 2 หรือไม่
Ut xD

1
ข้อควรทราบ - นั่นคือสิ่งที่ฉันใช้อยู่ ฉันคิดว่าฉันไม่ได้ตอบคำถามนี้เพราะมันมีอยู่แล้วในคำถาม
อเล็กซ์นอร์ ธ - คีย์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.