Docker & DevOps

    Docker Konteyner Kaynak Limitleri: CPU ve RAM

    Konteynerlere CPU ve bellek limiti koymanın yöntemleri, doğru değerin nasıl ölçüleceği ve OOM tuzakları.

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

    Docker'ın varsayılan davranışı şaşırtıcı derecede cömerttir: bir konteyner, host makinenin tüm belleğini ve tüm CPU'sunu kullanabilir. Bellek sızıntısı olan tek bir uygulama, aynı sunucudaki veritabanını, web sunucusunu ve hatta SSH oturumunuzu düşürebilir. Docker konteyner kaynak limitleri tam olarak bu senaryoyu önlemek için vardır ve tek satırlık bir ayarla bir sunucunun kaderini değiştirir: sorun tek bir konteynerin çökmesiyle sınırlı kalır, tüm makinenin donmasıyla değil.

    Bu rehberde bellek ve CPU limitlerinin nasıl çalıştığını, docker run ve docker compose tarafındaki karşılıklarını, rezervasyon ile sert limit arasındaki farkı, OOMKilled hatasının ne anlama geldiğini ve en önemlisi doğru değeri nasıl bulacağınızı ele alacağız. Tahminle limit koymak, çok düşükse uygulamayı sürekli öldürür, çok yüksekse hiçbir işe yaramaz; bu yüzden ölçüm bölümüne özel yer ayırdım.

    Limit Koymazsanız Ne Olur#

    Limitsiz bir konteyner, Linux için sıradan bir süreç grubudur ve sistemdeki tüm belleği talep edebilir. Bellek tükendiğinde çekirdeğin OOM (Out Of Memory) katili devreye girer ve en çok bellek kullanan süreci öldürür. Sorun şudur: bu süreç genellikle asıl suçlu değil, sunucudaki en büyük servistir — çoğu zaman veritabanınız. Yani sızıntı bir uygulamada olur, bedelini başka bir servis öder.

    CPU tarafında sonuç daha sinsidir. Tüm çekirdekleri doyuran bir konteyner sistemi çökertmez ama her şeyi yavaşlatır; web sunucusu yanıt süreleri artar, sağlık kontrolleri zaman aşımına uğrar ve orkestrasyon katmanı sağlıklı konteynerleri yeniden başlatmaya başlar. Bu durumda logda "neden yeniden başladı" sorusunun cevabı asla o konteynerin kendi logunda değildir.

    DurumLimitsiz sonuçLimitli sonuç
    Bellek sızıntısıHost belleği tükenir, rastgele servis ölürYalnızca o konteyner OOMKilled olur
    Sonsuz döngüTüm çekirdekler dolar, sistem yavaşlarKonteyner kendi payıyla sınırlı kalır
    Ani trafik artışıDiğer servisler etkilenirEtki tek servise sıkışır
    Hata ayıklamaSuçluyu bulmak zorÖlen konteyner doğrudan işaret eder

    Bu son satır sıkça gözden kaçar: limit koymak yalnızca bir koruma değil, aynı zamanda bir teşhis aracıdır. Limitli bir kurulumda "hangi servis sorunlu" sorusunun cevabı kendiliğinden ortaya çıkar.

    Bellek Limitleri#

    Bellek tarafında üç ayrı kavram vardır ve karıştırılmaları yaygındır.

    Sert limit (--memory) aşılamaz bir tavandır. Konteynerdeki süreçler bu değeri aşmaya çalıştığında çekirdek konteyner içindeki bir süreci öldürür ve konteynerin çıkış durumu OOMKilled olarak işaretlenir. Rezervasyon (--memory-reservation) ise yumuşak bir alt sınırdır: sistemde bellek bolken konteyner bu değeri aşabilir, baskı oluştuğunda çekirdek onu bu değere doğru sıkıştırmaya çalışır. Takas (--memory-swap) bellek ve takas alanının toplamını belirtir.

    # 512 MB sert limit, takas kapalı (swap = memory olduğunda takas kullanılmaz)
    docker run -d --name uygulama \
      --memory=512m \
      --memory-swap=512m \
      --memory-reservation=256m \
      uygulama:1.0
    
    # Limitin gerçekten uygulandığını doğrula
    docker inspect uygulama --format '{{.HostConfig.Memory}} {{.HostConfig.MemorySwap}}'
    # 536870912 536870912
    

    --memory-swap değerini --memory ile eşitlemek üretimde genellikle doğru tercihtir. Takas açık bırakılırsa bellek sızıntısı olan bir uygulama diske taşar; öldürülmez ama korkunç derecede yavaşlar ve disk G/Ç'sini doyurarak diğer servisleri de etkiler. Hızlıca ölmesi, saatlerce sürünmesinden iyidir.

    Uygulamanızın kendi bellek ayarları da limitle uyumlu olmalıdır. Konteyner içindeki bir süreç, host'un toplam belleğini görür ve buna göre kendini ayarlayabilir; bu, limitin çok altında bir değere göre planlanmış gibi görünüp aslında limiti aşan davranışlara yol açar. Java tabanlı uygulamalarda yığın boyutu, veritabanlarında tampon havuzu, PHP-FPM'de işçi sayısı bu sınıfa girer — hepsini konteyner limitine göre ayarlamak gerekir.

    CPU Limitleri#

    CPU tarafında üç mekanizma vardır ve amaçları farklıdır.

    --cpus en anlaşılır olanıdır: konteynerin kullanabileceği toplam çekirdek sayısını ondalıklı olarak verir. --cpus=1.5 demek, konteynerin her saniyede en fazla 1,5 çekirdek-saniye işlem yapabilmesi demektir. Bu bir tavandır ve sistem boşta olsa bile aşılmaz.

    --cpu-shares ise göreli bir ağırlıktır ve yalnızca rekabet anında devreye girer. Varsayılan 1024'tür; bir konteynere 2048 verirseniz CPU doyduğunda iki kat pay alır, sistem boştaysa hiçbir kısıt hissetmez. Kritik servisleri önceliklendirmek için idealdir.

    --cpuset-cpus konteyneri belirli fiziksel çekirdeklere sabitler. Önbellek yerelliği önemli olan iş yüklerinde ve gürültülü komşuları ayırmak için kullanılır.

    # Tavan: en fazla 1.5 çekirdek
    docker run -d --cpus=1.5 uygulama:1.0
    
    # Öncelik: rekabet anında iki kat pay
    docker run -d --cpu-shares=2048 kritik-servis:1.0
    
    # Sabitleme: yalnızca 0 ve 1 numaralı çekirdekler
    docker run -d --cpuset-cpus="0,1" hesaplama:1.0
    

    Pratikte çoğu kurulum için doğru araç --cpus'tur. --cpu-shares sadece aynı makinede öncelik farkı olan servisler varsa anlamlıdır; --cpuset-cpus ise özel durumlar dışında gereksiz karmaşıklık getirir. Bir uyarı: --cpus=1 vermek uygulamanın tek iş parçacığıyla çalışacağı anlamına gelmez, yalnızca toplam işlem bütçesini sınırlar; çok iş parçacıklı bir uygulama dört çekirdekte %25'er kullanarak aynı bütçeyi harcayabilir.

    docker compose ile Limit Tanımlama#

    Compose tarafında limitlerin nereye yazılacağı sık karıştırılan bir konudur. Modern Compose sürümlerinde deploy.resources bloğu tek başına docker compose up ile de uygulanır; ayrıca eski tarz kısayollar da desteklenir:

    services:
      web:
        image: uygulama:1.0
        restart: unless-stopped
        deploy:
          resources:
            limits:
              cpus: "1.5"
              memory: 512M
            reservations:
              cpus: "0.25"
              memory: 256M
    
      db:
        image: postgres:16-alpine
        restart: unless-stopped
        # Eski tarz kısayollar da çalışır
        mem_limit: 1g
        memswap_limit: 1g
        cpus: 2.0
        volumes:
          - pgdata:/var/lib/postgresql/data
    
    volumes:
      pgdata:
    

    Limitlerin gerçekten uygulandığını varsaymayın, doğrulayın. Compose dosyasını yazıp konteyneri başlattıktan sonra tek komutla kontrol edin:

    docker inspect $(docker compose ps -q web) \
      --format 'Memory: {{.HostConfig.Memory}} | NanoCpus: {{.HostConfig.NanoCpus}}'
    # Memory: 536870912 | NanoCpus: 1500000000
    # Sıfır dönüyorsa limit UYGULANMAMIŞ demektir
    

    Sıfır dönüyorsa iki olası neden vardır: yazım hatası (memory: 512M yerine memory: 512 gibi) ya da çekirdek tarafında cgroup desteğinin eksik olması. İkincisi özellikle rootless kurulumlarda karşınıza çıkar; ayrıntılar için Rootless Docker kurulumu yazısına bakın.

    Gerçek Kullanımı Ölçmek#

    Limit değerini tahminle koymak iki yönden de zararlıdır. En pratik yöntem, uygulamayı bir süre limitsiz ya da bol limitle çalıştırıp gerçek tüketimi ölçmek, sonra bunun üzerine güvenlik payı eklemektir.

    # Canlı tüketim tablosu
    docker stats --no-stream
    # NAME   CPU %   MEM USAGE / LIMIT     MEM %   NET I/O
    # web    12.40%  186.2MiB / 512MiB     36.37%  1.2MB / 840kB
    # db     3.10%   402.5MiB / 1GiB       39.31%  640kB / 2.1MB
    
    # Betikle işlemek için sabit biçim
    docker stats --no-stream --format '{{.Name}};{{.CPUPerc}};{{.MemUsage}}'
    

    Tek bir anlık ölçüm yanıltıcıdır; tepe noktalarını yakalamak için bir süre örnek toplayın. Basit bir yöntem, birkaç saat boyunca dakikada bir kayıt almaktır:

    # Yoğun saatlerde 2 saatlik örnekleme
    for i in $(seq 1 120); do
      docker stats --no-stream --format '{{.Name}};{{.MemUsage}};{{.CPUPerc}}' >> /tmp/olcum.csv
      sleep 60
    done
    
    # En yüksek bellek kullanımını gör
    sort -t';' -k2 -h /tmp/olcum.csv | tail -5
    

    Toplanan veriye göre şu basit kuralı uygulayabilirsiniz: bellek limiti = ölçülen tepe değerin yaklaşık 1,5 katı, CPU limiti = ortalama kullanımın 2-3 katı. Bellekte pay bırakmak önemlidir çünkü limit aşıldığında süreç ölür; CPU'da ise limit aşılınca yalnızca yavaşlama olur, bu yüzden CPU limitinde biraz daha cesur davranabilirsiniz.

    Ayrıca sunucunun toplam kapasitesini aşmamaya dikkat edin. Tüm konteynerlerin bellek limitleri toplamı fiziksel belleği geçiyorsa, hepsi aynı anda tavana dayandığında yine host çöker. Kabaca fiziksel belleğin %80'ini hedefleyip kalanını işletim sistemi ve dosya önbelleği için ayırın.

    Sık Yapılan Hatalar#

    OOMKilled ile karşılaşmak — Konteyner beklenmedik şekilde durduysa çıkış nedenini kontrol edin:

    docker inspect uygulama --format '{{.State.OOMKilled}} {{.State.ExitCode}}'
    # true 137  → bellek limiti aşıldı
    

    Çıkış kodu 137, SIGKILL ile öldürülme anlamına gelir ve OOMKilled: true ile birlikte geldiğinde teşhis kesindir. Çözüm ya limiti yükseltmek ya da uygulamanın bellek kullanımını düşürmektir. Limiti yükseltmeden önce gerçekten sızıntı olup olmadığına bakın; sürekli artan ve hiç düşmeyen bir bellek eğrisi sızıntının işaretidir ve limit yükseltmek sadece çöküşü erteler.

    Yeniden başlatma döngüsü — Limit çok düşük olduğunda konteyner başlar, belleği doldurur, öldürülür, restart politikası onu tekrar başlatır ve döngü kurulur. Bu döngünün diğer nedenlerini konteyner sürekli yeniden başlıyor yazısında ele aldık.

    Uygulama limiti görmüyor — Konteyner içinde free -m komutu host'un toplam belleğini gösterir, limitinizi değil. Bu yüzden kendi kendini ayarlayan uygulamalar yanlış hesap yapar. Doğru değeri cgroup dosyasından okuyabilirsiniz:

    docker exec uygulama cat /sys/fs/cgroup/memory.max
    # 536870912 → 512 MB
    

    Takas açık unutulmuş--memory verip --memory-swap vermezseniz konteyner limitin iki katı kadar takas kullanabilir. Sızıntı hâlinde bu, ölmek yerine diski doyurarak sürünmeye yol açar. İkisini eşitleyin.

    Compose'da limit yazılmış ama uygulanmamış — Yukarıdaki docker inspect doğrulamasını her zaman yapın. Sessizce yok sayılan bir limit, hiç limit olmamasından daha tehlikelidir çünkü korunduğunuzu sanırsınız.

    Veritabanına düşük limit vermek — Veritabanları belleği agresif kullanır ve bu normaldir; tampon havuzu büyüdükçe sorgular hızlanır. Veritabanı konteynerine limit koyarken uygulamanın kendi bellek ayarlarını da birlikte düşürmelisiniz, yoksa veritabanı limiti aşıp öldürülür.

    Sıkça Sorulan Sorular#

    Docker konteynerine limit koymazsam ne olur#

    Konteyner host makinenin tüm belleğini ve CPU'sunu kullanabilir. Bellek tükendiğinde Linux'un OOM katili devreye girer ve genellikle sorunlu konteyneri değil, sistemdeki en çok bellek tüketen süreci öldürür — bu çoğu zaman veritabanınızdır. Yani tek bir hatalı uygulama, ilgisiz servisleri düşürebilir. Üretimde limit koymak isteğe bağlı bir iyileştirme değil, temel bir gerekliliktir.

    Bellek limitini ne kadar vermeliyim#

    Uygulamayı gerçek yük altında bir süre çalıştırıp docker stats ile tepe bellek kullanımını ölçün, bu değerin yaklaşık 1,5 katını limit olarak verin. Ölçüm yapmadan tahmin etmek iki yönde de zarar verir: çok düşük limit uygulamayı düzenli olarak öldürür, çok yüksek limit ise koruma sağlamaz. Tüm konteynerlerin limitleri toplamının fiziksel belleğin %80'ini aşmamasına da dikkat edin.

    OOMKilled hatasını nasıl çözerim#

    Önce gerçekten bellek yetersizliği mi yoksa sızıntı mı olduğunu belirleyin. Kullanım grafiği sürekli yükselip hiç düşmüyorsa sızıntı vardır ve limiti yükseltmek yalnızca çöküşü erteler. Gerçek ihtiyaç limitin üzerindeyse limiti kademeli artırın. Ayrıca uygulamanın kendi bellek ayarlarını (yığın boyutu, işçi sayısı, tampon havuzu) konteyner limitine göre düşürmek çoğu zaman limiti yükseltmekten daha doğru çözümdür.

    CPU limiti uygulamayı yavaşlatır mı#

    Yalnızca uygulamanız verdiğiniz bütçeyi aşmaya çalıştığında yavaşlar; bunun altında kaldığı sürece hiçbir fark hissetmez. Yani limit bir vergi değil, tavan koymadır. Yine de tavanı gerçek ihtiyacın çok altına koyarsanız istek kuyrukları büyür ve yanıt süreleri artar. Ortalama kullanımın 2-3 katı, çoğu web uygulaması için makul bir başlangıç noktasıdır.

    docker stats yeterli mi, başka nasıl izlerim#

    docker stats anlık durum için pratiktir ama geçmişi tutmaz. Uzun vadeli izleme için bir metrik toplayıcı kurup zaman serisi saklamak gerekir; böylece haftalık desenleri ve yavaş büyüyen sızıntıları görebilirsiniz. Küçük kurulumlarda yukarıdaki gibi basit bir döngüyle CSV toplamak bile pek çok soruyu cevaplar ve hiçbir ek servis gerektirmez.

    Limitler rootless Docker'da çalışır mı#

    Çalışır ama koşulludur: cgroup v2 ve systemd delegasyonunun etkin olması gerekir. Modern dağıtımlarda bu hazır gelir, eski çekirdeklerde ise limitler sessizce yok sayılabilir. Bu yüzden rootless kurulumda limit tanımladıktan sonra docker inspect ile değerin gerçekten uygulandığını doğrulamak, klasik kuruluma göre daha da önemlidir.

    Kapanış#

    Kaynak limitleri konusunda akılda kalması gereken birkaç şey var: her üretim konteynerine mutlaka bellek limiti koyun, --memory-swap değerini --memory ile eşitleyip takas kaynaklı sürünmeyi kapatın, CPU'da --cpus ile tavan belirleyin, değeri tahminle değil docker stats ölçümüyle seçin ve limitin gerçekten uygulandığını docker inspect ile doğrulayın. Bir de uygulamanın kendi bellek ayarlarını konteyner limitine göre düşürmeyi unutmayın; ikisi çeliştiğinde kaybeden her zaman konteyner olur.

    Limit planlaması yaparken sunucunuzun toplam kapasitesi belirleyici olur; ihtiyacınız büyüdüğünde ölçeklenebilir bulut sunucu veya sabit kaynaklı VDS paketlerimiz arasında seçim yapabilir, yoğun ve öngörülebilir yükler için dedicated sunucu seçeneğine bakabilirsiniz. Kaynak izleme ve kapasite planlamasını devretmek isterseniz sunucu yönetimi hizmetimiz bu takibi üstlenir.

    DockerPerformansLinux

    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.