SSD Nodes Learn RAM 8GB — $66/tahun
Panduan Matt ConnorOleh Matt Connor · Dikemas kini 2026-08-01

Healthcheck Docker Compose yang benar-benar berfungsi

Fahami cara healthcheck dinilai, sebab depends_on tidak menunggu servis sedia, serta contoh semakan readiness untuk Postgres dan aplikasi anda.

Perkara yang sebenarnya dilakukan oleh healthcheck Docker Compose

Healthcheck Docker Compose ialah satu arahan yang Docker jalankan di dalam bekas mengikut pemasa. Docker tidak membaca log anda, memantau port anda atau memeriksa senarai proses anda. Docker menjalankan arahan itu, membaca kod keluar, kemudian menyimpan satu keadaan pada bekas: starting, healthy atau unhealthy. Kod keluar 0 bermaksud sihat. Sebarang kod keluar lain bermaksud tidak sihat, manakala kod keluar 2 dikhaskan oleh Docker. Oleh itu, jangan kembalikan kod tersebut dengan sengaja.

Itulah keseluruhan mekanismenya. Hampir semua masalah healthcheck berpunca daripada perkara yang sama: arahan yang anda tulis menjawab soalan yang berbeza daripada soalan yang ingin anda tanyakan. Panduan ini menganggap anda sudah tahu cara menulis fail compose pada VPS, dan meneruskan penerangan dari titik apabila stack dimulakan dalam susunan yang salah.

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: 30s

Nilai test mempunyai dua bentuk yang berguna. Senarai yang bermula dengan CMD menjalankan arahan secara langsung tanpa shell. Oleh itu, pipe, && dan pengembangan pemboleh ubah tidak berfungsi. Senarai yang bermula dengan CMD-SHELL menghantar baki senarai sebagai satu rentetan kepada /bin/sh -c di dalam bekas. Gunakan bentuk ini apabila semakan memerlukan sintaks shell. Rentetan biasa dianggap sebagai CMD-SHELL. Senarai yang mengandungi tepat ["NONE"] membuang healthcheck yang telah terbina dalam imej melalui Dockerfile-nya.

Semakan dijalankan di dalam bekas. Oleh itu, setiap binari yang dinamakan mestilah wujud dalam imej tersebut. Sahkan perkara ini terlebih dahulu. Imej langsing yang tiada curl menghasilkan bekas yang kekal tidak sihat atas sebab yang tidak pernah muncul dalam log aplikasi. Uji secara manual:

docker compose exec api curl --version

Binari yang tiada akan memberikan respons OCI runtime exec failed: exec: "curl": executable file not found in $PATH: unknown. Imej berasaskan Alpine biasanya disertakan dengan wget BusyBox. Oleh itu, semakan tersebut menjadi ["CMD", "wget", "-q", "-O", "-", "http://localhost:8080/healthz"].

Cara interval, retries dan start_period digabungkan

Lima tetapan mengawal pemasaan. Nilai lalainya datang daripada Docker Engine, bukan daripada Compose.

  • interval: tempoh antara dua pemeriksaan selepas container melepasi tempoh permulaannya. Lalai ialah 30s.
  • timeout: tempoh maksimum satu pelaksanaan pemeriksaan sebelum Docker menghentikannya dan mengira pelaksanaan itu sebagai kegagalan. Lalai ialah 30s.
  • retries: bilangan kegagalan berturut-turut yang diperlukan sebelum keadaan bertukar kepada unhealthy. Lalai ialah 3.
  • start_period: tempoh kelonggaran selepas container dimulakan. Lalai ialah 0s.
  • start_interval: kekerapan pemeriksaan dijalankan semasa tempoh permulaan. Lalai ialah 5s, dan tetapan ini memerlukan Docker Engine 25.0 atau lebih baharu.

Peraturan penting ialah: semasa tempoh permulaan, pemeriksaan yang gagal tidak dikira terhadap retries, dan container kekal dalam keadaan starting. Kali pertama pemeriksaan berjaya, container bertukar kepada healthy dan tempoh permulaan tamat serta-merta, walaupun sebahagian besar tempohnya belum digunakan. Jika tempoh permulaan tamat ketika pemeriksaan masih gagal, kiraan biasa bermula dan container memerlukan retries kegagalan berturut-turut sebelum ditandai sebagai unhealthy.

Oleh itu, masa kes terburuk dari permulaan container hingga unhealthy ialah start_period ditambah retries didarab dengan interval, kemudian ditambah timeout. Dengan nilai dalam fail di atas, pengiraannya ialah 30 ditambah 5 didarab dengan 13, iaitu 95 saat. Catat nombor itu sebelum anda menetapkan tamat masa pelancaran, kerana pelancaran yang berhenti selepas 60 saat tidak akan menyaksikan container ini mencapai keadaan akhir.

Kesilapan yang biasa berlaku ialah meningkatkan retries untuk menampung permulaan yang perlahan. Cara ini berkesan sekali, kemudian menjadi masalah berterusan: perkhidmatan yang memerlukan 8 percubaan untuk dimulakan kini membenarkan 8 kegagalan berturut-turut dalam persekitaran produksi sebelum sebarang tindakan diambil. Sebaliknya, gunakan start_period kerana tetapan itu hanya terpakai sebelum kejayaan pertama.

Mengapa depends_on sahaja tidak menjamin apa-apa

Bentuk ringkas depends_on merupakan punca utama kekeliruan.

  api:
    depends_on:
      - db

Maksudnya hanya satu: mulakan bekas db sebelum bekas api. Compose menunggu sehingga bekas dicipta dan dimulakan. Compose tidak menunggu PostgreSQL selesai melakukan inisialisasi kali pertama dan tidak menunggu port 5432 menerima sambungan. Aplikasi anda bermula kira-kira satu saat kemudian, cuba menyambung ke port yang belum didengari oleh apa-apa proses, lalu keluar. Dalam log, anda akan melihat Connection refused, atau FATAL: the database system is starting up apabila pelayan sudah aktif tetapi masih dalam proses pemulihan.

Bentuk panjang ialah perkara yang sebenarnya diperlukan:

  api:
    depends_on:
      db:
        condition: service_healthy
        restart: true
      migrate:
        condition: service_completed_successfully

condition mempunyai tiga nilai. service_started sama seperti bentuk ringkas. service_healthy menangguhkan perkhidmatan bergantung sehingga kebergantungan melaporkan status sihat. Ini hanya bermakna apabila kebergantungan tersebut mentakrifkan healthcheck, sama ada dalam fail compose atau dalam imejnya. service_completed_successfully menunggu bekas sekali jalan, seperti migrasi pangkalan data, keluar dengan status 0.

Dua medan tambahan berada bersebelahan dengan condition. restart: true memberitahu Compose supaya memulakan semula perkhidmatan ini selepas Compose mengemas kini perkhidmatan kebergantungan. required: false menurunkan kebergantungan yang tiada daripada ralat kepada amaran.

Kini had yang sering mengelirukan pengguna. Syarat ini dinilai apabila tindanan dimulakan. Syarat ini mengawal susunan permulaan, bukan peraturan penyeliaan. Jika pangkalan data dimulakan semula pada pukul tiga pagi, tiada apa-apa akan menilai semula service_healthy dan tiada apa-apa akan memulakan semula aplikasi anda untuk memenuhinya semula. Kod aplikasi anda masih perlu menyambung semula dengan sendiri. docker compose up --no-deps api sengaja melangkau keseluruhan mekanisme ini, begitu juga dengan memulakan bekas secara terus menggunakan docker start.

Tulis semakan yang menguji kesediaan, bukan kewujudan proses

Semakan seperti pgrep nginx membuktikan bahawa entri proses wujud dalam jadual proses. Semakan itu tidak membuktikan bahawa perkhidmatan boleh menjawab permintaan. Aplikasi web boleh mengekalkan soket pendengar terbuka lama selepas himpunan sambungan pangkalan datanya terhenti, manakala semakan proses kekal berjaya sepanjang gangguan itu.

Minta container melakukan tugas yang sepatutnya dilakukannya:

  • Untuk perkhidmatan HTTP, minta endpoint sebenar. curl -fsS keluar dengan kod bukan sifar bagi sebarang status 400 atau lebih tinggi kerana -f. Oleh itu, respons 500 daripada aplikasi yang rosak menyebabkan semakan gagal.
  • Untuk PostgreSQL, gunakan pg_isready. Perintah ini keluar dengan kod 0 apabila server menerima sambungan, 1 apabila server menolak sambungan, 2 apabila server langsung tidak memberikan respons, dan 3 apabila parameter yang dihantar salah.
  • Untuk Redis, gunakan redis-cli ping. Perintah ini mencetak PONG dan keluar dengan kod 0.
  • Untuk MariaDB, imej rasmi menyediakan skrip healthcheck.sh, dan healthcheck.sh --connect --innodb_initialized ialah bentuk yang didokumenkan oleh penyelenggaranya.

pg_isready mempunyai satu perangkap yang perlu diketahui. Pada permulaan pertama dengan direktori data yang kosong, imej rasmi postgres menjalankan penginitialan terhadap server sementara yang hanya mendengar pada soket Unix. pg_isready tanpa argumen hos menggunakan soket itu. Oleh itu, perintah tersebut boleh menjawab "menerima sambungan" walaupun port TCP 5432 masih tertutup kepada aplikasi anda. Tentukan TCP secara eksplisit dalam semakan dan masalah itu selesai kerana server sementara tersebut tidak menjawab melalui 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: 30s

Tanda dolar berganda bukan kesilapan taip. Compose mengembangkan $VAR sendiri semasa membaca fail, lalu nilai daripada persekitaran hos anda akan dimasukkan ke dalam semakan. $$ melepaskannya sebagai satu $ supaya shell di dalam container mengembangkannya berdasarkan persekitaran container itu sendiri.

Susunan postgres dan app yang dimulakan mengikut urutan yang betul

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:

Mulakannya dan pantau perubahan status:

docker compose up -d
docker compose ps

Lajur STATUS mengandungi status kesihatan dalam kurungan. Pasangan yang sihat memaparkan Up 41 seconds (healthy) pada kedua-dua baris. Semasa pangkalan data masih dimulakan, db memaparkan Up 4 seconds (health: starting) dan api tiada dalam senarai kerana Compose belum menciptanya.

Untuk mengetahui sebab pemeriksaan berjaya atau gagal, baca log kesihatan:

docker inspect --format '{{json .State.Health}}' "$(docker compose ps -q db)"

Docker menyimpan beberapa hasil terakhir, setiap satu dengan masa mula, masa tamat, ExitCode dan Output arahan tersebut. Output yang disimpan dipendekkan. Oleh itu, pemeriksaan yang mencetak kandungan halaman yang besar akan menghasilkan entri log yang tidak berguna. Pastikan pemeriksaan tidak menghasilkan banyak output.

Tindakan Docker apabila kontena menjadi tidak sihat

Tiada apa-apa. Inilah jawapan yang paling mengejutkan orang.

Docker Engine pada satu hos tidak memulakan semula kontena yang tidak sihat. Dasar restart: unless-stopped bertindak balas apabila proses utama keluar, manakala kontena yang tidak sihat belum keluar. Kontena itu boleh kekal pada unhealthy selama seminggu sementara Compose membiarkannya begitu sahaja. Mod Swarm menggantikan tugas yang tidak sihat, tetapi tindanan Compose biasa pada satu pelayan tidak berbuat demikian.

Terdapat dua pilihan yang munasabah. Jadikan proses keluar apabila proses itu mengetahui bahawa dirinya rosak, supaya dasar mula semula mempunyai keadaan untuk ditangani. Atau pantau keadaan tersebut dari luar dan keluarkan amaran mengenainya. Menetapkan pemantau Uptime Kuma pada titik akhir yang sama seperti yang dipanggil oleh healthcheck bermakna kebergantungan yang rosak akan muncul di kedua-dua tempat, dan anda akan mengetahuinya daripada pemantau, bukan daripada pengguna. Jika trafik sampai ke aplikasi melalui proksi songsang Traefik, ingat bahawa pandangan proksi terhadap backend adalah berasingan daripada keadaan kesihatan Docker. Oleh itu, satu tidak menggantikan yang lain.

Menyahpepijat pemeriksaan yang tidak pernah menjadi sihat

Jalankan perintah tepat itu sendiri, dalam kontena yang sama, dan semak kod keluar:

docker compose exec api curl -fsS http://localhost:8080/healthz; echo "exit=$?"

exit=0 di sini, apabila kontena masih melaporkan status tidak sihat, bermakna test compose anda berbeza daripada yang baru ditaip. Biasanya, ini berlaku kerana CMD digunakan sedangkan sintaks shell diperlukan.

Dua kesilapan merangkumi kebanyakan kes yang lain. Kesilapan pertama ialah port yang salah. Pemeriksaan kesihatan dijalankan di dalam kontena, jadi pemeriksaan itu mesti menggunakan port kontena, bukan port hos yang diterbitkan. Dengan ports: - "8080:3000", aplikasi mendengar pada port 3000. Pemeriksaan terhadap http://localhost:8080 akan gagal selama-lamanya walaupun laman berfungsi dengan baik dalam pelayar. Kesilapan kedua ialah hos yang salah. Dalam pemeriksaan, localhost merujuk kepada kontena yang sama. Ini betul untuk memeriksa kontena itu sendiri tetapi salah untuk memeriksa kontena bersebelahan. Dalam keadaan itu, anda memerlukan nama perkhidmatan, contohnya db.

Satu lagi kes patut dinamakan: pemeriksaan kesihatan berjaya sedangkan pengguna melihat ralat. Ini berlaku apabila titik akhir mengembalikan 200 statik tanpa menyentuh sebarang komponen sebenar. Titik akhir kesediaan yang tidak pernah membuat pertanyaan kepada pangkalan data tidak dapat memberitahu anda bahawa pangkalan data telah terputus. Jadikan titik akhir itu menjalankan satu pertanyaan sebenar yang ringan.

FAQ

Mengapakah aplikasi saya masih gagal disambungkan apabila depends_on menyatakan pangkalan data sihat?

Kerana condition: service_healthy dinilai sekali sahaja apabila tindanan dimulakan. Selepas itu, ia tidak memantau apa-apa. Jika bekas pangkalan data dimulakan semula kemudian, Compose tidak memulakan semula aplikasi anda untuk memenuhi syarat itu sekali lagi. Oleh itu, kod aplikasi anda memerlukan logik penyambungan semula dan percubaan semula sendiri. Syarat ini juga tidak berfungsi apabila anda memulakan satu bekas dengan docker start atau docker compose up --no-deps.

Adakah saya memerlukan healthcheck jika imej sudah mentakrifkan healthcheck?

Biasanya tidak. Mengatasinya juga sering merupakan langkah yang kurang baik kerana penyelenggara imej mengetahui maksud kesediaan untuk perisian tersebut. Tambahkan healthcheck sendiri hanya apabila pemeriksaan imej tidak sesuai dengan persediaan anda, contohnya apabila pemeriksaan itu menguji port yang telah anda pindahkan. Untuk melumpuhkan healthcheck imej, tetapkan test: ["NONE"] atau disable: true pada perkhidmatan.

Patutkah healthcheck menggunakan curl atau wget?

Gunakan yang sudah tersedia dalam imej, dan sahkannya dengan docker compose exec <service> curl --version sebelum bergantung padanya. Banyak imej berasaskan Debian tidak mempunyai kedua-duanya. Imej berasaskan Alpine mempunyai wget BusyBox. Jangan tambahkan pakej ke dalam imej hanya untuk menjalankan healthcheck apabila perisian tersebut menyediakan klien sendiri, seperti pg_isready atau redis-cli.

Adakah bekas yang tidak sihat dimulakan semula secara automatik?

Tidak oleh Docker Engine pada hos tunggal. Dasar mula semula bertindak balas apabila proses keluar, bukan berdasarkan keadaan kesihatan. Oleh itu, bekas yang tidak sihat kekal berjalan dan rosak sehingga sesuatu yang lain mengambil tindakan. Sama ada jadikan proses keluar apabila mengesan kegagalan, atau jalankan pemantau luaran yang memberikan amaran tentang keadaan tersebut.

Berapa lamakah tempoh start_period yang sesuai?

Tempohnya mestilah mencukupi untuk permulaan pertama yang sah dan paling perlahan yang telah anda ukur, serta mempunyai sedikit margin. Ukur tempoh itu dengan docker compose up menggunakan volum kosong kerana permulaan pertama pangkalan data jauh lebih perlahan berbanding setiap permulaan selepasnya. Tempoh mula yang terlalu panjang hanya melambatkan keputusan pertama unhealthy. Bilangan percubaan semula yang terlalu tinggi melemahkan pemeriksaan sepanjang hayat bekas, dan ini merupakan kegagalan yang lebih buruk.

#docker-compose#healthcheck#depends-on#docker#reliability