Cara Membuat Healthcheck Docker dan Memilih Restart Policy
Cara membuat healthcheck Docker: tulis cek-nya, pahami status starting, healthy, dan unhealthy, pakai --wait saat deploy, dan batas restart policy.
Oleh Matthews WongDitinjau pada
Poin utama
Healthcheck Docker adalah perintah yang dijalankan Docker di dalam container secara berkala: exit 0 berarti healthy, exit 1 berarti unhealthy. Statusnya dimulai dari starting, menjadi healthy saat cek pertama lolos, dan unhealthy setelah sejumlah kegagalan berturut-turut. Restart policy adalah hal terpisah: ia hanya bereaksi saat proses berhenti, jadi container yang unhealthy tetap berjalan.
Setelah pelajaran ini, Anda bisa
- Menulis health check di Dockerfile, file Compose, atau perintah docker run, lalu membaca status dan log-nya dengan docker inspect.
- Menghitung dari interval, timeout, retries, start_period, dan start_interval seberapa cepat container menjadi healthy, dan berapa lama kegagalan baru terlihat.
- Memilih restart policy, dan menjelaskan apa yang bereaksi terhadap status health (depends_on, --wait, Swarm) dan apa yang tidak.
Ikuti diagramnya langkah demi langkah
Tekan Putar, atau melangkah dengan panah. Setiap langkah juga ditulis di bawah diagram.
Siapa yang bereaksi saat container menjadi unhealthy
Sebuah service Compose dengan health check berpindah dari starting ke healthy, yang melepas docker compose up --wait dan service yang bergantung padanya. Saat aplikasinya hang, statusnya menjadi unhealthy dan tidak ada yang me-restart; hanya proses yang berhenti yang memicu restart policy.
Tombol: ← → langkah · Home / End · K putar atau jeda
Satu project Compose. web punya health check dan restart: unless-stopped. worker menunggu web healthy, dan deploy menjalankan docker compose up --wait.
web dimulai dengan status starting. Selama start_period 20 detik, Docker menjalankan cek setiap start_interval, default 5 detik, dan kegagalan di masa ini tidak dihitung.
start_period: 20sCek pertama yang lolos membuat web healthy. --wait selesai dengan exit 0, dan Compose menjalankan worker, yang menunggu condition: service_healthy.
condition: service_healthyAplikasinya hang. Setiap cek kini menunggu timeout 5 detik, lalu cek berikutnya jalan 30 detik kemudian. Setelah tiga kegagalan berturut-turut, web menjadi unhealthy.
retries: 3Tebak langkah berikutnya web sekarang unhealthy dan punya restart: unless-stopped. Apa yang dilakukan Docker berikutnya? (Me-restart web · Tidak ada: web tetap berjalan, unhealthy · Menghentikan worker)Tidak ada yang di-restart. Restart policy bereaksi saat proses berhenti, dan proses yang hang belum berhenti. worker juga tetap jalan: Compose hanya mengecek health saat menjalankannya.
Tebak langkah berikutnya Sekarang prosesnya crash dan berhenti dengan exit code 1. Apa yang terjadi? (Tetap mati: toh sudah unhealthy · Menyala lagi, dan status health kembali ke starting)Proses berhenti dengan exit code 1, dan itulah yang diawasi restart policy. unless-stopped menyalakan web lagi setelah jeda singkat, dan status health-nya kembali starting.
restart: unless-stopped
Apa itu healthcheck Docker?
Healthcheck Docker adalah perintah yang dijalankan Docker di dalam container yang sedang berjalan secara berkala, untuk menanyakan apakah aplikasinya masih berfungsi. Exit code 0 berarti healthy dan 1 berarti unhealthy; 2 dicadangkan, jadi jangan dipakai. Dalam uji coba, cek yang berhenti dengan exit 127 karena perintahnya tidak ada juga dihitung gagal. Jawabannya menjadi status di container, yang ditampilkan docker ps sebagai (healthy) atau (unhealthy).
Baca selengkapnya (2)Tutup
Anda bisa mendeklarasikannya di tiga tempat. Di Dockerfile, HEALTHCHECK [OPTIONS] CMD command menanamkannya ke image; hanya yang terakhir yang berlaku, dan HEALTHCHECK NONE mematikan cek warisan dari base image. Di Compose, healthcheck: menimpa nilai dari image. test-nya berupa list yang diawali CMD, CMD-SHELL, atau NONE, string biasa berarti CMD-SHELL, dan disable: true mematikan ceknya. Di docker run, keluarga flag --health-cmd melakukan hal yang sama, dan --no-healthcheck mematikannya.
CMD menjalankan programnya langsung, tanpa shell, jadi binary itu harus ada di image. CMD-SHELL menjalankan string lewat shell default container, sehingga bisa memakai || exit 1, tetapi butuh shell di image. Docker menyimpan 4096 byte pertama dari output setiap cek, dan docker inspect menampilkannya; di situlah Anda membaca kenapa sebuah cek gagal.
Bagaimana status health berubah dari waktu ke waktu?
Setiap container yang punya cek dimulai dengan status starting. Docker menjalankan cek pertama satu interval setelah container start, dan setiap cek berikutnya satu interval setelah cek sebelumnya selesai. Cek yang melewati timeout dihitung gagal, dan Docker mematikannya. Setelah gagal sebanyak retries kali berturut-turut, statusnya menjadi unhealthy, dan cek berikutnya yang lolos membuatnya healthy lagi. Default-nya 30 detik untuk interval dan timeout, 3 kali retries, dan tanpa start period.
Baca selengkapnya (2)Tutup
start_period adalah masa tenggang untuk aplikasi yang lambat booting. Kegagalan di dalamnya tidak dihitung ke retries, dan Docker menjalankan cek setiap start_interval, default 5 detik, bukan setiap interval; start_interval butuh Docker Engine 25.0 atau lebih baru. Cek pertama yang lolos di masa ini langsung mengakhirinya: sejak itu, setiap kegagalan dihitung.
Setiap perubahan status memunculkan event health_status, jadi docker events menampilkan healthy, lalu unhealthy, lalu healthy lagi, bukan setiap cek. docker inspect menampilkan FailingStreak, yaitu jumlah kegagalan beruntun saat ini, dan log hasil terbaru: dalam uji coba di Docker Engine 29.8.1, log itu menyimpan lima hasil terakhir.
Seberapa cepat cek di situs ini bereaksi?
Cek di situs ini berjalan setiap 30 detik dengan timeout 5 detik, 3 kali retries, dan start period 20 detik, tanpa start_interval. Kalau hanya melihat interval, cek pertama seolah baru jalan 30 detik kemudian. Kenyataannya tidak: di dalam start period, Docker mengecek setiap 5 detik. Dalam uji coba memakai container nginx dengan angka yang persis sama, cek pertama jalan 5 detik setelah container start, dan docker compose up --wait selesai dalam sekitar 6 detik.
Baca selengkapnya (2)Tutup
Arah sebaliknya lebih lambat. Dengan jarak 30 detik antar cek, tiga kegagalan berturut-turut butuh sekitar satu setengah menit: dalam uji coba yang sama, cek yang langsung gagal membuat container unhealthy 90 detik setelah aplikasinya rusak. Aplikasi yang hang butuh waktu lebih lama lagi, karena setiap cek yang gagal menunggu timeout 5 detik dulu. Rilis yang ceknya gagal sejak detik pertama menjadi unhealthy, dan membuat --wait gagal, setelah sekitar 80 detik.
Angka-angka itu adalah kompromi. Interval yang lebih pendek lebih cepat mendeteksi, tetapi lebih sering menjalankan proses di dalam container; retries yang lebih banyak tahan terhadap lambat sesaat, tetapi menunda alarm. Pilih angkanya berdasarkan berapa lama Anda sanggup melayani error sebelum ada yang bereaksi.
Apakah Docker me-restart container yang unhealthy?
Tidak, tidak dengan sendirinya. Dokumentasi Docker mendefinisikan setiap restart policy berdasarkan cara container berhenti, dan referensi HEALTHCHECK hanya menjelaskan sebuah status tanpa tindakan apa pun. Dalam uji coba di Docker Engine 29.8.1, container nginx dengan restart: unless-stopped tetap unhealthy lebih dari satu menit, dengan failing streak 13 dan RestartCount 0, dan terus menjawab setiap request dengan 403.
Baca selengkapnya (1)Tutup
Swarm mode adalah pengecualiannya. Di sana, jika container milik sebuah service gagal health check, task-nya berakhir, lalu orchestrator menghapusnya dan membuat penggantinya; pelajaran 19 membahas hal ini. Dengan Docker biasa atau Compose di satu host, harus ada pihak lain yang bertindak atas status unhealthy: deploy yang menunggu healthy, monitor yang memberi Anda alert, atau script buatan sendiri.
Apa saja di Compose yang bereaksi terhadap health?
Ada dua, dan keduanya hanya berlaku saat Compose sedang menjalankan service. depends_on dengan condition: service_healthy membuat Compose menunggu cek milik dependency lolos sebelum menjalankan service yang bergantung padanya. Bentuk singkatnya, berupa list nama saja, hanya menunggu sampai dependency start: dokumentasi Compose menyebut bahwa saat startup, Compose tidak menunggu container siap, hanya menunggu container berjalan.
Baca selengkapnya (2)Tutup
docker compose up --wait menunggu service berjalan atau healthy, otomatis berjalan dalam mode detached, dan berhenti dengan error jika ada yang menjadi unhealthy; --wait-timeout memberi batas waktu. Dalam uji coba, dependency yang ceknya tidak pernah lolos membuat service yang bergantung padanya hanya dibuat tanpa pernah dijalankan, dan bahkan up -d tanpa --wait gagal dengan pesan dependency failed to start.
Begitu service sudah jalan, Compose tidak lagi mengawasi. Dalam uji coba yang sama, service yang bergantung tetap berjalan setelah dependency-nya menjadi unhealthy. depends_on juga menerima restart: true, yang me-restart service yang bergantung saat Compose sendiri me-restart atau memperbarui dependency, bukan saat container runtime yang melakukannya.
Restart policy mana yang sebaiknya dipilih?
Restart policy memberi tahu daemon Docker apa yang harus dilakukan saat proses di container berhenti. no, yang merupakan default, tidak pernah me-restart. on-failure hanya me-restart setelah exit code bukan nol, dan on-failure:3 menyerah setelah tiga kali percobaan. always me-restart setelah exit apa pun. unless-stopped sama saja, kecuali container yang Anda hentikan sendiri tetap mati, bahkan setelah daemon di-restart.
Baca selengkapnya (3)Tutup
Ada dua aturan yang berlaku untuk semuanya. Container yang Anda hentikan dengan docker stop tetap mati sampai daemon di-restart atau Anda menyalakannya lagi, jadi restart policy tidak akan melawan Anda. Lalu, Docker menunggu sebelum setiap restart, mulai dari 100 ms dan berlipat dua sampai satu menit, lalu kembali ke awal begitu container berjalan selama 10 detik. Dalam uji coba, container yang berhenti setiap 2 detik sudah di-restart 8 kali setelah 30 detik.
Restart daemon termasuk saat server reboot, karena Debian dan Ubuntu menyalakan Docker saat boot. always dan unless-stopped menyalakan kembali container yang tadinya berjalan, dan dari keduanya, hanya always yang juga menghidupkan container yang sudah Anda hentikan. on-failure melihat exit code terakhir. Dokumentasi Docker menyebut policy ini tidak me-restart saat daemon di-restart, dan dalam uji coba itu benar untuk container yang berhenti dengan exit 0; container yang di-kill, dengan exit code 137, menyala lagi, bahkan yang dihentikan secara manual.
Untuk web service di satu server, unless-stopped adalah pilihan yang umum, dan itu yang dipakai situs ini: crash akan pulih, dan docker stop untuk maintenance tetap berlaku walau server reboot. Jangan membungkus container itu lagi dengan systemd atau supervisor; dokumentasi Docker memperingatkan bahwa menggabungkan restart policy dengan process manager di host menimbulkan konflik.
Kenapa situs ini mengecek robots.txt dengan wget ke 127.0.0.1?
Karena isi image-nya. Stage runtime-nya memakai node:20-alpine, yang punya wget dari BusyBox tetapi tidak punya curl, dan bentuk exec CMD tidak butuh shell. --spider hanya mengecek apakah URL-nya ada, dengan exit 0 jika ada; dalam uji coba, ia tetap mengirim GET. --no-verbose dan --tries=1 adalah opsi GNU wget yang diterima BusyBox walau tidak tercantum di help-nya, jadi yang menentukan adalah timeout dan retries milik Docker. robots.txt adalah route ringan yang membuktikan server Node menjawab, bukan bahwa setiap halaman ter-render.
Baca selengkapnya (1)Tutup
127.0.0.1 ditulis secara eksplisit dengan sengaja. Cek berjalan di dalam container, tempat localhost dipetakan ke 127.0.0.1 sekaligus ::1, dan wget BusyBox mencoba ::1 lebih dulu. Aplikasinya listen di 0.0.0.0, yang hanya mencakup IPv4, jadi dalam uji coba di node:20-alpine, wget ke localhost:3000 mendapat Connection refused, sedangkan request yang sama ke 127.0.0.1:3000 lolos.
Konfigurasi nyata di balik pelajaran ini
Kutipan dari repositori yang membangun dan men-deploy situs ini, apa adanya.
restart: unless-stoppedSatu baris ini menentukan apa yang terjadi setelah proses berhenti. Crash membuat container menyala lagi setelah jeda singkat, dan docker stop untuk maintenance membuatnya tetap mati, bahkan setelah reboot. Status unhealthy tidak mengubah apa pun di sini, karena prosesnya belum berhenti.
healthcheck:
test:
[
"CMD",
"wget",
"--no-verbose",
"--tries=1",
"--spider",
"http://127.0.0.1:3000/robots.txt",
]
interval: 30s
timeout: 5s
retries: 3
start_period: 20sBaca angka-angka ini sebagai garis waktu. Di dalam start period 20 detik, Docker mengecek setiap 5 detik, yaitu start_interval default, jadi container baru sudah healthy dalam hitungan detik. Setelah itu cek jalan setiap 30 detik dan menyerah setelah 5 detik, jadi tiga kegagalan berturut-turut butuh sekitar 90 detik sebelum tampil sebagai unhealthy. Bentuk exec menjalankan wget BusyBox tanpa shell, ke 127.0.0.1 milik container itu sendiri.
docker compose up -d --remove-orphans || {
docker rm -f matthewswong-portfolio 2>/dev/null || true
docker compose up -d --remove-orphans
}up -d selesai begitu container start, jadi health check tidak pernah menentukan apakah langkah ini lolos. Fallback-nya menangani bentrok nama container saat peralihan pertama. Kalau Anda menambahkan --wait, tambahkan di kedua baris up: dalam uji coba dengan file berisi satu service, --wait yang hanya ada di baris pertama gagal, fallback menjalankan rilis yang sama lagi tanpa menunggu, dan script-nya selesai dengan sukses.
# The container answers within seconds of `up -d`; this short pause
# saves most runs from spending a retry on a 502.
- name: Wait for the new container
run: sleep 20Job setelah deploy menunggu berdasarkan jam, bukan berdasarkan health: 20 detik, dan script-nya mengulang jika situs masih menjawab 502. Itu cukup untuk sekadar memberi tahu mesin pencari, karena meleset sekali tidak banyak ruginya. Deploy yang harus tahu rilisnya berfungsi sebaiknya menunggu health check, dengan docker compose up --wait; container uji dengan angka yang sama sudah healthy dalam sekitar 6 detik.
Kesalahan yang benar-benar membuang waktu
Unhealthy berjam-jam, dan tidak terjadi apa-apa
Cek yang gagal hanya mengubah status dan memunculkan event. Container tidak di-restart, tidak dihentikan, dan trafik tetap diarahkan ke sana, jadi aplikasi yang rusak terus melayani error sampai ada yang melihat. Buat sesuatu bertindak: --wait saat deploy, monitor yang memberi alert dari status itu atau dari URL di luar, atau Swarm mode, yang mengganti task yang container-nya gagal dicek.
Connection refused di log health, padahal aplikasinya jalan
Ceknya memanggil localhost, wget BusyBox mencoba ::1 lebih dulu, sedangkan aplikasinya listen di 0.0.0.0, yang hanya IPv4. Log-nya berisi can't connect to remote host: Connection refused di setiap cek. Tulis 127.0.0.1 di cek, seperti situs ini, atau buat aplikasinya juga listen di IPv6.
curl: not found, di setiap cek
Cek yang disalin dari tutorial memanggil curl, padahal image-nya tidak punya: node:20-alpine membawa wget BusyBox tanpa curl. Dalam uji coba, log-nya menampilkan /bin/sh: curl: not found dengan exit code 127, dan container menjadi unhealthy padahal aplikasinya menjawab dengan baik. Pakai tool yang ada di image, dan uji dulu perintah persisnya dengan docker exec.
Crash loop yang tidak ada habisnya
always dan unless-stopped me-restart container yang langsung berhenti, terus-menerus, dengan jeda yang makin panjang sampai satu menit. docker ps menampilkan Restarting, dan RestartCount terus naik. Baca docker logs untuk mencari penyebabnya, dan untuk job yang memang boleh menyerah, pakai on-failure dengan batas, misalnya on-failure:3.
| Policy | Exit dengan kode 0 | Exit dengan error | Setelah daemon di-restart atau reboot | Berjalan tetapi unhealthy |
|---|---|---|---|---|
| no (default) | Tetap mati | Tetap mati | Tetap mati | Tetap berjalan |
| on-failure[:N] | Tetap mati | Di-restart, maksimal N kali | Menyala lagi hanya jika exit code terakhirnya bukan 0 | Tetap berjalan |
| always | Di-restart | Di-restart | Menyala lagi, walau sudah Anda hentikan | Tetap berjalan |
| unless-stopped | Di-restart | Di-restart | Menyala lagi, kecuali sudah Anda hentikan | Tetap berjalan |
Lakukan di komputer Anda sendiri
Anda hanya butuh Docker dan curl di laptop; semua port tetap di 127.0.0.1 dan semua nama container diawali hc- atau health-lab. Anda akan melihat cek menjadi healthy, merusaknya, memulihkannya, menahan start Compose dengannya, lalu membuat container crash di bawah setiap policy.
Jalankan nginx dengan health check yang meniru milik situs ini, dengan angka waktu yang lebih pendek. Jalankan docker ps langsung dan sekali lagi 10 detik kemudian: starting, lalu healthy.
docker run -d --name hc-web --restart unless-stopped -p 127.0.0.1:8090:80 \ --health-cmd "wget --no-verbose --tries=1 --spider http://127.0.0.1/" \ --health-interval 5s --health-timeout 3s --health-retries 3 --health-start-period 10s \ nginx:alpine docker ps --filter name=hc-web --format '{{.Names}} {{.Status}}'Baca catatan health-nya: status, failing streak saat ini, serta exit code dan output dari setiap cek terbaru.
docker inspect --format '{{.State.Health.Status}} streak={{.State.Health.FailingStreak}}' hc-web docker inspect --format '{{range .State.Health.Log}}{{.ExitCode}} {{.Output}}{{end}}' hc-webRusak dengan menghapus halaman yang dicek. Sekitar 15 detik kemudian statusnya unhealthy, tetapi nginx masih menjawab (403) dan RestartCount tetap 0, walau memakai unless-stopped.
docker exec hc-web rm /usr/share/nginx/html/index.html # about 15 seconds later: docker ps --filter name=hc-web --format '{{.Names}} {{.Status}}' curl -I http://127.0.0.1:8090/ docker inspect --format 'restarts={{.RestartCount}}' hc-webPulihkan. Satu cek yang lolos sudah cukup untuk kembali healthy. Daftar event menampilkan setiap perubahan status, bukan setiap cek; jika baris healthy terakhir belum muncul, jalankan lagi beberapa detik kemudian.
docker exec hc-web sh -c 'echo ok > /usr/share/nginx/html/index.html' # about 10 seconds later: docker inspect --format '{{.State.Health.Status}}' hc-web docker events --since 5m --until 0s --filter container=hc-web --filter event=health_statusBuat folder kosong bernama health-lab dan simpan ini sebagai compose.yaml. CHECK_PATH memungkinkan Anda merusak cek dari command line, dan worker menunggu api healthy.
services: api: image: nginx:alpine restart: unless-stopped ports: - "127.0.0.1:8091:80" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1/${CHECK_PATH:-}"] interval: 5s timeout: 3s retries: 3 start_period: 10s worker: image: alpine:3 command: sleep 600 depends_on: api: condition: service_healthyDi folder itu, jalankan dengan --wait: exit 0 dalam hitungan detik. Lalu jalankan lagi dengan cek yang tidak mungkin lolos. Setelah sekitar 20 detik Compose mencetak dependency failed to start, exit 1, dan worker hanya Created tanpa pernah dijalankan.
docker compose up -d --wait; echo "exit=$?" docker compose ps docker compose down CHECK_PATH=missing docker compose up -d --wait; echo "exit=$?" docker compose ps -aBuat container crash di bawah tiga policy, masing-masing berhenti setelah 2 detik. Sekitar 20 detik kemudian: no punya 0 restart, on-failure:3 menyerah di angka 3, dan always terus jalan. Lalu hentikan yang always secara manual: ia tetap mati.
docker run -d --name hc-no --restart no alpine:3 sh -c 'sleep 2; exit 1' docker run -d --name hc-onf --restart on-failure:3 alpine:3 sh -c 'sleep 2; exit 1' docker run -d --name hc-always --restart always alpine:3 sh -c 'sleep 2; exit 0' # about 20 seconds later: docker inspect --format '{{.Name}} restarts={{.RestartCount}}' hc-no hc-onf hc-always docker stop hc-always docker ps -a --filter name=hc- --format '{{.Names}} {{.Status}}'Bersihkan container-nya, lalu, di dalam health-lab, project Compose-nya.
docker rm -f hc-web hc-no hc-onf hc-always docker compose down
Tiga pertanyaan sebelum lanjut
Pilih jawaban untuk melihat kenapa benar atau salah. Tidak ada nilai, jadi tebak saja dengan bebas; jawaban yang salah disimpan di browser ini agar kursus bisa menanyakannya lagi nanti.
Dua pertanyaan dari pelajaran sebelumnya
Mengingat kembali pelajaran lama setelah jeda membuatnya lebih melekat. Jawab dari ingatan sebelum mencari apa pun.
Jawab dalam hati, lalu cek
1.Apa arti exit code 0, 1, dan 2 bagi health check?Lihat jawabannya
Jawaban:0 berarti healthy, 1 berarti unhealthy, dan 2 dicadangkan, jadi jangan dipakai. Dalam uji coba, kode bukan nol lainnya, seperti 127 untuk perintah yang tidak ada, juga dihitung gagal.
2.worker punya depends_on: api dengan condition: service_healthy. Satu jam setelah startup, api menjadi unhealthy. Apa yang dilakukan Compose?Lihat jawabannya
Jawaban:Tidak ada. Compose hanya mengecek kondisi itu saat menjalankan service. Dalam uji coba, service yang bergantung tetap berjalan setelah dependency-nya menjadi unhealthy.
3.Anda menghentikan container dengan docker stop, lalu server reboot. Apakah ia menyala lagi dengan always? Dengan unless-stopped?Lihat jawabannya
Jawaban:Dengan always, ya: penghentian manual hanya berlaku sampai daemon di-restart. Dengan unless-stopped, tidak: ia tetap mati sampai Anda menyalakannya sendiri. Perbedaan itulah alasan memilih unless-stopped untuk service yang kadang Anda hentikan untuk maintenance.
Bahasan mendalam terkait di situs ini
Sumber
Sumber primer yang diperiksa pada tanggal riset. Ikuti tautannya untuk versi terbaru.
- Dokumentasi Docker: referensi Dockerfile, HEALTHCHECK
- Dokumentasi Docker: referensi service Compose (healthcheck, restart, depends_on)
- Dokumentasi Docker: menjalankan container secara otomatis (restart policy)
- Dokumentasi Docker: docker container run (flag --health-*, jeda restart)
- Dokumentasi Docker: docker compose up (--wait, --wait-timeout)
- Dokumentasi Docker: mengatur urutan startup di Compose
- Dokumentasi Docker: docker system events (health_status)
- Dokumentasi Docker: docker container ls (filter health)
- Dokumentasi Docker: cara kerja service di Swarm mode
- Dokumentasi Docker: langkah setelah instalasi di Linux (start saat boot)
- Bantuan perintah BusyBox (wget --spider)