Jinsi ya Kuandika Healthcheck za Docker Compose
Jifunze jinsi Docker Compose hutathmini healthcheck, kwa nini depends_on haisubiri utayari, na jinsi ya kuandika ukaguzi sahihi wa Postgres na programu yako.
Docker Compose healthcheck hufanya nini hasa
Docker Compose healthcheck ni amri moja ambayo Docker huendesha ndani ya container kwa vipindi maalum. Docker haisomi log zako, haifuatilii port yako, wala haikagui orodha ya michakato. Huendesha amri, husoma msimbo wa kutoka, kisha huhifadhi hali moja kwenye container: starting, healthy, au unhealthy. Msimbo wa kutoka 0 humaanisha kuwa iko katika hali nzuri. Msimbo mwingine wowote humaanisha kuwa iko katika hali isiyofaa, na msimbo wa kutoka 2 umehifadhiwa na Docker; kwa hiyo usiurudishe kwa makusudi.
Huo ndio utaratibu mzima. Karibu kila tatizo la healthcheck ni tatizo lilelile: amri uliyoandika inajibu swali tofauti na lile ulilokusudia kuuliza. Mwongozo huu unadhania kuwa tayari unajua jinsi ya kuandika faili ya compose kwenye VPS, na unaendelea kutoka hatua ambayo stack inaanza kwa mpangilio usio sahihi.
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: 30sThamani ya test ina miundo miwili muhimu. Orodha inayoanza na CMD huendesha amri moja kwa moja, bila shell, kwa hiyo mabomba na && pamoja na upanuzi wa vigezo havifanyi kazi. Orodha inayoanza na CMD-SHELL hupitisha sehemu iliyobaki kama mfuatano mmoja kwa /bin/sh -c ndani ya container. Hilo ndilo unalotaka wakati wowote ukaguzi unahitaji sintaksia ya shell. Mfuatano wa kawaida huchukuliwa kuwa CMD-SHELL. Orodha yenye ["NONE"] pekee huondoa healthcheck ambayo image iliijumuisha kupitia Dockerfile yake.
Ukaguzi huendeshwa ndani ya container, kwa hiyo kila binary inayotajwa lazima iwepo kwenye image hiyo. Thibitisha hilo kwanza, kwa sababu image nyepesi isiyo na curl hutengeneza container ambayo hubaki daima katika hali isiyofaa kwa sababu ambayo haionekani kamwe kwenye log ya programu. Ijaribu mwenyewe:
docker compose exec api curl --versionBinary iliyokosekana hujibu kwa OCI runtime exec failed: exec: "curl": executable file not found in $PATH: unknown. Image zinazotegemea Alpine kwa kawaida huja na BusyBox wget badala yake, kwa hiyo ukaguzi huwa ["CMD", "wget", "-q", "-O", "-", "http://localhost:8080/healthz"].
Jinsi interval, retries na start_period zinavyoungana
Mipangilio mitano hudhibiti muda. Thamani zake chaguomsingi hutoka kwenye Docker Engine, si kwenye Compose.
interval: muda kati ya ukaguzi wawili baada ya kontena kuvuka kipindi chake cha kuanza. Chaguo-msingi ni 30s.timeout: muda ambao utekelezaji mmoja wa ukaguzi unaweza kuchukua kabla ya Docker kuumaliza na kuhesabu utekelezaji huo kuwa umeshindwa. Chaguo-msingi ni 30s.retries: idadi ya kushindwa mfululizo inayohitajika kabla hali kubadilika kuwaunhealthy. Chaguo-msingi ni 3.start_period: kipindi cha msamaha baada ya kontena kuanza. Chaguo-msingi ni 0s.start_interval: mara ngapi ukaguzi unatekelezwa wakati wa kipindi cha kuanza. Chaguo-msingi ni 5s, na unahitaji Docker Engine 25.0 au toleo jipya zaidi.
Kanuni muhimu ni hii: wakati wa kipindi cha kuanza, ukaguzi unaoshindwa hauhesabiwi kuelekea retries, na kontena hubaki katika starting. Mara ya kwanza ukaguzi unapofaulu, kontena huwa healthy na kipindi cha kuanza huisha mara moja, hata kama muda wake mwingi haujatumiwa. Ikiwa kipindi cha kuanza kitaisha huku ukaguzi ukiendelea kushindwa, hesabu ya kawaida huanza, na kontena linahitaji kushindwa retries mfululizo kabla liwekwe alama ya unhealthy.
Kwa hiyo, muda wa hali mbaya zaidi kutoka kuanza kwa kontena hadi unhealthy ni start_period pamoja na retries ukizidishwa kwa interval, kisha kuongezwa timeout. Kwa thamani zilizo kwenye faili hapo juu, hiyo ni 30 pamoja na 5 mara 13, ambayo ni sekunde 95. Andika nambari hiyo kabla ya kuweka muda wa kuisha wa deployment, kwa sababu rollout inayokata tamaa baada ya sekunde 60 haitawahi kuona kontena hili likifika hali ya mwisho.
Kosa la kawaida hapa ni kuongeza retries ili kufidia kuanza kwa polepole. Hilo husaidia mara moja, lakini husababisha tatizo baadaye: huduma iliyohitaji majaribio 8 ili kuanza sasa itavumilia kushindwa 8 mfululizo katika production kabla mtu yeyote kugundua. Tumia start_period badala yake, kwa sababu inatumika tu kabla ya mafanikio ya kwanza.
Kwa nini depends_on peke yake haihakikishi chochote
Aina fupi ya depends_on ndiyo chanzo kikuu cha mkanganyiko.
api:
depends_on:
- dbHii ina maana moja tu: anza kontena la db kabla ya kontena la api. Compose husubiri kontena liundwe na lianze. Haisubiri PostgreSQL ikamilishe uanzishaji wake wa kwanza, wala haisubiri port 5432 ikubali muunganisho. Programu yako huanza takriban sekunde moja baadaye, hujaribu kuunganisha kwenye port ambayo hakuna kinachosikiliza bado, kisha hujizima. Kwenye log unaona Connection refused, au FATAL: the database system is starting up wakati server iko tayari lakini bado inarejesha hali yake.
Aina ndefu ndiyo watu wanayohitaji kwa kweli:
api:
depends_on:
db:
condition: service_healthy
restart: true
migrate:
condition: service_completed_successfullycondition ina thamani tatu. service_started ni sawa na aina fupi. service_healthy huchelewesha huduma tegemezi hadi tegemezi hiyo iripoti kuwa na afya, jambo lenye maana tu wakati tegemezi hiyo inafafanua healthcheck, ama kwenye faili ya compose au kwenye image yake. service_completed_successfully husubiri kontena la kazi ya mara moja, kama uhamishaji wa database, litoke likiwa na hali 0.
Sehemu mbili za ziada ziko kando ya condition. restart: true huiambia Compose iwashe upya huduma hii baada ya kusasisha huduma tegemezi. required: false hubadilisha tegemezi lililokosekana kutoka kosa kuwa onyo.
Sasa kuna kikomo kinachowachanganya watu. Masharti haya hutathminiwa wakati stack inapoanzishwa. Haya ni maagizo ya mpangilio wa kuanza, si kanuni ya usimamizi endelevu. Database ikiwasha upya saa 3 asubuhi, hakuna kitu kitakachotathmini tena service_healthy, wala hakuna kitakachowasha upya programu yako ili kutimiza sharti hilo tena. Msimbo wa programu yako bado lazima uunganishe tena kwa kujitegemea. docker compose up --no-deps api huruka utaratibu mzima kwa makusudi, na vivyo hivyo kuanzisha kontena moja kwa moja kwa docker start.
Andika ukaguzi unaopima utayari, si kuwepo kwa mchakato
Ukaguzi kama pgrep nginx unathibitisha tu kwamba ingizo la mchakato lipo kwenye jedwali la michakato. Haithibitishi kwamba huduma inaweza kujibu ombi. Programu ya wavuti inaweza kuendelea kushikilia soketi yake ya kusikiliza muda mrefu baada ya kundi lake la miunganisho ya hifadhidata kufeli, na ukaguzi wa mchakato utaendelea kuonyesha hali nzuri wakati wote wa hitilafu.
Iambie kontena ifanye kazi ambayo ipo kwa ajili yake:
- Kwa huduma ya HTTP, omba endpoint halisi.
curl -fsShurudi na hali isiyo ya sifuri kwa hali yoyote ya 400 au zaidi kwa sababu ya-f, kwa hiyo hali ya 500 kutoka kwa programu iliyoharibika husababisha ukaguzi kufeli. - Kwa PostgreSQL, tumia
pg_isready. Hurudi na 0 wakati seva inakubali miunganisho, 1 wakati inazikataa, 2 wakati haijibu kabisa, na 3 wakati vigezo ulivyopitisha si sahihi. - Kwa Redis, tumia
redis-cli ping. HuchapishaPONGna hurudi na 0. - Kwa MariaDB, image rasmi inajumuisha hati ya
healthcheck.sh, nahealthcheck.sh --connect --innodb_initializedndiyo muundo unaoelezwa na watengenezaji wake.
pg_isready ina mtego mmoja unaofaa kuujua. Katika kuwasha kwake kwa mara ya kwanza ikiwa saraka ya data iko tupu, image rasmi ya postgres huendesha uanzishaji wake dhidi ya seva ya muda inayosikiliza kwenye Unix socket pekee. pg_isready bila hoja ya host hutumia socket hiyo, kwa hiyo inaweza kujibu “inakubali miunganisho” wakati port ya TCP 5432 bado imefungwa kwa programu yako. Elekeza ukaguzi kwenye TCP waziwazi ili kuondoa tatizo hilo, kwa sababu seva ya muda haijibu kupitia TCP.
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: 30sAlama mbili za dola si kosa la kuchapa. Compose hupanua $VAR yenyewe inapoisoma faili, jambo ambalo lingeweka thamani kutoka kwenye mazingira ya host yako ndani ya ukaguzi. $$ huihifadhi kama $ moja, hivyo shell iliyo ndani ya kontena huipanua kulingana na mazingira ya kontena lenyewe.
Stack ya postgres na app inayoanza kwa mpangilio sahihi
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:Iwashe na ufuatilie jinsi hali zinavyobadilika:
docker compose up -d
docker compose psSafu ya STATUS inaonyesha hali ya afya ndani ya mabano. Jozi yenye afya huonyesha Up 41 seconds (healthy) katika safu zote mbili. Wakati hifadhidata bado inaanzishwa, db huonyesha Up 4 seconds (health: starting) na api haipo kwenye orodha, kwa sababu Compose bado haijaiunda.
Ili kuona kwa nini ukaguzi ulifaulu au umeshindwa, soma kumbukumbu ya afya:
docker inspect --format '{{json .State.Health}}' "$(docker compose ps -q db)"Docker huhifadhi matokeo machache ya mwisho, kila moja likiwa na muda wa kuanza, muda wa kumaliza, ExitCode na Output ya amri. Matokeo yaliyohifadhiwa hukatwa, kwa hiyo ukaguzi unaochapisha maudhui makubwa ya ukurasa hutoa ingizo lisilofaa kwenye kumbukumbu. Fanya ukaguzi uwe tulivu.
Docker hufanya nini kontena inapokuwa isiyo na afya
Hakuna. Hili ndilo jibu linalowashangaza watu zaidi.
Docker Engine kwenye hosti moja haiwashi upya kontena isiyo na afya. Sera ya restart: unless-stopped hujibu mchakato mkuu unapotoka, lakini kontena isiyo na afya haijatoka. Inaweza kubaki katika hali ya unhealthy kwa wiki moja huku Compose ikiiacha bila kuigusa. Hali ya Swarm hubadilisha tasks zisizo na afya, lakini stack ya kawaida ya Compose kwenye seva moja haifanyi hivyo.
Hivyo, kuna chaguo mbili za wazi. Fanya mchakato utoke unapojua kuwa umeharibika, ili sera ya kuwasha upya iwe na jambo la kushughulikia. Au fuatilia hali hiyo kutoka nje na utoe arifa inapojitokeza. Kuweka monitor ya Uptime Kuma kwenye endpoint ileile ambayo healthcheck yako inaita kunamaanisha kuwa utegemezi ulioharibika utaonekana katika maeneo yote mawili, na utapata taarifa kutoka kwa monitor badala ya mtumiaji. Ikiwa trafiki inafikia programu kupitia reverse proxy ya Traefik, kumbuka kuwa mtazamo wa proxy kuhusu backend ni tofauti na hali ya afya ya Docker, kwa hiyo kimoja hakiwezi kuchukua nafasi ya kingine.
Kutatua ukaguzi ambao hauwi na hali ya afya kamwe
Tekeleza amri hiyo kamili wewe mwenyewe, ndani ya container hiyo hiyo, kisha uangalie msimbo wa kutoka:
docker compose exec api curl -fsS http://localhost:8080/healthz; echo "exit=$?"exit=0 hapa wakati container bado inaripoti kuwa haina afya ina maana kuwa compose test yako inatofautiana na uliyoandika hivi punde. Kwa kawaida, hii hutokea kwa sababu CMD ilitumika mahali ambapo sintaksia ya shell ilihitajika.
Makosa mengine mengi yanatokana na mambo mawili. La kwanza ni kutumia port isiyo sahihi. Ukaguzi wa afya huendeshwa ndani ya container, hivyo lazima utumie port ya container, kamwe si port ya host iliyochapishwa. Kwa ports: - "8080:3000", programu husikiliza kwenye 3000, na ukaguzi dhidi ya http://localhost:8080 hushindwa daima huku tovuti ikifanya kazi vizuri kwenye browser. La pili ni kutumia host isiyo sahihi. Ndani ya ukaguzi, localhost ni container hiyo hiyo. Hilo ni sahihi unapokagua container yenyewe, lakini si sahihi unapokagua container jirani. Katika hali hiyo, unahitaji jina la service, kwa mfano db.
Kuna hali moja ya mwisho inayohitaji kutajwa: ukaguzi wa afya unafaulu huku watumiaji wakiona hitilafu. Hii hutokea wakati endpoint inarejesha 200 tuli bila kugusa huduma halisi. Endpoint ya utayari ambayo haiulizi database haiwezi kukuambia kuwa database haipatikani. Iweke itekeleze query moja rahisi ya kweli.
FAQ
Kwa nini programu yangu bado inashindwa kuunganishwa wakati depends_on inasema hifadhidata iko katika hali nzuri?
Kwa sababu condition: service_healthy hutathminiwa mara moja, wakati stack inapoanzishwa. Haifuatilii chochote baada ya hapo. Ikiwa kontena ya hifadhidata itawashwa upya baadaye, Compose haitawashi upya programu yako ili kutimiza sharti hilo tena. Kwa hiyo, msimbo wa programu yako unahitaji mantiki yake ya kuunganisha tena na kujaribu tena. Sharti hilo pia halifanyi chochote unapoanzisha kontena moja kwa docker start au kwa docker compose up --no-deps.
Je, ninahitaji healthcheck ikiwa image tayari ina healthcheck?
Kwa kawaida huhitaji, na kuibadilisha mara nyingi ni hatua ya kurudi nyuma, kwa sababu mtunzaji wa image anajua maana ya utayari wa programu hiyo. Ongeza healthcheck yako mwenyewe tu wakati ukaguzi wa image haufai kwa usanidi wako, kwa mfano unapokagua port ambayo umehamisha. Ili kuzima healthcheck ya image, weka test: ["NONE"] au disable: true kwenye service.
Je, healthcheck itumie curl au wget?
Tumia iliyo tayari kuwepo kwenye image, na ithibitishe kwa docker compose exec <service> curl --version kabla ya kuitumia kama msingi. Image nyingi zinazotegemea Debian hazina yoyote kati ya hizo. Image zinazotegemea Alpine zina BusyBox wget. Usiongeze package kwenye image kwa ajili ya kuendesha healthcheck pekee wakati software yenyewe inasafirisha client yake, kama vile pg_isready au redis-cli.
Je, kontena iliyo katika hali mbaya huwashwa upya kiotomatiki?
Si kwa Docker Engine kwenye host moja. Sera za kuwasha upya huitikia mchakato unapositishwa, si hali ya afya. Kwa hiyo, kontena iliyo katika hali mbaya hubaki ikiwa imewashwa na ikiwa na hitilafu hadi kitu kingine kichukue hatua. Ama fanya mchakato usitoke unapogundua hitilafu, au endesha kifuatiliaji cha nje kinachotoa tahadhari kuhusu hali hiyo.
start_period inapaswa kuwa ya muda gani?
Iwe ndefu vya kutosha kufunika uanzishaji wa kwanza halali ulio mrefu zaidi ulioupima, pamoja na muda wa ziada. Pima muda huo kwa docker compose up dhidi ya volume tupu, kwa sababu uanzishaji wa kwanza wa hifadhidata huwa wa polepole zaidi kuliko kila uanzishaji unaofuata. start_period iliyo ndefu kupita kiasi huchelewesha tu uamuzi wa kwanza wa unhealthy. Idadi kubwa kupita kiasi ya majaribio hudhoofisha ukaguzi kwa muda wote wa maisha ya kontena, jambo ambalo ni tatizo kubwa zaidi.