depends_on tayyorlikni kafolatlamaydi
Toza serverda birinchi marta docker compose up -d qildingiz. db ko'tarildi, api esa bir soniya ichida yiqildi. Qo'lda yana bir marta up -d — endi hammasi ishlaydi. Keyingi toza deploy'da hikoya takrorlanadi. Compose faylida esa hammasi joyida ko'rinadi:
services:
api:
depends_on:
- dbTartib yozilgan — api haqiqatan db dan keyin ishga tushadi. Muammo shundaki, depends_on tartib haqida, tayyorlik haqida emas. Quyida farqni real loglarda ko'ramiz, keyin ikki qatlamli yechim yig'amiz: to'g'ri yozilgan healthcheck va ilovaning o'zidagi qayta urinish.
Poygani o'zingiz takrorlang#
Nosozlikni ko'rish uchun ilova ham kerak emas. Quyidagi faylda api o'rnida bir martalik psql turibdi: u bazaga ulanadi, select 1 bajaradi va chiqadi.
services:
api:
image: postgres:16-alpine
command: ["psql", "-h", "db", "-U", "app", "-d", "app", "-c", "select 1"]
environment:
PGPASSWORD: demo
depends_on:
- db
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: demoParol faylga yozilgan, chunki bu faqat lokal tajriba — ishlab chiqarishda bunday qilinmaydi. Faylni myapp katalogiga saqlang, docker compose up -d qiling va bir necha soniyadan keyin natijaga qarang. Qayta takrorlashdan oldin docker compose down -v qiling: baza volume'i bo'sh bo'lgandagina poyga eng yaqqol ko'rinadi.
$ docker compose ps -a --format 'table {{.Service}}\t{{.Status}}'
SERVICE STATUS
api Exited (2) 8 seconds ago
db Up 9 seconds
$ docker compose logs api
api-1 | psql: error: connection to server at "db" (192.168.0.2), port 5432 failed: Connection refused
api-1 | Is the server running on that host and accepting TCP/IP connections?db ishlab turibdi, api esa Connection refused olib, 2 kodi bilan chiqib ketgan — psql serverga ulana olmasa shu kodni qaytaradi. Python yoki Node'dagi ilovada matn boshqacha bo'ladi (psycopg.OperationalError, ECONNREFUSED), lekin ma'nosi bitta: 5432-port hali ochilmagan.
Nima uchun: ishga tushgan konteyner hali tayyor baza emas#
Qisqa yozuv aslida nimani anglatishini docker compose config ochiq aytadi. Uning chiqishida api ning bog'liqligi shunday ko'rinadi:
depends_on:
db:
condition: service_started
required: trueservice_started — Compose faqat db konteyneri ishga tushishini, ya'ni entrypoint jarayoni start olishini kutadi. Postgres esa shu paytda endi ish boshlayapti. Bo'sh volume'da u avval initdb bilan klaster yaratadi, keyin quyidagi ketma-ketlik keladi (docker compose logs db dan tanlangan qatorlar):
db-1 | 2026-09-21 08:15:41.360 UTC [42] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
db-1 | 2026-09-21 08:15:41.393 UTC [42] LOG: database system is ready to accept connections
db-1 | /usr/local/bin/docker-entrypoint.sh: ignoring /docker-entrypoint-initdb.d/*
db-1 | 2026-09-21 08:15:42.864 UTC [42] LOG: database system is shut down
db-1 | PostgreSQL init process complete; ready for start up.
db-1 | 2026-09-21 08:15:43.163 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432
db-1 | 2026-09-21 08:15:43.244 UTC [1] LOG: database system is ready to accept connectionsE'tibor bering: ready to accept connections ikki marta chiqadi. Birinchisi — vaqtinchalik server ([42]). Image'ning entrypoint skripti uni faqat Unix socket'da ko'taradi, /docker-entrypoint-initdb.d/ dagi skriptlarni bajaradi va to'xtatadi. TCP orqali ulanishni faqat ikkinchi, asosiy server ([1]) qabul qiladi. Shu oraliqda api ulanishga urinsa — Connection refused.
Volume allaqachon to'la bo'lsa, initsializatsiya bosqichi bo'lmaydi va oyna ancha qisqaradi, lekin yo'qolmaydi. Konteyner SIGKILL bilan o'ldirilgan yoki server toki uzilgan bo'lsa, Postgres keyingi startda avval WAL bo'yicha tiklanadi. Bu WAL hajmiga qarab soniyalar, ba'zan daqiqalar davom etadi, va shu vaqtda ulanishlar FATAL: the database system is not yet accepting connections xatosini oladi.
Birinchi qatlam: healthcheck va service_healthy#
Compose tayyorlikni o'zi aniqlay olmaydi — unga qanday tekshirishni aytish kerak. db ga healthcheck yoziladi, api esa uni service_healthy sharti bilan kutadi:
services:
api:
depends_on:
db:
condition: service_healthy
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_DB: app
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
healthcheck:
test: ["CMD-SHELL", "pg_isready -h 127.0.0.1 -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 10s
timeout: 3s
retries: 5
start_period: 30s
start_interval: 1sHar bir qatorning o'z sababi bor:
-h 127.0.0.1— tekshiruv TCP orqali boradi.-hsizpg_isreadyUnix socket'ga ulanadi, vaqtinchalik server esa aynan socket'da tinglaydi. Natijada healthcheck init skriptlari ishlayotgan paytdayoq healthy deydi vaapibaribir Connection refused oladi. Buni tekshirish oson:/docker-entrypoint-initdb.d/gasleep 8qiladigan skript qo'ying va-hsiz variantni ishga tushiring —service_healthybo'lsa ham poyga qaytadi.-Uva-d—pg_isreadylogin ma'lumotlarini tekshirmaydi, faqat server javob beryaptimi-yo'qligini aniqlaydi. Lekin postgres image'idaUSERbelgilanmagan, healthcheckrootnomidan ishlaydi, va-Ubo'lmasa Postgres har tekshiruvda loggaFATAL: role "root" does not existyozadi.-dham shu sababli kerak: busiz foydalanuvchi nomidagi baza so'raladi, u yo'q bo'lsa —FATAL: database "..." does not exist. Haqiqiy xatolar shu shovqin ostida ko'milib ketadi.$$— Compose${...}ni faylni o'qiyotganda o'zi almashtiradi.$${POSTGRES_USER}esa konteynerga${POSTGRES_USER}bo'lib yetadi, va uni konteyner ichidagi shell servisning o'z muhitidan o'qiydi.interval,timeout,retries— tekshiruvlar oralig'i, bitta tekshiruvga ajratilgan vaqt va konteyner unhealthy deb e'lon qilinishidan oldingi ketma-ket muvaffaqiyatsizliklar soni.start_periodbo'lmasa, birinchi tekshiruv konteyner ishga tushgandanintervalo'tib bajariladi.intervalning standart qiymati esa 30 soniya — baza bir soniyada tayyor bo'lsa ham,apikamida yarim daqiqa kutadi.start_period— ishga tushish uchun ajratilgan vaqt. Bu oraliqdagi muvaffaqiyatsiz tekshiruvlarretriesga qo'shilmaydi, birinchi muvaffaqiyatli tekshiruv esa konteynerni darhol healthy qiladi. Sekin diskli serverda birinchi start ancha cho'ziladi, shuning uchun bu yerda saxiy bo'ling.start_interval—start_perioddavomidagi tekshiruvlar oralig'i: ishga tushishda tez-tez, keyinintervalbo'yicha kamroq. Docker Engine 25.0 va undan yangisini talab qiladi.
Natija docker compose ps da ko'rinadi. Ishga tushish paytida:
$ docker compose ps --format 'table {{.Service}}\t{{.Status}}'
SERVICE STATUS
db Up 2 seconds (health: starting)db tayyor bo'lgach, api ishga tushadi va bu safar muvaffaqiyatli chiqadi. Tekshiruvlarning o'zini docker inspect ko'rsatadi (loyiha katalogi myapp bo'lgani uchun konteyner nomi myapp-db-1):
$ docker compose ps -a --format 'table {{.Service}}\t{{.Status}}'
SERVICE STATUS
api Exited (0) 1 second ago
db Up 31 seconds (healthy)
$ docker inspect -f '{{range .State.Health.Log}}{{.ExitCode}} {{.Output}}{{end}}' myapp-db-1
2 127.0.0.1:5432 - no response
2 127.0.0.1:5432 - no response
2 127.0.0.1:5432 - no response
1 127.0.0.1:5432 - rejecting connections
0 127.0.0.1:5432 - accepting connectionsDocker oxirgi beshta tekshiruv natijasini saqlaydi. pg_isready kodlari: 0 — ulanishlar qabul qilinyapti, 1 — server ishga tushmoqda va ulanishlarni rad etyapti, 2 — javob yo'q. Asosiy server ko'tarilmaguncha TCP tekshiruv healthy demaydi — bizga aynan shu kerak.
depends_on ning uchta sharti#
| Shart | Compose nimani kutadi | Qachon ishlatiladi |
|---|---|---|
service_started | Konteyner ishga tushdi. Qisqa yozuv shu degani | Tayyorlikni tekshirishning iloji yo'q yoki kerak emas |
service_healthy | Healthcheck healthy holatga o'tdi | Baza, cache, navbat — ulanadigan har qanday servis |
service_completed_successfully | Konteyner 0 kodi bilan tugadi | Migratsiya, bir martalik tayyorlov ishi |
Migratsiya bor stack'da oxirgi ikki shart birga ishlaydi: baza sog'lom — migratsiya boshlanadi, migratsiya muvaffaqiyatli tugadi — API ko'tariladi.
services:
api:
depends_on:
migrate:
condition: service_completed_successfully
migrate:
command: alembic upgrade head
restart: "no"
depends_on:
db:
condition: service_healthyShart bajarilmasa, Compose cheksiz kutmaydi: up noldan farqli kod bilan tugaydi va aybdorni nomlaydi.
dependency failed to start: container myapp-db-1 is unhealthy
service "migrate" didn't complete successfully: exit 1Shart nomidagi xato esa undan ham oldin ushlanadi: condition: healthy yozilsa, docker compose config faylni rad etadi va ruxsat etilgan uchta qiymatni sanab beradi.
Deploy skripti yoki CI uchun docker compose up -d --wait qulay: buyruq servislar ishlab turgan (healthcheck bo'lsa — healthy) holatga kelguncha kutadi va nimadir yiqilsa xato kodi bilan chiqadi. Bitta nozik joyi bor: hech kim service_completed_successfully bilan kutmaydigan bir martalik servis 0 bilan tugasa ham, --wait buni xato deb hisoblaydi (Compose v2.40 da shunday). Migratsiyani yuqoridagidek bog'lab qo'ysangiz, bu muammo bo'lmaydi.
depends_on qayerda ishlamaydi#
depends_on — Compose'ning qoidasi, Docker daemon'niki emas. U faqat Compose o'zi servislarni ko'tarayotganda amal qiladi. Undan tashqarida himoya yo'q:
- Server qayta yuklanganda konteynerlarni
restartsiyosati bo'yicha Docker daemon'ning o'zi ko'taradi. Compose bu jarayonda ishtirok etmaydi va healthcheck'larni kutmaydi —apibazadan oldin turishi mumkin. - Baza ish vaqtida qayta ishga tushsa — yangilanish, crash yoki
docker compose restart db—apidagi ochiq ulanishlar uziladi. Hech qaysi shart bunga ta'sir qilmaydi. - Kubernetes'da servislar orasida ishga tushish tartibi degan narsaning o'zi yo'q.
Demak, kutishni ilovaning o'zi ham bilishi kerak.
Ikkinchi qatlam: ilova qayta urinadi#
Ilova ulanish xatosini kutilgan holat deb qabul qilishi kerak — lekin cheklangan vaqt ichida. Python va psycopg 3 bilan minimal misol:
import sys
import time
import psycopg
def connect_with_retry(dsn: str, deadline: float = 60.0) -> psycopg.Connection:
"""Baza tayyor bo'lguncha kutadi, lekin cheksiz emas."""
started = time.monotonic()
delay = 0.5
while True:
try:
return psycopg.connect(dsn, connect_timeout=3)
except psycopg.OperationalError as exc:
if time.monotonic() - started + delay > deadline:
raise # muddat tugadi: jim kutmasdan, ochiq yiqilamiz
print(f"baza hali tayyor emas ({exc}); {delay:.1f}s dan keyin qayta", file=sys.stderr, flush=True)
time.sleep(delay)
delay = min(delay * 2, 5.0)Bu yerda uchta qaror bor:
- Kutish oralig'i har safar ikki barobar o'sadi (0.5, 1, 2, 4 soniya), lekin 5 soniyadan oshmaydi. Tiklanayotgan bazaga ulanish so'rovlari yog'dirilmaydi.
- Muddat cheklangan. 60 soniyadan keyin xato yuqoriga uzatiladi; uni hech kim tutmasa, Python 1 kodi bilan chiqadi, va
restartsiyosati bo'lsa konteyner qayta ko'tariladi. Cheksiz jim kutish muammoni yashiradi: konteyner Up, lekin hech narsa ishlamaydi. - Har bir urinish logga yoziladi, shuning uchun nosozlik paytida
docker compose logs apianiq manzarani beradi.
Ish vaqtidagi uzilishlar uchun ham xuddi shu tamoyil. SQLAlchemy'da create_engine(url, pool_pre_ping=True) ulanishni pool'dan berishdan oldin tekshiradi va o'lganini yangisiga almashtiradi. Baza qaytib kelgandan keyingi birinchi so'rov eski, uzilgan ulanishga tushib qolmaydi.
Qanday tekshirish kerak#
docker compose configni ishga tushiring: qisqa yozuvcondition: service_startedga aylanganini, uzun yozuvda esa siz yozgan shart turganini ko'rasiz.- Eng og'ir holatni ataylab yarating — bo'sh volume bilan birinchi start:
docker compose down -v, keyindocker compose up -d. Faqat lokal yoki test muhitida:-vnomlangan volume'larni, ya'ni baza ma'lumotlarini o'chiradi. docker compose psdadbavval(health: starting), keyin(healthy)bo'lishi,apiesa shundan keyingina ishga tushishi kerak.docker inspectdagi health logida no response dan accepting connections ga o'tishni kuzating.- Ikkinchi qatlamni ham sinang: ishlab turgan stack'da
docker compose restart dbqiling.apio'zini qayta ishga tushirmasdan tiklanishi, keyingi so'rov esa muvaffaqiyatli o'tishi kerak.
Amaliyot#
Quyidagi lab'larda xuddi shu xatolar kichik compose fayllarda turibdi — tuzating va server tekshiruvidan o'tkazing:
docker compose ps, logs va config qo'l xotirasiga o'tishi uchun: