Systemd’yi ilk öğrendiğimde, eski SysV init scriptlerinden gelen biri olarak açıkçası biraz direniş gösterdim. “Neden bu kadar karmaşık olsun ki?” diye düşündüm. Ama zamanla anladım ki systemd, servis yönetimini aslında çok daha tutarlı ve güçlü hale getiriyor. Bugün kendi servislerinizi nasıl oluşturacağınızı, yöneteceğinizi ve sorun giderme süreçlerini nasıl yürüteceğinizi ele alacağız.
Systemd Nedir ve Neden Önemlidir?
Systemd, Linux sistemlerde PID 1 olarak çalışan init sistemi ve servis yöneticisidir. Neredeyse tüm modern Linux dağıtımlarında (Ubuntu 15.04+, CentOS 7+, Debian 8+, Fedora 15+) varsayılan olarak kullanılmaktadır.
Eski SysV init scriptleri ile karşılaştırdığınızda systemd’nin getirdiği avantajlar şunlardır:
- Paralel başlatma: Servisler bağımlılıklarına göre paralel olarak başlatılabilir, bu da boot süresini önemli ölçüde kısaltır
- Otomatik yeniden başlatma: Çöken servisler otomatik olarak yeniden başlatılabilir
- Bağımlılık yönetimi: Servisler arasındaki ilişkiler açıkça tanımlanabilir
- Merkezi log yönetimi: journald ile tüm loglar tek yerden takip edilir
- Cgroup entegrasyonu: Kaynak kullanımı servis bazında kontrol edilebilir
Unit Dosyası Anatomisi
Systemd’de her şey “unit” kavramı üzerine kuruludur. Servisler .service, zamanlayıcılar .timer, mount noktaları .mount uzantılı dosyalar olarak tanımlanır. Biz bu yazıda ağırlıklı olarak .service dosyalarına odaklanacağız.
Unit dosyaları üç ana bölümden oluşur: [Unit], [Service] ve [Install]. Hepsinin kendine özgü direktifleri var ve bunları doğru kullanmak sağlam servisler oluşturmanın temelidir.
Unit dosyaları iki farklı konumda bulunabilir:
- /etc/systemd/system/: Sistem yöneticisinin oluşturduğu veya özelleştirdiği unit dosyaları (yüksek öncelik)
- /lib/systemd/system/: Paket yöneticisi tarafından kurulan unit dosyaları
- /usr/lib/systemd/system/: Dağıtıma özel unit dosyaları
- /run/systemd/system/: Çalışma zamanında oluşturulan geçici unit dosyaları
Kendi servislerinizi her zaman /etc/systemd/system/ altına koyun. Bu dizin en yüksek önceliğe sahiptir ve sistem güncellemelerinden etkilenmez.
İlk Servisinizi Oluşturmak
Gerçek bir senaryo ile başlayalım. Diyelim ki bir Python Flask uygulamanız var ve bunu systemd servisi olarak çalıştırmak istiyorsunuz.
Önce basit bir Flask uygulaması oluşturalım:
mkdir -p /opt/myapp
cat > /opt/myapp/app.py << 'EOF'
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "Merhaba, Systemd!"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
EOF
Şimdi bu uygulama için bir servis kullanıcısı oluşturalım. Servisleri root ile çalıştırmak güvenlik açısından kötü bir pratiktir:
useradd -r -s /bin/false -d /opt/myapp myapp
chown -R myapp:myapp /opt/myapp
Şimdi asıl servis dosyasını oluşturalım:
cat > /etc/systemd/system/myapp.service << 'EOF'
[Unit]
Description=My Flask Application
Documentation=https://wiki.sirket.com/myapp
After=network.target
Wants=network-online.target
[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/python3 /opt/myapp/app.py
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp
# Güvenlik ayarları
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/opt/myapp/logs
[Install]
WantedBy=multi-user.target
EOF
Dosyayı oluşturduktan sonra systemd’ye yeni unit dosyasını tanıtmak için daemon-reload çalıştırmanız gerekir:
systemctl daemon-reload
systemctl enable myapp.service
systemctl start myapp.service
systemctl status myapp.service
[Unit] Bölümünün Direktifleri
[Unit] bölümü servisin metadata bilgilerini ve bağımlılıklarını tanımlar.
- Description: İnsan okunabilir kısa açıklama.
systemctl statusçıktısında görünür - Documentation: Dökümantasyon URL’i veya man sayfası referansı
- After: Bu servis listelenen unitlerden SONRA başlar (başlatma sırası, bağımlılık değil)
- Before: Bu servis listelenen unitlerden ÖNCE başlar
- Requires: Güçlü bağımlılık. Listelenen unit başlamazsa bu servis de başlamaz
- Wants: Zayıf bağımlılık. Listelenen unit başlamazsa bu servis yine de çalışır
- BindsTo: Requires’dan daha güçlü. Bağlı unit durduğunda bu da durur
- Conflicts: Listelenen unitlerle aynı anda çalışamaz
After ve Requires farkını anlamak kritik öneme sahiptir. Requires=network.target derseniz servis network.target olmadan başlamaz, ama başlatma sırası garanti edilmez. Hem After hem Requires kullanırsanız sıra da garanti altına alınmış olur.
[Service] Bölümünün Direktifleri
Bu bölüm servisin nasıl çalışacağını tanımlar ve en fazla direktife sahip kısımdır.
Servis Tipleri
Type direktifi systemd’ye sürecin nasıl davranacağını söyler:
- Type=simple: Varsayılan. ExecStart’ta belirtilen process ana process olarak kabul edilir
- Type=forking: Servis başlarken fork eder ve parent process çıkar. Geleneksel daemon’lar için
- Type=exec: simple gibi ama exec() tamamlanana kadar başlatma işlemi bitmez
- Type=oneshot: Kısa ömürlü işler için. Process çıktıktan sonra systemd servisi “aktif” olarak işaretler
- Type=notify: Servis başlamaya hazır olduğunda systemd’ye bildirim gönderir
- Type=dbus: Servis D-Bus üzerinden bildirim gönderir
- Type=idle: Tüm diğer servisler tamamlanana kadar bekler
ExecStart, ExecStop ve Diğerleri
- ExecStart: Servisi başlatmak için çalıştırılacak komut
- ExecStartPre: ExecStart’tan önce çalışan komutlar
- ExecStartPost: ExecStart’tan sonra çalışan komutlar
- ExecStop: Servisi durdurmak için kullanılacak komut (varsayılan: SIGTERM)
- ExecReload: Reload sinyali için kullanılacak komut
- ExecStopPost: Servis durduktan sonra temizlik için çalışan komut
Yeniden Başlatma Politikaları
- Restart=no: Yeniden başlatma yapılmaz (varsayılan)
- Restart=on-success: Sadece başarılı çıkışta (exit code 0)
- Restart=on-failure: Başarısız çıkışta, sinyal ile durdurmada
- Restart=on-abnormal: Sinyal veya timeout ile durdurmada
- Restart=always: Her durumda yeniden başlatır
- RestartSec: Yeniden başlatmadan önce bekleme süresi
Gerçek Dünya Senaryosu: Node.js API Servisi
Biraz daha karmaşık bir örnek yapalım. Bir Node.js API’sini production ortamında çalıştırmak istediğinizi düşünelim:
cat > /etc/systemd/system/nodeapi.service << 'EOF'
[Unit]
Description=Node.js REST API Servisi
Documentation=file:///opt/nodeapi/README.md
After=network.target postgresql.service redis.service
Requires=postgresql.service
Wants=redis.service
[Service]
Type=simple
User=nodeapi
Group=nodeapi
WorkingDirectory=/opt/nodeapi
# Ortam değişkenleri
Environment=NODE_ENV=production
Environment=PORT=3000
EnvironmentFile=-/etc/nodeapi/env.conf
ExecStartPre=/usr/bin/node --version
ExecStartPre=/bin/bash -c 'cd /opt/nodeapi && /usr/bin/npm run migrate'
ExecStart=/usr/bin/node /opt/nodeapi/server.js
ExecReload=/bin/kill -USR2 $MAINPID
# Yeniden başlatma
Restart=on-failure
RestartSec=10
StartLimitIntervalSec=60
StartLimitBurst=3
# Loglama
StandardOutput=journal
StandardError=journal
SyslogIdentifier=nodeapi
# Güvenlik sertleştirme
NoNewPrivileges=true
PrivateTmp=true
PrivateDevices=true
ProtectSystem=full
ProtectHome=true
ReadWritePaths=/opt/nodeapi/uploads /var/log/nodeapi
# Kaynak limitleri
LimitNOFILE=65536
LimitNPROC=512
MemoryMax=512M
CPUQuota=80%
[Install]
WantedBy=multi-user.target
EOF
Burada dikkat çeken birkaç önemli nokta var. EnvironmentFile=-/etc/nodeapi/env.conf satırındaki - işareti, dosya yoksa hata verme anlamına gelir. Bu çok işe yarayan bir trick’tir. Ayrıca StartLimitBurst=3 ve StartLimitIntervalSec=60 kombinasyonu, 60 saniye içinde 3 kez başlatma başarısız olursa servisi durdurup sizi uyarır. Böylece sonsuz döngüyü engellemiş olursunuz.
Servis Yönetimi Komutları
Artık servisiniz var, onu nasıl yönetirsiniz?
# Servisi başlat/durdur/yeniden başlat
systemctl start nodeapi
systemctl stop nodeapi
systemctl restart nodeapi
systemctl reload nodeapi
# Servis durumunu kontrol et
systemctl status nodeapi
# Sistem başlangıcında otomatik başlat
systemctl enable nodeapi
# Otomatik başlatmayı devre dışı bırak
systemctl disable nodeapi
# Servisi hem enable et hem başlat
systemctl enable --now nodeapi
# Servis aktif mi?
systemctl is-active nodeapi
systemctl is-enabled nodeapi
systemctl is-failed nodeapi
# Tüm aktif servisleri listele
systemctl list-units --type=service --state=active
# Başarısız servisleri listele
systemctl list-units --type=service --state=failed
# Servisin tüm bağımlılıklarını göster
systemctl list-dependencies nodeapi
Journald ile Log Yönetimi
Systemd’nin entegre log sistemi journald, servis loglarını yönetmek için çok güçlü bir araçtır:
# Belirli bir servisin loglarını görüntüle
journalctl -u nodeapi
# Son 100 satırı görüntüle
journalctl -u nodeapi -n 100
# Gerçek zamanlı log takibi
journalctl -u nodeapi -f
# Belirli zaman aralığı için loglar
journalctl -u nodeapi --since "2024-01-15 10:00:00" --until "2024-01-15 11:00:00"
# Son 1 saat
journalctl -u nodeapi --since "1 hour ago"
# Sadece hata ve üzeri seviye
journalctl -u nodeapi -p err
# JSON formatında çıktı (log analizi için)
journalctl -u nodeapi -o json-pretty | head -50
# Boot'tan bu yana olan loglar
journalctl -u nodeapi -b
# Log disk kullanımını kontrol et
journalctl --disk-usage
# Eski logları temizle (2 haftadan eskiler)
journalctl --vacuum-time=2weeks
Drop-in Dosyaları ile Servisleri Özelleştirme
Paket yöneticisi tarafından kurulan bir servisin unit dosyasını doğrudan düzenlemek yerine “drop-in” dosyaları kullanmalısınız. Bu yöntem sistem güncellemelerinden etkilenmez:
# Drop-in dizinini oluştur
mkdir -p /etc/systemd/system/nginx.service.d/
# Override dosyası oluştur
cat > /etc/systemd/system/nginx.service.d/override.conf << 'EOF'
[Service]
# Mevcut limiti override et
LimitNOFILE=100000
# Ortam değişkeni ekle
Environment=NGINX_ENVSUBST_OUTPUT_DIR=/etc/nginx/conf.d
# Yeniden başlatma politikasını değiştir
Restart=always
RestartSec=3
EOF
systemctl daemon-reload
systemctl restart nginx
Alternatif olarak systemctl edit servis_adi komutu drop-in dosyasını otomatik olarak oluşturur ve editörde açar. systemctl edit --full servis_adi ise tam unit dosyasının kopyasını düzenlemenize olanak sağlar.
Zamanlayıcı ile Cron Görevi Oluşturma
Systemd timer’ları cron’a güçlü bir alternatiftir. Günlük yedekleme görevi örneği:
# Önce yedekleme scriptini oluştur
cat > /usr/local/bin/backup.sh << 'EOF'
#!/bin/bash
BACKUP_DIR="/backup/$(date +%Y%m%d)"
mkdir -p "$BACKUP_DIR"
pg_dump mydb > "$BACKUP_DIR/mydb.sql"
tar -czf "$BACKUP_DIR/uploads.tar.gz" /opt/myapp/uploads
find /backup -mtime +30 -delete
echo "Yedekleme tamamlandi: $BACKUP_DIR"
EOF
chmod +x /usr/local/bin/backup.sh
# Servis dosyası oluştur
cat > /etc/systemd/system/backup.service << 'EOF'
[Unit]
Description=Gunluk Yedekleme Servisi
After=postgresql.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
User=backup
StandardOutput=journal
StandardError=journal
EOF
# Timer dosyası oluştur
cat > /etc/systemd/system/backup.timer << 'EOF'
[Unit]
Description=Gunluk Yedekleme Zamanlayicisi
Requires=backup.service
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=600
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
systemctl enable --now backup.timer
# Timer durumunu kontrol et
systemctl list-timers backup.timer
Persistent=true direktifi önemlidir. Sistem o saat kapalıysa bir sonraki başlatmada görevi hemen çalıştırır. RandomizedDelaySec ise birden fazla sunucunuzda aynı anda yük oluşmaması için rasgele gecikme ekler.
Sorun Giderme
Bir servis çalışmıyorsa sistematik yaklaşım şu şekilde olmalıdır:
# Önce genel duruma bak
systemctl status myapp.service
# Daha detaylı log için
journalctl -u myapp.service -n 50 --no-pager
# Servis başlatılabilir mi test et
systemd-analyze verify /etc/systemd/system/myapp.service
# Unit dosyasını doğrula
systemd-analyze security myapp.service
# Boot süresi analizi
systemd-analyze blame
# Kritik zinciri göster
systemd-analyze critical-chain
# Servis bağımlılık grafiği (SVG olarak)
systemd-analyze dot myapp.service | dot -Tsvg > myapp-deps.svg
Sık karşılaşılan sorunlar ve çözümleri:
- “Failed to start” hatası: journalctl çıktısına bakın, genellikle ExecStart komutunun yolu yanlış veya çalıştırma izni eksiktir
- “Control process exited with error code”: ExecStartPre veya ExecStartPost’ta hata var
- Servis başlıyor ama hemen duruyor: Type=forking kullanıyorsanız süreç gerçekten fork ediyor mu kontrol edin
- “Dependency failed”: After ve Requires direktiflerindeki servisler çalışıyor mu kontrol edin
- Daemon-reload unutuldu: Unit dosyasını düzenledikten sonra mutlaka
systemctl daemon-reloadçalıştırın
Güvenlik Sertleştirme Direktifleri
Modern systemd versiyonlarında servis güvenliğini artırmak için kullanabileceğiniz direktifler:
- NoNewPrivileges=true: Süreç ve child’larının yeni yetkiler edinmesini engeller
- PrivateTmp=true: Servis için izole /tmp dizini oluşturur
- PrivateDevices=true: /dev erişimini kısıtlar
- ProtectSystem=strict: /usr, /boot, /etc’yi salt okunur yapar
- ProtectHome=true: /home, /root, /run/user erişimini engeller
- ReadWritePaths: ProtectSystem=strict ile birlikte yazma izni verilecek yollar
- ReadOnlyPaths: Salt okunur erişim verilecek yollar
- CapabilityBoundingSet: Sürecin kullanabileceği Linux capability’lerini sınırlar
- SystemCallFilter: İzin verilen sistem çağrılarını whitelist veya blacklist ile sınırlar
- IPAddressDeny=any: Ağ erişimini tamamen engeller (sadece lokal gerekiyorsa)
systemd-analyze security servis_adi komutu servisinizin güvenlik puanını hesaplar ve iyileştirme önerileri sunar. Production’daki servislerinizi düzenli olarak bu komutla kontrol etmenizi tavsiye ederim.
Sonuç
Systemd servis yönetimi ilk bakışta karmaşık görünse de mantığını kavradıktan sonra inanılmaz derecede güçlü bir araç olduğunu anlıyorsunuz. Özet olarak dikkat etmeniz gereken kritik noktalar şunlardır:
- Her zaman
/etc/systemd/system/altında çalışın ve her değişiklikten sonradaemon-reloadyapmayı unutmayın - Servisleri root yerine dedicated kullanıcılarla çalıştırın
- Güvenlik direktiflerini ihmal etmeyin,
systemd-analyze securityile düzenli kontrol yapın - Log takibi için journalctl’i etkin kullanın
- Paket servislerini özelleştirmek için drop-in dosyalarını tercih edin
- Cron yerine systemd timer’ları kullanmayı değerlendirin, özellikle loglama ve bağımlılık yönetimi açısından çok daha avantajlıdır
Systemd ile ilgili aklınızda bulundurmanız gereken en önemli şey şu: Unit dosyalarınızı versiyon kontrolüne (git) alın. /etc/systemd/system/ dizinini bir git reposuna eklemek, özellikle birden fazla sunucu yönetiyorsanız hayat kurtarıcı olacaktır.