การเริ่ม OpenVPN ที่ล้มเหลวมักมาในรูปข้อร้องเรียนเรื่องทรัพยากรพร้อมข้อเสนอที่มีเจตนาดีให้เพิ่มขีดจำกัด และข้อเสนอนั้นได้ผลเพียงประมาณครึ่งหนึ่งของกรณีเท่านั้น เบื้องหลังความล้มเหลวเหล่านั้นมีตัวนับสองตัวที่ไม่เกี่ยวข้องกัน ตัวหนึ่งคือขีดจำกัดของเคอร์เนลต่อจำนวนงานที่บัญชีผู้ใช้จริงเป็นเจ้าของ ซึ่งสืบทอดมาเมื่อโปรเซสถูกเรียกให้ทำงาน อีกตัวคืองบประมาณ cgroup ที่ตัวจัดการบริการใช้กับหน่วยหนึ่งและทุกสิ่งที่หน่วยนั้นแตกออกมา การอ่านค่าที่มีผลจริงของแต่ละตัวสำหรับหน่วยที่ทำงานจริงนั้นเสียเวลาน้อยกว่าการเดา

สองตัวนับ สองคำถาม

LimitNPROC คือ RLIMIT_NPROC ภายใต้ชื่อของ systemd และหน่วยบัญชีของมันคือบัญชีผู้ใช้จริง ทุกโปรเซสที่ผู้ใช้คนนั้นเป็นเจ้าของถูกนับรวมในยอดเดียวกัน รวมถึงงานที่หน่วยอื่น ระบบเข้าสู่ระบบ หรืองานของตัวจัดตารางเริ่มไว้ด้วย ดังนั้นขีดจำกัดนี้จึงไม่ใช่เพดานต่อบริการ แม้จะถูกตั้งไว้ภายในหน่วยบริการ และบริการที่แตกตัวช่วยเหลือออกมาสำหรับไคลเอนต์แต่ละรายก็แข่งขันกับทุกสิ่งอื่นที่ทำงานภายใต้บัญชีนั้น งาน cron ที่ยุ่งอยู่ภายใต้ผู้ใช้คนเดียวกันอาจดันหน่วย VPN ให้ข้ามเส้นที่ตัวมันเองไม่เคยข้าม

TasksMax มีเจ้าของอีกคนหนึ่ง คือตัวควบคุม pids บน cgroup ของหน่วย ซึ่งนับงานไม่ใช่โปรเซส เธรดก็นับเป็นงาน ดังนั้นดีมอนที่ดูเหมือนมีเธรดเดียวแต่มีกลุ่มผู้ทำงานอาจใช้งบประมาณที่เมื่อมองแวบแรกดูใหญ่กว่าจำนวนโปรเซสของมันมาก EffectiveTasksMax รายงานว่าลำดับชั้น cgroup อนุญาตอะไรจริงเมื่อส่วนที่เหนือกว่าได้พูดของตัวเองแล้ว

ทำไมข้อยกเว้นของ root จึงเปลี่ยนคำตอบ

เคอร์เนลไม่นำ RLIMIT_NPROC ไปใช้กับโปรเซสที่มี CAP_SYS_RESOURCE หรือ CAP_SYS_ADMIN ดังนั้นหน่วยที่ทำงานเป็น root โดยไม่ลดทิ้งสิทธิพิเศษจึงแทบไม่มีขีดจำกัดจากค่านั้น ข้อยกเว้นเดียวนี้เองที่อธิบายคำแนะนำที่ขัดแย้งกันซึ่งผู้คนแลกเปลี่ยนกัน คือการเพิ่ม LimitNPROC จะซ่อมการติดตั้งที่ลดสิทธิพิเศษลงแล้ว และไม่ทำอะไรเลยกับการติดตั้งที่ยังคงมีสิทธิพิเศษ ตรวจดู User= และดูว่าดีมอนเปลี่ยนตัวตนหลังเริ่มทำงานหรือไม่ ก่อนจะให้เครดิตหรือโทษกับการตั้งค่านั้น หน่วยที่มีสิทธิพิเศษซึ่งภายหลังเปลี่ยนไปใช้บัญชีที่ไม่มีสิทธิพิเศษจะกลับมาต่ำกว่าขีดจำกัดในขณะที่เปลี่ยน ซึ่งทำให้เส้นเวลาของความล้มเหลวน่าอ่านด้วย

ขั้นตอนที่ล้มเหลวทำให้แคบลงอีก การถูกปฏิเสธระหว่างเตรียมการทำงาน ข้อผิดพลาดในการแตกโปรเซสที่ตัวดีมอนเองรายงาน และการสร้างงานที่ถูกปฏิเสธภายใน cgroup ให้บรรทัด journal ที่ต่างกัน และมีเพียงหนึ่งในนั้นที่ตอบด้วยงบประมาณที่ใหญ่กว่า

อ่านค่าที่มีผลจริง ไม่ใช่ค่าของเชลล์

ulimit -u ของเชลล์แบบโต้ตอบอธิบายเชลล์นั้น มันไม่ได้อธิบายสิ่งที่ตัวจัดการบริการส่งให้ดีมอน และไม่ได้พูดอะไรเกี่ยวกับการบัญชีของ cgroup ให้ถามหน่วยนั้นแทน โดยใช้ชื่ออินสแตนซ์ที่มีอยู่บนโฮสต์ ไม่ใช่ตัวอย่างจากคู่มือ

systemctl show [email protected] -p User -p LimitNPROC -p TasksCurrent -p TasksMax -p EffectiveTasksMax
cat /proc/$(systemctl show -p MainPID --value [email protected])/limits | grep -i processes

ภายในคอนเทนเนอร์คำตอบต้องอ่านเพิ่มอีกสองอย่าง เพราะค่าภายในอย่าง infinity อธิบายเพียงมุมมองของผู้เยี่ยมชม ถามเจ้าของโฮสต์ถึงเพดาน pids ของคอนเทนเนอร์เอง และยืนยันว่าตัวจัดการบริการภายในสามารถมอบหมายตัวควบคุมนั้นได้หรือไม่ หน่วยที่ซ้อนอยู่ไม่สามารถถืองบประมาณที่ cgroup แม่ของมันไม่เคยให้

กำหนดขนาดของการแก้ไขและพิสูจน์ภายใต้ภาระ

เลือกตัวเลขจากภาระงาน ไม่ใช่จากข้อความแสดงข้อผิดพลาด นับโปรเซสที่ดีมอนเริ่มต่อไคลเอนต์ที่เชื่อมต่อหรือต่อการเรียกตัวช่วยแต่ละครั้ง คูณด้วยความพร้อมกันที่การติดตั้งคาดว่าจะรับได้ เพิ่มส่วนเผื่อสำหรับวาระของผู้ดูแลระบบ แล้วเปรียบเทียบตัวเลขนั้นกับค่าที่มีผลจริงทั้งสอง การเขียนทับแบบ drop-in ในเครื่องทำให้การเปลี่ยนแปลงตรวจสอบได้และอยู่รอดจากการอัปเกรดแพ็กเกจ ขณะที่ การแก้ไขไฟล์หน่วยที่จัดส่งมาซ่อนการเปลี่ยนแปลงไว้จนกว่าการอัปเดตครั้งถัดไปจะมาแทนที่ เขียนการคำนวณไว้ข้างการเขียนทับ เพราะคนถัดไปที่จะเพิ่มหรือลดค่านั้นต้องการสมมติฐาน ไม่ใช่ตัวเลขสุดท้าย

การเปลี่ยนแปลงงบประมาณมีผลเมื่อทำงาน ดังนั้นการโหลดซ้ำอย่างเดียวไม่เปลี่ยนขนาดของโปรเซสที่ทำงานอยู่แล้ว ให้รีสตาร์ตหน่วยผ่านวงจรชีวิตปกติ ยอมรับการหยุดชะงักของวาระ แล้วอ่านตัวนับอีกครั้งบนอินสแตนซ์ใหม่

sudo systemctl daemon-reload
sudo systemctl restart [email protected]
systemctl show [email protected] -p TasksCurrent -p EffectiveTasksMax
sudo journalctl -u [email protected] -b --no-pager | tail -n 20

สิ่งที่พิสูจน์การซ่อมคือหน่วยที่รีสตาร์ตแล้วซึ่งจำนวนงานยังต่ำกว่าเพดานทั้งสองขณะแบกภาระที่ตั้งใจไว้ พร้อมกับ journal ที่ไม่รายงานการหมดของงานหรือการแตกโปรเซสในขั้นตอนเดิมอีก เก็บตัวเลขก่อนและหลังไว้พร้อมกับการเปลี่ยนแปลง เพื่อให้การสอบสวนครั้งถัดไปเริ่มจากการวัด ไม่ใช่จากคำแนะนำเดิม หากตัวเลขยังคงถึงเพดาน แสดงว่าการคำนวณผิด ไม่ใช่ขีดจำกัดเล็กเกินไปโดยบังเอิญ และความต่างนั้นควรรู้ไว้ก่อนการรีสตาร์ตครั้งถัดไป

บทความที่เกี่ยวข้อง