Docker & DevOps

    Docker Healthcheck Kullanımı

    HEALTHCHECK yönergesiyle konteynerin gerçekten çalıştığını ölçmenin ve bağımlılık kurmanın rehberi.

    11 dk okuma Güncellendi: 25 Ağustos 2026

    Sabahın köründe gelen "site açılmıyor" mesajından sonra sunucuya bağlanıp docker ps yazdığınızda tüm konteynerlerin Up 6 days yazdığını görmek, bu işi yapan herkesin bir kez yaşadığı sinir bozucu andır. Konteyner ayaktadır, süreç çalışmaktadır, Docker'a göre her şey yolundadır; ama uygulama veritabanı bağlantısını kaybetmiş, bellek havuzu tükenmiş ya da bir kilitlenme yüzünden hiçbir isteğe cevap vermiyordur. Docker'ın varsayılan olarak bildiği tek şey, birinci sürecin (PID 1) hâlâ çalışıp çalışmadığıdır. Bu, "çalışıyor" ile "hizmet veriyor" arasındaki farkı ölçmez.

    Docker healthcheck tam olarak bu boşluğu doldurur. Konteynerin içinde belirli aralıklarla küçük bir komut çalıştırırsınız; komut sıfır dönerse konteyner sağlıklı, sıfırdan farklı dönerse belirli sayıda denemeden sonra sağlıksız kabul edilir. Bu tek bilgi; Compose'un başlatma sırasını doğru kurmasını, Swarm'ın bozuk görevleri değiştirmesini ve yük dengeleyicinin çökmüş bir örneğe istek göndermemesini sağlar. Bu rehberde HEALTHCHECK yönergesini, Compose karşılığını, depends_on ile sağlık koşullu bağımlılığı, her servis türü için doğru kontrol komutunu ve en sık düşülen tuzakları uçtan uca ele alacağım.

    Running Neden Sağlıklı Anlamına Gelmez#

    Docker bir konteyneri "çalışıyor" olarak işaretlemek için tek bir şeye bakar: ENTRYPOINT ya da CMD ile başlattığınız süreç hayatta mı? Süreç canlıysa durum running'dir. Uygulamanız bir istisna yakalayıp sonsuz döngüye girse, veritabanı havuzu tükenip her isteğe 500 dönse, dosya tanımlayıcı sınırına çarpıp yeni bağlantı kabul edemese bile süreç ölmediği sürece Docker hiçbir sorun görmez.

    Bu davranış aslında mantıklıdır: Docker uygulamanızın ne yaptığını bilemez, sağlığı ancak siz tanımlayabilirsiniz. HEALTHCHECK yönergesi de bunun için vardır. Sağlık kontrolü tanımladığınız anda konteyner üç durumdan birinde olur:

    DurumAnlamıNe zaman görülür
    startingHenüz karar verilmedistart-period süresi boyunca
    healthySon kontrol başarılıKomut 0 döndü
    unhealthyÜst üste retries kadar başarısızKomut 0 dışında bir değer döndü

    Bu durumu docker ps çıktısında parantez içinde görürsünüz:

    docker ps --format "table {{.Names}}\t{{.Status}}"
    # NAMES        STATUS
    # firmaniz-app Up 4 minutes (healthy)
    # firmaniz-db  Up 4 minutes (healthy)
    # firmaniz-api Up 2 minutes (unhealthy)
    

    Konteynerin sürekli yeniden başladığı bambaşka bir durumdur ve sağlık kontrolüyle karıştırılmamalıdır; o senaryoyu konteyner sürekli yeniden başlıyor yazısında ayrıca ele aldım.

    HEALTHCHECK Yönergesinin Anatomisi#

    Yönerge Dockerfile içinde tek satırla tanımlanır ve dört seçenek alır. Ardından CMD ile çalıştırılacak komut gelir:

    FROM node:22-alpine
    WORKDIR /app
    COPY . .
    RUN npm ci --omit=dev
    
    HEALTHCHECK --interval=30s --timeout=3s --start-period=20s --retries=3 \
      CMD wget --no-verbose --tries=1 --spider http://127.0.0.1:3000/healthz || exit 1
    
    CMD ["node", "server.js"]
    

    Dört seçeneğin ne işe yaradığını tek tek bilmek, ayarları körlemesine kopyalamaktan çok daha faydalıdır:

    SeçenekVarsayılanNe yapar
    --interval30sİki kontrol arasındaki bekleme
    --timeout30sKomut bu sürede bitmezse başarısız sayılır
    --start-period0sBu süredeki başarısızlıklar retries sayacına yazılmaz
    --retries3Kaç ardışık başarısızlıktan sonra unhealthy

    --start-period çoğu kişinin atladığı ama en kritik olan seçenektir. Bir Java uygulaması ya da büyük bir veritabanı ilk açılışta 40 saniye boyunca cevap veremeyebilir. Başlangıç periyodu tanımlamazsanız konteyner daha ayağa kalkmadan unhealthy işaretlenir ve bu duruma tepki veren bir orkestratör varsa onu boş yere yeniden başlatır; sonuç sonsuz bir yeniden başlatma döngüsüdür.

    Komutun çıkış kodu her şeyi belirler. 0 sağlıklı, 1 sağlıksız demektir. 2 Docker tarafından ayrılmıştır ve kullanılmamalıdır. Komutun ekrana bastığı çıktı ilk 4 KB'ı kadar saklanır ve docker inspect ile okunabilir, bu yüzden hata mesajınızı anlamlı yazın.

    Temel imajdan miras kalan bir sağlık kontrolünü iptal etmek isterseniz tek satır yeterlidir:

    # Temel imajin sagik kontrolunu devre disi birak
    HEALTHCHECK NONE
    

    Compose Tarafında healthcheck Bloğu#

    Compose kullanıyorsanız aynı ayarları imajı yeniden derlemeden servis tanımına yazabilirsiniz. Bu, üçüncü taraf imajlarda (Postgres, Redis, RabbitMQ) tek pratik yöntemdir çünkü onların Dockerfile'ını değiştiremezsiniz.

    services:
      db:
        image: postgres:16-alpine
        environment:
          POSTGRES_PASSWORD: ${DB_PASSWORD:?zorunlu}
          POSTGRES_USER: firmaniz
        healthcheck:
          test: ["CMD-SHELL", "pg_isready -U firmaniz -d postgres"]
          interval: 10s
          timeout: 3s
          retries: 5
          start_period: 30s
    
      redis:
        image: redis:7-alpine
        healthcheck:
          test: ["CMD", "redis-cli", "ping"]
          interval: 10s
          timeout: 3s
          retries: 3
    
      app:
        image: firmaniz/web:1.4.0
        healthcheck:
          test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8080/healthz"]
          interval: 30s
          timeout: 5s
          retries: 3
          start_period: 20s
    

    test alanının iki biçimi arasındaki fark önemlidir. ["CMD", ...] komutu doğrudan çalıştırır, kabuk devreye girmez; bu yüzden ||, && ya da değişken genişletmesi işlemez. ["CMD-SHELL", "tek satirlik komut"] ise komutu /bin/sh -c ile çalıştırır ve kabuk sözdizimine izin verir. Boru, yönlendirme veya ortam değişkeni kullanacaksanız CMD-SHELL seçin.

    Bir servisin sağlık kontrolünü geçici olarak kapatmak isterseniz healthcheck: { disable: true } yazabilirsiniz. Bu, imajdan gelen kontrolü devre dışı bırakır ve hata ayıklarken işe yarar. Compose dosyanızın genel yapısı ve servisler arası ağ konusunda temel bilgi için Docker Compose kullanımı yazısına bakabilirsiniz.

    depends_on ile Başlatma Sırasını Doğru Kurmak#

    Sağlık kontrolünün en somut faydası burada ortaya çıkar. Sade depends_on yalnızca başlatma sırasını belirler; Compose veritabanı konteynerini önce başlatır ama içindeki Postgres'in bağlantı kabul etmeye hazır olmasını beklemez. Sonuç, uygulamanın açılışta connection refused alıp çökmesidir. Herkesin bir yerlerden kopyaladığı sleep 10 çözümü ise ya çok kısadır ya da her açılışta boşuna bekletir.

    Doğru yöntem, bağımlılığı sağlık koşuluna bağlamaktır:

    services:
      app:
        image: firmaniz/web:1.4.0
        depends_on:
          db:
            condition: service_healthy      # db saglikli olana kadar bekle
          redis:
            condition: service_started      # sadece baslamis olmasi yeterli
          migrate:
            condition: service_completed_successfully   # is bitip 0 ile cikmali
    
      migrate:
        image: firmaniz/web:1.4.0
        command: ["./bin/migrate", "up"]
        depends_on:
          db:
            condition: service_healthy
        restart: "no"
    

    Üç koşulun anlamı şudur: service_started eski davranıştır ve yalnızca konteynerin başlatıldığını garanti eder; service_healthy hedef servisin sağlık kontrolünün healthy dönmesini bekler; service_completed_successfully ise hedef konteynerin çalışıp 0 çıkış koduyla sonlanmasını bekler ki bu, migration konteynerleri için tam olarak istediğiniz davranıştır.

    service_healthy koşulunu kullanabilmek için hedef servisin bir sağlık kontrolü olması zorunludur. Yoksa Compose hata verir. Bu yüzden veritabanı, önbellek ve kuyruk servislerinize sağlık kontrolü eklemeyi standart bir alışkanlık hâline getirin.

    Servise Göre Doğru Sağlık Komutu#

    Sağlık kontrolü konteynerin içinde çalışır. Bu, imajda bulunmayan bir aracı çağıramayacağınız anlamına gelir; en sık yapılan hata curl kullanmaktır çünkü alpine tabanlı imajların çoğunda curl yoktur. Aşağıdaki tablo sık kullanılan servisler için imajda hazır bulunan doğru komutları toplar:

    ServisÖnerilen kontrol
    PostgreSQLpg_isready -U kullanici -d veritabani
    MySQL / MariaDBmysqladmin ping -h 127.0.0.1 -u root --password=$MYSQL_ROOT_PASSWORD
    Redisredis-cli ping
    RabbitMQrabbitmq-diagnostics -q ping
    nginxnginx -t ya da yerel bir HTTP isteği
    Alpine tabanlı uygulamawget -qO- http://127.0.0.1:PORT/healthz
    Kabuk içermeyen imajUygulamanın kendi alt komutu, örn. /server -health

    Uygulamanızın kendi sağlık uç noktasını yazarken bir dengeyi gözetin. Yalnızca 200 OK dönen boş bir uç nokta, süreç canlı olduğu sürece hep başarılı olur ve hiçbir şey ölçmez. Öte yandan her istekte veritabanına, önbelleğe ve üç harici API'ye giden bir uç nokta, dakikada iki kez çalıştığında ciddi yük yaratır ve harici bir servis yavaşladığında kendi konteynerinizi sağlıksız ilan eder. Pratik denge şudur: kendi süreç durumunuzu ve kendi veritabanı bağlantı havuzunuzu kontrol edin, harici bağımlılıkları ayrı bir "readiness" uç noktasına bırakın.

    Kabuk içermeyen distroless imajlarda CMD-SHELL çalışmaz. Bu durumda uygulamanızın ikili dosyasına küçük bir sağlık alt komutu ekleyip HEALTHCHECK CMD ["/server", "-health"] biçiminde çağırmak en temiz çözümdür. İmajı ince tutmanın diğer yolları için multi-stage build yazısına göz atın.

    Sağlık Durumunu İzlemek ve Hata Ayıklamak#

    Bir konteyner unhealthy olduğunda ilk soru "hangi komut, neden başarısız oldu?" olmalıdır. Docker son beş kontrolün çıktısını konteynerde saklar ve docker inspect ile okunabilir:

    # Sadece mevcut durum
    docker inspect --format='{{.State.Health.Status}}' firmaniz-app
    
    # Son kontrollerin cikti ve cikis kodlari
    docker inspect --format='{{json .State.Health}}' firmaniz-app | python3 -m json.tool
    
    # Tum konteynerlerin sagligini tek bakista listele
    docker ps --filter health=unhealthy --format "table {{.Names}}\t{{.Status}}"
    

    Sağlık kontrolü durum değiştirdiğinde Docker bir olay üretir. Bunu canlı izlemek, aralıklı olarak bozulan servisleri yakalamanın en hızlı yoludur:

    # Saglik durumu degisikliklerini canli izle
    docker events --filter event=health_status
    
    # Belirli bir konteyner icin son bir saatlik olaylar
    docker events --since 1h --filter container=firmaniz-app
    

    Kontrolün kendisinin doğru yazıldığından emin olmak için komutu elle çalıştırın; sağlık kontrolü konteyner içinde çalıştığı için testi de orada yapmalısınız:

    docker exec firmaniz-app wget -qO- http://127.0.0.1:8080/healthz; echo "cikis kodu: $?"
    

    Çıkış kodu 127 ise komut imajda yok demektir, 1 ise uygulama gerçekten cevap vermiyordur. Konteyner içine girip komut çalıştırma ve log okuma konusunda daha fazlası için konteyner loglarını okuma ve exec yazısı iyi bir başlangıçtır.

    Sağlıksız Konteyner Kendiliğinden Yeniden Başlar mı#

    Bu, en çok yanlış bilinen konudur ve net cevabı şudur: tek makinede çalışan Docker, sağlıksız bir konteyneri yeniden başlatmaz. restart: always politikası yalnızca konteyner çıkış yaptığında devreye girer; unhealthy durumu bir çıkış değildir. Yani sağlık kontrolü sorunu tespit eder, raporlar ve orada durur.

    Bunun üç pratik çözümü vardır. Birincisi ve en temizi, uygulamanın kendi kendini öldürmesidir: sağlık uç noktanız kurtarılamaz bir durum tespit ettiğinde süreci sonlandırın, restart politikası gerisini halletsin. İkincisi Docker Swarm kullanmaktır; Swarm sağlıksız görevleri otomatik olarak durdurup yenisini başlatır ve bu, sağlık kontrolünün en verimli çalıştığı ortamdır. Üçüncüsü, Docker soketini izleyip sağlıksız konteynerleri yeniden başlatan küçük bir yardımcı konteyner çalıştırmaktır.

      autoheal:
        image: willfarrell/autoheal
        restart: always
        environment:
          AUTOHEAL_CONTAINER_LABEL: autoheal      # sadece etiketli konteynerler
          AUTOHEAL_INTERVAL: 15
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock
    

    Bu yaklaşımın bedelini bilerek kabul edin: Docker soketini bir konteynere bağlamak, o konteynere makine üzerinde kök yetkisine eşdeğer güç verir. Yalnızca güvendiğiniz bir imajı, salt ihtiyacınız olan etiket filtresiyle kullanın. Otomatik yeniden başlatmanın nasıl davrandığını daha derin anlamak için Docker restart politikaları yazısına bakın.

    Sık Yapılan Hatalar#

    İmajda olmayan aracı çağırmak. curl alpine imajlarında yoktur; kontrol her seferinde 127 döner, konteyner sürekli sağlıksız görünür ve siz uygulamada hata ararsınız. wget alpine'da hazır gelir, ama distroless imajda o da yoktur.

    localhost yerine dış adres kullanmak. Kontrol konteynerin içinde çalıştığı için http://127.0.0.1:PORT yazmalısınız. Sunucunun genel IP'sini ya da servis adını yazmak, ağ katmanını da teste dahil eder ve yanlış yerde arıza raporlar.

    Yayınlanan port ile dinlenen portu karıştırmak. Compose'da ports: ["8080:3000"] yazdıysanız uygulama konteyner içinde 3000 portunu dinler. Sağlık kontrolünde 8080 yazmak her zaman başarısız olur.

    Aralığı gereksiz kısa tutmak. interval: 2s yazan bir kontrol, günde kırk binden fazla süreç açar. On beş servisli bir yığında bu, sunucunun boşta bile sürekli meşgul görünmesine yol açar. Çoğu servis için 10s ile 30s arası yeterlidir.

    Başlangıç periyodunu atlamak. Yavaş açılan servislerde start_period yoksa konteyner daha hazır olmadan sağlıksız damgası yer. Uygulamanızın en kötü senaryodaki açılış süresini ölçün ve üstüne pay bırakın.

    Sağlık uç noktasını kimlik doğrulamasız ve ağır yazmak. Uç nokta dışarıya açıksa ve her çağrıda ağır sorgular çalıştırıyorsa, basit bir istek seliyle veritabanınızı yorabilirler. Uç noktayı hafif tutun; mümkünse yalnızca yerel arayüzden erişilebilir yapın.

    Sıkça Sorulan Sorular#

    Docker healthcheck ücretsiz mi, ek araç gerekir mi#

    Sağlık kontrolü Docker'ın çekirdek özelliğidir; ek bir araç, ajan ya da lisans gerektirmez. Tek yapmanız gereken Dockerfile'a HEALTHCHECK satırı eklemek ya da Compose dosyasında healthcheck bloğu tanımlamaktır. Kontrol komutu konteynerin içinde çalıştığı için tek maliyeti, her aralıkta açılan küçük sürecin CPU tüketimidir; makul aralıklarla bu tüketim ölçülemeyecek kadar küçüktür.

    Sağlık kontrolü konteyneri yavaşlatır mı#

    Doğru yapılandırıldığında etkisi ihmal edilebilir düzeydedir. Yavaşlama iki durumda ortaya çıkar: aralığı saniyeler mertebesinde tutmak ve kontrol komutunu ağır yazmak. interval: 30s ile hafif bir HTTP isteği yapan bir kontrol günde sadece 2880 kez çalışır. Buna karşılık her çağrıda üç tabloyu tarayan bir uç nokta, üretimde gerçek yük yaratır ve harici bir servis yavaşladığında yanlış alarm üretir.

    Konteyner unhealthy olunca ne yapmalıyım#

    Önce nedenini görün: docker inspect --format='{{json .State.Health}}' konteyner_adi komutu son kontrollerin çıktısını ve çıkış kodlarını gösterir. Çıkış kodu 127 ise komut imajda yoktur, yani sorun uygulamada değil kontrol tanımındadır. Uygulama gerçekten cevap vermiyorsa konteyner loglarına bakın; bellek yetersizliği, veritabanı bağlantı hatası veya dosya tanımlayıcı sınırı en sık görülen üç nedendir.

    depends_on service_healthy neden çalışmıyor#

    En yaygın sebep, bağımlı olduğunuz servisin hiç sağlık kontrolü tanımlamamış olmasıdır; bu durumda Compose koşulu değerlendiremez ve hata verir. İkinci sebep sözdizimidir: kısa depends_on: [db] biçimi koşul kabul etmez, uzun biçimi kullanmanız gerekir. Üçüncü sebep, bekleme süresinin start_period ile birlikte hesaplanmasıdır; sağlık kontrolü hiçbir zaman healthy olmuyorsa bağımlı servis de hiç başlamaz.

    Sağlık kontrolünü Dockerfile'a mı Compose'a mı yazmalıyım#

    Kendi yazdığınız uygulamalar için Dockerfile daha doğrudur; kontrol imajın bir parçası olur ve imajı nerede çalıştırırsanız çalıştırın birlikte gelir. Üçüncü taraf imajlarda ise Compose bloğu tek pratik yoldur, çünkü onların Dockerfile'ını değiştiremezsiniz. Her ikisi de tanımlıysa Compose'daki tanım imajdan geleni ezer, bu da ortama göre farklı eşikler vermenizi sağlar.

    Sağlıksız konteynerler için alarm nasıl kurarım#

    En hafif yöntem docker events --filter event=health_status çıktısını izleyen küçük bir betiktir; durum değiştiğinde bir web kancası tetiklersiniz. Daha yapısal bir kurulumda konteyner metriklerini toplayan bir izleme yığını (örneğin Prometheus ve cAdvisor) sağlık durumunu da metrik olarak dışa aktarır ve eşik tabanlı alarm kurmanızı sağlar. Servislerinizin dışarıdan erişilebilirliğini de izlemek isterseniz panelimizdeki uptime uyarıları bu boşluğu tamamlar.

    Healthcheck ile readiness ve liveness farkı nedir#

    Kubernetes dünyasından gelen bu ayrım Docker'da tek bir mekanizmada toplanmıştır ama kavramsal olarak hâlâ geçerlidir. Liveness "süreç kurtarılamaz durumda mı, yeniden başlatılmalı mı" sorusunu, readiness ise "şu an trafik alabilir mi" sorusunu sorar. Docker healthcheck'i pratikte liveness gibi davranır. İkisini ayırmak istiyorsanız uygulamanızda iki ayrı uç nokta açıp sağlık kontrolünü hafif olana, yük dengeleyicinizi ise diğerine bağlayabilirsiniz.

    Kapanış#

    Sağlık kontrolü, birkaç satırlık yapılandırmayla karşılığında çok şey veren nadir özelliklerden biridir. Aklınızda tutmanız gereken dört alışkanlık şunlar: her servise gerçekten anlamlı bir kontrol yazın, komutun imajda var olduğunu doğrulayın, yavaş açılan servislere mutlaka start_period verin ve veritabanı bağımlılıklarını condition: service_healthy ile kurun. Bunlara bir de tek makinede unhealthy durumunun kendiliğinden yeniden başlatma tetiklemediği gerçeğini eklerseniz, gece yarısı "ayakta ama çalışmıyor" sürprizleriyle karşılaşma ihtimaliniz belirgin biçimde azalır.

    Konteynerlerinizi çalıştırdığınız altyapının da izlenebilir olması gerekir. Tam root erişimiyle kendi izleme yığınınızı kurmak için VDS ve bulut sunucu paketlerimiz uygundur; sunucu izleme, güncelleme ve müdahale işini devretmek isterseniz sunucu yönetimi hizmetimiz bunu üstlenir. Uygulamanızın dışarıdan erişilebilirliğini ölçmek için araçlar bölümümüzdeki uptime ve SLA hesaplayıcı da işinizi görür.

    DockerHealthcheckİzleme

    Uygulamaya geçmeye hazır mısınız?

    NVMe SSD, ücretsiz SSL ve %99.9 uptime garantisiyle Clou.TR hosting ve sunucu çözümleriyle projenizi hayata geçirin.