Ders 24 / 24
Zamanlanmış Görevler
Süreli görev tanımının okunması, zamanlayıcı ortamının etkileşimli kabuktan farkı, çıktı yönetimi, üst üste binmeyi engelleyen sarmalayıcı.
İçindekiler
Betik doğru, sağlam ve denetlenmiş durumda — ama hâlâ elle çalıştırılıyor. Bir raporun değeri düzenli üretilmesindedir: her sabah bir önceki günün kütüğünü çözümleyen ve sonucu bir yere bırakan bir araç, elle çalıştırılan aynı araçtan farklı bir şeydir.
Bu ders, bir betiği zamanlanmış çalışmaya hazırlamayı ele alır. Konu yalnızca zamanlama tanımı değildir; asıl mesele, betiğin insan olmayan bir çağırıcı tarafından, dar bir ortamda ve gözlemsiz çalıştırılacak olmasıdır.
Süreli Görev Tanımı
Unix sistemlerinde süreli görevler bir zamanlayıcı hizmetine tanımlanır. Tanım tablosu, kullanıcı başına tutulur ve her satırı beş zaman alanı ile bir komuttan oluşur:
dakika saat ayın günü ay haftanın günü komut
| Alan | Aralık |
|---|---|
| dakika | 0–59 |
| saat | 0–23 |
| ayın günü | 1–31 |
| ay | 1–12 |
| haftanın günü | 0–7 (0 ve 7 pazar) |
Her alan dört biçimden birini alır: sabit sayı, * (her değer), a-b (aralık), a,b,c
(liste). Bir aralığın ardına /n eklenirse n adımlı seçim yapılır.
30 6 * * * her gun 06:30 0 */4 * * * her dort saatte bir, saat basi 15 2 * * 1 her pazartesi 02:15 0 0 1 * * her ayin birinci gunu gece yarisi */10 * * * * her on dakikada bir
İki alan birlikte kısıt getirir ama ayın günü ile haftanın günü ikilisi istisnadır:
ikisi de * dışında bir değer alırsa koşullar “ve” ile değil “veya” ile birleşir. 0 0 13 * 5
tanımı, ayın 13’ünde ya da cuma günü çalışır. Bu, tanımların en sık yanlış okunan
noktasıdır.
Tanım tablosu crontab -e ile düzenlenir, crontab -l ile listelenir. crontab -r
tabloyu onay sormadan siler; -e ile arasında tek harf bulunduğu için, listeleme
alışkanlığı -l üzerinden kurulmalıdır.
Bazı zamanlayıcılar @daily, @hourly, @reboot gibi kısaltmalar tanır. Bunlar POSIX’te
tanımlı değildir; taşınabilir tanımlarda beş alanlı biçim kullanılır.
Zamanlayıcı hizmetinin bir başka sınıfı da servis yöneticisinin zamanlayıcı birimleridir. Bağımlılık tanımlama, kaçırılan çalışmaları telafi etme ve çalışmaların kütüğünü servis altyapısıyla birleştirme yetenekleri sunar. Servis yöneticisi modeli Sistem Yönetimi kursunun konusudur.
Zamanlayıcı Ortamı
Zamanlanmış görevlerin başarısız olmasının en yaygın nedeni, betikte değil ortamdadır. Zamanlayıcı, komutu oturum açma kabuğu olmadan çalıştırır: kabuk başlangıç dosyaları okunmaz, kabuk fonksiyonları ve takma adlar yoktur, ortam değişkenlerinin çoğu tanımsızdır.
En belirgin fark arama yoludur. Dar bir ortamda çalıştırılan aynı betik:
env -i PATH=/usr/bin:/bin bash ortam.sh
PATH=/usr/bin:/bin shellcheck bulundu mu: hayir
Etkileşimli oturumda bulunan bir araç, zamanlanmış çalışmada bulunamayabilir. Sonuç, “betik elle çalışıyor ama zamanlayıcıda çalışmıyor” biçiminde ortaya çıkar ve zamanlayıcı sessiz olduğu için fark edilmesi gecikir.
Üç önlem alınır.
Arama yolunu betikte belirtin. Betiğin başına export PATH=... yazmak, çağırma
ortamından bağımsızlık sağlar. Kullanılan araçların yolları biliniyorsa bu, en sağlam
çözümdür.
Dosya yollarını saltık yazın. Zamanlayıcının başlangıç dizini garanti değildir; göreli yollar farklı bir dizinde çözülür. Betiğin okuduğu ve yazdığı her yol saltık olmalıdır.
Gereken ortam değişkenlerini betikte tanımlayın. Yerel ayar, dil ve saat dilimi
değişkenleri etkileşimli oturumdan devralınmaz. Bu kursta export LC_ALL=C satırının
betiğin başına konması, bu nedenle de gereklidir: sıralama düzeni, zamanlayıcı ortamında
başka türlü belirsiz kalırdı.
Tanım tablosuna özgü bir ayrıntı daha vardır: komut alanında % karakteri satır sonu
anlamına gelir ve kaçırılmadan yazılamaz. Tarih biçimlendirmesi gibi % içeren komutlar
doğrudan tabloya yazılmaz — bir sarmalayıcı betiğe konur.
Çıktı Nereye Gider
Zamanlayıcı, görevin ürettiği standart çıktı ve standart hatayı toplayıp kullanıcıya iletmeye çalışır. İletim yapılandırılmamışsa çıktı kaybolur ya da yerel bir posta kuyruğunda birikir.
Bu davranış iki sonuç doğurur. Birincisi, her çalıştırmada çıktı üreten bir görev sürekli bildirim üretir; bildirimler bir süre sonra okunmaz olur. İkincisi, hata iletileri de aynı yolla kaybolur.
Doğru düzen, çıktıyı açıkça yönetmektir:
30 6 * * * /opt/rapor/rapor-gorevi.sh
ve yönlendirmeleri betiğin içinde yapmak. Betiğin içinde yapıldığında yollar okunur kalır,
% sorunu doğmaz ve yönlendirme mantığı sürüm denetimine girer.
Konunun ilk dersinde kurulan akış ayrımı burada karşılığını bulur: raporun kendisi bir dosyaya, tanı iletileri ayrı bir dosyaya yazılır. İkisi karıştırılırsa raporu okuyan bir sonraki adım uyarı metnini veri sanar.
Sarmalayıcı Betik
Zamanlanmış görev, iş yapan betiği doğrudan çağırmaz; onu bir sarmalayıcıyla sarar. Sarmalayıcı ortamı kurar, kilidi alır, çıktıyı yönlendirir ve iş betiğini çağırır.
#!/usr/bin/env bash set -Eeuo pipefail export PATH=/usr/local/bin:/usr/bin:/bin export LC_ALL=C KOK="/opt/rapor" KILIT="$KOK/kilit" KUTUK_DIZINI="$KOK/kutuk" mkdir -p "$KUTUK_DIZINI" if ! mkdir "$KILIT" 2>/dev/null; then echo "onceki calisma suruyor" >&2 exit 75 fi trap 'rmdir "$KILIT"' EXIT "$KOK/rapor.sh" -n 3 "$KOK/erisim.log" \ >> "$KUTUK_DIZINI/rapor.out" 2>> "$KUTUK_DIZINI/rapor.err"
Beş karar bir arada duruyor.
Kilit. Tuzaklar ve Temizlik dersinde kurulan mkdir deseni burada gerçek işlevini
buluyor: her on dakikada bir çalışan ve bazen on dakikadan uzun süren bir görev, kilit
olmadan kendi kendine çakışır. 75 kodu “geçici hata” bildirir.
Ortam. PATH ve LC_ALL betiğin içinde sabitlendi; zamanlayıcının verdiği ortama
güvenilmiyor.
Saltık yollar. Hiçbir göreli yol yok; başlangıç dizini ne olursa olsun betik aynı dosyaları bulur.
Ayrık çıktı. Rapor rapor.out, tanı iletileri rapor.err dosyasına ekleniyor.
Yönlendirmeler >> biçiminde; her çalıştırma öncekinin üzerine yazmıyor.
Katı kip. Sarmalayıcının kendisi de betiktir ve aynı kurallara tabidir.
Kütük dosyalarının sonsuza dek büyümesi ayrı bir sorundur ve dosya döndürme araçlarıyla çözülür; bu araçlar Sistem Yönetimi kursunda ele alınır.
Görevi Sınamak
Zamanlanmış bir görev, tanımlanmadan önce zamanlayıcının koşullarında sınanmalıdır. Sınama sırası şudur:
- Sarmalayıcıyı elle çalıştırın; çıktı dosyalarının doğru yerde oluştuğunu doğrulayın.
- Aynı çağrıyı dar bir ortamda yineleyin:
env -i PATH=/usr/bin:/bin ./rapor-gorevi.sh. Bu, zamanlayıcı ortamına yakın bir benzetimdir. - Kilidi sınayın: bir örnek çalışırken ikincisini başlatın ve
75kodunu doğrulayın. - Hata yolunu sınayın: var olmayan bir kütük adı verin ve
rapor.errdosyasına anlamlı bir ileti düştüğünü görün. - Tanımı tabloya, gerçek zamanından birkaç dakika sonrasına yazın ve ilk çalışmayı gözleyin.
Çıkış kodu bu zincirin son halkasıdır. Görevin başarısız olduğunu bir izleme sistemine bildiren şey çıkış kodudur; bu yüzden betiğin ürettiği kodlar bilinçli seçilmiş olmalıdır. Bu kursta kullanılan kod düzeni şöyleydi:
| Kod | Anlamı |
|---|---|
| 0 | rapor üretildi |
| 2 | kullanım hatası (geçersiz seçenek ya da değer) |
| 3 | girdi hatası (kütük yok, okunamıyor) |
| 75 | geçici hata, sonra yeniden dene (kilit alınamadı) |
| 128 + n | sinyalle sonlandırıldı |
Tamamlanmış Betik
Kurs boyunca geliştirilen betiğin son hâli:
#!/usr/bin/env bash # rapor.sh — erisim kutugunden ozet rapor uretir. # Kullanim: rapor.sh [-n SAYI] [KUTUK] set -Eeuo pipefail export LC_ALL=C BETIK_ADI="rapor.sh" SATIR_KALIBI='^[0-9.]+ - - \[[^]]+\] "[A-Z]+ [^ ]+ HTTP/[0-9.]+" [0-9]{3} [0-9]+$' gecici="" hata() { printf '%s: hata: %s\n' "$BETIK_ADI" "$*" >&2; } uyari() { printf '%s: uyari: %s\n' "$BETIK_ADI" "$*" >&2; } oldur() { hata "$1"; exit "${2:-1}"; } bolum() { printf '\n== %s ==\n' "$*"; } temizle() { local kod=$? [ -n "$gecici" ] && rm -rf "$gecici" return "$kod" } trap temizle EXIT trap 'temizle; trap - INT; kill -INT $$' INT trap 'temizle; trap - TERM; kill -TERM $$' TERM trap 'hata "$LINENO. satirda beklenmeyen hata"' ERR ozet() { awk ' { istek++; bayt += $10; sinif[substr($9, 1, 1)]++ } END { printf " istek : %d\n", istek printf " bayt : %d\n", bayt printf " ortalama: %.1f bayt\n", (istek ? bayt / istek : 0) for (s = 2; s <= 5; s++) printf " %sxx %4d %%%.1f\n", s, sinif[s], (istek ? sinif[s] * 100 / istek : 0) } ' "$1" } en_cok_yol() { cut -d' ' -f7 "$1" \ | sed -E 's#/[0-9]+#/:id#g' \ | sort | uniq -c | sort -k1,1nr -k2,2 | head -"$2" } hata_yollari() { awk '$9 ~ /^[45]/ { n[$7]++ } END { for (y in n) printf "%3d %s\n", n[y], y }' "$1" \ | sort -k1,1nr -k2,2 } adet=5 while getopts ':n:' secenek; do case "$secenek" in n) adet="$OPTARG" ;; :) oldur "-$OPTARG deger bekliyor" 2 ;; \?) oldur "bilinmeyen secenek -$OPTARG" 2 ;; esac done shift $((OPTIND - 1)) kutuk="${1:-erisim.log}" [[ "$adet" =~ ^[1-9][0-9]*$ ]] || oldur "-n icin pozitif tam sayi bekleniyor: $adet" 2 [ -f "$kutuk" ] || oldur "duz dosya degil ya da yok: $kutuk" 3 [ -r "$kutuk" ] || oldur "okuma izni yok: $kutuk" 3 [ -s "$kutuk" ] || uyari "kutuk bos: $kutuk" gecici=$(mktemp -d) gecerli="$gecici/gecerli.log" grep -E "$SATIR_KALIBI" "$kutuk" | awk 'NF == 10' > "$gecerli" || true bozuk=$(( $(grep -c '' "$kutuk") - $(grep -c '' "$gecerli") )) [ "$bozuk" -eq 0 ] || uyari "$bozuk satir beklenen bicimde degil, cozumleme disi" bolum "Kutuk: $kutuk" ozet "$gecerli" bolum "En cok istenen $adet yol" en_cok_yol "$gecerli" "$adet" bolum "Hata donduren yollar" hata_yollari "$gecerli"
Betiğin son eklentisi, çözümlemeden önce geçerli satırları ayıran adımdır. Düzenli İfadeler dersindeki satır kalıbı ile Alan Tabanlı İşleme dersindeki alan sayısı sınaması birlikte uygulanıyor: ikisi de tek başına yetersizdi. Elenen satırlar sayılıp standart hataya bildiriliyor, sayımlar ise yalnızca geçerli satırlar üzerinden yapılıyor — böylece bozuk bir girdi raporu sessizce çarpıtmıyor.
Temiz kütükle:
./rapor.sh -n 3
== Kutuk: erisim.log == istek : 30 bayt : 117780 ortalama: 3926.0 bayt 2xx 18 %60.0 3xx 3 %10.0 4xx 6 %20.0 5xx 3 %10.0 == En cok istenen 3 yol == 6 /api/veri 6 /urun/:id 4 /index.html == Hata donduren yollar == 4 /yok.html 3 /api/veri 2 /gizli
İki bozuk satır eklenmiş bir kütükle:
./rapor.sh -n 2 tumu.log
rapor.sh: uyari: 2 satir beklenen bicimde degil, cozumleme disi == Kutuk: tumu.log == istek : 30 bayt : 117780 ortalama: 3926.0 bayt 2xx 18 %60.0 ...
Uyarı standart hataya, rapor standart çıktıya gitti ve sayımlar bozuk satırlardan etkilenmedi.
Betiğin bilinen bir davranışı vardır: çıktısı head gibi erken kapanan bir komuta
bağlandığında, pipefail etkin olduğu için ERR kancası SIGPIPE nedeniyle tetiklenir.
Bu, katı kipin bilinçli bir ödünüdür; çıktıyı kısaltmak -n seçeneğiyle yapılmalıdır.
Özet
- Süreli görev tanımı beş zaman alanı ve bir komuttan oluşur; ayın günü ile haftanın günü birlikte verildiğinde koşullar “veya” ile birleşir.
- Zamanlayıcı, komutu oturum açma kabuğu olmadan çalıştırır; arama yolu dardır, başlangıç dosyaları okunmaz ve ortam değişkenlerinin çoğu tanımsızdır.
- Betikte arama yolu ve yerel ayar sabitlenir, tüm dosya yolları saltık yazılır.
- Çıktı açıkça yönetilir: rapor ve tanı iletileri ayrı dosyalara eklenir.
- Sarmalayıcı betik ortamı kurar, kilidi alır ve çıktıyı yönlendirir; kilit üst üste binen çalışmaları engeller.
- Çıkış kodları izleme sisteminin tek girdisidir; kullanım, girdi ve geçici hata sınıfları ayrı kodlarla bildirilir.
Kurs Kapanışı
Bu kurs tek bir soruyla başladı: bir komut yazdığı veriyi tam olarak nereye yazar? Yanıtı üç akış ve dosya tanıtıcısıydı. Oradan çıkan her şey, aynı fikrin genişletilmesidir.
Akışlar ve boru hatları komutları birbirine bağlamayı kurdu: yönlendirmenin komuttan önce uygulanması, boru hattındaki süreçlerin eşzamanlı çalışması, alt kabukta kaybolan değişkenler ve çıkış kodu sözleşmesi.
Betik yapısı tek satırlık hatları bir programa dönüştürdü. Bu bölümün merkezinde, kabuğu diğer dillerden ayıran şey duruyordu: genişletmenin sonucu yeniden çözümlenir. Kelime bölme, yol adı genişletmesi ve alıntılama kuralları bu kursun akademik çekirdeğidir; kabuk betiklerindeki hataların çoğu buradan doğar.
Metin işleme işi asıl araçlarına devretti: kalıp dili, satır süzme, akış editörü, alan tabanlı işleme ve sıralama ailesi. Bölümü kapatan seçim ölçütü — en dar aracı seç, üç araçtan fazlasını gerektiren problemde aracı değiştir — bu kursun pratik mirasıdır.
Sağlam betik yazımı betiği gözlemsiz çalışmaya hazırladı: katı kip, temizlik kancaları, izleme araçları, statik çözümleme ve zamanlanmış çalışma.
Kurs boyunca aynı betik geliştirildi. rapor.sh sabit bir boru hattı olarak başladı;
değişken kazandı, argüman arayüzü edindi, girdilerini sınamayı öğrendi, fonksiyonlara
ayrıldı, sayımlarını tek geçişe indirdi, hatalarda durmayı ve arkasını toplamayı öğrendi.
Son hâli, denetimden geçen ve zamanlayıcıya verilebilen bir araçtır.
Kabuğun sınırı da bu yolculukta göründü. Kabuk, süreçleri birbirine bağlamakta ve dosya sistemiyle çalışmakta rakipsizdir. Karmaşık veri yapıları, ondalıklı hesap, yapılandırılmış veri biçimleri ve kapsamlı hata yönetimi için tasarlanmamıştır. İyi bir kabuk betiği, kendi sınırını bilen betiktir.
Sonraki kurs — Sistem Yönetimi — bu betiğin çalıştığı sistemi konu alır. rapor.sh
bir zamanlayıcıya verildi; peki o zamanlayıcının kendisi bir servis olarak nasıl tanımlanır
ve denetlenir? Betik bir süreç yarattı; süreçler nasıl izlenir, önceliklendirilir ve
sinyallerle yönetilir? Kütük dosyaları büyüyor; disk ve dosya sistemi yapılandırması nasıl
değiştirilir? Bu kursta sinyaller, çıkış kodları ve süreçler bir betiğin içinden görüldü.
Sonraki kurs onlara sistemin dışından bakar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.