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 nalalapat
May dalawang spelling para sa parehong konsepto, kaya nakalilito ito.
Ang 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 ito ng Compose Specification, ang format na binabasa ngayon ng docker compose.
Pareho silang gumagana sa isang host. Inilalapat ng Docker Compose, ang 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 noong 2026-10-06). Dumiretso ito mula v2 sa v5 at nilaktawan ang 3 at 4 upang maiwasan ang pagkalito sa mga lumang bersyon ng Compose file format. Walang v5 release na nagbago sa paghawak ng resource limits. Ang mga Swarm-only na bahagi ng deploy block ay ang iba pang key: ang mode, placement, update_config at endpoint_mode ay may kahulugan sa docker stack deploy at hindi pinapansin ng docker compose up. Kaya mali ang karaniwang payo na "kailangan ng Swarm ang deploy" para sa resources subsection. Kapag sinunod ito, walang limitasyon ang iyong mga service.
Pumili ng isang spelling para sa bawat project. Kapag isinulat mo ang mem_limit: 512m at deploy.resources.limits.memory: 1g sa parehong service, magiging mahirap basahin agad ang file. Sa halip na hulaan kung aling value ang nalapat, tanungin ang daemon:
docker inspect --format '{{.HostConfig.Memory}} {{.HostConfig.MemoryReservation}} {{.HostConfig.NanoCpus}}' app-1Nasa bytes ang memory values, kaya lumalabas ang 1g bilang 1073741824. Nasa nano CPUs ang CPU value, kaya lumalabas ang 1.5 bilang 1500000000. Ang 0 sa anumang field ay nangangahulugang walang itinakdang limitasyon. 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 ng page ang isang process at nasa memory.max na ang cgroup, unang binabawi ng kernel ang kaya nitong bawiin sa loob ng cgroup na iyon: clean page cache, saka ang mga page na maaaring i-swap. Kung hindi sapat ang mabawi, pipili ang cgroup OOM (out of memory) killer ng process sa loob ng container at papadalhan ito ng SIGKILL. Kapag pinatay ang PID 1 ng container, magtatapos ang container. Ang exit code 137 ay 128 dagdag ang signal 9, kaya ang 137 ay fingerprint ng anumang SIGKILL, hindi patunay na OOM ang sanhi.
docker compose ps -a
docker inspect --format '{{.State.OOMKilled}} {{.State.ExitCode}}' app-1Ang true 137 ay isang OOM kill. Ibig sabihin ng false 137, may ibang nagpadala ng SIGKILL, at ang karaniwang sanhi ay naabot ng docker compose stop ang ten second grace period nito dahil binalewala ng app ang SIGTERM. Mahalaga ang pagkakaibang ito dahil magkaibang-magkaiba ang dalawang problema.
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). Ang linyang walang prefix na Memory cgroup ay host OOM. Ibig sabihin, naubusan mismo ng RAM ang machine. Ito ang failure na dapat pigilan ng limits, kaya kapag nakita mo ito, malamang na masyadong mataas ang kabuuan ng iyong limits o may ilang service na walang limitasyon.
Sa restart: unless-stopped, madaling hindi mapansin ang OOM loop dahil lumalabas na up ang service sa docker compose ps isang segundo matapos itong mamatay. Suriin ang uptime column at ang restart count. Iugnay din ang limit sa isang healthcheck na nag-uulat na unhealthy ang app para makita ang container na paulit-ulit na namamatay kahit hindi mo ito mino-monitor.
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.
Bakit mas malaki ang tingin sa memory usage kaysa sa aktuwal
Kasama sa figure na MEM USAGE sa docker stats ang page cache, kaya ang container na nagbabasa ng malalaking file ay umaakyat hanggang sa limit nito at nananatili roon. Normal ito at hindi ito memory leak, dahil nire-reclaim ang malinis na cache bago man tawagin ang OOM killer. Ang serbisyong gaya ng self-hosted na Jellyfin media server ay magmumukhang palaging malapit sa ceiling nito dahil dito.
Hatiin ang bilang 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. Ibase ang laki ng limit sa anon at dagdag na margin, hindi sa kabuuang halaga. Agad na nilulutas ng memory.events file ang usapin: kapag higit sa zero ang oom_kill counter, may pinatay ang kernel sa container na ito mula nang magsimula ito; kapag tumataas ang max counter, kasalukuyang pinipigilan ang container sa ceiling nito. Kailangan ng dalawang command ng shell at coreutils sa loob ng image, kaya mabibigo ang mga ito sa distroless o scratch image.
Mga limitasyon sa laki 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 humigit-kumulang 7GB na maaaring ilaan sa mga serbisyo, at dapat manatiling mas mababa rito ang kabuuan ng lahat ng container limit. Gumagana ang overcommit hanggang sa araw na sabay mag-peak ang dalawang serbisyo.
Isang praktikal na paghahati sa isang 8GB machine:
- Reverse proxy: 128m limit. Maliit itong process, at agad na matutukoy ng ganitong kasikip na limit ang runaway config reload.
- PostgreSQL: 2g limit, na ang
shared_buffersay itinatakda sa humigit-kumulang 512MB sa database config. - Application container: 1g limit.
- Background worker: 512m limit.
- Media o file service: 2g limit, na karamihan ay mapupunta sa 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 maayos na serbisyo kapag may normal na biglaang pagtaas ng traffic.
May isang karaniwang problema na dapat bigyan ng sariling paliwanag. Hindi nakikita ng karamihan sa runtime ang limit maliban kung ipapaalam mo ito sa kanila. Masayang itatakda ng PostgreSQL ang shared_buffers at work_mem nang lampas sa container limit nito, at pagkatapos ay mapapatay ito. Kailangan ng JVM (Java virtual machine) ang -XX:MaxRAMPercentage=75 upang ibatay ang laki ng heap sa cgroup limit sa halip na sa RAM ng host. Kailangan ng Node.js ang --max-old-space-size na nasa megabytes at mas mababa sa container limit; kung hindi, palalakihin ng garbage collector ang heap hanggang mamagitan ang kernel. Ganito rin sa Ollama, pero iba ang setting, dahil ang pagtaas sa num_ctx ay nagpapalaki sa KV cache ng daan-daang megabytes 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 nang walang Docker Swarm?
Oo. Inilalapat ng Docker Compose, ang docker compose plugin (ngayon ay nasa v5 line na), 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 talagang nangangailangan ng Swarm ay mode, placement, update_config at endpoint_mode.
Ano ang ibig sabihin ng exit code 137 sa Docker Compose?
Ibig sabihin, nakatanggap ang pangunahing process ng SIGKILL, dahil ang 137 ay 128 dagdag ang signal 9. Karaniwang sanhi ang kernel OOM killer, pero nagbubunga rin ng parehong code ang shutdown timeout kapag 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?
Pareho itong gumagana 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 ibang bahagi ng file mo ang mga mas lumang top-level key. Kapag parehong itinakda sa isang service, mas mahirap lamang basahin ang file, kaya pumili ng isa at i-verify ang resulta gamit ang docker inspect.
Bakit nasa buong memory limit nito ang container ko pero hindi ito napatay?
Kasama sa usage figure ng docker stats ang page cache. Inaalis ito ng kernel kapag mataas ang 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. Ito ang working set na hindi maaaring i-reclaim. Ang mataas na value ng file kasabay ng mababang value ng anon ay nagpapahiwatig na nagsasagawa ang container ng disk input at output, hindi na malapit na itong mamatay.
Gaano karaming RAM ang dapat kong iwanang hindi inilaan sa isang 8GB VPS?
Mag-iwan ng humigit-kumulang 1GB para sa kernel, Docker daemon, sshd, journald at sarili mong shell. Pagkatapos, panatilihing mas mababa sa natitirang 7GB ang kabuuan ng lahat ng container limit. Subaybayan ang peak anon value ng bawat container habang may tunay na load sa loob ng isang araw bago magtakda ng mga halaga. Ituring ang kabuuan bilang budget, hindi bilang target na kailangang punan.