సరిగ్గా పనిచేసే Docker Compose healthcheckలు
Docker Compose healthcheckలు exit code ను మాత్రమే ఎలా అంచనా వేస్తాయో, depends_on ఎందుకు readiness కోసం వేచి ఉండదో, Postgres మరియు app checks ఎలా రాయాలో తెలుసుకోండి.
Docker Compose healthcheck వాస్తవంగా చేసే పని
Docker Compose healthcheck అనేది Docker కంటైనర్లో టైమర్ ఆధారంగా అమలు చేసే ఒక కమాండ్. Docker మీ logs ను చదవదు, మీ port ను monitor చేయదు, process list ను పరిశీలించదు. అది కమాండ్ను అమలు చేసి, exit code ను చదివి, కంటైనర్పై ఒకే ఒక state ను నిల్వ చేస్తుంది: starting, healthy, లేదా unhealthy. Exit code 0 అంటే healthy. ఇతర ఏ exit code అయినా unhealthy అని అర్థం. Exit code 2 ను Docker కోసం కేటాయించారు. కాబట్టి ఉద్దేశపూర్వకంగా దాన్ని ఎప్పుడూ return చేయవద్దు.
ఇదే మొత్తం విధానం. దాదాపు ప్రతి healthcheck సమస్యకు మూల కారణం ఒకటే: మీరు రాసిన కమాండ్, మీరు అడగాలనుకున్న ప్రశ్నకు బదులుగా వేరే ప్రశ్నకు సమాధానం ఇస్తుంది. VPSలో compose file ఎలా రాయాలో మీకు ఇప్పటికే తెలుసని భావించి, stack తప్పు క్రమంలో ప్రారంభమయ్యే దశ నుంచి ఈ guide కొనసాగుతుంది.
services:
api:
image: ghcr.io/example/api:1.4.0
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:8080/healthz"]
interval: 10s
timeout: 3s
retries: 5
start_period: 30stest విలువకు రెండు ఉపయోగకరమైన రూపాలు ఉన్నాయి. CMD తో ప్రారంభమయ్యే list కమాండ్ను shell లేకుండా నేరుగా అమలు చేస్తుంది. అందువల్ల pipes, &&, మరియు variable expansion పనిచేయవు. CMD-SHELL తో ప్రారంభమయ్యే list, మిగతా భాగాన్ని ఒక stringగా కంటైనర్లోని /bin/sh -c కు పంపుతుంది. Check కు shell syntax అవసరమైనప్పుడు ఇదే ఉపయోగించాలి. Plain string ను CMD-SHELL గా పరిగణిస్తారు. ఖచ్చితంగా ["NONE"] ఉన్న list, image యొక్క Dockerfile ద్వారా ముందుగానే అమర్చిన healthcheck ను తొలగిస్తుంది.
Check కంటైనర్లోనే అమలవుతుంది. అందువల్ల అది పేర్కొన్న ప్రతి binary ఆ imageలో ఉండాలి. ముందుగా దీన్ని నిర్ధారించండి. ఎందుకంటే curl లేని slim image, application logలో ఎప్పుడూ కనిపించని కారణంతో కంటైనర్ను శాశ్వతంగా unhealthy గా ఉంచుతుంది. దీన్ని చేతితో పరీక్షించండి:
docker compose exec api curl --versionBinary లేకపోతే OCI runtime exec failed: exec: "curl": executable file not found in $PATH: unknown తో సమాధానం వస్తుంది. Alpine ఆధారిత images సాధారణంగా దాని బదులుగా BusyBox wget ను ship చేస్తాయి. కాబట్టి check ఇలా మారుతుంది: ["CMD", "wget", "-q", "-O", "-", "http://localhost:8080/healthz"].
interval, retries మరియు start_period ఎలా కలిసి పనిచేస్తాయి
సమయ నియంత్రణను ఐదు సెట్టింగ్లు ప్రభావితం చేస్తాయి. వీటి డిఫాల్ట్లు Compose నుంచి కాకుండా Docker Engine నుంచి వస్తాయి.
interval: కంటైనర్ start period దాటిన తర్వాత రెండు తనిఖీల మధ్య ఉండే సమయం. డిఫాల్ట్ 30s.timeout: ఒక తనిఖీ అమలు గరిష్ఠంగా తీసుకునే సమయం. ఈ సమయం దాటితే Docker దాన్ని నిలిపివేసి, ఆ అమలును వైఫల్యంగా లెక్కిస్తుంది. డిఫాల్ట్ 30s.retries: స్థితిunhealthyకి మారడానికి అవసరమైన వరుస వైఫల్యాల సంఖ్య. డిఫాల్ట్ 3.start_period: కంటైనర్ ప్రారంభమైన తర్వాత ఇచ్చే అదనపు సమయం. డిఫాల్ట్ 0s.start_interval: start period సమయంలో తనిఖీ ఎంత తరచుగా అమలు కావాలో నిర్ణయించే సమయం. డిఫాల్ట్ 5s. దీనికి Docker Engine 25.0 లేదా తదుపరి వెర్షన్ అవసరం.
గుర్తుంచుకోవాల్సిన నియమం: start period సమయంలో విఫలమైన తనిఖీ retriesలో లెక్కించబడదు. కంటైనర్ startingలోనే ఉంటుంది. తనిఖీ మొదటిసారి విజయవంతమైన వెంటనే కంటైనర్ healthyగా మారుతుంది. దాని మిగిలిన start period సమయం ఎంత ఉన్నా, start period వెంటనే ముగుస్తుంది. తనిఖీ ఇంకా విఫలమవుతుండగా start period ముగిస్తే, సాధారణ లెక్కింపు ప్రారంభమవుతుంది. కంటైనర్ unhealthyగా గుర్తించబడే ముందు వరుసగా retries వైఫల్యాలు రావాలి.
అందువల్ల, కంటైనర్ ప్రారంభమైనప్పటి నుంచి unhealthyకు చేరే వరకు గరిష్ఠ సమయం start_periodకు, retriesను intervalతో గుణించిన ఫలితాన్ని కలిపి, ఆపై timeoutను కలిపినంత ఉంటుంది. పై ఫైల్లోని విలువలతో ఇది 30 plus 5 times 13, అంటే 95 seconds. deploy timeout సెట్ చేసే ముందు ఈ సంఖ్యను రాసి ఉంచండి. 60 seconds తర్వాత నిలిపివేసే rollout ఈ కంటైనర్ తుది స్థితికి చేరడాన్ని ఎప్పటికీ చూడదు.
ఇక్కడ సాధారణంగా చేసే పొరపాటు, నెమ్మదిగా ప్రారంభమయ్యే సేవకు సమయం ఇవ్వడానికి retriesను పెంచడం. ఇది ఒకసారి పనిచేసినా, తర్వాత నిరంతరం సమస్యను పెంచుతుంది. ప్రారంభం కావడానికి 8 retries అవసరమైన సేవ, productionలో ఏదైనా గుర్తించబడే ముందు వరుసగా 8 వైఫల్యాలను సహిస్తుంది. దాని బదులుగా start_periodను ఉపయోగించండి. ఇది మొదటి విజయానికి ముందు మాత్రమే వర్తిస్తుంది.
depends_on ఒక్కటిగా ఎటువంటి హామీ ఇవ్వదు
depends_on యొక్క సంక్షిప్త రూపమే ఎక్కువ గందరగోళానికి కారణం.
api:
depends_on:
- dbదీని అర్థం ఒక్కటే: api container కంటే ముందు db container ను ప్రారంభించాలి. Compose, container సృష్టించబడి ప్రారంభమయ్యే వరకు వేచి ఉంటుంది. PostgreSQL మొదటిసారి initialization పూర్తయ్యే వరకు లేదా port 5432 connection అంగీకరించే వరకు అది వేచి ఉండదు. మీ app సుమారు ఒక సెకను తర్వాత ప్రారంభమవుతుంది, అప్పటికి ఏదీ listening చేయని port కు connect అవుతుంది, తరువాత exit అవుతుంది. Log లో Connection refused కనిపిస్తుంది. Server ప్రారంభమై ఇంకా recovery లో ఉన్నప్పుడు FATAL: the database system is starting up కనిపిస్తుంది.
వాస్తవానికి ప్రజలు కోరుకునేది దీర్ఘరూపం:
api:
depends_on:
db:
condition: service_healthy
restart: true
migrate:
condition: service_completed_successfullycondition కు మూడు విలువలు ఉంటాయి. service_started సంక్షిప్త రూపంతో సమానం. service_healthy dependency healthy అని report చేసే వరకు dependent service ను నిలిపి ఉంచుతుంది. ఇది dependency లో healthcheck నిర్వచించబడినప్పుడు మాత్రమే అర్థవంతంగా ఉంటుంది. ఆ healthcheck compose file లో లేదా దాని image లో ఉండవచ్చు. service_completed_successfully one shot container కోసం వేచి ఉంటుంది. ఉదాహరణకు, database migration status 0 తో exit అయ్యే వరకు ఇది వేచి ఉంటుంది.
condition పక్కన మరో రెండు fields ఉంటాయి. Dependency service ను update చేసిన తర్వాత ఈ service ను restart చేయమని restart: true Compose కు చెబుతుంది. Dependency కనిపించకపోవడాన్ని error బదులు warning గా పరిగణించమని required: false చెబుతుంది.
ఇప్పుడు చాలామందిని ఇబ్బంది పెట్టే పరిమితి. Stack ప్రారంభమైనప్పుడు ఈ conditions evaluate అవుతాయి. ఇవి start ordering మాత్రమే; supervision rule కాదు. తెల్లవారుజామున మూడు గంటలకు database restart అయితే, service_healthy ను మళ్లీ evaluate చేయడం జరగదు. దాన్ని మళ్లీ నెరవేర్చడానికి మీ app ను కూడా restart చేయదు. మీ application code స్వయంగా మళ్లీ connect కావాలి. docker compose up --no-deps api ఉద్దేశపూర్వకంగా మొత్తం mechanism ను దాటవేస్తుంది. docker start తో container ను నేరుగా ప్రారంభించినా అదే జరుగుతుంది.
ఒక ప్రక్రియ ఉందో లేదో కాకుండా, సిద్ధంగా ఉందో లేదో పరీక్షించే check రాయండి
pgrep nginx వంటి check ద్వారా process table entry ఉందని మాత్రమే నిర్ధారించవచ్చు. ఆ service requestకు సమాధానం ఇవ్వగలదో లేదో మాత్రం నిర్ధారించలేం. ఒక web application యొక్క database pool నిలిచిపోయిన తర్వాత కూడా, అది listening socketను ఎక్కువసేపు తెరిచి ఉంచవచ్చు. అప్పుడు మొత్తం outage సమయంలో process check greenగానే ఉంటుంది.
Container చేయాల్సిన అసలు పనినే చేయమని అడగండి:
- HTTP service కోసం, నిజమైన endpointకు request పంపండి.
curl -fsSలో-fకారణంగా 400 లేదా అంతకంటే ఎక్కువ status వచ్చినప్పుడు non zeroతో exit అవుతుంది. అందువల్ల broken app నుంచి 500 వచ్చినా check విఫలమవుతుంది. - PostgreSQL కోసం
pg_isreadyఉపయోగించండి. Server connectionsను స్వీకరిస్తున్నప్పుడు ఇది 0తో exit అవుతుంది, connectionsను తిరస్కరిస్తున్నప్పుడు 1తో exit అవుతుంది, అసలు స్పందించనప్పుడు 2తో exit అవుతుంది. మీరు పంపిన parameters తప్పుగా ఉన్నప్పుడు 3తో exit అవుతుంది. - Redis కోసం
redis-cli pingఉపయోగించండి. ఇదిPONGను print చేసి 0తో exit అవుతుంది. - MariaDB కోసం official imageలో
healthcheck.shscript ఉంటుంది. దాని maintainers document చేసిన రూపంhealthcheck.sh --connect --innodb_initialized.
pg_isreadyలో తెలుసుకోవాల్సిన ఒక ముఖ్యమైన సమస్య ఉంది. ఖాళీ data directoryతో మొదటిసారి start చేసినప్పుడు, official postgres image తన initialisationను Unix socketపై మాత్రమే listening చేసే temporary serverకు వ్యతిరేకంగా అమలు చేస్తుంది. Host argument లేకుండా pg_isreadyను ఉపయోగిస్తే అదే socketను ఉపయోగిస్తుంది. అందువల్ల TCP port 5432 మీ applicationకు ఇంకా మూసి ఉన్నప్పటికీ, అది “accepting connections” అని సమాధానం ఇవ్వవచ్చు. Checkను TCPకు స్పష్టంగా point చేయండి. అప్పుడు సమస్య తొలగిపోతుంది, ఎందుకంటే temporary server అక్కడ సమాధానం ఇవ్వదు.
healthcheck:
test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -p 5432 -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 10
start_period: 30sరెట్లు ఉన్న dollar signs typo కావు. Fileను చదివేటప్పుడు Compose స్వయంగా $VARను expand చేస్తుంది. దాంతో మీ host environmentలోని value checkలో స్థిరంగా చేర్చబడుతుంది. $$ దాన్ని ఒకే $గా escape చేస్తుంది. అందువల్ల containerలోని shell దాన్ని container స్వంత environment ఆధారంగా expand చేస్తుంది.
సరైన క్రమంలో ప్రారంభమయ్యే postgres మరియు app stack
services:
db:
image: postgres:17.5
environment:
POSTGRES_USER: appuser
POSTGRES_PASSWORD: ${DB_PASSWORD:?set DB_PASSWORD in .env}
POSTGRES_DB: appdb
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -p 5432 -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 10
start_period: 30s
restart: unless-stopped
api:
image: ghcr.io/example/api:1.4.0
environment:
DATABASE_URL: postgres://appuser:${DB_PASSWORD}@db:5432/appdb
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:8080/healthz"]
interval: 10s
timeout: 3s
retries: 5
start_period: 30s
ports:
- "127.0.0.1:8080:8080"
restart: unless-stopped
volumes:
pgdata:దీన్ని ప్రారంభించి, స్థితుల్లో మార్పులను గమనించండి:
docker compose up -d
docker compose psSTATUS నిలువు వరుసలో బృందాల్లో health state చూపబడుతుంది. ఆరోగ్యంగా ఉన్న జంటలో రెండు వరుసల్లోనూ Up 41 seconds (healthy) కనిపిస్తుంది. database ఇంకా ప్రారంభమవుతున్నప్పుడు, db విలువ Up 4 seconds (health: starting)గా ఉంటుంది. అలాగే api జాబితాలో ఉండదు, ఎందుకంటే Compose దాన్ని ఇంకా సృష్టించలేదు.
ఏ check విజయవంతమైందో లేదా విఫలమైందో తెలుసుకోవడానికి health log చదవండి:
docker inspect --format '{{json .State.Health}}' "$(docker compose ps -q db)"Docker చివరి కొన్ని ఫలితాలను ఉంచుతుంది. ప్రతి ఫలితంలో ప్రారంభ సమయం, ముగింపు సమయం, ఒక ExitCode మరియు command యొక్క Output ఉంటాయి. నిల్వ చేసిన output పరిమిత పరిమాణంలోనే ఉంటుంది. అందువల్ల పెద్ద page bodyని ముద్రించే check పనికిరాని log entryని ఇస్తుంది. Checksలో outputను తక్కువగా ఉంచండి.
కంటైనర్ అనారోగ్య స్థితికి మారినప్పుడు Docker చేసే పని
ఏమీ చేయదు. ఎక్కువ మందిని ఆశ్చర్యపరిచే సమాధానం ఇదే.
ఒకే hostపై నడుస్తున్న Docker Engine, అనారోగ్య స్థితిలో ఉన్న కంటైనర్ను రీస్టార్ట్ చేయదు. restart: unless-stopped policy ప్రధాన process ఆగిపోయినప్పుడు స్పందిస్తుంది. అనారోగ్య స్థితిలో ఉన్న కంటైనర్ ఆగిపోలేదు. Compose ఎలాంటి చర్య తీసుకోకుండా అది unhealthy వద్ద ఒక వారం ఉండవచ్చు. Swarm mode అనారోగ్య స్థితిలో ఉన్న tasksను భర్తీ చేస్తుంది. కానీ ఒక serverపై నడుస్తున్న సాధారణ Compose stack అలా చేయదు.
దీనికి రెండు వాస్తవిక మార్గాలు ఉన్నాయి. Processలో లోపం ఉన్నట్లు గుర్తించినప్పుడు అది exit అయ్యేలా చేయండి. అప్పుడు restart policy చర్య తీసుకోగలదు. లేదా బయట నుంచి stateను పర్యవేక్షించి, దాని ఆధారంగా alert పంపండి. మీ healthcheck పిలిచే అదే endpointకు Uptime Kuma monitorను కేటాయిస్తే, విఫలమైన dependency రెండు చోట్ల కనిపిస్తుంది. User కంటే ముందే monitor ద్వారా మీకు సమాచారం అందుతుంది. Traffic, Traefik reverse proxy ద్వారా appకు చేరితే, backendపై proxyకి కనిపించే స్థితి Docker health stateకు వేరుగా ఉంటుందని గుర్తుంచుకోండి. అందువల్ల ఒకటి మరొకదానికి ప్రత్యామ్నాయం కాదు.
ఎప్పటికీ ఆరోగ్యంగా మారని check ను డీబగ్ చేయడం
అదే container లో ఖచ్చితమైన command ను మీరే అమలు చేసి, exit code ను చూడండి:
docker compose exec api curl -fsS http://localhost:8080/healthz; echo "exit=$?"container ఇంకా unhealthy గా ఉన్నప్పటికీ ఇక్కడ exit=0 కనిపిస్తే, మీరు ఇప్పుడే టైప్ చేసిన దానితో మీ compose test భిన్నంగా ఉందని అర్థం. సాధారణంగా shell syntax అవసరమైన చోట CMD ఉపయోగించడం దీనికి కారణం.
మిగిలిన సందర్భాల్లో ఎక్కువగా కనిపించే రెండు తప్పులు ఇవి. మొదటిది తప్పు port. healthcheck container లోపల అమలవుతుంది. అందువల్ల అది ఎల్లప్పుడూ container port ను ఉపయోగించాలి; ప్రచురించిన host port ను ఉపయోగించకూడదు. ports: - "8080:3000" తో application 3000 పై listen చేస్తుంది. check http://localhost:8080 ను పరిశీలిస్తే, browser లో site సరిగ్గా పనిచేస్తున్నప్పటికీ అది ఎప్పటికీ విఫలమవుతుంది. రెండవది తప్పు host. check లోపల localhost అదే container ను సూచిస్తుంది. తనను తాను check చేసుకోవడానికి ఇది సరైనది. పక్కనున్న service ను check చేయడానికి ఇది తప్పు. అలాంటి సందర్భంలో db వంటి service name ను ఉపయోగించాలి.
ఇంకో సందర్భానికి ప్రత్యేకంగా పేరు పెట్టాలి: healthcheck విజయవంతమైనప్పటికీ users కు errors కనిపిస్తాయి. endpoint నిజంగా ఏదీ పరిశీలించకుండా static 200 ను తిరిగి ఇచ్చినప్పుడు ఇది జరుగుతుంది. database ను ఎప్పుడూ query చేయని readiness endpoint, database అందుబాటులో లేదని గుర్తించదు. అది ఒక చిన్న, నిజమైన query ను అమలు చేసేలా చేయండి.
FAQ
depends_on database ఆరోగ్యంగా ఉందని చెప్పినప్పటికీ నా app కనెక్ట్ అవ్వడంలో ఎందుకు విఫలమవుతోంది?
ఎందుకంటే condition: service_healthy stack ప్రారంభమైనప్పుడు ఒక్కసారి మాత్రమే మూల్యాంకనం చేయబడుతుంది. ఆ తర్వాత ఇది ఏదినీ పర్యవేక్షించదు. తరువాత database container మళ్లీ ప్రారంభమైతే, ఆ conditionను మళ్లీ నెరవేర్చడానికి Compose మీ applicationను మళ్లీ ప్రారంభించదు. అందువల్ల మీ application codeలో స్వంత reconnect మరియు retry logic ఉండాలి. docker start లేదా docker compose up --no-depsతో ఒకే containerను ప్రారంభించినప్పుడు కూడా ఈ condition ఎలాంటి పని చేయదు.
imageలో healthcheck ఇప్పటికే నిర్వచించబడి ఉంటే, నాకు మరో healthcheck అవసరమా?
సాధారణంగా అవసరం లేదు. దాన్ని override చేయడం తరచుగా వెనుకడుగు వేయడమే, ఎందుకంటే ఆ softwareకు readiness అంటే ఏమిటో image maintainerకు బాగా తెలుసు. మీ setupకు image check సరిపోనప్పుడు మాత్రమే స్వంత healthcheckను జోడించండి. ఉదాహరణకు, మీరు మార్చిన portను అది probe చేస్తున్నప్పుడు ఇది అవసరం కావచ్చు. image healthcheckను ఆపివేయడానికి serviceపై test: ["NONE"] లేదా disable: true సెట్ చేయండి.
healthcheckలో curl లేదా wgetను ఉపయోగించాలా?
imageలో ఇప్పటికే ఉన్నదానిని ఉపయోగించండి. దానిపై ఆధారపడే ముందు docker compose exec <service> curl --versionతో అది ఉందో నిర్ధారించండి. అనేక Debian ఆధారిత imagesలో ఈ రెండింటిలో ఏదీ ఉండదు. Alpine ఆధారిత imagesలో BusyBox wget ఉంటుంది. healthcheckను అమలు చేయడం కోసమే imageకు packageను జోడించవద్దు, ముఖ్యంగా software స్వంత clientను అందించినప్పుడు. ఉదాహరణకు pg_isready లేదా redis-cli అందుబాటులో ఉంటే దానినే ఉపయోగించండి.
unhealthy container స్వయంచాలకంగా మళ్లీ ప్రారంభించబడుతుందా?
ఒకే hostపై Docker Engine స్వయంచాలకంగా అలా చేయదు. Restart policies health stateకు కాకుండా process exit కావడంపై స్పందిస్తాయి. అందువల్ల unhealthy container అమలులోనే ఉండి, సమస్యతో కొనసాగుతుంది, మరేదైనా చర్య తీసుకునే వరకు. Failureను గుర్తించినప్పుడు process exit అయ్యేలా చేయండి. ప్రత్యామ్నాయంగా, stateపై alert ఇచ్చే external monitorను అమలు చేయండి.
start_period ఎంత ఉండాలి?
మీరు కొలిచిన అత్యంత నెమ్మదైన సముచిత first startకు సరిపడేంత సమయం, దానికి అదనపు marginతో కలిపి ఉండాలి. ఖాళీ volumeపై docker compose upతో సమయాన్ని కొలవండి, ఎందుకంటే database యొక్క మొదటి start, ఆ తరువాత జరిగే ప్రతి start కంటే చాలా నెమ్మదిగా ఉంటుంది. start period చాలా ఎక్కువగా ఉంటే మొదటి unhealthy verdict మాత్రమే ఆలస్యం అవుతుంది. Retries చాలా ఎక్కువగా ఉంటే container మొత్తం lifetimeలో check బలహీనపడుతుంది. ఇదే మరింత తీవ్రమైన failure.