วิธีจำกัด RAM ใน Docker Compose ป้องกันเซิร์ฟเวอร์ล่ม
เรียนรู้วิธีตั้งค่า deploy.resources และ mem_limit ใน Docker Compose เพื่อป้องกันคอนเทนเนอร์กิน RAM จนเกิด exit 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 อ่านในปัจจุบัน
ทั้งสองแบบใช้งานได้บนโฮสต์เดียว Compose V2 ซึ่งเป็นปลั๊กอิน docker compose จะนำ deploy.resources.limits และ deploy.resources.reservations ไปใช้เมื่อคุณรัน docker compose up โดยไม่จำเป็นต้องมีคลัสเตอร์ Swarm ส่วนที่เป็นของ Swarm เท่านั้นในบล็อก deploy คือคีย์อื่นๆ ได้แก่ 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 จะปฏิเสธการเริ่มทำงาน
เกิดอะไรขึ้นเมื่อคอนเทนเนอร์ถึงขีดจำกัด
คอนเทนเนอร์จะไม่ทำงานช้าลง แต่จะหยุดทำงานทันที
เมื่อกระบวนการร้องขอหน่วยความจำและ cgroup ถึงขีดจำกัด memory.max แล้ว เคอร์เนลจะพยายามเรียกคืนทรัพยากรภายใน cgroup นั้นก่อน โดยเริ่มจาก clean page cache ตามด้วยหน้าหน่วยความจำที่สามารถสลับออกไปได้ (swap) หากการเรียกคืนทรัพยากรไม่เพียงพอ ตัว OOM (out of memory) killer ของ cgroup จะเลือกกระบวนการหนึ่งภายในคอนเทนเนอร์และส่งสัญญาณ SIGKILL ให้ การสั่งยุติ PID 1 ของคอนเทนเนอร์จะทำให้คอนเทนเนอร์นั้นสิ้นสุดการทำงาน รหัสสถานะ 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 ในเวอร์ชันเก่า) เป็นค่าขีดจำกัดแบบผ่อนปรน (soft floor) 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 ช้าลง ไม่ใช่ทำให้โอกาสเกิดน้อยลง เพราะกระบวนการที่หน่วยความจำรั่วไหล (leaking process) จะเติมข้อมูลลงใน swap ได้รวดเร็วพอๆ กับที่เติมลงใน RAM ในขณะเดียวกัน คอนเทนเนอร์ที่ใช้งาน 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 ซึ่งเป็น working set ที่ไม่สามารถลบออกได้ ส่วน file คือ page cache ซึ่งสามารถลบออกได้ การกำหนดขีดจำกัดควรพิจารณาจาก anon บวกด้วยส่วนเผื่อ ไม่ใช่พิจารณาจากผลรวมทั้งหมด ไฟล์ memory.events จะช่วยตัดสินประเด็นนี้ได้อย่างชัดเจน: หากตัวนับ oom_kill มีค่ามากกว่าศูนย์ หมายความว่าเคอร์เนลได้สั่งยุติกระบวนการบางอย่างในคอนเทนเนอร์นี้ไปแล้วนับตั้งแต่เริ่มทำงาน และหากตัวนับ max กำลังเพิ่มขึ้น หมายความว่าคอนเทนเนอร์กำลังถูกจำกัดอยู่ที่เพดานการใช้งานในขณะนี้ คำสั่งทั้งสองต้องการ shell และ coreutils ภายในอิมเมจ ดังนั้นคำสั่งเหล่านี้จะใช้งานไม่ได้บนอิมเมจแบบ distroless หรือ scratch
การกำหนดขีดจำกัดขนาดบน VPS ขนาด 8GB
ให้เริ่มพิจารณาจากโฮสต์ ไม่ใช่จากตัวแอปพลิเคชัน บน VPS ขนาด 8GB ควรเผื่อพื้นที่ประมาณ 1GB สำหรับ kernel, Docker daemon, sshd, journald และ shell สำหรับล็อกอินของคุณ พื้นที่ที่เหลือประมาณ 7GB คือส่วนที่สามารถจัดสรรได้ และผลรวมของขีดจำกัดของทุกคอนเทนเนอร์ควรอยู่ภายใต้ค่านี้ การจัดสรรทรัพยากรเกินตัว (Overcommitting) อาจใช้งานได้จนถึงวันที่บริการสองอย่างทำงานหนักพร้อมกัน
ตัวอย่างการแบ่งทรัพยากรที่ใช้งานได้จริงบนเครื่องขนาด 8GB:
- Reverse proxy: จำกัดที่ 128m เป็นกระบวนการขนาดเล็ก การจำกัดที่เข้มงวดเช่นนี้จะช่วยให้ตรวจพบการโหลด config ที่ผิดพลาดได้ทันที
- PostgreSQL: จำกัดที่ 2g โดยตั้งค่า
shared_buffersไว้ที่ประมาณ 512MB ในไฟล์คอนฟิกของฐานข้อมูล - Application container: จำกัดที่ 1g
- Background worker: จำกัดที่ 512m
- Media หรือ file service: จำกัดที่ 2g ซึ่งส่วนใหญ่จะเป็น page cache
อย่าคัดลอกตัวเลขเหล่านี้ไปใช้ใน stack ของคุณโดยตรง ให้รันบริการภายใต้ภาระงานจริงเป็นเวลาหนึ่งวัน ตรวจสอบ docker stats ดูค่าสูงสุดของ anon ในแต่ละคอนเทนเนอร์ แล้วบวกเพิ่มอีกประมาณครึ่งหนึ่งเพื่อเป็นพื้นที่สำรอง การตั้งขีดจำกัดที่เข้มงวดเกินไปนั้นแย่ยิ่งกว่าการไม่ตั้งเลย เพราะมันจะทำให้บริการที่ทำงานปกติถูกสั่งหยุดทำงานในช่วงที่มีการใช้งานหนาแน่น
มีกับดักหนึ่งอย่างที่ควรระวังเป็นพิเศษ ขีดจำกัดนี้จะไม่ถูกมองเห็นโดย runtime ส่วนใหญ่หากคุณไม่ได้ระบุให้มันทราบ PostgreSQL จะปรับขนาด shared_buffers และ work_mem เกินขีดจำกัดของคอนเทนเนอร์จนถูกสั่งหยุดทำงานได้ ส่วน JVM (Java virtual machine) จำเป็นต้องใช้ -XX:MaxRAMPercentage=75 เพื่อปรับขนาด heap ตามขีดจำกัดของ cgroup แทนที่จะอ้างอิงจาก RAM ของโฮสต์ Node.js จำเป็นต้องตั้งค่า --max-old-space-size เป็นหน่วยเมกะไบต์โดยให้ต่ำกว่าขีดจำกัดของคอนเทนเนอร์ มิฉะนั้น garbage collector จะปล่อยให้ heap ขยายตัวจนกระทั่ง kernel เข้ามาแทรกแซง สำหรับ Ollama ก็เป็นกรณีเดียวกันแต่ใช้ตัวแปรอื่น เพราะ การเพิ่ม num_ctx จะทำให้ KV cache ขยายตัว ขึ้นหลายร้อยเมกะไบต์ จนทำให้คอนเทนเนอร์หยุดทำงานระหว่างการประมวลผล prompt ที่ยาว cgroup ไม่มีการเจรจาต่อรอง มันจะสั่งหยุดทำงานทันที
การจำกัด CPU มีพฤติกรรมที่แตกต่างออกไปโดยสิ้นเชิง
cpus: "1.5" หมายถึง 150% ของหนึ่งคอร์ ซึ่งบังคับใช้ผ่านโควตาของ CFS (Completely Fair Scheduler) คอนเทนเนอร์จะได้รับเวลา CPU 150ms ในทุกช่วงเวลา 100ms โดยแบ่งใช้ร่วมกันระหว่างเธรดทั้งหมด เมื่อใช้เวลาจนครบ เคอร์เนลจะบังคับให้รอจนกว่าจะถึงช่วงเวลาถัดไป
นี่คือข้อแตกต่างที่สำคัญ คอนเทนเนอร์ที่ใช้หน่วยความจำเกินขีดจำกัดจะถูกสั่ง kill แต่คอนเทนเนอร์ที่ใช้ CPU เกินขีดจำกัดจะถูก throttled และทำงานต่อไปได้แต่ช้าลง ดังนั้นการตั้งค่า CPU limit จึงสามารถทำได้อย่างเข้มงวด ในขณะที่ memory limit จำเป็นต้องเผื่อพื้นที่ไว้
cpu_shares เป็นเครื่องมือที่ต่างออกไป คือการกำหนดน้ำหนักสัมพัทธ์ซึ่งจะมีผลก็ต่อเมื่อ CPU ทำงานเต็มประสิทธิภาพเท่านั้น คอนเทนเนอร์สองตัวที่มีค่า shares เป็น 1024 และ 512 จะแบ่งการใช้งานคอร์ที่ว่างอยู่ด้วยสัดส่วนประมาณสองต่อหนึ่ง และหากเครื่องไม่ได้ใช้งานหนัก ทั้งสองตัวก็จะไม่ถูกจำกัดการทำงาน ให้ใช้ shares เพื่อจัดลำดับความสำคัญของบริการ และใช้ cpus เมื่อคุณต้องการกำหนดเพดานการใช้งานจริง เช่น เพื่อป้องกันไม่ให้งานแปลงไฟล์ที่รันตอนกลางคืนดึงทรัพยากรไปจนเว็บเซิร์ฟเวอร์ทำงานไม่ได้
FAQ
deploy.resources.limits ทำงานโดยไม่มี Docker Swarm ได้หรือไม่
ได้ Compose V2 จะนำ 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 สาเหตุที่พบบ่อยคือการที่ kernel 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 เข้าไปด้วย ซึ่ง kernel จะล้างข้อมูลส่วนนี้ออกเมื่อเกิดแรงกดดันแทนที่จะสั่ง OOM kill ให้รัน docker compose exec <service> grep -E '^(anon|file) ' /sys/fs/cgroup/memory.stat และอ่านค่า anon ซึ่งเป็น working set ที่ไม่สามารถเรียกคืนได้ หากค่า file สูงในขณะที่ค่า anon ต่ำ แสดงว่าคอนเทนเนอร์กำลังทำกิจกรรมอ่านเขียนดิสก์ ไม่ใช่คอนเทนเนอร์ที่กำลังจะหยุดทำงาน
ควรเหลือ RAM ที่ไม่ได้จัดสรรไว้เท่าใดบน VPS ขนาด 8GB
ควรเหลือไว้ประมาณ 1GB สำหรับ kernel, Docker daemon, sshd, journald และ shell ของคุณ จากนั้นให้คุมผลรวมของขีดจำกัดคอนเทนเนอร์ทั้งหมดให้อยู่ภายใต้ 7GB ที่เหลืออยู่ ให้เฝ้าสังเกตค่าสูงสุดของ anon ในแต่ละคอนเทนเนอร์ภายใต้ภาระงานจริงเป็นเวลาหนึ่งวันก่อนที่จะกำหนดตัวเลข และให้มองว่าผลรวมทั้งหมดเป็นงบประมาณ ไม่ใช่เป้าหมายที่ต้องใช้ให้เต็ม