5 puan yazan GN⁺ 19 시간 전 | 1 yorum | WhatsApp'ta paylaş
  • systemd zamanlayıcıları, cron için pratik bir alternatif olup programa göre .service gibi üniteleri çalıştırır ve geçmiş, çıktı ile ortam yönetimini daha açık hale getirir
  • Geleneksel cron, belirsiz $PATH, kolayca kaybolabilen stdout/stderr, izlenmesi zor çalışma geçmişi ve okunması güç zamanlama sözdizimi gibi zayıf yönlere sahiptir
  • Zamanlayıcılar aynı köke sahip .timer ve .service dosyalarını bağlar; OnCalendar, OnBootSec, OnUnitActiveSec ile saat ve olay tabanlı çalıştırmayı ifade eder
  • systemd-analyze calendar ve systemctl list-timers ile zaman ifadeleri ve bir sonraki çalışma zamanı kontrol edilebilir; WakeSystem= ise askı durumundayken bile sistemi uyandırıp çalıştırabilir
  • RandomizedOffsetSec ve FixedRandomDelay= eşzamanlı çalışma zirvelerini azaltır; Persistent= ise devre dışı kalınan sürede kaçırılan çalıştırmaları sistem yeniden çevrimiçi olur olmaz telafi eder

cron yerine neden systemd zamanlayıcısı kullanılmalı

  • cron job, gerçek cron arka plan süreci olmasa bile “bunu her gün çalıştır”, “şunu her ay çalıştır” gibi takvime göre işleri yürüten temel bir bilişim yapıtaşını anlatmak için yaygın biçimde kullanılır
  • systemd zamanlayıcısı, belirli bir programa göre başka bir systemd ünitesini, genellikle bir .service ünitesini çalıştıran bir systemd ünitesidir ve geleneksel cron arka plan sürecinin işlevsel yerine geçebilir
  • Geleneksel cronun pratikte bazı zayıf yönleri vardır
    • Belirsiz $PATH ayarı yüzünden betiklerin nasıl çalışacağını öngörmek zordur
    • stdout ve stderr çıktıları sık sık bir kara deliğe gider ya da makinenin posta sistemine gönderilir
    • Çalışma geçmişini izlemek ve sorgulamak zordur
    • 01,31 04,05 1-15 1,6 * gibi zamanlama sözdizimi insanlar için ne kolay okunur ne de sezgiseldir
  • systemd zamanlayıcıları bu sorunları azaltırken cron tarzı ifadelere benzeyen takvim yapılandırmaları da sunar

Temel yapı: servis ve zamanlayıcı

  • systemd zamanlayıcılarının çalıştıracağı bir hedefe ihtiyacı vardır; .service ünitesi mantıksal olarak bir betik gibi düşünülebilir
  • Örneğin /etc/systemd/system/roulette.service içine aşağıdaki üniteyi koyarsanız, bilgisayarı 10'da 1 ihtimalle kapatan bir servis kurmuş olursunuz
[Unit]
Description=1 in 10 chance to break your chains

[Service]
ExecStart=/usr/bin/env bash -c '[[ $(($RANDOM % 10)) == 0 ]] && systemctl poweroff || echo LIVE ANOTHER DAY'
  • ExecCondition=, koşullu çalıştırmayı systemd servis seçeneği olarak ifade etmenin daha bütünleşik bir yoludur ve “çalışmaya devam edilmeli mi?” sorusunu ünite seviyesinde daha açık gösterir
[Unit]
Description=1 in 10 chance to break your chains

[Service]
ExecCondition=/run/current-system/sw/bin/bash -c '[[ $(($RANDOM % 10)) == 0 ]]'
ExecStart=/run/current-system/sw/bin/systemctl poweroff
  • Koşul sağlanmazsa günlükte daha açık bir ifade kalır
May 05 11:05:32 diesel systemd[3117]: Condition check resulted in 1 in 10 chance to break your chains being skipped.
  • Genel olarak systemd'nin sunduğu seçeneklerden yararlanmak, her şeyi kendiniz betiklemeye göre daha iyi bir deneyim sağlar
    • OnFailure= servis betiği başarısız olduğunda tepki vermek için kullanılabilir
    • Restart= geçici hatalardan kurtulmayı denemek için kullanılabilir

Zamanlayıcı ünitesini bağlama ve çalıştırma

  • Aynı dosya köküne sahip bir /etc/systemd/system/roulette.timer dosyası oluşturarak zamanlayıcıyı roulette.service ile bağlayabilirsiniz
[Unit]
Description=impending destruction

[Timer]
OnCalendar=10:00

[Install]
WantedBy=timers.target
  • Varsayılan olarak zamanlayıcının Unit= ayarı, aynı köke sahip .service uzantılı servis ünitesini seçer
    • Bu örnekte roulette.service seçilir
    • Farklı isimli bir servis ünitesini çalıştırmak istiyorsanız Unit= değerini değiştirebilirsiniz
  • ExecStart= hedefi varsayılan olarak kabuk komutu olarak çalıştırılmaz
    • Mutlak yol hedefleri, betik olarak ya da bir betiği dize argümanı olarak bekleyen yorumlayıcı gibi ele alınmalıdır
    • ExecStart=/usr/bin/echo Hello | /usr/bin/awk burada boru anlam taşımadığından çalışmaz
  • ExecStart= argümanları varsayılan olarak, bazı sistem yöneticisi varsayılanları dışında ortam değişkenlerini miras almaz
    • Varsayılan $PATH neredeyse boş sayılabilecek durumdadır
    • /usr/bin/env çalıştırmak, systemctl gibi öğelerin kullanılabilmesini sağlayan basit bir koruma olur
    • Yalnızca ExecStart=/usr/bin/bash kullanılsaydı $PATH içine varsayılan girdiler gelirdi, ama env kullanımı ek bir güvenlik önlemidir
  • Servisi zamanlayıcı olmadan da doğrudan çalıştırabilirsiniz
systemctl start roulette
  • [Install] bölümü olmayan servisler enable edilemez; bu yapıda zamanlayıcı, servisi tutarlı biçimde çalıştırmanın standart yoludur
  • systemctl, açıkça sonek verilmemiş olsa bile varsayılan olarak roulette.service üzerinde işlem yapar
  • .timer ünitesine systemctl start uygulamak zamanlayıcıyı etkin duruma getirir, fakat gerçek Unit= hedef servisini hemen çalıştırmaz
systemctl start roulette.timer
  • status, zamanlayıcının bir sonraki ne zaman çalışacağını gösterir
systemctl status roulette.timer
Trigger: Sat 2026-04-18 10:00:00 MDT; 35min left
  • En basit akış, çalıştırılacak servisi oluşturmak, aynı yerde programı olan bir zamanlayıcı tanımlamak ve hedef yerine zamanlayıcıyı başlatmaktır
  • Zamanlayıcı ünitesinin [Install] bölümünde WantedBy= varsa, açılışta da etkinleşmesi sağlanabilir
systemctl enable roulette.timer

Zaman ifadeleri: takvim olayları ve süreler

  • Zamanlayıcılarda zamanın nasıl ifade edildiği önemlidir; yinelenen zaman aralıkları ile takvim olayları veya zaman damgaları arasındaki farkı ayırmak gerekir
  • systemd.time(7) kılavuzu bol örnek içerir ve zamanlayıcı yazarken ilk başvurulacak kaynak olmaya uygundur
  • systemd-analyze, zaman ifadelerini doğrulayabilir ve açıklayabilir
systemd-analyze calendar '*-*-* *:*:*'
Normalized form: *-*-* *:*:*
    Next elapse: Sat 2026-04-18 16:44:26 MDT
       (in UTC): Sat 2026-04-18 22:44:26 UTC
       From now: 431ms left
  • systemd zamanlayıcıları yalnızca yinelenen duvar saati zamanlarını değil, geleneksel crondan farklı olarak, önceki bir olaya göre yinelenen süreleri de tanımlayabilir
  • daily ifadesinin tam açılımı, her yıl, her ay, her gün saat 00:00:00'da çalışmasıdır
*-*-* 00:00:00
│ │ │ │  │  ╰── at second 00
│ │ │ │  ╰───── at minute 00
│ │ │ ╰──────── at hour 00
│ │ ╰────────── every day
│ ╰──────────── every month
╰────────────── every year
  • daily gibi kısaltmalar, tam biçim ve systemd.time(7) içindeki diğer desteklenen değerler kullanılabilir; varsayımlarınızı systemd-analyze ile doğrulayabilirsiniz

Olay tabanlı çalıştırmanın daha uygun olduğu durumlar

  • Gerçek işlerde çoğu zaman “her gün aynı saatte çalıştır” yerine “başka bir olaydan sonra çalıştır” yaklaşımı daha uygundur
  • Geçici dizinleri temizleyen bir işte, açılıştan hemen sonra cron ifadesindeki saat geçmişse /tmp içinde temizlenecek çok az şey olabilir
  • “Bilgisayar açıldıktan bir saat sonra çalıştır, ardından her saat tekrar çalıştır” ifadesi, servisin gerçek davranışıyla ve zamanlama mantığıyla daha iyi örtüşür
[Timer]
OnBootSec=1h
OnUnitActiveSec=1h
  • OnBootSec=1h, makine açıldıktan bir saat sonra bir kez çalıştırmak anlamına gelir
  • OnUnitActiveSec=1h, Unit= çalıştırıldıktan bir saat sonra tekrar çalıştırmak anlamına gelir ve zamanlayıcının örtük olarak tekrar etmesini sağlar
  • Bu tür periyodik süre ifadeleri, “her saatin şu dakikasında çalıştır” ifadesine göre “arada bir çalıştır” türü işler için daha sık uygundur
  • Advent of Code API'sini sorgulayan Slack botu örneğinde */15 cron ifadesi API'nin “15 dakikada bir” kuralına uyar, ancak herkes aynı şekilde sorgularsa trafik yığılabilir
  • Kod değişikliğinden sonra zamanlayıcıyı başlatıp her 15 dakika geçtiğinde çalışacak şekilde ayarlamak, gereken davranışı karşılayıp thundering herd sorununu azaltabilir

Zamanlayıcı durumunu tek bakışta görmek

  • systemctl list-timers, bir makinedeki zamanlayıcı durumunu özetleyen üst düzey bir komuttur
systemctl list-timers
NEXT                                 LEFT LAST                                  PASSED UNIT                         ACTIVATES
Mon 2026-04-20 15:15:00 MDT      1min 40s Mon 2026-04-20 15:00:05 MDT        13min ago zfs-snapshot-frequent.timer  zfs-snapshot-frequent.service
Mon 2026-04-20 15:32:16 MDT         18min Mon 2026-04-20 14:22:15 MDT        51min ago fwupd-refresh.timer          fwupd-refresh.service
Mon 2026-04-20 16:00:00 MDT         46min Mon 2026-04-20 15:00:05 MDT        13min ago logrotate.timer              logrotate.service
Mon 2026-04-20 16:00:00 MDT         46min Mon 2026-04-20 15:00:05 MDT        13min ago zfs-snapshot-hourly.timer    zfs-snapshot-hourly.service
Tue 2026-04-21 00:00:00 MDT            8h Mon 2026-04-20 09:43:22 MDT     5h 29min ago zfs-snapshot-daily.timer     zfs-snapshot-daily.service
Tue 2026-04-21 07:31:28 MDT           16h Sun 2026-04-19 20:15:47 MDT           7h ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Mon 2026-04-27 00:00:00 MDT        6 days Mon 2026-04-20 09:43:22 MDT     5h 29min ago zfs-snapshot-weekly.timer    zfs-snapshot-weekly.service
Mon 2026-04-27 01:09:27 MDT        6 days Mon 2026-04-20 09:43:22 MDT     5h 29min ago fstrim.timer                 fstrim.service
Mon 2026-04-27 04:28:38 MDT        6 days Mon 2026-04-20 09:43:22 MDT     5h 29min ago zpool-trim.timer             zpool-trim.service
Fri 2026-05-01 00:00:00 MDT 1 week 3 days Wed 2026-04-01 10:07:51 MDT 1 week 1 day ago zfs-snapshot-monthly.timer   zfs-snapshot-monthly.service
Fri 2026-05-01 03:17:17 MDT 1 week 3 days Wed 2026-04-01 10:07:51 MDT 1 week 1 day ago zfs-scrub.timer              zfs-scrub.service

11 timers listed.
Pass --all to see loaded but inactive timers, too.
  • Tek bir komutla, zamanlayıcı takvimine göre çalışan öğelerin genel görünümünü elde edebilirsiniz
  • list-timers, sık kullanılan systemd alt komutları ailesinin bir parçasıdır
    • list-units de kullanışlıdır
    • list-paths, systemctle daha yakın zamanda eklenen bir alt komuttur

Askı durumundayken uyandırıp çalıştırma

  • WakeSystem=, süresi dolan bir zamanlayıcının sistemi askı durumundan uyandırmasını sağlayabilir
WakeSystem=
    Takes a boolean argument. If true, an elapsing timer will
    cause the system to resume from suspend, should it be
    suspended and if the system supports this.
...
  • Bu özellik, birinin dizüstü bilgisayar kapağını açmasına gerek kalmadan önemli betiklerin çalışması gerektiğinde kullanışlıdır
  • Arch ya da NixOS gibi, kullanımdan önce paket güncellemesi indirmeyi destekleyen dağıtımlarda, gece geç saatlerde güncelleme paketleri önceden indirilebilir ve sabah klavye başına geçtiğinizde güncelleme yapılabilir
  • Kılavuzda, .service tamamlandıktan sonra sistemin yeniden askıya alınmasını istiyorsanız bunun elle yeniden yapılması gerektiği belirtilir

Çalışma zamanlarını dağıtma ve thundering herd etkisini azaltma

  • thundering herd sorunu, birçok sürecin aynı anda uyanmasıyla ortaya çıkan bir sistem problemidir
  • Dünyadaki tüm Debian sistemleri 00:00:00 anında apt update çalıştıracak şekilde sabit kodlanmış olsaydı, gece yarısı herkes için kötü bir trafik zirvesi olurdu
  • FixedRandomDelay= ve RandomizedOffsetSec= çalışma zamanlarını dağıtmaya yardımcı olur
FixedRandomDelay=
    Takes a boolean argument. When enabled, the randomized delay
    specified by RandomizedDelaySec= is chosen deterministically,
    and remains stable between all firings of the same timer,
    even if the manager is restarted. ...

RandomizedOffsetSec=
    Offsets the timer by a stable, randomly-selected, and evenly
    distributed amount of time between 0 and the specified time
    value. ...
  • Bu ayarlar, yazılım güncellemelerini kontrol eden gerçek sistemlerde kullanılabilir
  • Çalıştırmaları eşit dağılımla yaymak, thundering herd sorununu azaltmaya; davranışı tutarlı hale getirmeye ve dağıtık servisleri koordine eden arka plan süreçlerinin yeniden başlatılması gibi bozucu faaliyetlerden kaçınmaya yardımcı olur
  • Zamanlama seçenekleri genel olarak çok yapılandırılabilir ve ince taneli denetim sunar

Kaçırılan çalıştırmaları hemen telafi etme

  • Persistent=, özellikle askıdaki dizüstü bilgisayar yüzünden kaçırılmaması gereken ama WakeSystem= kadar da gerekli olmayan zamanlanmış betikler için uygundur
Persistent=
    Takes a boolean argument. If true, the time when the service
    unit was last triggered is stored on disk. When the timer is
    activated, the service unit is triggered immediately if it
    would have been triggered at least once during the time when
    the timer was inactive. ...
  • Yapılandırma yönetimi check-in'i planlanmış bir sistem kesinti yaşadıysa, .timer içine yalnızca Persistent= eklemek bile sistem çevrimiçi olur olmaz doğru duruma yakınsamasını sağlayabilir
  • Persistent= yoksa zamanlayıcının normal çalışma anını beklemeniz gerekebilir ve bu süre uzun olabilir
  • Kaçırılan etkinleştirmeyi fark ettiğinde beklememesi gereken başka işler arasında sistem güncellemeleri ve toplu iş kontrolleri de vardır

Zamanlayıcı yazarken dikkat edilmesi gerekenler

  • systemctl --user ile yönetilen kullanıcı yöneticisi bağlamındaki zamanlayıcılar da geçerlidir, ancak [Install] içinde kullanılacak hedefe dikkat etmek gerekir
  • Dağıtıma bağlı olarak kullanıcı zamanlayıcıları için uygun hedef default.target olabilir
  • Tıpkı cronda olduğu gibi, sistem saatinin doğru tutulmasına dair genel uyarı burada da geçerlidir
  • systemd kullanıcıları eşzamanlama durumunu timedatectl timesync-status ile kontrol edebilir
  • Birçok düzenleyici systemd ünite dosyası biçimini varsayılan olarak destekler; bu da ünite dosyaları büyüdüğünde yardımcı olur
  • Emacs'te emacs systemd paketi kullanılabilir

1 yorum

 
Lobste.rs görüşleri
  • systemd mükemmel değil ama birçok tasarım kararının daha geleneksel eski yaklaşımlardan çıkarılan derslere dayandığı hissine kapılıyorum
    Yakın zamanda Lennart Poettering’in bunun arka planını anlattığı CRE’nin 2015 tarihli bölümünü yeniden dinledim; hâlâ tavsiye etmeye değer

  • İçten içe systemd’den hiç hoşlanmayan taraftayım ama systemd.timers’ın bu üründeki “eh işte daha iyi” fikirlerden biri olduğunu düşünüyorum
    Bu yüzden yazarın, geçerli şikâyetleri olan insanları küçümser gibi savunmaya geçmesi biraz şaşırtıcıydı
    Yine de at komutuyla birlikte kullanmak güzel. Belirli bir zamanda bir kez çalıştırılacak komutlar için at, diğer her şey içinse systemd zamanlayıcıları ve basit unit dosyaları kullanıyorum
    En çok görmek istediğim iyileştirme, zamanlayıcıyı hangi kullanıcının çalıştırdığını görebilmek. 2026’da shell box işleten sayılı insanlardan biri olabilirim ama her saniye diske vuran bir zamanlayıcıyı hangi kullanıcının oluşturduğunu bilmek faydalı olurdu

    • Bu amaç için, herkesin sistem zamanlayıcısı kurmasını sağlamak yerine kullanıcı unit’leri kullanılabilir diye düşünüyorum
      loginctl enable-linger ile etkin bir kullanıcı oturumu olmadan da çalışabildiklerini sanıyorum. Elbette bunun yetmeyeceği kullanım örnekleri de vardır, somut durumu bilmiyorum
  • Özellikle kullanıcı yönetimi tarafında systemd zamanlayıcılarının başlangıç yükü daha düşük olsa iyi olurdu
    Gerekli yapılandırma miktarına bakınca crontab -e’yi geçmek gerçekten zor

    • Tek bir zamanlayıcı kurmak için birden fazla yapılandırma dosyası ve servis gerektiren systemd yaklaşımı… API tercihi olarak savunulamayacak kadar kötü
  • Cron betiği loglarını düzenli biçimde toplamanın bir yolunu uzun süre düşündükten sonra, aslında sadece systemd zamanlayıcıları kullanmam gerektiğini fark ettim
    Loglama sorunu çözülmüş oldu. Artık cron’a geri dönmek için bir nedenim yok; keşke bunu daha önce bilseydim

    • logger ile pipe etmek ya da log dosyasına >> ile eklemek veya varsayılanı bırakıp e-posta almak yeterli değil mi?
  • İstediğiniz kadar eski kafalı deyin ama sunucudan bana ulaşan e-postayı hâlâ yapılandırıyorum
    Otomatikleştirince her yeni hostta bedavaya geliyor ve günlük kullanımda da oldukça pratik
    Örneğin bir multiplexer açıp long_running_process | mail root@localhost -s "done $?" çalıştırıyor ve sonra unutuyorum

  • Güzel bir yazı; benim de benzer bir yazının taslağı vardı ve yakın zamanda tekrar başvurmam gerekti
    Benim gibi systemd tavşan deliğine girerseniz, ilgili proje klasöründeki unit dosyalarını ve zamanlayıcıları /etc/systemd/system/ içine sembolik bağlantı ile koymanızı öneririm
    systemd ile ilgili bir şikâyetim, dağıtımın kurduğu unit’lerle benim yazdığım unit’leri ayırt etmemesi; sembolik bağlantılarla bu ayrımı kendiniz koruyabilirsiniz

    • Aslında bu ayrımı yol belirliyor
      Sistem/paket/dağıtım unit’leri /usr/lib/systemd/system altında olur, yerel override’lar veya yerel unit’ler ise /etc/systemd/system altında yer alır