Docker Compose memory limits para iwas OOM
Itakda ang memory at CPU limits sa Docker Compose para hindi pabagsakin ng isang greedy container ang VPS. Alamin ang deploy.resources, mem_limit, exit 137, swap, at tamang sizing.
Ano ang ginagawa ng Docker Compose memory limit
Ang Docker Compose memory limit ay hard cap na ipinapataw ng Linux kernel sa cgroup (control group, isang feature ng kernel na sumusukat sa paggamit ng resources ng isang set ng mga proseso) ng isang container. Itakda ang deploy.resources.limits.memory sa isang service at hindi kailanman lalampas ang container na iyon sa itinakdang halaga. Kapag sinubukan nitong lumampas, papatayin ng kernel ang isang proseso sa loob ng container, at karaniwang lalabas ang container na may code 137.
Pinakamahalaga ito sa isang VPS, kung saan fixed ang RAM at walang ekstrang memorya sa host na maaaring hiramin. Maaaring ubusin ng isang container na may memory leak o maling query ang bawat libreng page sa isang 8GB na server. Pagkatapos, papatayin ng kernel ang prosesong itinuturing nitong pinakamalalang gumamit ng resources. Kadalasan, database o SSH session ang napapatay sa halip na ang container na sanhi ng problema. Ginagawang isang service na maaaring mag-restart ng limits ang outage ng buong server.
services:
app:
image: ghcr.io/example/app:1.4
deploy:
resources:
limits:
cpus: "1.5"
memory: 1g
reservations:
memory: 256mIlapat ito at kumpirmahing aktibo na ang limit:
docker compose up -d
docker stats --no-streamDapat magpakita ang MEM USAGE / LIMIT column ng halagang gaya ng 142MiB / 1GiB. Kung ipinapakita ng limit column ang buong RAM ng host, hindi nailapat ang setting. Hindi makatutulong ang natitirang bahagi ng gabay na ito hangga't hindi ito naaayos. Kung bago sa iyo ang compose file mismo, ipinapaliwanag ng mga pangunahing kaalaman sa Docker Compose para sa VPS ang file layout na pinagbabatayan nito.
deploy.resources.limits o mem_limit: alin ang ginagamit
May dalawang spelling para sa parehong konsepto, kaya nakalilito ito.
mem_limit, mem_reservation, memswap_limit, cpus at cpu_shares ay mga top-level service key na minana mula sa mas lumang Compose file format. Nagmula ang deploy.resources sa Swarm schema at bahagi na ngayon ito ng Compose Specification, na siyang format na binabasa ng docker compose sa kasalukuyan.
Parehong gumagana ang mga ito sa iisang host. Ipinapatupad ng Docker Compose, ng docker compose plugin, ang deploy.resources.limits at deploy.resources.reservations kapag pinatakbo mo ang docker compose up, kahit walang Swarm cluster. Nasa v5 line na ngayon ang plugin nito. Ang v5.6.0 ang pinakabagong stable release nang suriin ito noong 2026-10-10. Mula v2, dumiretso ito sa v5 at nilaktawan ang 3 at 4 upang maiwasan ang kalituhan sa mga lumang bersyon ng Compose file format. Walang v5 release na nagbago sa paraan ng paghawak sa resource limits. Ang mga bahagi ng deploy block na nakatuon sa Swarm ay ang iba pang key, gaya ng placement, update_config at endpoint_mode. Inilalarawan ng mga ito ang scheduling at rollout sa maraming node sa isang cluster para sa docker stack deploy. Kaya mali ang karaniwang payo na “kailangan ng Swarm ang deploy” para sa resources subsection. Kapag sinunod mo ito, walang limit ang iyong services.
Pumili ng isang spelling para sa bawat project. Hindi lang mahirap basahin kapag isinulat mo ang mem_limit: 512m at deploy.resources.limits.memory: 1g sa iisang service. Ayon sa Compose Specification, kapag nakatakda ang mem_limit, dapat itong pare-pareho sa limits.memory. Hindi nito sinasabi kung aling value ang mananaig kapag magkaiba ang mga ito. Anuman ang spelling na gamitin mo, kumpirmahin kung ano talaga ang inilapat ng daemon:
docker inspect --format '{{.HostConfig.Memory}} {{.HostConfig.MemoryReservation}} {{.HostConfig.NanoCpus}}' app-1Ang memory value ay nasa bytes, kaya ipinapakita ng 1g ang 1073741824. Ang CPU ay nasa nano CPUs, kaya ipinapakita ng 1.5 ang 1500000000. Kapag may 0 sa anumang field, nangangahulugang walang limit na itinakda. Ang pinakamaliit na memory limit na tinatanggap ng Docker ay 6m. Kapag mas mababa rito, hindi magsisimula ang container.
Ano ang nangyayari kapag naabot ng container ang limit
Hindi bumabagal ang container. Tumitigil ito.
Kapag humingi ang isang proseso ng page at nasa memory.max na ang cgroup, unang binabawi ng kernel ang makakaya nito sa loob ng cgroup na iyon: clean page cache, at pagkatapos ay mga page na maaaring i-swap. Kung hindi sapat ang makuhang memory, pipili ang cgroup OOM (out of memory) killer ng isang proseso sa loob ng container at padadalhan ito ng SIGKILL. Kapag pinatay ang PID 1 ng container, matatapos ang container. Ang exit code na 137 ay 128 plus signal 9 lamang, kaya ang 137 ay fingerprint ng anumang SIGKILL, hindi patunay ng OOM sa sarili nito.
docker compose ps -a
docker inspect --format '{{.State.OOMKilled}} {{.State.ExitCode}}' app-1Ang true 137 ay isang OOM kill. Ang false 137 ay karaniwang nangangahulugang may ibang nagpadala ng SIGKILL (maaari ring sadyang mag-exit ang isang program na may 137, pero bihira ito), at ang karaniwang sanhi ay naabot ng docker compose stop ang ten-second grace period nito dahil hindi pinansin ng app ang SIGTERM. Makakatipid ng ilang oras ang pagkilalang ito, dahil walang kaugnayan ang dalawang problemang ito.
May dalawa pang lugar na nagtatala ng event. I-monitor nang live ang daemon:
docker events --filter event=oomPagkatapos, basahin ang kernel log. Ito ang record na nananatili kahit mag-restart:
sudo dmesg -T | grep -i -E 'memory cgroup out of memory|killed process'Nagpi-print ang cgroup kill ng linyang nagsisimula sa Memory cgroup out of memory: Killed process 24713 (node). Ibig sabihin ng linyang iyon, may isang container na umabot sa sarili nitong limit, at maaari itong lumitaw kahit marami pang libreng RAM ang host. Ang linyang walang prefix na Memory cgroup ay host OOM, na nangangahulugang naubusan ng RAM ang mismong machine. Ito ang failure na dapat pigilan ng mga limit, kaya karaniwang nangangahulugan ang paglitaw nito na masyadong mataas ang kabuuan ng mga limit mo, may ilang service na walang limit, o may gumagamit sa labas ng Docker ng mas maraming memory kaysa sa headroom na iniwan mo.
Sa restart: unless-stopped, madaling hindi mapansin ang OOM loop, dahil mukhang umaandar ang service sa docker compose ps isang segundo matapos itong mamatay. Suriin ang uptime column at restart count, at itambal ang limit sa isang healthcheck na nag-uulat na unhealthy ang app para makita ang container na paulit-ulit na namamatay nang hindi mo ito kailangang bantayan.
Reservation ay pahiwatig; ang limit ang panuntunan
Ang reservations.memory (ang mas lumang mem_reservation) ay isang soft floor. Inilalarawan ito ng Docker bilang soft limit na ina-activate kapag may contention o mababa ang memory sa host. Hindi nito kailanman pinipigilan ang container na lumampas dito, at hindi rin nito ginagarantiyang magiging available ang memory kapag humiling ang container. Sa halip, hinihikayat lamang nito ang kernel na unahin ang pag-reclaim mula sa mga container na lampas sa kanilang reservation.
Kaya walang napoprotektahan ang reservation kapag nag-iisa. Gamitin ito para tukuyin ang serbisyong gusto mong mas pagbigyan kapag mataas ang pressure, at umasa sa limit para sa kaligtasan. Panatilihing mas mababa ang reservation kaysa sa limit; kung hindi, hindi magsisimula ang container: tatanggihan ng Docker ang config dahil sa Minimum memory limit can not be less than memory reservation limit.
Swap accounting, sa totoo lang
Karamihan sa VPS images ay walang swap file. Patakbuhin ang swapon --show at free -h. Kung zero ang kabuuang swap, walang epekto ang lahat ng setting na nauugnay sa swap sa ibaba, at purong RAM cap ang memory limit mo.
Ang memswap_limit ay hindi dami ng swap. Ito ang kabuuan ng memory at swap. Sa mem_limit: 1g at memswap_limit: 2g, makakakuha ang container ng 1GB RAM at 1GB swap. Kapag magkapareho ang dalawang value, wala nang swap ang container. Kapag itinakda ang mem_limit at iniwang unset ang memswap_limit, maaari muling gumamit ang container ng swap hanggang sa laki ng memory limit nito.
Gamit ang default na cgroup v2 ang Ubuntu 24.04 at Debian 13, kung saan hiwalay na counter ang swap (memory.swap.max), at gumagana ito nang walang karagdagang setup. Ang lumang mensaheng Your kernel does not support swap limit capabilities ay nagmumula sa mga cgroup v1 host na nag-boot nang wala ang swapaccount=1. Sa mga host na iyon, ipinapatupad pa rin ang memory limit, pero binabalewala ang bahagi para sa swap.
Maging tapat sa pakinabang ng swap. Pinapabagal nito ang OOM kill, pero hindi nito binabawasan ang posibilidad nito, dahil kasingdali para sa memory leak na proseso na punuin ang swap gaya ng pagpuno nito sa RAM. Samantala, kapag ang isang container ay patuloy na gumagamit ng swap sa shared VPS storage, bumabagal ang bawat iba pang serbisyo sa server. Para sa anumang sensitibo sa latency, mas mabilis at mas predictable ang failure kapag tama ang limit at walang swap.
Kung bakit mas masama tingnan ang memory usage kaysa sa aktuwal na kalagayan
Sa Linux, ang figure na MEM USAGE sa docker stats ay nagbabawas lamang ng inactive page cache (inactive_file sa cgroup v2), kaya kasama pa rin ang cache na kamakailang ginamit. Ang container na nagbabasa ng malalaking file ay tataas hanggang sa limit nito at mananatili roon. Normal ito at hindi memory leak, dahil nire-reclaim ang malinis na cache bago tawagin ang OOM killer. Ang service gaya ng self-hosted na Jellyfin media server ay mananatiling mukhang malapit sa ceiling nito dahil mismo sa kadahilanang ito.
Hatiin ang value sa cache at aktuwal na working set mula sa loob ng container:
docker compose exec app grep -E '^(anon|file) ' /sys/fs/cgroup/memory.stat
docker compose exec app cat /sys/fs/cgroup/memory.eventsAng anon ay anonymous memory, ang working set na hindi maaaring i-drop. Ang file ay page cache, na maaaring i-drop. I-set ang limit batay sa anon kasama ang margin, hindi batay sa total. Direktang nilulutas ng memory.events file ang usapin: ang oom_kill counter na higit sa zero ay nangangahulugang may pinatay ang kernel sa container na ito mula nang magsimula ito, at ang tumataas na max counter ay nangangahulugang kasalukuyang itinutulak ang container hanggang sa ceiling nito. Parehong nangangailangan ang mga command ng shell at coreutils sa loob ng image, kaya hindi gagana ang mga ito sa distroless o scratch image.
Mga limitasyon sa sizing sa isang 8GB VPS
Magsimula sa host, hindi sa mga app. Sa isang 8GB VPS, maglaan ng humigit-kumulang 1GB para sa kernel, Docker daemon, sshd, journald, at sarili mong login shell. Mag-iiwan ito ng tinatayang 7GB na maaaring ilaan, at dapat manatiling mas mababa rito ang kabuuan ng bawat container limit. Gumagana ang overcommit hanggang sa araw na sabay mag-peak ang dalawang serbisyo.
Isang praktikal na hatian sa isang 8GB na server:
- Reverse proxy: 128m na limit. Maliit itong proseso, at agad na mahuhuli ng ganitong kasikip na limit ang runaway config reload.
- PostgreSQL: 2g na limit, at itakda ang
shared_bufferssa humigit-kumulang 512MB sa database config. - Application container: 1g na limit.
- Background worker: 512m na limit.
- Media o file service: 2g na limit, na karamihan ay gagamitin bilang page cache.
Huwag basta kopyahin ang mga numerong ito sa sarili mong stack. Patakbuhin ang mga serbisyo sa totoong load sa loob ng isang araw, i-monitor ang docker stats, kunin ang peak na anon value ng bawat container, at magdagdag ng humigit-kumulang kalahati nito bilang headroom. Mas masama ang limit na masyadong mahigpit kaysa walang limit, dahil pinapatay nito ang malusog na serbisyo kapag may normal na traffic spike.
May isang karaniwang bitag na nangangailangan ng hiwalay na paliwanag. May ilang runtime na binabalewala ang limit hangga't hindi mo ito ipinapaalam sa kanila, at ang mga runtime na nakakabasa nito ay maaaring hindi mag-size sa paraang inaasahan mo. Maluwag na ise-size ng PostgreSQL ang shared_buffers at work_mem lampas sa container limit nito, kaya mapapatay ito. Awtomatikong nade-detect ng modern JVM (Java virtual machine) ang cgroup limit, pero ang mga HotSpot build gaya ng OpenJDK ay nagbibigay lamang ng 25 percent nito sa heap. Kaya ang -XX:MaxRAMPercentage=75 ang karaniwang paraan para magamit ng heap ang mas malaking bahagi ng limit habang nag-iiwan ng espasyo para sa memory sa labas ng heap. Mula pa sa version 12, sine-size ng Node.js ang default heap nito batay sa cgroup limit, sa humigit-kumulang kalahati ng container limit at unti-unting tumitigil sa bandang 2GB. Itakda lamang ang --max-old-space-size sa megabytes kapag ibang ceiling ang kailangan mo, at panatilihin itong mas mababa sa container limit upang makapagtrabaho ang garbage collector bago mamagitan ang kernel. Ganoon din sa Ollama, ngunit ibang knob ang ginagamit, dahil lumalaki ng daan-daang megabytes ang KV cache kapag itinataas ang num_ctx at namamatay ang container sa kalagitnaan ng mahabang prompt. Hindi nakikipag-negosasyon ang cgroup. Pinapatay nito.
Ibang-iba ang paraan ng pagpatupad ng CPU limits
Ang cpus: "1.5" ay nangangahulugang 150% ng isang core, na ipinapatupad bilang CFS (completely fair scheduler) quota. Nakakakuha ang container ng 150ms na CPU time sa bawat 100ms na period, na pinaghahatian ng lahat ng thread nito. Kapag naubos nito ang oras na iyon, paghihintayin ito ng kernel hanggang sa susunod na period.
Ito ang mahalagang pagkakaiba. Kapag lumampas ang container sa memory limit nito, pinapatay ito. Kapag lumampas ito sa CPU limit, tina-throttle ito at nagpapatuloy, ngunit mas mabagal. Kaya ligtas magtakda ng agresibong CPU limit, samantalang kailangang may sapat na headroom ang memory limit.
Ang cpu_shares ay ibang tool: relative weight ito na may epekto lamang kapag aktuwal na saturated ang mga CPU. Ang dalawang container na may shares na 1024 at 512 ay maghahati sa abalang core nang humigit-kumulang two-to-one. Kapag idle ang server, walang paghihigpit sa alinman sa mga ito. Gamitin ang shares upang itakda ang priority ng mga serbisyo batay sa kahalagahan, at gamitin ang cpus kapag kailangan mo ng aktuwal na ceiling, halimbawa upang hindi maagawan ng nightly transcode job ng CPU ang web server.
FAQ
Gumagana ba ang deploy.resources.limits kahit walang Docker Swarm?
Oo. Inilalapat ng Docker Compose, ang docker compose plugin (kasalukuyang nasa v5 line), ang deploy.resources.limits at deploy.resources.reservations kapag pinatakbo mo ang docker compose up sa isang host. Kumpirmahin ito gamit ang docker inspect --format '{{.HostConfig.Memory}}' <container>, na nagpi-print ng limit sa bytes at nagpi-print ng 0 kapag walang nailapat na limit. Ang mga key sa loob ng deploy na isinulat para sa Swarm ay ang mga pang-cluster, gaya ng placement, update_config at endpoint_mode.
Ano ang ibig sabihin ng exit code 137 sa Docker Compose?
Halos palagi itong nangangahulugang nakatanggap ang pangunahing proseso ng SIGKILL, dahil ang 137 ay 128 dagdag sa signal 9. Maaari ring sadyang mag-exit ang isang program gamit ang 137, pero bihira ito. Karaniwang sanhi ang OOM killer ng kernel, pero pareho rin ang code kapag nag-timeout ang shutdown dahil hindi pinapansin ng app ang SIGTERM. Patakbuhin ang docker inspect --format '{{.State.OOMKilled}} {{.State.ExitCode}}' <container> upang matukoy ang pagkakaiba. Ang true 137 ay memory kill, samantalang hindi ganoon ang false 137.
Dapat ko bang itakda ang mem_limit o deploy.resources.limits.memory?
Parehong gumagana ang mga ito sa docker compose. Ang deploy.resources.limits.memory ang kasalukuyang anyo sa Compose Specification at mas mainam na default para sa bagong file. Panatilihin ang mem_limit kung ginagamit na ng iba pang bahagi ng file mo ang mas lumang top-level keys. Kinakailangan ng Compose Specification na consistent ang dalawa kapag parehong nakatakda, kaya pumili ng isa at i-verify ang resulta gamit ang docker inspect.
Bakit umaabot sa buong memory limit ang container ko pero hindi ito nati-terminate?
Kasama pa rin sa usage figure ng docker stats ang page cache na kamakailang ginamit. Inaalis ito ng kernel kapag may memory pressure sa halip na mag-trigger ng OOM kill. Patakbuhin ang docker compose exec <service> grep -E '^(anon|file) ' /sys/fs/cgroup/memory.stat at basahin ang value ng anon, na tumutukoy sa working set na hindi maaaring i-reclaim. Kapag mataas ang value ng file habang mababa ang value ng anon, nangangahulugan itong nagsasagawa ng disk input at output ang container, hindi na malapit itong mamatay.
Gaano karaming RAM ang dapat kong iwang hindi naka-allocate sa isang 8GB VPS?
Maglaan ng humigit-kumulang 1GB para sa kernel, Docker daemon, sshd, journald at sarili mong shell. Panatilihing mas mababa sa natitirang 7GB ang kabuuan ng lahat ng container limit. Subaybayan ang peak anon value ng bawat container sa totoong load sa loob ng isang araw bago magtakda ng mga numero, at ituring ang kabuuan bilang budget, hindi target na kailangang punuin.