systemd Path Unit ile Dosya Değişikliklerini İzleme ve Otomatik Tetikleme
Dosya sistemi üzerinde bir şeyler değiştiğinde otomatik olarak bir aksiyon almak istediğinizde aklınıza ilk ne geliyor? Büyük ihtimalle inotifywait ile sarılı bir bash scripti ya da cron ile periyodik kontrol. Ama systemd’nin path unit türü, bu iş için hem daha temiz hem de sistem ile daha entegre bir çözüm sunuyor. Üstelik çoğu sysadmin’in radarına pek girmeyen bu özellik, production ortamlarında gerçekten hayat kurtarıcı olabiliyor.
systemd Path Unit Nedir?
systemd, servis yönetiminin çok ötesinde bir init sistemi. service, timer, socket, mount unit türlerinin yanı sıra path unit türü de mevcut. Path unit’ler, dosya sistemi üzerindeki belirli değişiklikleri izleyerek başka bir unit’i (genellikle bir service) tetiklemek için kullanılıyor.
Temel mantık şu: bir .path dosyası oluşturuyorsunuz, bu dosya hangi dizini veya dosyayı izleyeceğini ve değişiklik olduğunda ne yapacağını biliyor. Alt tarafta kernel’in inotify mekanizmasını kullanıyor, yani polling yok, gereksiz CPU tüketimi yok.
Neden inotifywait veya Cron Değil?
inotifywait ile yazılmış bir script’i daemon olarak çalıştırmak için yine bir systemd service yazmanız gerekiyor. Restart politikası, logging, bağımlılık yönetimi derken işler karmaşıklaşıyor. cron ise polling mantığıyla çalıştığı için hem gecikmeli tepki veriyor hem de küçük aralıklarla çalışan cron job’lar gereksiz yük oluşturuyor.
Path unit ile:
- Kernel seviyesinde inotify entegrasyonu sayesinde anlık tepki alıyorsunuz
- systemd’nin kendi logging altyapısı (journald) ile log yönetimi otomatik geliyor
- Restart, dependency, rate limiting gibi özellikler kutudan çıkıyor
- Unit enable/disable ile standart systemd yönetimi mümkün
Path Unit Dosya Yapısı
Bir .path dosyası /etc/systemd/system/ altına yerleştiriliyor ve iki ana bölümden oluşuyor: [Unit] ve [Path].
# /etc/systemd/system/deploy-trigger.path
[Unit]
Description=Deploy tetikleyici - /tmp/deploy-ready dosyasini izler
Documentation=https://wiki.sirketim.com/deploy-process
[Path]
PathExists=/tmp/deploy-ready
Unit=deploy-runner.service
[Install]
WantedBy=multi-user.target
[Path] bölümündeki direktifler şunlar:
- PathExists: Belirtilen dosya veya dizin var olmaya başladığında tetiklenir
- PathExistsGlob: Glob pattern ile eşleşen bir dosya oluştuğunda tetiklenir
- PathChanged: Dosya değiştirildiğinde (yazma, boyut değişikliği) tetiklenir
- PathModified: PathChanged’e benzer ama dosyaya sadece açılıp kapatılması bile tetikler
- DirectoryNotEmpty: Dizin boş olmaktan çıktığında tetiklenir
- MakeDirectory: İzlenecek dizin yoksa otomatik oluşturulsun mu? (bool)
- DirectoryMode: MakeDirectory ile oluşturulan dizinin izinleri
- Unit: Tetiklenecek unit adı (belirtilmezse .path ile aynı isimli .service aranır)
İlk Pratik Örnek: Config Değişikliğinde Servis Yeniden Başlatma
Diyelim ki /etc/myapp/config.yaml dosyası değiştiğinde myapp servisini otomatik olarak yeniden başlatmak istiyorsunuz.
Önce path unit’i yazalım:
# /etc/systemd/system/myapp-config-watcher.path
[Unit]
Description=MyApp config degisikliklerini izle
After=network.target
[Path]
PathModified=/etc/myapp/config.yaml
Unit=myapp-reload.service
[Install]
WantedBy=multi-user.target
Ardından tetiklenecek service unit:
# /etc/systemd/system/myapp-reload.service
[Unit]
Description=MyApp servisini yeniden yukle
After=myapp.service
Requires=myapp.service
[Service]
Type=oneshot
ExecStart=/bin/systemctl reload myapp.service
ExecStartPost=/usr/bin/logger -t myapp-reload "Config degisikligi algilandi, servis yeniden yuklendi"
Dikkat edin: bu service’de [Install] bölümü yok. Çünkü bu service doğrudan enable edilmiyor, sadece path unit tarafından tetikleniyor.
Aktif etmek için:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp-config-watcher.path
sudo systemctl status myapp-config-watcher.path
Gelişmiş Senaryo: Otomatik Deploy Pipeline
Bu senaryo production’da gerçekten işe yarıyor. CI/CD sisteminiz bir dosyayı belirli bir dizine bıraktığında deploy sürecini başlatmak istiyorsunuz.
# /etc/systemd/system/autodeploy.path
[Unit]
Description=Otomatik deploy tetikleyici
Documentation=man:systemd.path(5)
[Path]
PathExists=/opt/deploy/incoming/deploy.trigger
MakeDirectory=yes
DirectoryMode=0755
Unit=autodeploy.service
[Install]
WantedBy=multi-user.target
# /etc/systemd/system/autodeploy.service
[Unit]
Description=Otomatik deploy islemi
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=deploy
Group=deploy
WorkingDirectory=/opt/deploy
ExecStartPre=/bin/rm -f /opt/deploy/incoming/deploy.trigger
ExecStart=/opt/deploy/scripts/run-deploy.sh
ExecStartPost=/usr/bin/curl -s -X POST https://hooks.slack.com/services/TOKEN -d '{"text":"Deploy tamamlandi"}'
StandardOutput=journal
StandardError=journal
SyslogIdentifier=autodeploy
# Deploy basarisiz olursa tekrar deneme
Restart=on-failure
RestartSec=30s
Burada ExecStartPre ile önce trigger dosyasını siliyoruz. Bu önemli bir detay, çünkü dosya kaldırılmazsa bir sonraki path unit aktivasyonunda servis tekrar tetiklenebilir.
Deploy script’inin temel yapısı:
#!/bin/bash
# /opt/deploy/scripts/run-deploy.sh
set -euo pipefail
DEPLOY_LOG="/var/log/autodeploy/$(date +%Y%m%d-%H%M%S).log"
mkdir -p /var/log/autodeploy
exec > >(tee -a "$DEPLOY_LOG") 2>&1
echo "Deploy basliyor: $(date)"
echo "Host: $(hostname)"
# Artifact'i cek
cd /opt/deploy
git pull origin main
# Dependency yükle
npm ci --production
# Zero-downtime restart
systemctl reload-or-restart myapp.service
echo "Deploy tamamlandi: $(date)"
DirectoryNotEmpty ile Upload İşleme
Bir upload dizinini izleyip yeni dosyalar geldiğinde işleme almak sık karşılaşılan bir senaryo.
# /etc/systemd/system/upload-processor.path
[Unit]
Description=Upload dizinini izle
[Path]
DirectoryNotEmpty=/var/spool/uploads/pending
MakeDirectory=yes
DirectoryMode=0770
Unit=upload-processor.service
[Install]
WantedBy=multi-user.target
# /etc/systemd/system/upload-processor.service
[Unit]
Description=Upload dosyalarini isle
[Service]
Type=oneshot
User=www-data
Group=www-data
ExecStart=/usr/local/bin/process-uploads.sh
# Servis bitince path unit tekrar aktif olmadan once bekle
StandardOutput=journal
StandardError=journal
SyslogIdentifier=upload-processor
İşte burada önemli bir nokta var. DirectoryNotEmpty kullandığınızda, servis çalışırken yeni dosyalar gelebilir. Servis bittiğinde dizin hala dolu olabilir ve path unit hemen tekrar tetikleyebilir. Bu durumu script içinde yönetmek gerekiyor:
#!/bin/bash
# /usr/local/bin/process-uploads.sh
PENDING_DIR="/var/spool/uploads/pending"
PROCESSING_DIR="/var/spool/uploads/processing"
DONE_DIR="/var/spool/uploads/done"
mkdir -p "$PROCESSING_DIR" "$DONE_DIR"
# Tüm bekleyen dosyaları processing'e tasi
find "$PENDING_DIR" -maxdepth 1 -type f | while read -r file; do
mv "$file" "$PROCESSING_DIR/"
done
# Processing dizinindeki dosyaları isle
for file in "$PROCESSING_DIR"/*; do
[ -f "$file" ] || continue
echo "Isleniyor: $file"
# Burada gercek isleme mantigi gelir
if /usr/local/bin/process-single-file.sh "$file"; then
mv "$file" "$DONE_DIR/"
echo "Basarili: $(basename "$file")"
else
echo "HATA: $file islenemedi" >&2
mv "$file" "$DONE_DIR/failed-$(basename "$file")"
fi
done
Rate Limiting ve Cooldown Yönetimi
Path unit’ler hızlı değişimlerde çok sık tetiklenebilir. systemd’nin service bazlı rate limiting özelliğini kullanabilirsiniz:
# /etc/systemd/system/log-analyzer.service
[Unit]
Description=Log analiz servisi
[Service]
Type=oneshot
ExecStart=/usr/local/bin/analyze-logs.sh
StandardOutput=journal
StandardError=journal
# Burst korumasi: 30 saniyede en fazla 3 kez calistir
StartLimitIntervalSec=30
StartLimitBurst=3
# Limit asilirsa 5 dakika bekle
StartLimitAction=none
Daha granüler kontrol için path unit içinde de bekleyebilirsiniz:
# /etc/systemd/system/log-watcher.path
[Unit]
Description=Log dosyasini izle
[Path]
PathModified=/var/log/myapp/app.log
Unit=log-analyzer.service
[Install]
WantedBy=multi-user.target
Service tarafında debounce mantığı eklemek için küçük bir bekleme kullanabilirsiniz:
# Service içinde ExecStartPre ile kısa bekleme
ExecStartPre=/bin/sleep 2
ExecStart=/usr/local/bin/analyze-logs.sh
Bu 2 saniyelik bekleme, hızlı arka arkaya gelen değişikliklerin tek bir çalışmada toplanmasını sağlar.
Glob Pattern ile Birden Fazla Dosya İzleme
# /etc/systemd/system/ssl-cert-watcher.path
[Unit]
Description=SSL sertifika yenilemelerini izle
[Path]
PathExistsGlob=/etc/letsencrypt/live/*/fullchain.pem
Unit=nginx-reload.service
[Install]
WantedBy=multi-user.target
# /etc/systemd/system/nginx-reload.service
[Unit]
Description=Nginx SSL yenileme sonrasi yeniden yukle
After=nginx.service
[Service]
Type=oneshot
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/bin/systemctl reload nginx.service
ExecStartPost=/usr/bin/logger -t ssl-renewal "Nginx SSL sertifikasi yenilendi ve servis yeniden yuklendi"
Bu senaryo, certbot ile Let’s Encrypt yenilemelerini otomatik olarak Nginx’e yansıtmak için kullanışlı. certbot post-hook yazmak yerine bu yaklaşımı tercih edebilirsiniz.
Debugging ve Sorun Giderme
Path unit’lerle çalışırken en çok karşılaşılan sorunlar ve çözümleri:
Path unit durumunu kontrol etmek için:
# Path unit'in aktif olup olmadigini ve izledigi dosyayi gör
systemctl status myapp-config-watcher.path
# Son tetiklenme zamanlarini ve durumu gör
systemctl show myapp-config-watcher.path
# Path unit'in tetikledigini journald'den takip et
journalctl -u myapp-config-watcher.path -f
# Hem path hem de service loglarini birlikte izle
journalctl -u myapp-config-watcher.path -u myapp-reload.service -f --since "1 hour ago"
Sık karşılaşılan sorunlar:
- Servis tetiklenmiyor: Path unit aktif mi kontrol edin (
systemctl is-active). Dosya yolunun doğru yazıldığından emin olun. - Çok sık tetikleniyor:
PathModifiedyerinePathChangeddeneyin.PathChangeddaha az hassas. - İzin hatası: Service’in çalıştığı kullanıcının izlenen dizine erişim yetkisi olması gerekiyor. Path unit ise root olarak çalışıyor.
- Service defalarca başlıyor: Tetiklenme koşulu hala geçerliyse (dosya hala var vs.) path unit tekrar tetikleyebilir. ExecStartPre ile koşulu temizleyin.
Systemd’nin debug modunda ne gördüğünü anlamak için:
# inotify event'lerini dogrudan test etmek icin
inotifywait -m -e modify,create,delete /etc/myapp/config.yaml
# Systemd'nin path unit icin hangi inotify eventleri bekledigini gör
systemd-analyze verify /etc/systemd/system/myapp-config-watcher.path
Birden Fazla Path Direktifi Kullanmak
Tek bir path unit içinde birden fazla izleme direktifi tanımlayabilirsiniz. Herhangi biri tetiklendiğinde servis çalışır:
# /etc/systemd/system/multi-watcher.path
[Unit]
Description=Birden fazla konum izle
[Path]
PathModified=/etc/myapp/config.yaml
PathModified=/etc/myapp/secrets.yaml
PathExists=/tmp/force-reload
DirectoryNotEmpty=/etc/myapp/conf.d/pending
Unit=myapp-full-reload.service
[Install]
WantedBy=multi-user.target
Bu yaklaşım, uygulama yapılandırmasını birden fazla dosyada yöneten sistemlerde çok işe yarıyor. Herhangi bir yapılandırma dosyası değiştiğinde tek bir reload mekanizması devreye giriyor.
Systemd Instantiated Units ile Dinamik İzleme
Template unit’lerle birden fazla dizini ayrı ayrı izleyebilirsiniz:
# /etc/systemd/system/[email protected]
[Unit]
Description=%i dizinini izle
[Path]
DirectoryNotEmpty=/opt/watch/%i/incoming
MakeDirectory=yes
Unit=dir-processor@%i.service
[Install]
WantedBy=multi-user.target
# /etc/systemd/system/[email protected]
[Unit]
Description=%i icin dizin isleyici
[Service]
Type=oneshot
ExecStart=/usr/local/bin/process-dir.sh %i
Aktifleştirmek için:
systemctl enable --now [email protected]
systemctl enable --now [email protected]
systemctl enable --now [email protected]
Her instance /opt/watch/projectA/incoming, /opt/watch/projectB/incoming gibi ayrı dizinleri izliyor. Yeni bir proje eklemek için sadece yeni bir instance enable etmeniz yeterli.
Güvenlik Değerlendirmeleri
Path unit ve tetiklenen service’lerde güvenlik sertleştirmesi önemli:
# /etc/systemd/system/secure-processor.service
[Unit]
Description=Guvenli dosya isleyici
[Service]
Type=oneshot
User=processor
Group=processor
ExecStart=/usr/local/bin/process.sh
# Systemd sertlestirme direktifleri
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/spool/processor /var/log/processor
CapabilityBoundingSet=
RestrictSUIDSGID=yes
SystemCallFilter=@system-service
Bu direktiflerle servis, ihtiyacı olan dizinler dışında dosya sistemi üzerine yazamıyor, yeni process ayrıcalıkları kazanamıyor ve gereksiz system call’ları kullanamıyor.
Sonuç
systemd path unit, dosya sistemi olaylarına tepki vermek için en temiz ve sistem dostu çözümlerden biri. İnotify altyapısını doğrudan kullandığı için polling yok, gereksiz kaynak tüketimi yok. Journald entegrasyonu sayesinde ne zaman ne tetiklendiğini her zaman görebiliyorsunuz. Enable/disable mekanizması ile standart systemd yönetim pratiğine tam uyum sağlıyor.
Production’da özellikle şu senaryolar için tavsiye ederim: config değişikliklerinde otomatik reload, CI/CD tetikleyici mekanizmaları, upload dizini işleme pipeline’ları ve sertifika yenileme sonrası servis yönetimi. Template unit’lerle birleştiğinde ölçeklenebilir, bakımı kolay bir izleme altyapısı kurabilirsiniz.
Bir sysadmin olarak inotifywait scriptlerinden ve cron polling’den uzak durmanızı öneririm. systemd zaten orada, path unit de zaten hazır. Neden daha fazla moving part ekleyesiniz ki?
