Server

systemd Servisi Sürekli Yeniden Başlıyor: Teşhis

Hızlı özet

systemd servisi sürekli yeniden başlıyorsa önce servis durumunu ve journal kayıtlarını inceleyin; hemen daha uzun bir RestartSec değeri eklemek asıl hatayı gizleyebilir.

  • systemctl status ile son çıkış kodunu, sinyali ve systemd’nin verdiği uyarıyı görün.
  • journalctl -u kayıtlarında uygulamanın neden kapandığını, eksik ortam değişkenini veya port çakışmasını arayın.
  • Uygulama hatasını düzelttikten sonra servis dosyasını yeniden yükleyin ve kontrollü biçimde başlatın.
  • Hız sınırlamasına takıldıysanız reset-failed kullanın; bu komut uygulama arızasını düzeltmez.

Çalışan bir uygulama birkaç saniyede bir kapanıyor ve yeniden açılıyorsa sorun yalnızca systemd katmanında aranmayabilir. systemd, servis tanımında Restart= etkin olduğu için kapanan süreci tekrar çalıştırıyor olabilir. Asıl neden; yanlış komut, eksik dosya, hatalı izin, kullanılamayan port, eksik ortam değişkeni veya uygulamanın kendi başlangıç hatası olabilir.

Bu rehberde teşhisi systemd katmanından uygulama katmanına doğru daraltacaksınız. Önce servis adını ve gerçek çalışma biçimini doğrulayın, ardından günlük kaydındaki çıkış kodunu yorumlayın. Değişiklik yapmadan önce mevcut unit dosyasını ve uygulama yapılandırmasını yedekleyin; her düzeltmeden sonra servisin gerçekten sağlıklı kaldığını gözlemleyin.

Yeniden başlama döngüsünü doğrulayın

İlk belirti genellikle systemctl status çıktısında görülür. Servis kısa süreliğine active (running) görünür, ardından failed olur veya tekrar başlatılır. Komutu servis adınızla çalıştırın:

sudo systemctl status ornek-servis.service --no-pager -l

Buradaki ornek-servis.service ifadesini gerçek unit adıyla değiştirin. Önkoşul olarak sunucuda sudo yetkiniz ve servisin tanımlı olduğu systemd tabanlı bir işletim sistemi bulunmalıdır. Çıktıda özellikle şu satırlara bakın:

  • Active: Servisin mevcut durumunu ve son durum değişikliğinin zamanını gösterir.
  • Main PID: Uygulamanın ana işlem kimliğidir. Her denemede değişiyorsa süreç yeniden oluşturuluyor olabilir.
  • code=exited ve status=: Uygulamanın normal bir çıkışla mı, hata koduyla mı kapandığını gösterir.
  • code=killed ve sinyal bilgisi: Sürecin bir sinyal ile sonlandırıldığını düşündürür.
  • Start request repeated too quickly: systemd’nin kısa sürede çok sayıda başarısız başlatma gördüğünü ve yeni denemeyi geçici olarak sınırladığını belirtir.

Bu son uyarı kök neden değildir. Başarısız başlangıçların sonucunu ve systemd’nin koruma davranışını gösterir. Gerçek hata çoğunlukla daha önceki uygulama log satırlarında bulunur.

Unit dosyasının hangi kaynaktan geldiğini görün

Bir servis dosyası dağıtım tarafından, paket yöneticisiyle veya sizin oluşturduğunuz bir override ile gelmiş olabilir. Etkin yapılandırmayı ve override dosyalarını birlikte inceleyin:

sudo systemctl cat ornek-servis.service

Çalışma dizini, kullanıcı, başlatma komutu, ortam dosyası ve yeniden başlatma ayarlarını ayrıca listelemek için:

sudo systemctl show ornek-servis.service -p FragmentPath -p DropInPaths -p ExecStart -p User -p Group -p WorkingDirectory -p EnvironmentFiles -p Restart -p RestartSec

systemctl cat çıktısı, ana unit dosyasını ve /etc/systemd/system/ornek-servis.service.d/ altındaki ek ayarları görmenizi sağlar. Aynı ayarın birden fazla yerde tanımlanması teşhisi zorlaştırabileceğinden, etkin dosya ile override dosyasını birlikte değerlendirin.

Dikkat

Paket tarafından kurulan unit dosyasını doğrudan değiştirmek güncelleme sırasında kaybolabilir. Kalıcı bir değişiklik gerekiyorsa önce mevcut tanımı yedekleyin, ardından mümkünse systemctl edit ile dar kapsamlı bir override oluşturun. Uygulamanın nasıl başlatıldığını değiştiren her düzenlemenin geri dönüşünü not edin.

Journal kayıtlarında gerçek hatayı bulun

Servis durumundaki son birkaç satır çoğu zaman yeterli değildir. Başlangıç denemelerinin tamamını zaman bilgisiyle birlikte inceleyin:

sudo journalctl -u ornek-servis.service -b --no-pager -n 200

-b mevcut sistem açılışındaki kayıtlarla sınırlar, -n 200 ise son 200 satırı getirir. Sorun önceki açılışta başladıysa -b -1 ile bir önceki açılışa bakabilirsiniz. Belirli bir zaman aralığını filtrelemek için:

sudo journalctl -u ornek-servis.service --since "30 minutes ago" --no-pager

Kayıtları okurken systemd’nin servis başlatıldı mesajından önce veya sonra gelen uygulama satırlarına odaklanın. Örneğin address already in use portun başka bir süreçte olduğunu, permission denied dosya veya dizin erişiminin reddedildiğini, no such file or directory ise komutun, çalışma dizininin veya beklenen dosyanın bulunamadığını gösterebilir.

Çıkış kodu ve sinyal nasıl yorumlanır?

status=1/FAILURE gibi bir değer, uygulamanın hata koduyla çıktığını gösterir; fakat tek başına hatanın nedenini söylemez. Hata kodunu uygulamanın kendi dokümantasyonu ve journal satırlarıyla eşleştirin. status=0/SUCCESS görülmesine rağmen servis yeniden başlıyorsa uygulama kendisini normal biçimde sonlandırıyor olabilir ve unit dosyasında Restart=always bulunabilir.

SIGTERM veya SIGINT, kontrollü durdurma isteğiyle uyumlu olabilir. SIGKILL ise zaman aşımı, dışarıdan sonlandırma veya bellek baskısı gibi nedenlerle ilişkili olabilir. Kesin yorum için kernel kayıtlarını da kontrol edin:

sudo journalctl -k --since "30 minutes ago" --no-pager | grep -Ei "oom|out of memory|killed process"

Bu komut boş sonuç verirse bellek sorunu kesin olarak dışlanmış olmaz; yalnızca kernel günlüğünde bu ifadelerin bulunmadığını gösterir. Servisin kendi uygulama logları ayrı bir dosyada tutuluyorsa o dosyanın izinlerini ve aynı zaman aralığını da kontrol edin.

Başlatma komutunu systemd dışından sınayın

Journal kaydında uygulama hatası görünüyorsa en dar teşhis, systemd’nin kullandığı komutu aynı kullanıcı ve çalışma diziniyle kontrollü olarak çalıştırmaktır. Önce komutu systemctl cat çıktısından alın. Uygulamayı root olarak denemek yanıltıcı olabilir; servis hangi kullanıcıyla çalışıyorsa aynı kullanıcıyı kullanın.

sudo -u uygulama-kullanicisi sh -c 'cd /srv/ornek-uygulama && exec /usr/bin/ornek-uygulama --config /etc/ornek-uygulama/config.yml'

Bu örnekte uygulama-kullanicisi, dizin ve komut sizin unit dosyanıza göre değiştirilmelidir. Canlı uygulamada veri yazan veya migration çalıştıran bir komut kullanmayın; yalnızca normal başlatma komutunu uygun bakım planı içinde test edin. Komut interaktif ortam değişkenlerine ihtiyaç duyuyorsa systemd servisinde bu değişkenlerin tanımlı olup olmadığını ayrıca inceleyin.

Terminalde başarısız olan bir komut uygulama hatasına işaret eder; ancak terminalde çalışan bir komutun systemd altında da çalışacağı varsayılmamalıdır. İki çalışma ortamı arasındaki farklar çoğunlukla şunlardır:

  • Servis farklı bir kullanıcıyla çalışır ve dosya ya da socket erişimine sahip değildir.
  • WorkingDirectory beklenen dizin değildir; göreli yollar yanlış yere çözülür.
  • Shell profilinde bulunan PATH, HOME veya uygulama değişkenleri servise aktarılmaz.
  • Gizli bilgiler veya yapılandırma dosyaları servis kullanıcısı için okunabilir değildir.
  • Terminalde çalışan süreç portu zaten kullanıyordur ve yeni systemd denemesi aynı porta bağlanamaz.

Teşhis amacıyla tüm ortamı genişletmek yerine yalnızca uygulamanın ihtiyaç duyduğu değişkenleri unit dosyasına veya bir EnvironmentFile dosyasına ekleyin. Secret dosyalarının izinlerini genişletmek yerine servis kullanıcısının gerekli dosyayı okuyabildiğini doğrulayın.

Sık görülen kök nedenleri daraltın

Yanlış ExecStart veya eksik dosya

ExecStart satırındaki ikili dosya yolu, çalışma dizini ve argümanlar birebir kontrol edilmelidir. Sanal ortamla kurulan Python uygulamalarında sistemdeki farklı Python yorumlayıcısı, Node.js uygulamalarında farklı Node yolu kullanılabilir. Komutun mutlak yolunu ve dosyanın varlığını kontrol edin:

command -v /usr/bin/ornek-uygulama
sudo -u uygulama-kullanicisi test -x /usr/bin/ornek-uygulama
sudo -u uygulama-kullanicisi test -r /etc/ornek-uygulama/config.yml

Yanlış yolu düzeltmeden önce unit dosyasının nereden geldiğini belirleyin. Yalnızca eksik veya hatalı argümanı değiştirin; çalışır durumdaki diğer kaynak yönetimi ayarlarını aynı anda değiştirmeyin.

Port çakışması ve bağımlılık sırası

Uygulama bir TCP portuna bağlanırken address already in use veriyorsa önce portu hangi sürecin kullandığını bulun:

sudo ss -ltnp | grep ':8080'

8080 yerine uygulamanın gerçek portunu yazın. Portu kullanan süreç aynı uygulamanın eski bir kopyasıysa onu rastgele sonlandırmak yerine nasıl başlatıldığını ve hangi unit tarafından yönetildiğini belirleyin. Port başka bir servis için ayrılmışsa uygulama portunu değiştirmek yerine çakışan yapılandırmayı değerlendirin.

Veritabanı, socket veya ağ servisi başlangıçta hazır değilse uygulama birkaç kez başarısız olabilir. After= yalnızca başlatma sırasını düzenler; bir servisin gerçekten hazır olduğunu garanti etmez. Uygulama hazır olma kontrolü destekliyorsa uygun bir health-check veya uygulamanın yeniden deneme davranışı kullanılmalıdır. Sırf döngüyü gizlemek için uzun bir gecikme eklemek bağımlılık problemini çözmez.

İzinler ve çalışma dizini

Unit dosyasında User= tanımlıysa uygulama dosya sistemi erişimini o kullanıcı üzerinden yapar. Dizinlerin yalnızca dosya izinlerine değil, üst dizinlerdeki geçiş (x) iznine de ihtiyacı vardır. Teşhis sırasında aşağıdaki komutla yolu inceleyin:

namei -l /srv/ornek-uygulama/config.yml

Bulduğunuz tek dosya veya dizin için en dar izin düzeltmesini yapın. Yapılandırmayı herkese okunabilir hale getirmek, özellikle kimlik bilgileri içeriyorsa, güvenli bir çözüm değildir. Değişiklikten sonra aynı servis kullanıcısıyla okuma testini tekrarlayın.

FPM, CLI ve kuyruk işçilerini karıştırmayın

Bir web uygulamasında PHP-FPM web işçileri ile CLI üzerinden çalışan cron veya kuyruk işçileri farklı süreçlerdir. HTTP isteklerini işleyen FPM işçilerinin yeniden başlaması, CLI kuyruk işçisinin de aynı nedenle durduğu anlamına gelmez. CLI işi HTTP çağrısı yapmadıkça FPM işçisi tüketmez.

Bu ayrım, yanlış unit üzerinde teşhis yapmanızı engeller. Web isteklerinin hatası için FPM unit loglarını; kuyruk veya zamanlanmış iş için ilgili CLI process, cron ya da worker unit kayıtlarını inceleyin. Zamanlayıcı seçimi konusunda karar veriyorsanız cron ve systemd Timer karşılaştırması yardımcı olabilir; ancak Timer ile çalışan işin başarısızlığı, web servisinin restart döngüsünden ayrı incelenmelidir.

Örnek senaryo

Varsayalım ki app-worker.service her 5 saniyede bir yeniden başlıyor ve journal kaydında yapılandırma dosyasının bulunamadığı yazıyor. Unit dosyasında WorkingDirectory=/srv/app/current tanımlı, fakat sembolik bağlantı yeni sürüm dağıtımından sonra kaldırılmış. Bu durumda doğru dar düzeltme, geçerli sürüm yolunu ve bağlantının hedefini geri yüklemektir; yalnızca RestartSec=60 eklemek hatayı çözmez. Bu, teşhisi açıklamak için kurulmuş bir örnek senaryodur.

Restart ayarını ne zaman değiştirmelisiniz?

Restart=on-failure, hata kodu veya beklenmeyen sinyal ile kapanan bir servisi yeniden başlatır. Restart=always ise normal çıkışlarda bile tekrar başlatabilir. RestartSec iki deneme arasındaki bekleme süresini belirler. Bu ayarlar sürekli çalışan servislerde yararlı olabilir; fakat uygulama her başlangıçta aynı hatayı veriyorsa sürekli deneme logları büyütür ve kök nedeni görünmez kılabilir.

Önce uygulama hatasını düzeltin. Geçici teşhis sırasında döngüyü durdurmanız gerekiyorsa servis yapılandırmasını kalıcı biçimde değiştirmek yerine servisi durdurup journal kayıtlarını inceleyin:

sudo systemctl stop ornek-servis.service
sudo journalctl -u ornek-servis.service --since "10 minutes ago" --no-pager

Servisin otomatik olarak tekrar başlaması için Restart= ayarını değiştirecekseniz mevcut dosyanın yedeğini alın. Örneğin yalnızca servis hata ile kapandığında yeniden başlaması isteniyorsa Restart=on-failure daha dar bir tercih olabilir; bu karar uygulamanın beklenen çalışma modeline bağlıdır. Değişikliğin amacı ve geri dönüş komutu not edilmeden üretim sunucusunda düzenleme yapmayın.

Birçok başarısız denemeden sonra systemd başlatmayı sınırladıysa, kök neden düzeltildikten sonra başarısız durum sayacını temizleyebilirsiniz:

sudo systemctl reset-failed ornek-servis.service
sudo systemctl start ornek-servis.service

reset-failed yalnızca systemd’nin başarısızlık durumunu sıfırlar. Uygulama hâlâ kapanıyorsa servis tekrar failed durumuna düşer. Komuttan sonra en az birkaç yeniden başlatma aralığını gözlemleyin.

Düzeltmeyi uygulayın ve doğrulayın

Unit dosyasını veya override’ı değiştirdiyseniz systemd’nin yeni içeriği okuması gerekir:

sudo systemctl daemon-reload

Ardından servisi başlatın ve birden fazla kontrol yapın:

sudo systemctl restart ornek-servis.service
sudo systemctl is-active ornek-servis.service
sudo systemctl status ornek-servis.service --no-pager -l
sudo journalctl -u ornek-servis.service --since "2 minutes ago" --no-pager

is-active komutunun active dönmesi tek başına yeterli değildir. Uygulamanın beklenen porta bağlandığını, gerekli endpoint’in yanıt verdiğini ve journal’da yeni hata olmadığını da kontrol edin. Dışarıdan erişilen bir web uygulamasında uygulama logları ile web sunucusunun 4xx-5xx kayıtları farklı katmanları gösterir; bunun için Hosting sunucu loglarını okuma yaklaşımını ayrıca kullanabilirsiniz.

Yeni sürüm dağıtımı sırasında sorun çıktıysa unit dosyasını düzeltmek yerine önce bir önceki bilinen sürüme dönmek daha güvenli olabilir. Sembolik sürüm dizinleri kullanıyorsanız eski hedefin hâlâ mevcut olduğunu, uygulama dosyalarının ve yapılandırmanın birlikte uyumlu olduğunu doğrulayın. Geri dönüşten sonra servis kararlı kalırsa yeni sürümün bağımlılıklarını ayrı bir bakım adımında inceleyin.

WordPress veya Node.js gibi uygulamalarda systemd yalnızca başlatma katmanıdır. Uygulamanın kendi bağımlılıkları, release komutu ve proxy ayarları da kontrol edilmelidir. Canlıya alma sürecini yeniden düzenliyorsanız PM2 ve systemd ile canlıya alma yaklaşımını mevcut servis tanımınızla karşılaştırın; aynı uygulamayı iki farklı process manager’ın birlikte yönetmediğinden emin olun.

Sık Sorulan Sorular

systemd servisi neden sürekli yeniden başlıyor ama status active görünüyor?

status anlık görüntü verir. Komut çalışırken süreç yeniden başlatma aralığında olabilir. journalctl kayıtlarında PID değişimini ve her başlangıç arasındaki zamanları inceleyin.

Restart=always kullanmak sorunu çözer mi?

Hayır. Bu ayar yalnızca kapanan süreci yeniden çalıştırır. Uygulama yanlış yapılandırma nedeniyle kapanıyorsa aynı hata tekrarlanır ve loglar hızla büyüyebilir.

Servis manuel başlıyor, systemd ile neden başlamıyor?

Manuel oturumdaki kullanıcı, çalışma dizini, PATH ve ortam değişkenleri systemd servisinden farklıdır. Unit içindeki User, WorkingDirectory, ExecStart ve environment tanımlarını karşılaştırın.

reset-failed komutundan sonra servis yine durursa ne anlama gelir?

Başarısızlık sayacı temizlenmiştir; fakat başlangıç hatası devam ediyordur. Yeni journal kaydındaki ilk uygulama hatasını inceleyerek teşhise dönün.

Son kontrol listesi

  • Gerçek unit adını ve etkin override dosyalarını doğrulayın.
  • systemctl status ve journalctl -u çıktılarında çıkış kodunu, sinyali ve ilk uygulama hatasını bulun.
  • ExecStart, kullanıcı, çalışma dizini, ortam dosyası ve dosya izinlerini aynı çalışma koşullarıyla test edin.
  • Port çakışması, bağımlılık sırası ve bellek baskısı ihtimallerini ayrı ayrı kontrol edin.
  • Uygulama hatası çözülmeden yalnızca RestartSec veya Restart= değiştirerek döngüyü gizlemeyin.
  • Değişiklikten önce yedeği ve geri dönüş yolunu saklayın; sonrasında daemon-reload, kontrollü başlatma ve journal doğrulaması yapın.

Bir sonraki adım, servis durumunu ve son 200 journal satırını aynı zaman aralığında inceleyip ilk gerçek hata mesajını ayırmaktır. İlk hata belirlendiğinde systemd ayarını değil, o hatanın ait olduğu katmanı düzeltin.

↑