Docker Composeలో OOM ఆపే మెమరీ పరిమితులు
ఒక container మీ VPSను నిలిపివేయకుండా Docker Composeలో deploy.resources, mem_limitతో memory, CPU పరిమితులు సెట్ చేయండి. exit 137, swap, సరైన sizing వివరాలు తెలుసుకోండి.
Docker Compose మెమరీ పరిమితి చేసే పని
Docker Compose మెమరీ పరిమితి అనేది ఒక container యొక్క cgroup (control group; ప్రక్రియల సమూహం ఉపయోగించే వనరులను కొలిచే kernel సదుపాయం)పై Linux kernel విధించే కఠిన గరిష్ఠ పరిమితి. ఒక serviceపై deploy.resources.limits.memory సెట్ చేస్తే, ఆ container మీరు పేర్కొన్న పరిమాణం కంటే ఎక్కువ మెమరీని ఎప్పటికీ ఉపయోగించదు. అది ఆ పరిమితిని దాటే ప్రయత్నం చేసినప్పుడు, kernel containerలోని ఒక ప్రక్రియను నిలిపివేస్తుంది. సాధారణంగా container code 137తో నిష్క్రమిస్తుంది.
ఇది VPSలో అత్యంత ముఖ్యమైనది. VPSలో RAM స్థిరంగా ఉంటుంది. ఉపయోగించని host memoryను అదనంగా తీసుకునే అవకాశం ఉండదు. memory leak లేదా తప్పు query ఉన్న ఒక container, 8GB serverలోని మొత్తం ఖాళీ pageలను వినియోగించగలదు. అప్పుడు kernel అత్యంత సమస్యాత్మకంగా భావించిన ప్రక్రియను నిలిపివేస్తుంది. తరచుగా అది సమస్యకు కారణమైన container కాకుండా database లేదా మీ SSH session అవుతుంది. పరిమితులు మొత్తం serverలో ఏర్పడే అంతరాయాన్ని, restart అయ్యే ఒక serviceకు పరిమితం చేస్తాయి.
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-streamMEM USAGE / LIMIT columnలో 142MiB / 1GiB వంటి విలువ కనిపించాలి. పరిమితి columnలో మొత్తం host RAM కనిపిస్తే, setting వర్తించలేదు. అది వర్తించే వరకు ఈ మార్గదర్శకంలోని మిగతా సూచనలు సహాయపడవు. compose file మీకు కొత్తగా ఉంటే, VPS కోసం Docker Compose ప్రాథమిక అంశాలు ఈ విధానానికి ఆధారమైన file layoutను వివరిస్తాయి.
deploy.resources.limits లేదా mem_limit: ఏది వర్తిస్తుంది
ఒకే భావానికి రెండు రకాల పేర్లు ఉన్నాయి. అందుకే ఇది గందరగోళంగా ఉంటుంది.
mem_limit, mem_reservation, memswap_limit, cpus మరియు cpu_shares పాత Compose file formats నుంచి వచ్చిన service-level keys. deploy.resources Swarm schema నుంచి వచ్చింది. ప్రస్తుతం docker compose చదివే format అయిన Compose Specificationలో ఇది భాగంగా ఉంది.
ఒకే hostలో రెండూ పనిచేస్తాయి. Swarm cluster లేకపోయినా, docker compose plugin అయిన Compose V2, మీరు docker compose up అమలు చేసినప్పుడు deploy.resources.limits మరియు deploy.resources.reservationsను వర్తింపజేస్తుంది. deploy blockలో Swarmకు మాత్రమే సంబంధించిన ఇతర keys ఇవి: mode, placement, update_config మరియు endpoint_mode. వీటి అర్థాన్ని docker stack deploy మాత్రమే గుర్తిస్తుంది. docker compose up వీటిని విస్మరిస్తుంది. అందువల్ల "resources subsectionకు deploy అవసరం అంటే Swarm అవసరం" అనే సాధారణ సలహా తప్పు. దాన్ని అనుసరిస్తే మీ servicesకు ఎలాంటి limit ఉండదు.
ప్రతి projectకు ఒక spellingనే ఎంచుకోండి. ఒకే serviceలో mem_limit: 512m మరియు deploy.resources.limits.memory: 1g రాస్తే, ఆ fileను చూసిన వెంటనే ఎవరూ అర్థం చేసుకోలేరు. ఏ విలువ వర్తించిందో ఊహించకుండా daemonను అడగండి:
docker inspect --format '{{.HostConfig.Memory}} {{.HostConfig.MemoryReservation}} {{.HostConfig.NanoCpus}}' app-1Memory values bytesలో ఉంటాయి. అందువల్ల 1g, 1073741824గా చూపిస్తుంది. CPU nano CPUsలో ఉంటుంది. అందువల్ల 1.5, 1500000000గా చూపిస్తుంది. ఏ fieldలోనైనా 0 ఉంటే, ఎలాంటి limit సెట్ చేయలేదని అర్థం. Docker అంగీకరించే కనిష్ఠ memory limit 6m. దానికంటే తక్కువ limit ఉంటే container start కాదు.
కంటైనర్ పరిమితిని చేరుకున్నప్పుడు ఏమి జరుగుతుంది
కంటైనర్ నెమ్మదించదు. అది ఆగిపోతుంది.
ఒక process ఒక page కోసం అభ్యర్థించినప్పుడు, cgroup ఇప్పటికే దాని memory.max వద్ద ఉంటే, kernel ముందుగా ఆ cgroup లోపల సాధ్యమైన వాటిని తిరిగి స్వాధీనం చేసుకుంటుంది: ముందుగా clean page cache, తర్వాత swap చేయగల pages. Reclaim ద్వారా తగినంత స్థలం విడుదల కాకపోతే, cgroup OOM (out of memory) killer కంటైనర్లోని ఒక process ను ఎంచుకుని దానికి SIGKILL పంపుతుంది. కంటైనర్ యొక్క PID 1 ను terminate చేస్తే కంటైనర్ ముగుస్తుంది. Exit code 137 అనేది 128 plus signal 9 మాత్రమే. అందువల్ల 137 అనేది ఏదైనా SIGKILL కు గుర్తు మాత్రమే; దానితో OOM జరిగిందని స్వతంత్రంగా నిర్ధారించలేం.
docker compose ps -a
docker inspect --format '{{.State.OOMKilled}} {{.State.ExitCode}}' app-1true 137 అనేది OOM kill. false 137 అంటే మరేదో SIGKILL పంపింది. సాధారణ కారణం ఏమిటంటే, app SIGTERM ను పట్టించుకోకపోవడంతో docker compose stop దాని ten second grace period ను చేరుకోవడం. ఈ తేడా వల్ల గంటల సమయం ఆదా అవుతుంది, ఎందుకంటే ఈ రెండు సమస్యలకు సంబంధం లేదు.
ఈ సంఘటనను మరో రెండు ప్రదేశాలు నమోదు చేస్తాయి. daemon ను ప్రత్యక్షంగా monitor చేయండి:
docker events --filter event=oomతర్వాత kernel log ను చదవండి. Restart తర్వాత కూడా మిగిలే record ఇదే:
sudo dmesg -T | grep -i -E 'memory cgroup out of memory|killed process'cgroup kill, Memory cgroup out of memory: Killed process 24713 (node) తో ప్రారంభమయ్యే line ను ముద్రిస్తుంది. Memory cgroup prefix లేని line host OOM ను సూచిస్తుంది. అంటే machine లోని RAM పూర్తిగా అయిపోయిందని అర్థం. Limits నివారించాల్సిన failure ఇదే. అలాంటి line కనిపిస్తే, మీ limits మొత్తంగా చాలా ఎక్కువగా ఉన్నాయని లేదా కొన్ని services కు అసలు limit లేదని అర్థం.
restart: unless-stopped తో OOM loop సులభంగా దాగిపోతుంది, ఎందుకంటే service ఆగిపోయిన ఒక second తర్వాత docker compose ps లో up గా కనిపిస్తుంది. Uptime column మరియు restart count ను తనిఖీ చేయండి. అలాగే limit ను app ను unhealthy గా నివేదించే healthcheck తో కలిపి ఉపయోగించండి. అలా చేస్తే నిరంతరం ఆగిపోతున్న కంటైనర్ను మీరు స్వయంగా monitor చేయకపోయినా గుర్తించవచ్చు.
రిజర్వేషన్ ఒక సూచన, పరిమితే నియమం
reservations.memory (పాత mem_reservation) ఒక సాఫ్ట్ కనిష్ఠ పరిమితి. హోస్ట్లో పోటీ లేదా తక్కువ మెమరీ ఉందని daemon గుర్తించినప్పుడు సక్రియమయ్యే సాఫ్ట్ పరిమితిగా Docker దీన్ని వివరిస్తుంది. ఇది container ఆ పరిమితిని మించకుండా ఎప్పుడూ ఆపదు. అలాగే container అడిగినప్పుడు మెమరీ ఖాళీగా ఉంటుందని ఎప్పుడూ హామీ ఇవ్వదు. రిజర్వేషన్ కంటే ఎక్కువగా ఉన్న containers నుంచి ముందుగా మెమరీని reclaim చేయేలా ఇది kernel ప్రాధాన్యతను మాత్రమే మార్చుతుంది.
అందువల్ల, reservation ఒక్కదానితో ఏదీ రక్షించబడదు. ఒత్తిడి ఉన్నప్పుడు అనుకూలంగా పరిగణించాలనుకునే serviceను గుర్తించడానికి దీన్ని ఉపయోగించండి. భద్రత కోసం limitపై ఆధారపడండి. reservationను limit కంటే తక్కువగా ఉంచండి. లేకపోతే container ప్రారంభం కాదు: Docker configను Minimum memory limit can not be less than memory reservation limitతో తిరస్కరిస్తుంది.
Swap లెక్కింపు, వాస్తవంగా
చాలా VPS imagesలో అసలు swap file ఉండదు. swapon --show మరియు free -h అమలు చేయండి. Swap మొత్తం సున్నా అయితే, దిగువన ఉన్న swap సంబంధిత ప్రతి setting పనిచేయదు. మీ memory limit పూర్తిగా RAM పరిమితిగానే ఉంటుంది.
memswap_limit swap పరిమాణం కాదు. ఇది memory మరియు swap మొత్తం. mem_limit: 1g మరియు memswap_limit: 2g ఉపయోగిస్తే, containerకు 1GB RAM మరియు 1GB swap లభిస్తాయి. రెండు విలువలను సమానంగా సెట్ చేస్తే, containerకు swap ఉండదు. mem_limit సెట్ చేసి, memswap_limitను సెట్ చేయకుండా వదిలేస్తే, container మళ్లీ తన memory limit పరిమాణం వరకు swap ఉపయోగించగలదు.
Ubuntu 24.04 మరియు Debian 13లో defaultగా cgroup v2 ఉపయోగించబడుతుంది. ఇందులో swap ప్రత్యేక counter (memory.swap.max)గా ఉంటుంది. అదనపు setup లేకుండానే ఇది పనిచేస్తుంది. Your kernel does not support swap limit capabilities అనే పాత సందేశం swapaccount=1 లేకుండా boot చేసిన cgroup v1 hosts నుంచి వస్తుంది. వాటిలో memory limit వర్తిస్తుంది, కానీ swap భాగం పట్టించుకోబడదు.
Swap వల్ల మీకు ఏమి లభిస్తుందో వాస్తవంగా అంచనా వేయండి. ఇది OOM killను నెమ్మదిగా చేస్తుంది. కానీ అది జరిగే అవకాశాన్ని తగ్గించదు. ఎందుకంటే memory leak ఉన్న process RAMను నింపినట్లే swapను కూడా నింపుతుంది. అదే సమయంలో, shared VPS storageలో swapను అధికంగా ఉపయోగించే container ఆ hostలోని ఇతర ప్రతి serviceను నెమ్మదిస్తుంది. Latencyపై ఆధారపడే ఏదైనా పనికి, swap లేకుండా సరైన limitను సెట్ చేస్తే అది మరింత వేగంగా మరియు ముందుగా అంచనా వేయగల విధంగా విఫలమవుతుంది.
మెమరీ వినియోగం వాస్తవం కంటే అధ్వాన్నంగా ఎందుకు కనిపిస్తుంది
docker stats లోని MEM USAGE గణాంకంలో page cache కూడా ఉంటుంది. అందువల్ల పెద్ద ఫైళ్లను చదివే container దాని పరిమితికి చేరువగా పెరిగి, అక్కడే ఉంటుంది. ఇది సాధారణమే. ఇది leak కాదు, ఎందుకంటే OOM killer ప్రారంభించబడేలోపు clean cache తిరిగి వినియోగానికి అందుతుంది. స్వయంగా హోస్ట్ చేసిన Jellyfin మీడియా server వంటి service కూడా ఇదే కారణంతో శాశ్వతంగా దాని పరిమితికి సమీపంలో ఉన్నట్లు కనిపిస్తుంది.
container లోపల నుంచే ఆ సంఖ్యను 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 file దీనిని స్పష్టంగా నిర్ధారిస్తుంది: సున్నా కంటే ఎక్కువగా ఉన్న oom_kill counter, container ప్రారంభమైన తర్వాత kernel అందులో ఏదో ఒకదాన్ని చంపిందని అర్థం. పెరుగుతున్న max counter, container ప్రస్తుతం దాని పరిమితి వద్ద నిరోధించబడుతోందని అర్థం. రెండు commands కు image లో shell మరియు coreutils అవసరం. అందువల్ల distroless లేదా scratch image లో అవి విఫలమవుతాయి.
8GB VPSలో పరిమాణ పరిమితులు
యాప్ల నుంచి కాకుండా host నుంచి ప్రారంభించండి. 8GB VPSలో kernel, Docker daemon, sshd, journald మరియు మీ login shell కోసం సుమారు 1GB వదిలివేయండి. దాంతో సుమారు 7GB మిగులుతుంది. ప్రతి container limit మొత్తం ఈ పరిమితిలోనే ఉండాలి. రెండు services ఒకేసారి గరిష్ఠ వినియోగానికి చేరే రోజు వరకు overcommitting పనిచేస్తుంది.
8GB boxలో ఉపయోగించగల విభజన:
- Reverse proxy: 128m limit. ఇది చిన్న process. ఇంత కఠినమైన limit runaway config reloadను వెంటనే గుర్తిస్తుంది.
- PostgreSQL: 2g limit. Database configలో
shared_buffersను సుమారు 512MBగా సెట్ చేయండి. - Application container: 1g limit.
- Background worker: 512m limit.
- Media లేదా file service: 2g limit. ఇందులో ఎక్కువ భాగం page cache కోసం ఉపయోగించబడుతుంది.
ఈ సంఖ్యలను మీ స్వంత stackలో నేరుగా ఉపయోగించవద్దు. Servicesను నిజమైన loadలో ఒక రోజు అమలు చేయండి. docker statsను monitor చేయండి. ప్రతి containerకు గరిష్ఠ anon విలువను నమోదు చేసి, దానికి సుమారు సగం అదనంగా headroomగా జోడించండి. చాలా తక్కువగా సెట్ చేసిన limit, limit లేకపోవడం కంటే ప్రమాదకరం. సాధారణ network traffic పెరుగుదల సమయంలో అది ఆరోగ్యంగా ఉన్న serviceను terminate చేస్తుంది.
ఒక సమస్యకు ప్రత్యేకంగా గమనించాలి. మీరు తెలియజేయకపోతే, చాలా runtimesకు ఈ limit కనిపించదు. PostgreSQL shared_buffers మరియు work_mem పరిమాణాలను తన container limit కంటే ఎక్కువగా సెట్ చేసి, terminate చేయబడవచ్చు. JVM (Java virtual machine) host RAM ఆధారంగా కాకుండా cgroup limit ఆధారంగా తన heap పరిమాణాన్ని సెట్ చేయడానికి -XX:MaxRAMPercentage=75 అవసరం. Node.jsకు megabytesలో --max-old-space-size అవసరం. దీనిని container limit కంటే తక్కువగా సెట్ చేయాలి. లేకపోతే దాని garbage collector heapను పెంచుతూనే ఉంటుంది, చివరకు kernel జోక్యం చేసుకుంటుంది. cgroup చర్చించదు. అది terminate చేస్తుంది.
CPU పరిమితులు పూర్తిగా భిన్నంగా పనిచేస్తాయి
cpus: "1.5" అంటే ఒక కోర్ సామర్థ్యంలో 150%. ఇది CFS (completely fair scheduler) కోటాగా అమలు చేయబడుతుంది. ప్రతి 100ms వ్యవధిలో కంటైనర్కు 150ms CPU సమయం లభిస్తుంది. ఈ సమయం దాని అన్ని థ్రెడ్లకు భాగస్వామ్యంగా ఉంటుంది. ఆ సమయం పూర్తయినప్పుడు, తదుపరి వ్యవధి వరకు kernel దాన్ని వేచి ఉండేలా చేస్తుంది.
ఇదే ముఖ్యమైన తేడా. కంటైనర్ తన memory పరిమితిని మించితే, దాన్ని నిలిపివేస్తారు. కంటైనర్ తన CPU పరిమితిని మించితే, దాని CPU వినియోగాన్ని throttle చేసి, తక్కువ వేగంతో కొనసాగిస్తారు. అందువల్ల CPU పరిమితిని కఠినంగా సెట్ చేయడం సాధారణంగా సురక్షితం. అయితే memory పరిమితికి అదనపు headroom అవసరం.
cpu_shares వేరే సాధనం. ఇది relative weight. CPUs వాస్తవంగా పూర్తిగా వినియోగంలో ఉన్నప్పుడు మాత్రమే దీనికి ప్రభావం ఉంటుంది. 1024 మరియు 512 shares ఉన్న రెండు కంటైనర్లు, busy coreను సుమారు రెండు-ఒకటి నిష్పత్తిలో పంచుకుంటాయి. idle boxలో అయితే ఏ కంటైనర్పైనా పరిమితి ఉండదు. సేవల ప్రాముఖ్యతను క్రమబద్ధీకరించడానికి sharesను ఉపయోగించండి. వాస్తవ పరిమితి అవసరమైనప్పుడు cpusను ఉపయోగించండి. ఉదాహరణకు, రాత్రి సమయంలో నడిచే transcode job మీ web serverకు CPU కొరత కలిగించకుండా ఆపడానికి ఇది ఉపయోగపడుతుంది.
FAQ
Docker Swarm లేకుండా deploy.resources.limits పనిచేస్తుందా?
అవును. ఒకే hostపై docker compose up ను అమలు చేసినప్పుడు Compose V2 deploy.resources.limits మరియు deploy.resources.reservations ను వర్తింపజేస్తుంది. పరిమితి bytesలో ప్రింట్ అవుతుందో, ఎలాంటి పరిమితి వర్తించనప్పుడు 0 ప్రింట్ అవుతుందో చూపించే docker inspect --format '{{.HostConfig.Memory}}' <container> తో దీనిని నిర్ధారించండి. deploy లో Swarm అవసరమయ్యే keys mode, placement, update_config మరియు endpoint_mode మాత్రమే.
Docker Composeలో exit code 137 అంటే ఏమిటి?
ప్రధాన process SIGKILL అందుకున్నదని అర్థం. ఎందుకంటే 137 అనేది 128కు signal 9ను కలిపిన విలువ. సాధారణ కారణం kernel OOM killer. అయితే app SIGTERMను పట్టించుకోనప్పుడు shutdown timeout వల్ల కూడా ఇదే code వస్తుంది. ఈ రెండు కారణాలను వేరు చేయడానికి docker inspect --format '{{.State.OOMKilled}} {{.State.ExitCode}}' <container> ను అమలు చేయండి. true 137 memory killను సూచిస్తుంది. false 137 అలా సూచించదు.
mem_limit లేదా deploy.resources.limits.memoryలో ఏదిని సెట్ చేయాలి?
docker compose తో రెండూ పనిచేస్తాయి. ప్రస్తుత Compose Specificationలో ఉపయోగించే రూపం deploy.resources.limits.memory. కొత్త fileకు ఇది మెరుగైన default. మీ fileలో మిగతా భాగం ఇప్పటికే పాత top-level keysను ఉపయోగిస్తే mem_limit ను ఉంచండి. ఒకే serviceలో రెండింటినీ సెట్ చేస్తే file చదవడం మాత్రమే కష్టమవుతుంది. కాబట్టి ఒకదాన్ని ఎంచుకుని docker inspect తో ఫలితాన్ని నిర్ధారించండి.
నా containerను చంపకుండా దాని పూర్తి memory limit వద్ద ఎందుకు ఉంచుతోంది?
docker stats లోని usage figureలో page cache కూడా ఉంటుంది. OOM killను ప్రారంభించడానికి బదులుగా kernel ఒత్తిడి సమయంలో page cacheను తొలగిస్తుంది. docker compose exec <service> grep -E '^(anon|file) ' /sys/fs/cgroup/memory.stat ను అమలు చేసి, తిరిగి పొందలేని working set అయిన anon విలువను చదవండి. తక్కువ anon విలువ పక్కన ఎక్కువ file విలువ ఉంటే, అది disk input మరియు output చేస్తున్న container. అది త్వరలో నిలిచిపోబోయే container కాదు.
8GB VPSలో ఎంత RAMను కేటాయించకుండా ఉంచాలి?
kernel, Docker daemon, sshd, journald మరియు మీ స్వంత shell కోసం సుమారు 1GB ఉంచండి. మిగిలిన 7GB కంటే అన్ని container limits మొత్తాన్ని తక్కువగా ఉంచండి. సంఖ్యలను ఖరారు చేసే ముందు, నిజమైన loadలో ఒక రోజు పాటు ప్రతి containerకు సంబంధించిన గరిష్ఠ anon విలువను పరిశీలించండి. మొత్తం పరిమితిని నింపాల్సిన లక్ష్యంగా కాకుండా బడ్జెట్గా పరిగణించండి.