Jinsi ya kuweka kikomo cha RAM kwenye Docker Compose
Zuia container moja kutumia RAM yote ya VPS kwa kutumia deploy.resources.limits.memory. Epuka hitilafu ya exit 137 na linda seva yako dhidi ya kuporomoka kwa ghafla.
Kikomo cha kumbukumbu katika Docker Compose hufanya nini
Kikomo cha kumbukumbu katika Docker Compose ni ukomo mgumu ambao kernel ya Linux huweka kwenye cgroup ya container moja (control group, kipengele cha kernel kinachopima rasilimali kwa ajili ya seti ya michakato). Weka deploy.resources.limits.memory kwenye huduma na container hiyo haitaweza kutumia zaidi ya namba uliyoiandika. Inapojaribu kufanya hivyo, kernel huua mchakato ndani ya container, na container hiyo kwa kawaida huzimika ikiwa na code 137.
Hili ni muhimu zaidi kwenye VPS, ambapo RAM imepangwa na hakuna kumbukumbu ya ziada ya seva ya kukopa. Container moja yenye memory leak au query mbaya itachukua kila ukurasa wa kumbukumbu ulio wazi kwenye seva ya 8GB. Kernel kisha huua mchakato wowote inaouona kuwa mbaya zaidi, ambao mara nyingi huwa ni database au session yako ya SSH badala ya container iliyosababisha tatizo. Vikomo hubadilisha hitilafu ya seva nzima kuwa huduma moja inayojianzisha upya.
services:
app:
image: ghcr.io/example/app:1.4
deploy:
resources:
limits:
cpus: "1.5"
memory: 1g
reservations:
memory: 256mItumie na uthibitishe kuwa kikomo kimeanza kufanya kazi:
docker compose up -d
docker stats --no-streamSafu ya MEM USAGE / LIMIT inapaswa kusomeka kama 142MiB / 1GiB. Ikiwa safu ya kikomo inaonyesha RAM yote ya seva, mpangilio huo haukufanya kazi, na mwongozo huu hautakusaidia hadi hapo utakapofanya kazi. Ikiwa faili ya compose yenyewe ni ngeni kwako, misingi ya Docker Compose kwa VPS inaelezea muundo wa faili ambao mwongozo huu unajengwa juu yake.
deploy.resources.limits au mem_limit: ni ipi inayotumika
Kuna tahajia mbili kwa wazo moja, ndiyo maana hii inachanganya.
mem_limit, mem_reservation, memswap_limit, cpus na cpu_shares ni funguo za ngazi ya juu za huduma (service keys) zilizorithiwa kutoka kwenye miundo ya zamani ya faili za Compose. deploy.resources ilitoka kwenye schema ya Swarm na sasa ni sehemu ya Compose Specification, ambayo ndiyo muundo unaosomwa na docker compose leo.
Zote hufanya kazi kwenye seva moja. Docker Compose, ambayo ni docker compose plugin, hutumia deploy.resources.limits na deploy.resources.reservations unapoendesha docker compose up, bila kuwa na Swarm cluster yoyote. Plugin hii sasa iko kwenye mstari wa v5 (v5.6.0 ilikuwa toleo thabiti la hivi karibuni lilipokaguliwa tarehe 2026-10-10). Iliruka kutoka v2 moja kwa moja hadi v5, ikiruka 3 na 4 ili kuepuka mkanganyiko na matoleo ya zamani ya faili za Compose, na hakuna toleo la v5 lililobadilisha jinsi mipaka ya rasilimali inavyoshughulikiwa. Sehemu zinazoelekea Swarm za block ya deploy ni funguo nyingine, kama vile placement, update_config na endpoint_mode, zinazoelezea upangaji na utekelezaji (rollouts) kwenye cluster ya nodes kwa ajili ya docker stack deploy. Kwa hivyo, ushauri wa kawaida kwamba "deploy inahitaji Swarm" si sahihi kwa sehemu ndogo ya resources, na kuufuata kutakuacha na huduma zisizo na kikomo chochote.
Chagua tahajia moja kwa kila mradi. Kuandika mem_limit: 512m na deploy.resources.limits.memory: 1g kwenye huduma moja ni zaidi ya kuwa vigumu kusoma: Compose Specification inasema kwamba wakati mem_limit imewekwa lazima iwe thabiti na limits.memory, na haisemi ni thamani ipi inayoshinda zinapokinzana. Tahajia yoyote unayotumia, thibitisha kile ambacho daemon imetekeleza kweli:
docker inspect --format '{{.HostConfig.Memory}} {{.HostConfig.MemoryReservation}} {{.HostConfig.NanoCpus}}' app-1Thamani za kumbukumbu ni bytes, kwa hivyo 1g huchapishwa kama 1073741824. CPU iko katika nano CPUs, kwa hivyo 1.5 huchapishwa kama 1500000000. 0 katika uwanja wowote inamaanisha hakuna kikomo kilichowekwa. Kikomo kidogo zaidi cha kumbukumbu ambacho Docker hukubali ni 6m, na chini ya hapo container hukataa kuanza.
Nini hutokea wakati container inapofikia kikomo
Container haipunguzi kasi. Inakufa.
Wakati mchakato unapoitisha ukurasa na cgroup imefikia memory.max yake, kernel kwanza hurejesha kile inachoweza ndani ya cgroup hiyo: clean page cache, kisha kurasa zinazoweza kupelekwa kwenye swap. Ikiwa urejeshaji hautoa nafasi ya kutosha, OOM (out of memory) killer ya cgroup huchagua mchakato ndani ya container na kuutumia SIGKILL. Kuua PID 1 ya container husitisha container hiyo. Exit code 137 ni 128 jumlisha signal 9, kwa hivyo 137 ni alama ya SIGKILL yoyote, si uthibitisho wa OOM pekee.
docker compose ps -a
docker inspect --format '{{.State.OOMKilled}} {{.State.ExitCode}}' app-1true 137 ni OOM kill. false 137 kwa kawaida humaanisha kitu kingine kimetuma SIGKILL (programu inaweza pia kutoka na 137 kwa makusudi, lakini hilo ni nadra), na sababu ya kawaida ni docker compose stop kufikia muda wake wa neema wa sekunde kumi kwa sababu programu ilipuuza SIGTERM. Tofauti hiyo huokoa saa nyingi, kwa sababu matatizo hayo mawili hayana uhusiano wowote.
Maeneo mengine mawili hurekodi tukio hilo. Fuatilia daemon moja kwa moja:
docker events --filter event=oomKisha soma log ya kernel, ambayo ndiyo rekodi inayobaki baada ya kuanzishwa upya:
sudo dmesg -T | grep -i -E 'memory cgroup out of memory|killed process'Uuaji wa cgroup huchapisha mstari unaoanza na Memory cgroup out of memory: Killed process 24713 (node). Mstari huo humaanisha container moja imefikia kikomo chake, na unaweza kuonekana wakati host bado ina RAM nyingi ya bure. Mstari usio na prefix ya Memory cgroup ni host OOM, ambayo humaanisha mashine yenyewe imepungukiwa RAM. Hilo ndilo tatizo ambalo vikomo vinalenga kuzuia, kwa hivyo kuliona kwa kawaida humaanisha jumla ya vikomo vyako ni kubwa mno, kwamba baadhi ya huduma hazina kikomo kabisa, au kwamba kitu nje ya Docker kinatumia zaidi ya nafasi uliyoiacha.
Kwa restart: unless-stopped, OOM loop hujificha vizuri, kwa sababu huduma huonekana iko juu katika docker compose ps sekunde moja baada ya kufa. Angalia safu ya uptime na idadi ya restart, na unganisha kikomo na healthcheck inayoripoti programu kama isiyo na afya ili container inayokufa mara kwa mara ionekane bila wewe kuifuatilia.
Reservation ni kidokezo, limit ndiyo sheria
reservations.memory (au mem_reservation ya zamani) ni kiwango cha chini cha kuanzia (soft floor). Docker inaielezea kama soft limit inayowashwa wakati daemon inapogundua msongamano au uhaba wa kumbukumbu kwenye host. Hii haizuii container kuvuka kiwango hicho, na haitoi hakikisho kwamba kumbukumbu itapatikana wakati container inapoiomba. Inachofanya ni kuipa kernel kipaumbele cha kurejesha kumbukumbu kutoka kwa container zilizovuka reservation yao kwanza.
Kwa hivyo, reservation haitoi ulinzi wowote yenyewe. Itumie kutambulisha huduma unayotaka ipewe kipaumbele wakati wa msongamano, na utegemee limit kwa ajili ya usalama. Weka reservation chini ya limit, vinginevyo container haitaanza: Docker itakataa configuration hiyo kwa kutoa Minimum memory limit can not be less than memory reservation limit.
Uhasibu wa swap, kwa uwazi
Picha nyingi za VPS husafirishwa bila faili yoyote ya swap. Tekeleza swapon --show na free -h. Ikiwa jumla ya swap ni sifuri, kila mpangilio wa swap hapa chini haufanyi kazi, na kikomo chako cha kumbukumbu ni uwezo wa RAM pekee.
memswap_limit si kiasi cha swap. Ni jumla ya kumbukumbu pamoja na swap. Kwa mem_limit: 1g na memswap_limit: 2g, kontena hupata 1GB ya RAM na 1GB ya swap. Kuweka thamani hizi mbili kuwa sawa kunafanya kontena isiwe na swap kabisa. Kuweka mem_limit na kuacha memswap_limit bila kuwekwa thamani huruhusu kontena kutumia swap hadi kufikia ukubwa wa kikomo chake cha kumbukumbu tena.
Ubuntu 24.04 na Debian 13 hutumia cgroup v2 kwa chaguo-msingi, ambapo swap ni kaunta tofauti (memory.swap.max) na hii hufanya kazi bila usanidi wa ziada. Ujumbe wa zamani Your kernel does not support swap limit capabilities hutoka kwa seva za cgroup v1 zilizowashwa bila swapaccount=1. Kwenye hizo, kikomo cha kumbukumbu bado hutumika wakati sehemu ya swap hupuuzwa.
Kuwa mkweli kuhusu kile ambacho swap inakupa. Inafanya OOM kill kuwa polepole, siyo kutokuwa na uwezekano, kwa sababu mchakato unaovuja kumbukumbu hujaza swap kwa furaha kama unavyojaza RAM. Wakati huo huo, kontena inayotumia swap kupita kiasi kwenye hifadhi ya pamoja ya VPS hupunguza kasi ya kila huduma nyingine kwenye seva hiyo. Kwa chochote kinachohitaji latency ndogo, kikomo sahihi bila swap hushindwa haraka na kwa njia inayotabirika zaidi.
Kwa nini matumizi ya kumbukumbu huonekana kuwa mabaya kuliko hali halisi
Kwenye Linux, thamani ya MEM USAGE katika docker stats hupunguza tu cache ya kurasa isiyotumika (inactive_file kwenye cgroup v2), kwa hivyo cache iliyotumika hivi karibuni bado huhesabiwa, na container inayosoma faili kubwa hupanda kuelekea kikomo chake na kubaki hapo. Hiyo ni kawaida, na si uvujaji wa kumbukumbu, kwa sababu cache safi hurejeshwa kabla ya OOM killer kuitwa. Huduma kama seva ya media ya Jellyfin inayojiendesha itaonekana kuwa karibu na kikomo chake kila wakati kwa sababu hii hasa.
Gawa namba hiyo katika cache na working set halisi ukiwa ndani ya container:
docker compose exec app grep -E '^(anon|file) ' /sys/fs/cgroup/memory.stat
docker compose exec app cat /sys/fs/cgroup/memory.eventsanon ni kumbukumbu ya anonymous, working set ambayo haiwezi kufutwa. file ni page cache, ambayo inaweza kufutwa. Panga kikomo chako kulingana na anon pamoja na ziada, si kulingana na jumla. Faili ya memory.events hutatua mjadala huu moja kwa moja: counter ya oom_kill iliyo juu ya sifuri inamaanisha kernel imeuwa kitu fulani kwenye container hii tangu ilipoanza, na counter ya max inayopanda inamaanisha container inashikiliwa kwenye kikomo chake hivi sasa. Amri zote mbili zinahitaji shell na coreutils ndani ya image, kwa hivyo zinashindwa kufanya kazi kwenye image ya distroless au scratch.
Vikomo vya ukubwa kwenye VPS ya 8GB
Anza na seva pangishi (host), siyo programu zako. Kwenye VPS ya 8GB, acha takriban 1GB kwa ajili ya kernel, Docker daemon, sshd, journald, na shell yako ya kuingia. Hii inakuachia takriban 7GB ya kugawanya, na jumla ya vikomo vya kila container inapaswa kubaki chini ya kiasi hicho. Overcommitting hufanya kazi hadi siku ambapo huduma mbili zinafikia kilele kwa wakati mmoja.
Mgawanyo unaofaa kwenye seva ya 8GB:
- Reverse proxy: kikomo cha 128m. Hii ni process ndogo, na kikomo hiki kidogo hutambua mara moja usanidi (config) mbaya wakati wa reload.
- PostgreSQL: kikomo cha 2g, huku
shared_buffersikiwa imewekwa takriban 512MB kwenye usanidi wa database. - Application container: kikomo cha 1g.
- Background worker: kikomo cha 512m.
- Media au file service: kikomo cha 2g, ambapo sehemu kubwa itakuwa page cache.
Usinakili namba hizo kwenye stack yako. Endesha huduma hizo chini ya mzigo halisi kwa siku moja, fuatilia docker stats, chukua thamani ya juu kabisa ya anon kwa kila container na uongeze takriban nusu yake kama nafasi ya ziada (headroom). Kikomo kilichowekwa kwa kubana sana ni kibaya kuliko kutokuwa na kikomo, kwa sababu huua huduma inayofanya kazi vizuri wakati wa ongezeko la kawaida la trafiki.
Mtego mmoja unastahili tahadhari maalum. Runtime nyingine hupuuza kikomo isipokuwa ukiwaambia, na zile zinazokisoma zinaweza zisijipange ukubwa kama unavyotarajia. PostgreSQL itajipanga kwa furaha shared_buffers na work_mem kupita kikomo cha container yake na hatimaye kuuawa. JVM (Java virtual machine) ya kisasa hutambua kikomo cha cgroup kwa chaguo-msingi, lakini matoleo ya HotSpot kama OpenJDK huipa heap asilimia 25 tu ya kikomo hicho, kwa hivyo -XX:MaxRAMPercentage=75 ndiyo njia ya kawaida ya kuruhusu heap kutumia sehemu kubwa ya kikomo huku ukiiachia nafasi kumbukumbu iliyo nje ya heap. Node.js imekuwa ikipanga ukubwa wa heap yake ya chaguo-msingi kulingana na kikomo cha cgroup tangu toleo la 12, kwa takriban nusu ya kikomo cha container na kusimama karibu na 2GB, kwa hivyo weka --max-old-space-size katika megabytes pale tu unapotaka ukomo tofauti, na uiweke chini ya kikomo cha container ili garbage collector ifanye kazi kabla kernel haijaingilia kati. Ollama ni hadithi hiyo hiyo na kitufe tofauti, kwa sababu kuongeza num_ctx kunakuza KV cache kwa mamia ya megabytes na container hufa katikati ya prompt ndefu. Cgroup haifanyi mazungumzo. Inaua.
Vikomo vya CPU hufanya kazi kwa njia tofauti kabisa
cpus: "1.5" inamaanisha 150% ya core moja, inayotekelezwa kama quota ya CFS (completely fair scheduler). Container hupata 150ms ya muda wa CPU katika kila kipindi cha 100ms, ikigawanywa kati ya threads zake zote. Inapotumia muda huo, kernel huilazimisha kusubiri kipindi kijacho.
Huo ndio utofauti muhimu. Container inayozidi kikomo chake cha kumbukumbu huawa (killed). Container inayozidi kikomo chake cha CPU hupunguziwa kasi (throttled) na kuendelea kufanya kazi, lakini kwa mwendo wa polepole. Kwa hivyo, ni salama kuweka kikomo cha CPU kwa ukali, wakati kikomo cha kumbukumbu kinahitaji nafasi ya ziada.
cpu_shares ni zana tofauti: ni uzito wa kulinganisha (relative weight) ambao hufanya kazi tu wakati CPU zimejaa. Container mbili zenye shares za 1024 na 512 hugawanya core yenye shughuli nyingi kwa uwiano wa takriban mbili kwa moja, na kwenye seva isiyo na shughuli, hakuna hata moja inayozuiwa. Tumia shares kupanga huduma kulingana na umuhimu wake, na utumie cpus wakati unahitaji kikomo cha kweli, kwa mfano kuzuia kazi ya transcode ya usiku isinyime rasilimali server yako ya wavuti.
FAQ
Je, deploy.resources.limits hufanya kazi bila Docker Swarm?
Ndiyo. Docker Compose, kupitia plugin ya docker compose (ambayo sasa iko kwenye toleo la v5), hutumia deploy.resources.limits na deploy.resources.reservations unapoendesha docker compose up kwenye seva moja. Thibitisha hili kwa kutumia docker inspect --format '{{.HostConfig.Memory}}' <container>, ambayo huonyesha kikomo hicho kwa bytes na kuchapisha 0 ikiwa hakuna kikomo kilichowekwa. Funguo zilizo ndani ya deploy zilizoundwa kwa ajili ya Swarm ni zile za cluster, kama vile placement, update_config, na endpoint_mode.
Exit code 137 inamaanisha nini kwenye Docker Compose?
Karibu kila mara inamaanisha mchakato mkuu umepokea SIGKILL, kwa sababu 137 ni 128 jumlisha signal 9 (programu inaweza pia kutoka na 137 kwa makusudi, lakini hilo ni nadra). OOM killer ya kernel ndiyo sababu ya kawaida, lakini muda wa kusubiri wakati wa kuzima (shutdown timeout) hutoa code hiyo hiyo wakati programu inapuuza SIGTERM. Endesha docker inspect --format '{{.State.OOMKilled}} {{.State.ExitCode}}' <container> ili kutofautisha sababu hizo. true 137 ni mauaji ya kumbukumbu (memory kill), na false 137 si hivyo.
Je, niweke mem_limit au deploy.resources.limits.memory?
Zote hufanya kazi na docker compose. deploy.resources.limits.memory ndiyo muundo wa sasa wa Compose Specification na ndiyo chaguo bora kwa faili mpya. Bakiza mem_limit ikiwa faili yako yote inatumia funguo za zamani za ngazi ya juu. Compose Specification inahitaji hizi mbili ziwe sawa zinapowekwa zote, kwa hivyo chagua moja na uthibitishe matokeo kwa docker inspect.
Kwa nini container yangu inakaa kwenye kikomo chake cha juu cha kumbukumbu bila kuuawa?
Takwimu ya matumizi katika docker stats bado huhesabu page cache iliyotumika hivi karibuni, ambayo kernel huiondoa inapokuwa na shinikizo badala ya kusababisha OOM kill. Endesha docker compose exec <service> grep -E '^(anon|file) ' /sys/fs/cgroup/memory.stat na usome thamani ya anon, ambayo ni working set isiyoweza kurejeshwa. Thamani ya juu ya file kando ya thamani ya chini ya anon inamaanisha container inafanya shughuli za kusoma na kuandika kwenye diski (disk I/O), si container inayokaribia kufa.
Ni kiasi gani cha RAM ninapaswa kukiacha bila kukitenga kwenye VPS ya 8GB?
Acha takriban 1GB kwa ajili ya kernel, Docker daemon, sshd, journald, na shell yako, kisha weka jumla ya vikomo vyote vya container chini ya 7GB iliyobaki. Fuatilia thamani ya kilele ya anon kwa kila container chini ya mzigo halisi wa kazi kwa siku moja kabla ya kuamua namba, na chukulia jumla hiyo kama bajeti badala ya lengo la kuijaza.