วิธีจำกัด RAM ใน Docker Compose ป้องกันเซิร์ฟเวอร์ล่ม
เรียนรู้วิธีตั้งค่า deploy.resources และ mem_limit ใน Docker Compose เพื่อป้องกันคอนเทนเนอร์ใช้ RAM เกินจนเกิด exit code 137 และช่วยรักษาเสถียรภาพของ VPS ให้ทำงานได้ต่อเนื่อง
ขีดจำกัดหน่วยความจำของ Docker Compose ทำหน้าที่อะไร
ขีดจำกัดหน่วยความจำของ Docker Compose คือเพดานสูงสุดที่ Linux kernel บังคับใช้กับ cgroup (control group ซึ่งเป็นฟีเจอร์ของ kernel ที่ใช้ควบคุมทรัพยากรสำหรับกลุ่มกระบวนการ) ของคอนเทนเนอร์หนึ่งๆ เมื่อคุณตั้งค่า deploy.resources.limits.memory ไว้ใน service คอนเทนเนอร์นั้นจะไม่สามารถใช้หน่วยความจำเกินกว่าจำนวนที่กำหนดได้ หากพยายามใช้เกิน kernel จะสั่งยุติกระบวนการภายในคอนเทนเนอร์นั้น และคอนเทนเนอร์มักจะหยุดทำงานด้วย exit code 137
เรื่องนี้มีความสำคัญอย่างยิ่งบน VPS ซึ่งมี RAM จำกัดและไม่มีหน่วยความจำสำรองจากโฮสต์ให้ดึงมาใช้ หากคอนเทนเนอร์ใดมีปัญหา memory leak หรือมีการประมวลผลคำสั่งที่ผิดพลาด มันอาจดึงหน่วยความจำว่างทั้งหมดบนเซิร์ฟเวอร์ขนาด 8GB ไปใช้จนหมด จากนั้น kernel จะสั่งยุติกระบวนการที่มันประเมินว่า "แย่ที่สุด" ซึ่งบ่อยครั้งมักจะเป็นฐานข้อมูลหรือเซสชัน SSH ของคุณ แทนที่จะเป็นคอนเทนเนอร์ที่เป็นต้นเหตุ การกำหนดขีดจำกัดจึงช่วยเปลี่ยนจากเหตุการณ์เซิร์ฟเวอร์ล่มทั้งระบบ ให้เหลือเพียงบริการเดียวที่รีสตาร์ทตัวเองแทน
services:
app:
image: ghcr.io/example/app:1.4
deploy:
resources:
limits:
cpus: "1.5"
memory: 1g
reservations:
memory: 256mนำไปใช้และตรวจสอบว่าขีดจำกัดมีผลจริง:
docker compose up -d
docker stats --no-streamคอลัมน์ MEM USAGE / LIMIT ควรแสดงค่าประมาณ 142MiB / 1GiB หากคอลัมน์ขีดจำกัดแสดงค่า RAM ทั้งหมดของโฮสต์ แสดงว่าการตั้งค่าไม่มีผล และคำแนะนำที่เหลือในบทความนี้จะใช้งานไม่ได้จนกว่าจะแก้ไขให้ถูกต้อง หากคุณยังไม่คุ้นเคยกับไฟล์ compose สามารถดู พื้นฐาน Docker Compose สำหรับ VPS เพื่อศึกษาโครงสร้างไฟล์ที่ใช้เป็นรากฐานในที่นี้ได้
deploy.resources.limits หรือ mem_limit: ควรใช้ตัวไหน
การสะกดสองแบบสำหรับแนวคิดเดียวกันทำให้เกิดความสับสน
mem_limit, mem_reservation, memswap_limit, cpus และ cpu_shares เป็นคีย์ระดับบนสุดของ service ที่สืบทอดมาจากรูปแบบไฟล์ Compose รุ่นเก่า ส่วน deploy.resources มาจาก schema ของ Swarm และปัจจุบันเป็นส่วนหนึ่งของ Compose Specification ซึ่งเป็นรูปแบบที่ docker compose อ่านในปัจจุบัน
ทั้งสองแบบใช้งานได้บนโฮสต์เดียว Docker Compose ซึ่งเป็นปลั๊กอิน docker compose จะนำ deploy.resources.limits และ deploy.resources.reservations ไปใช้เมื่อคุณรัน docker compose up โดยไม่จำเป็นต้องมีคลัสเตอร์ Swarm ปัจจุบันปลั๊กอินอยู่ในสายการพัฒนา v5 (v5.6.0 เป็นรุ่นเสถียรล่าสุด ณ วันที่ 2026-10-06) โดยข้ามจาก v2 ไป v5 เพื่อหลีกเลี่ยงความสับสนกับเวอร์ชันไฟล์ Compose แบบเก่า และไม่มีรุ่น v5 ใดที่เปลี่ยนแปลงวิธีการจัดการขีดจำกัดทรัพยากร ส่วนคีย์อื่นๆ ในบล็อก deploy ที่ใช้ได้เฉพาะกับ Swarm เท่านั้น ได้แก่ mode, placement, update_config และ endpoint_mode ซึ่งมีความหมายต่อ docker stack deploy แต่จะถูก docker compose up เพิกเฉย ดังนั้นคำแนะนำทั่วไปที่ว่า "deploy ต้องใช้ Swarm" จึงไม่ถูกต้องสำหรับส่วนย่อย resources และการทำตามคำแนะนำนั้นจะทำให้ service ของคุณไม่มีการจำกัดทรัพยากรเลย
ให้เลือกใช้การสะกดแบบใดแบบหนึ่งต่อหนึ่งโปรเจกต์ การเขียนทั้ง mem_limit: 512m และ deploy.resources.limits.memory: 1g ใน service เดียวกันจะทำให้ไฟล์อ่านยาก แทนที่จะเดาว่าค่าใดจะมีผล ให้สอบถามจาก daemon โดยตรง:
docker inspect --format '{{.HostConfig.Memory}} {{.HostConfig.MemoryReservation}} {{.HostConfig.NanoCpus}}' app-1ค่าหน่วยความจำเป็นไบต์ ดังนั้น 1g จะแสดงผลเป็น 1073741824 ส่วน CPU มีหน่วยเป็น nano CPUs ดังนั้น 1.5 จะแสดงผลเป็น 1500000000 ค่า 0 ในฟิลด์ใดก็ตามหมายความว่าไม่มีการกำหนดขีดจำกัด ขีดจำกัดหน่วยความจำต่ำสุดที่ Docker ยอมรับคือ 6m หากต่ำกว่านั้น container จะปฏิเสธการเริ่มทำงาน
เกิดอะไรขึ้นเมื่อคอนเทนเนอร์ถึงขีดจำกัด
คอนเทนเนอร์จะไม่ทำงานช้าลง แต่จะหยุดทำงานทันที
เมื่อกระบวนการร้องขอหน้าหน่วยความจำ (page) ในขณะที่ cgroup ถึงขีดจำกัด memory.max แล้ว เคอร์เนลจะพยายามเรียกคืนหน่วยความจำภายใน cgroup นั้นก่อน โดยเริ่มจาก clean page cache ตามด้วยหน้าหน่วยความจำที่สามารถสลับออก (swap) ได้ หากการเรียกคืนไม่สามารถเพิ่มพื้นที่ว่างได้เพียงพอ ตัว OOM (out of memory) killer ของ cgroup จะเลือกกระบวนการหนึ่งภายในคอนเทนเนอร์แล้วส่งสัญญาณ SIGKILL ให้ การสั่งยุติ PID 1 ของคอนเทนเนอร์จะทำให้คอนเทนเนอร์นั้นหยุดทำงาน รหัสสถานะ (exit code) 137 คือผลรวมของ 128 บวกกับสัญญาณ 9 ดังนั้น 137 จึงเป็นเพียงร่องรอยของ SIGKILL เท่านั้น ไม่ใช่หลักฐานยืนยันว่าเกิดเหตุการณ์ OOM โดยตรง
docker compose ps -a
docker inspect --format '{{.State.OOMKilled}} {{.State.ExitCode}}' app-1true 137 คือการถูกสั่งยุติจากเหตุการณ์ OOM ส่วน false 137 หมายความว่ามีบางอย่างส่งสัญญาณ SIGKILL มา ซึ่งสาเหตุทั่วไปคือ docker compose stop ทำงานเกินระยะเวลาผ่อนผัน 10 วินาที เนื่องจากแอปพลิเคชันเพิกเฉยต่อสัญญาณ SIGTERM การแยกแยะความแตกต่างนี้ช่วยประหยัดเวลาได้มาก เพราะปัญหาทั้งสองอย่างไม่มีส่วนเกี่ยวข้องกันเลย
มีอีกสองจุดที่บันทึกเหตุการณ์นี้ไว้ ให้ตรวจสอบ daemon แบบเรียลไทม์:
docker events --filter event=oomจากนั้นให้อ่าน log ของเคอร์เนล ซึ่งเป็นบันทึกที่ยังคงอยู่แม้จะมีการรีสตาร์ทระบบ:
sudo dmesg -T | grep -i -E 'memory cgroup out of memory|killed process'การสั่งยุติโดย cgroup จะพิมพ์บรรทัดที่ขึ้นต้นด้วย Memory cgroup out of memory: Killed process 24713 (node) หากบรรทัดใดไม่มีคำนำหน้า Memory cgroup แสดงว่าเป็นเหตุการณ์ OOM ของโฮสต์ ซึ่งหมายความว่าเครื่องเซิร์ฟเวอร์เองมี RAM ไม่เพียงพอ นี่คือความล้มเหลวที่ขีดจำกัดถูกออกแบบมาเพื่อป้องกัน ดังนั้นการพบเหตุการณ์นี้เป็นสัญญาณว่าผลรวมของขีดจำกัดที่คุณตั้งไว้สูงเกินไป หรือมีบางบริการที่ไม่ได้กำหนดขีดจำกัดไว้เลย
เมื่อใช้ restart: unless-stopped การวนซ้ำของ OOM จะถูกซ่อนไว้ได้ง่าย เพราะบริการจะปรากฏสถานะว่าทำงานอยู่ใน docker compose ps เพียงหนึ่งวินาทีหลังจากที่มันหยุดทำงาน ให้ตรวจสอบคอลัมน์ uptime และจำนวนการรีสตาร์ท พร้อมทั้งจับคู่ขีดจำกัดเข้ากับ การตรวจสอบสถานะสุขภาพที่รายงานว่าแอปไม่พร้อมใช้งาน เพื่อให้คุณสามารถสังเกตเห็นคอนเทนเนอร์ที่หยุดทำงานซ้ำๆ ได้โดยไม่ต้องเฝ้าดูตลอดเวลา
Reservation เป็นเพียงคำแนะนำ แต่ limit คือกฎเกณฑ์
reservations.memory (หรือ mem_reservation ในเวอร์ชันเก่า) เป็นค่าขั้นต่ำแบบยืดหยุ่น Docker อธิบายว่าเป็น soft limit ที่จะทำงานเมื่อ daemon ตรวจพบการแย่งชิงทรัพยากรหรือหน่วยความจำบนโฮสต์เหลือน้อย ค่านี้ไม่เคยหยุดยั้งไม่ให้ container ใช้หน่วยความจำเกินกว่าที่กำหนด และไม่รับประกันว่าจะมีหน่วยความจำว่างเมื่อ container ร้องขอ มันเพียงแค่ส่งผลให้ kernel เลือกดึงหน่วยความจำคืนจาก container ที่ใช้เกินค่า reservation ก่อนเป็นอันดับแรก
ดังนั้น reservation จึงไม่ได้ปกป้องสิ่งใดด้วยตัวมันเอง ให้ใช้มันเพื่อระบุบริการที่คุณต้องการให้ได้รับการปฏิบัติเป็นพิเศษเมื่อระบบอยู่ภายใต้ภาระงานหนัก และใช้ limit เพื่อความปลอดภัย ให้ตั้งค่า reservation ไว้ต่ำกว่า limit เสมอ มิฉะนั้น container จะไม่เริ่มทำงาน โดย Docker จะปฏิเสธการตั้งค่าดังกล่าวด้วยข้อผิดพลาด Minimum memory limit can not be less than memory reservation limit
ความจริงเกี่ยวกับ Swap accounting
VPS ส่วนใหญ่ที่ติดตั้งมามักไม่มี swap file ให้ใช้งาน ให้รันคำสั่ง swapon --show และ free -h หากค่ารวมของ swap เป็นศูนย์ การตั้งค่าที่เกี่ยวข้องกับ swap ทั้งหมดด้านล่างนี้จะไม่มีผล และขีดจำกัดหน่วยความจำของคุณจะเป็นเพียงการจำกัด RAM เท่านั้น
memswap_limit ไม่ใช่ปริมาณของ swap แต่เป็นผลรวมของหน่วยความจำและ swap หากใช้ mem_limit: 1g และ memswap_limit: 2g คอนเทนเนอร์จะได้ RAM 1GB และ swap 1GB การตั้งค่าทั้งสองค่าให้เท่ากันจะทำให้คอนเทนเนอร์ไม่มี swap เลย การตั้งค่า mem_limit โดยปล่อยให้ memswap_limit ว่างไว้ จะทำให้คอนเทนเนอร์สามารถใช้ swap ได้สูงสุดเท่ากับขนาดของขีดจำกัดหน่วยความจำเดิม
Ubuntu 24.04 และ Debian 13 ใช้ cgroup v2 เป็นค่าเริ่มต้น ซึ่ง swap จะถูกนับเป็นตัวนับแยกต่างหาก (memory.swap.max) และทำงานได้โดยไม่ต้องตั้งค่าเพิ่มเติม ข้อความแจ้งเตือนเก่าๆ อย่าง Your kernel does not support swap limit capabilities มาจากโฮสต์ที่ใช้ cgroup v1 ซึ่งไม่ได้บูตด้วยพารามิเตอร์ swapaccount=1 ในกรณีเหล่านั้น ขีดจำกัดหน่วยความจำยังคงมีผล แต่ส่วนของ swap จะถูกละเว้น
ควรพิจารณาตามความเป็นจริงว่า swap มีประโยชน์อย่างไร มันเพียงแค่ทำให้การเกิด OOM kill ช้าลงเท่านั้น ไม่ได้ทำให้โอกาสเกิดน้อยลง เพราะกระบวนการที่หน่วยความจำรั่วไหลจะเติม swap ได้รวดเร็วพอๆ กับการเติม RAM ในขณะเดียวกัน หากคอนเทนเนอร์มีการทำ thrashing บน swap ของพื้นที่จัดเก็บข้อมูล VPS ที่ใช้ร่วมกัน จะทำให้บริการอื่นๆ ทั้งหมดบนเครื่องทำงานช้าลง สำหรับงานที่ไวต่อความหน่วง (latency sensitive) การกำหนดขีดจำกัดที่ถูกต้องโดยไม่มี swap จะทำให้ระบบล้มเหลวได้เร็วกว่าและคาดการณ์ได้มากกว่า
เหตุใดการใช้หน่วยความจำจึงดูสูงกว่าความเป็นจริง
ตัวเลข MEM USAGE ใน docker stats รวมเอา page cache เข้าไปด้วย ดังนั้นคอนเทนเนอร์ที่อ่านไฟล์ขนาดใหญ่จึงมีตัวเลขการใช้งานพุ่งสูงขึ้นจนเกือบถึงขีดจำกัดและค้างอยู่ที่นั่น นี่เป็นเรื่องปกติและไม่ใช่การรั่วไหลของหน่วยความจำ (memory leak) เนื่องจาก cache ที่สะอาดจะถูกดึงกลับมาใช้ใหม่ก่อนที่ OOM killer จะทำงาน บริการอย่าง Jellyfin media server ที่โฮสต์เอง จะแสดงผลว่ามีการใช้งานใกล้เพดานอยู่ตลอดเวลาด้วยเหตุผลนี้
ให้แยกตัวเลขระหว่าง cache และ working set จริงจากภายในคอนเทนเนอร์:
docker compose exec app grep -E '^(anon|file) ' /sys/fs/cgroup/memory.stat
docker compose exec app cat /sys/fs/cgroup/memory.eventsanon คือ anonymous memory ซึ่งเป็น working set ที่ไม่สามารถลบออกได้ ส่วน file คือ page cache ซึ่งสามารถลบออกได้ ให้กำหนดขีดจำกัดโดยอิงจาก anon บวกด้วยส่วนเผื่อ ไม่ใช่อิงจากยอดรวม ไฟล์ memory.events จะช่วยยุติข้อถกเถียงนี้ได้ทันที: หากตัวนับ oom_kill มีค่ามากกว่าศูนย์ หมายความว่า kernel ได้สั่งยุติกระบวนการบางอย่างในคอนเทนเนอร์นี้ไปแล้วนับตั้งแต่เริ่มทำงาน และหากตัวนับ max กำลังเพิ่มขึ้น หมายความว่าคอนเทนเนอร์กำลังถูกจำกัดอยู่ที่เพดานการใช้งานในขณะนี้ ทั้งสองคำสั่งจำเป็นต้องมี shell และ coreutils อยู่ภายใน image ดังนั้นคำสั่งเหล่านี้จะใช้งานไม่ได้บน distroless หรือ scratch image
การกำหนดขีดจำกัดขนาดบน VPS ขนาด 8GB
ให้เริ่มพิจารณาจากโฮสต์ ไม่ใช่จากแอปพลิเคชัน บน VPS ขนาด 8GB ควรแบ่งพื้นที่ประมาณ 1GB ไว้สำหรับเคอร์เนล, Docker daemon, sshd, journald และเชลล์สำหรับล็อกอินของคุณ พื้นที่ที่เหลือประมาณ 7GB คือส่วนที่สามารถจัดสรรได้ และผลรวมของขีดจำกัดในทุกคอนเทนเนอร์ไม่ควรเกินค่านี้ การจัดสรรทรัพยากรเกินจริง (Overcommitting) อาจใช้งานได้จนกว่าจะถึงวันที่บริการสองอย่างทำงานหนักพร้อมกัน
ตัวอย่างการแบ่งทรัพยากรที่ใช้งานได้จริงบนเครื่องขนาด 8GB:
- Reverse proxy: จำกัดที่ 128m เป็นกระบวนการขนาดเล็ก การจำกัดที่เข้มงวดเช่นนี้ช่วยให้ตรวจพบการโหลดคอนฟิกที่ผิดปกติได้ทันที
- PostgreSQL: จำกัดที่ 2g โดยตั้งค่า
shared_buffersไว้ที่ประมาณ 512MB ในไฟล์คอนฟิกของฐานข้อมูล - Application container: จำกัดที่ 1g
- Background worker: จำกัดที่ 512m
- Media หรือ file service: จำกัดที่ 2g ซึ่งส่วนใหญ่จะถูกใช้เป็น page cache
อย่าคัดลอกตัวเลขเหล่านี้ไปใช้กับ stack ของคุณโดยตรง ให้รันบริการภายใต้โหลดจริงเป็นเวลาหนึ่งวัน ตรวจสอบ docker stats นำค่า anon สูงสุดของแต่ละคอนเทนเนอร์มาคำนวณ แล้วบวกเพิ่มอีกประมาณครึ่งหนึ่งเพื่อเป็นพื้นที่สำรอง การตั้งขีดจำกัดที่เข้มงวดเกินไปนั้นแย่ยิ่งกว่าการไม่ตั้งเลย เพราะมันจะทำให้บริการที่ทำงานปกติถูกสั่งปิดในระหว่างที่มีการใช้งานพุ่งสูงขึ้น
มีกับดักหนึ่งอย่างที่ควรระวังเป็นพิเศษ ขีดจำกัดนี้จะไม่ปรากฏให้รันไทม์ส่วนใหญ่เห็นหากคุณไม่ได้ระบุไว้ PostgreSQL จะปรับขนาด shared_buffers และ work_mem เกินขีดจำกัดของคอนเทนเนอร์และถูกสั่งปิดในที่สุด JVM (Java virtual machine) ต้องการ -XX:MaxRAMPercentage=75 เพื่อปรับขนาด heap ตามขีดจำกัดของ cgroup แทนที่จะอ้างอิงจาก RAM ของโฮสต์ Node.js ต้องการ --max-old-space-size ในหน่วยเมกะไบต์ โดยต้องตั้งค่าให้ต่ำกว่าขีดจำกัดของคอนเทนเนอร์ มิฉะนั้น garbage collector จะปล่อยให้ heap ขยายตัวจนกระทั่งเคอร์เนลเข้ามาแทรกแซง Ollama ก็ประสบปัญหาเดียวกันแต่ใช้การตั้งค่าที่ต่างออกไป เนื่องจาก การเพิ่ม num_ctx จะทำให้ KV cache ขยายตัว ขึ้นหลายร้อยเมกะไบต์จนคอนเทนเนอร์หยุดทำงานระหว่างการประมวลผล prompt ที่ยาว cgroup ไม่มีการเจรจาต่อรอง มันจะสั่งปิดทันที
การจำกัด CPU มีพฤติกรรมที่แตกต่างออกไปโดยสิ้นเชิง
cpus: "1.5" หมายถึง 150% ของหนึ่งคอร์ โดยบังคับใช้ผ่านโควตาของ CFS (completely fair scheduler) คอนเทนเนอร์จะได้รับเวลา CPU 150ms ในทุกๆ รอบเวลา 100ms ซึ่งจะถูกแบ่งใช้โดยทุกเธรดภายในคอนเทนเนอร์ เมื่อใช้เวลาจนครบ Kernel จะทำให้คอนเทนเนอร์ต้องรอจนกว่าจะถึงรอบเวลาถัดไป
นี่คือข้อแตกต่างที่สำคัญ คอนเทนเนอร์ที่ใช้หน่วยความจำเกินขีดจำกัดจะถูกสั่ง kill แต่คอนเทนเนอร์ที่ใช้ CPU เกินขีดจำกัดจะถูก throttled และทำงานต่อไปได้เพียงแต่ช้าลง ดังนั้นการตั้งค่า CPU limit จึงสามารถทำได้อย่างเข้มงวด ในขณะที่ memory limit จำเป็นต้องเผื่อพื้นที่ไว้
cpu_shares เป็นเครื่องมือที่แตกต่างออกไป คือการกำหนดน้ำหนักสัมพัทธ์ซึ่งจะมีผลก็ต่อเมื่อ CPU ทำงานเต็มประสิทธิภาพเท่านั้น คอนเทนเนอร์สองตัวที่มีค่า shares เป็น 1024 และ 512 จะแบ่งการใช้งานคอร์ที่กำลังยุ่งในสัดส่วนประมาณสองต่อหนึ่ง แต่หากเครื่องว่างอยู่ ทั้งสองตัวจะไม่ถูกจำกัดการใช้งาน ให้ใช้ shares เพื่อจัดลำดับความสำคัญของบริการ และใช้ cpus เมื่อคุณต้องการกำหนดเพดานการใช้งานจริง ตัวอย่างเช่น เพื่อป้องกันไม่ให้งาน transcode ที่รันในตอนกลางคืนแย่งทรัพยากรจนเว็บเซิร์ฟเวอร์ของคุณทำงานไม่ได้
FAQ
deploy.resources.limits ทำงานได้หรือไม่หากไม่มี Docker Swarm?
ได้ Docker Compose ซึ่งเป็นปลั๊กอิน docker compose (ปัจจุบันอยู่ในสาย v5) จะนำ deploy.resources.limits และ deploy.resources.reservations ไปใช้งานเมื่อคุณรัน docker compose up บนโฮสต์เดียว คุณสามารถตรวจสอบได้ด้วย docker inspect --format '{{.HostConfig.Memory}}' <container> ซึ่งจะแสดงค่าขีดจำกัดเป็นไบต์ และแสดง 0 หากไม่มีการกำหนดขีดจำกัดไว้ คีย์ภายใน deploy ที่จำเป็นต้องใช้ Swarm จริงๆ คือ mode, placement, update_config และ endpoint_mode
exit code 137 ใน Docker Compose หมายความว่าอย่างไร?
หมายความว่ากระบวนการหลักได้รับสัญญาณ SIGKILL เนื่องจาก 137 คือ 128 บวกกับสัญญาณที่ 9 สาเหตุที่พบบ่อยคือ OOM killer ของเคอร์เนล แต่การหมดเวลาปิดระบบ (shutdown timeout) ก็ทำให้เกิดรหัสนี้ได้เช่นกันหากแอปพลิเคชันเพิกเฉยต่อ SIGTERM ให้รัน docker inspect --format '{{.State.OOMKilled}} {{.State.ExitCode}}' <container> เพื่อแยกแยะสาเหตุ โดย true 137 คือการถูกสั่งหยุดเนื่องจากหน่วยความจำเต็ม ส่วน false 137 ไม่ใช่
ควรตั้งค่า mem_limit หรือ deploy.resources.limits.memory?
ใช้ได้ทั้งสองแบบกับ docker compose โดย deploy.resources.limits.memory เป็นรูปแบบตามข้อกำหนด Compose Specification ปัจจุบันและเป็นค่าเริ่มต้นที่ดีกว่าสำหรับไฟล์ใหม่ ให้คง mem_limit ไว้หากไฟล์ส่วนที่เหลือของคุณใช้คีย์ระดับบนสุดแบบเก่าอยู่แล้ว การตั้งค่าทั้งสองอย่างในบริการเดียวจะทำให้ไฟล์อ่านยากขึ้น ดังนั้นให้เลือกใช้อย่างใดอย่างหนึ่งและตรวจสอบผลลัพธ์ด้วย docker inspect
ทำไมคอนเทนเนอร์ของฉันถึงใช้หน่วยความจำเต็มขีดจำกัดโดยไม่ถูกสั่งหยุด?
ตัวเลขการใช้งานใน docker stats รวมเอา page cache เข้าไปด้วย ซึ่งเคอร์เนลจะล้างออกเมื่อมีแรงกดดันแทนที่จะสั่ง OOM kill ให้รัน docker compose exec <service> grep -E '^(anon|file) ' /sys/fs/cgroup/memory.stat และอ่านค่า anon ซึ่งเป็น working set ที่ไม่สามารถเรียกคืนได้ ค่า file ที่สูงควบคู่กับค่า anon ที่ต่ำ หมายถึงคอนเทนเนอร์กำลังทำกิจกรรมอ่านเขียนดิสก์ ไม่ใช่คอนเทนเนอร์ที่กำลังจะหยุดทำงาน
ควรเหลือ RAM ไว้เท่าใดบน VPS ขนาด 8GB?
ควรเหลือไว้ประมาณ 1GB สำหรับเคอร์เนล, Docker daemon, sshd, journald และเชลล์ของคุณ จากนั้นให้ควบคุมผลรวมของขีดจำกัดคอนเทนเนอร์ทั้งหมดให้อยู่ภายใต้ 7GB ที่เหลือ ให้เฝ้าสังเกตค่าสูงสุดของ anon ในแต่ละคอนเทนเนอร์ภายใต้ภาระงานจริงเป็นเวลาหนึ่งวันก่อนที่จะกำหนดตัวเลข และให้มองว่ายอดรวมเป็นงบประมาณ ไม่ใช่เป้าหมายที่ต้องใช้ให้เต็ม