Gece MySQL kendiliğinden kapanmış, site "Error establishing a database connection" veriyor ya da bir Node.js uygulaması sebepsiz yere yeniden başlamış. Uygulama loglarında hiçbir hata yok. Böyle durumlarda ilk şüphelenmeniz gereken, Linux çekirdeğinin OOM killer (Out Of Memory killer) mekanizmasıdır: bellek tamamen tükendiğinde çekirdek, sistemi ayakta tutmak için bir süreci seçip zorla sonlandırır.

Bu rehberde OOM olayının loglarda nasıl göründüğünü, dmesg ve journalctl ile nasıl bulunacağını, çekirdeğin kurbanı nasıl seçtiğini (oom_score ve oom_score_adj), MySQL'in neden çoğu zaman ilk hedef olduğunu, systemd ile servis bazında bellek sınırı koymayı ve kalıcı çözümleri anlatıyoruz. Komutlar Ubuntu 24.04 LTS içindir.

Belirtiler

Belirtiler: Bir servis uyarısız kapanıyor, systemctl status içinde signal=KILL veya..., Uygulama logunda kapanış nedeni yazmıyor; çünkü SIGKILL sinyali yakalanamaz..., Olaydan...
  • Bir servis uyarısız kapanıyor, systemctl status içinde signal=KILL veya oom-kill ifadesi görünüyor.
  • Uygulama logunda kapanış nedeni yazmıyor; çünkü SIGKILL sinyali yakalanamaz, süreç kendi logunu yazamadan ölür.
  • Olaydan önce sunucu belirgin şekilde yavaşlıyor, swap yoğun kullanılıyor.
  • Sorun hep yoğun saatlerde veya belirli bir cron işi sırasında oluyor.

Adım 1: OOM olayını loglarda bulun

OOM killer çekirdek mesajı yazar. Linux çekirdeği kaynak kodu: mm/oom_kill.c dosyasındaki mesaj biçimlerine göre aranacak ifadeler şunlardır:

bash
sudo dmesg -T | grep -iE "out of memory|oom-killer|killed process"
sudo journalctl -k --since "2 days ago" | grep -iE "out of memory|oom-killer|killed process"
# Bir servis özelinde:
sudo journalctl -u mysql --since today | tail -n 30

Tipik bir olay iki ana satırdan oluşur (sayılar örnektir):

text
php-fpm8.3 invoked oom-killer: gfp_mask=0x140cca(GFP_HIGHUSER_MOVABLE|__GFP_COMP), order=0, oom_score_adj=0
...
Out of memory: Killed process 1234 (mysqld) total-vm:2457600kB, anon-rss:1310720kB, file-rss:0kB, shmem-rss:0kB, UID:110 pgtables:3200kB oom_score_adj:0
ParçaAnlamı
... invoked oom-killerBellek isteyip karşılanamayan süreç. Kurban olmak zorunda değildir.
Out of memory: Killed processSistem genelinde bellek bitti ve bu süreç öldürüldü.
Memory cgroup out of memorySistem değil, bir cgroup sınırı (örneğin systemd MemoryMax veya bir Docker konteyner limiti) aşıldı.
anon-rssSürecin kullandığı anonim bellek (heap, buffer pool gibi); kurbanın ne kadar yer tuttuğunu gösterir.
oom_score_adjKurbanın ayar değeri; aşağıda anlatılıyor.

Adım 2: Çekirdek kurbanı nasıl seçer?

proc_pid_oom_score(5) kılavuz sayfasına göre /proc/[pid]/oom_score, çekirdeğin o sürece OOM için verdiği güncel puandır; puan ne kadar yüksekse sürecin seçilme olasılığı o kadar yüksektir ve puanın temeli sürecin kullandığı bellek miktarıdır. proc_pid_oom_score_adj(5) sayfasına göre oom_score_adj değeri bu puana eklenir; en düşük değer olan -1000, o süreç için OOM ile öldürmeyi fiilen kapatır.

bash
# En yüksek OOM puanına sahip 10 süreç
for p in /proc/[0-9]*; do
  printf "%s %s %s\n" "$(cat $p/oom_score 2>/dev/null)" "${p#/proc/}" "$(cat $p/comm 2>/dev/null)"
done | sort -rn | head -n 10

# Belirli bir sürecin ayar değeri
cat /proc/$(pgrep -o mysqld)/oom_score_adj

MySQL neden hep ilk ölen?

MySQL neden hep ilk ölen? Paylaşılan sunucuda, PHP-FPM, Bellek sızıntısı

Puan büyük ölçüde bellek kullanımına dayandığı için, sunucudaki en büyük süreç genellikle veritabanıdır. MySQL/InnoDB, verileri ve indeksleri buffer pool adı verilen bellek alanında tutar. MySQL 8.4 Reference Manual: InnoDB Buffer Pool sayfası, yalnızca veritabanına ayrılmış sunucularda fiziksel belleğin yüzde 80'ine kadarının buffer pool'a verildiğini belirtir. Aynı sunucuda Nginx, PHP-FPM, Redis ve başka servisler de çalışıyorsa bu oran fazladır ve diğer servisler büyüdükçe bellek biter; çekirdek de en büyük süreci, yani MySQL'i seçer.

bash
sudo mysql -e "SELECT @@innodb_buffer_pool_size/1024/1024 AS buffer_pool_mb;"
free -h
  • Paylaşılan sunucuda buffer pool'u, diğer servislerin ihtiyacını çıkardıktan sonra kalan belleğe göre boyutlandırın.
  • PHP-FPM işçi sayısı (pm.max_children) çarpı işçi başına bellek, en sık gözden kaçan tüketicidir. Hesap yöntemi PHP-FPM yapılandırma yazısında.
  • Bellek sızıntısı olan uygulamalarda PHP için pm.max_requests (PHP belgesine göre işçiyi belirli sayıda istekten sonra yeniden başlatır; varsayılan 0, yani kapalı) yardımcı olabilir.

Adım 3: Kimin ne kadar bellek kullandığını görün

bash
free -h
ps -eo pid,user,rss,%mem,cmd --sort=-rss | head -n 15   # rss KiB cinsinden
systemd-cgtop -m                                       # servis bazında bellek

free -h çıktısında available sütunu, uygulamaların swap'a düşmeden kullanabileceği tahmini belleği gösterir; free sütununun küçük olması tek başına sorun değildir, çünkü Linux boş belleği önbellek için kullanır. Anlık ölçümün yanında vmstat 2 ile si/so sütunlarını izleyin; ayrıntılar load average rehberinde.

Adım 4: systemd ile servis bazında bellek sınırı

Sorunlu bir servisin tüm sunucuyu çökertmesini önlemek için onu kendi bellek sınırına hapsedebilirsiniz. systemd.resource-control(5) belgesine göre MemoryHigh= bir kısma (throttling) sınırıdır: aşılırsa süreçler ciddi şekilde yavaşlatılır. MemoryMax= ise mutlak sınırdır: kullanım bu sınırın altında tutulamazsa OOM killer yalnızca o birimin içinde çalışır. Belge, ana kontrol olarak MemoryHigh= kullanılmasını ve MemoryMax='ın son savunma hattı olarak tutulmasını önerir. K, M, G, T son ekleri ve fiziksel belleğe göre yüzde değerleri kabul edilir.

bash
sudo systemctl edit php8.3-fpm
ini
[Service]
MemoryHigh=1500M
MemoryMax=2G
bash
sudo systemctl restart php8.3-fpm
systemctl show php8.3-fpm -p MemoryHigh -p MemoryMax
systemctl status php8.3-fpm   # Memory satırında sınır görünür

Değerler örnektir; kendi sunucunuzun RAM'ine ve ölçtüğünüz kullanıma göre belirleyin. Bu sınır aşıldığında loglarda "Memory cgroup out of memory" mesajını görürsünüz ve yalnızca o servis etkilenir, veritabanınız ayakta kalır. Drop-in dosyaları ve servis yapısı için systemd servis dosyası rehberi.

Adım 5: Kritik servisi korumak: OOMScoreAdjust

systemd.exec(5) belgesine göre OOMScoreAdjust=, birimin süreçleri için -1000 ile 1000 arasında bir değer alır; -1000 OOM ile öldürmeyi kapatır, 1000 ise bellek baskısında öldürülmeyi çok olası kılar. Veritabanının daha geç seçilmesini isterseniz:

bash
sudo systemctl edit mysql
# Açılan dosyaya:
# [Service]
# OOMScoreAdjust=-500
sudo systemctl restart mysql

Kalıcı çözümler

Kalıcı çözümler: Swap hiç yok, PHP-FPM işçi sayısı fazla, MySQL buffer pool büyük, Bellek sızıntısı
NedenÇözüm
Swap hiç yokKüçük bir swap, ani sıçramalarda tampon sağlar: Sunucuda swap alanı. Kernel belgelerine göre vm.swappiness varsayılanı 60'tır.
PHP-FPM işçi sayısı fazlapm.max_children değerini işçi başı belleğe göre hesaplayın: PHP-FPM yapılandırma
MySQL buffer pool büyükDiğer servisleri hesaba katarak küçültün
Bellek sızıntısıUygulamayı düzeltin; geçici olarak pm.max_requests veya servis bazında MemoryMax
Gerçekten RAM yetmiyorDaha fazla RAM'li plana geçin veya veritabanını ayrı sunucuya taşıyın

Önleme ve izleme

Sık Sorulan Sorular

OOM killer'ı tamamen kapatabilir miyim?

Önerilmez. OOM killer, bellek bittiğinde sistemin tamamen kilitlenmesini önleyen son mekanizmadır. Kapatmaya çalışmak yerine bellek tüketimini sınırlayın ve kapasiteyi doğru planlayın.

Swap eklemek OOM'u tamamen çözer mi?

Kısa süreli sıçramaları yumuşatır, ama sürekli bellek açığını çözmez. Swap aktif olarak kullanılıyorsa sunucu ciddi şekilde yavaşlar; bu da ayrı bir sorundur. Swap'ı tampon olarak görün, RAM yerine değil.

Docker konteynerim neden öldü?

Konteynere bellek sınırı verdiyseniz ve aşıldıysa çekirdek loglarında "Memory cgroup out of memory" görürsünüz. Bu durumda sorun sunucunun değil konteynerin sınırıdır; sınırı uygulamanın gerçek ihtiyacına göre ayarlayın.

Hangi servis öldürüldü, sonradan nasıl anlarım?

journalctl -k çıktısında "Killed process" satırını arayın; PID ve süreç adı oradadır. Kalıcı log ayarları ve log okuma için Linux loglarını okuma ve logrotate rehberi yazılarına bakın.

Yazılım ve hizmet adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen projeler veya şirketlerle ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Load average nedir · CPU yüzde 100 tespiti · inode doldu hatası · logrotate rehberi · Sunucuda swap alanı · PHP-FPM yapılandırma · systemd servis dosyası