BIAnalytics yükleniyor

ETL otomasyonu: cron mu Airflow mu, systemd mi?

ETL otomasyonu için cron, systemd timer ve Airflow'u sorularla karşılaştırın; flock'lu crontab satırı, tek DAG ve yeniden çalıştırılabilir SQL örneği.

BIAnalytics BIAnalytics

ETL işleri tek sunucuda, birbirinden bağımsız ve gece bir kez çalışıyorsa cron yeter, ama iki şartla: çakışan çalışmayı kilitle engellemeniz ve işi aynı tarih için tekrar çalıştırdığınızda verinin ikilenmemesi gerekir. Seçimi "kaç iş var" sorusu belirlemez. Üç soru belirler: işler birbirini bekliyor mu, kaçan ya da hatalı günleri geriye dönük yeniden doldurmanız gerekiyor mu, hata olduğunda kim ve nasıl haberdar oluyor.

Üç seçenek yan yana

Sorulacak sorucronsystemd timerAirflow
Makine kapalıyken kaçan zamanlama ne olur?crontab(5) telafiden söz etmez; davranışı varsaymayın, deneyinPersistent=true ile son tetiklenme zamanı diskte tutulur; zamanlayıcı yeniden etkinleşince, kapalıyken en az bir kez tetiklenecek olan servis hemen tetiklenir (yalnız OnCalendar= ile)catchup ayarı belirler; yazmazsanız sürümün varsayılanına kalırsınız
Aynı iş bitmeden yenisi başlarsa?Kilidi siz eklersiniz (flock)Etkinleştirilecek servis zaten çalışıyorsa yeniden başlatılmaz, çalışmaya devam ederDAG'daki max_active_runs eşzamanlı aktif çalışma sayısını sınırlar
Geçmiş günleri yeniden doldurmaBetiğe tarih argümanı verip elle ya da döngüyle çalıştırırsınızAynıBackfill var; tamamlanmış çalışma temizlenerek (clear) arayüzden ya da komut satırından yeniden denenebilir
Hatada yeniden denemeKomutun içine sizin yazdığınız mantıkServis tanımında ya da betikte sizin kurduğunuz mantıkGörevde retries, retry_delay, execution_timeout
Bağımlılık (A bitmeden B başlamasın)Saat farkıyla tahminZamanlayıcı tek bir servisi tetikler; zincir kurmak için birimleri siz bağlarsınız (bu yazıda denenmedi)DAG'ın kendisi bağımlılık grafiğidir
İşletme yüküSıfıra yakınSıfıra yakınAyrı bir platformu kurmak, yükseltmek ve izlemek

Cron'da kalacaksanız: çakışmayı kilitleyin

Saatlik çalışan bir yükleme bir saati aşarsa ya da gece işi sürerken biri aynı betiği elle başlatırsa, iki kopya aynı tabloya yazar. flock bunu dosya kilidiyle önler. Üç seçenek işe yarar (flock(1)): -n kilit hemen alınamazsa beklemeden başarısız olur, -w verilen saniye içinde alınamazsa başarısız olur, -E bu iki durumda dönecek çıkış kodunu belirler (varsayılan 1). Kilit, dosya kapanınca otomatik düşer; iş çökse de kilit takılı kalmaz. Betik arka planda çocuk süreç bırakırsa kilit onda kalabilir; bu durumda -o kilit tutan dosya tanımlayıcısını komut çalışmadan önce kapatır.

[email protected]
0 2 * * * flock -n -E 75 /var/lock/etl-satis.lock /opt/etl/yukle.sh || /opt/etl/alarm.sh $?

yukle.sh aşağıdaki bölümde. Kilit dosyasının bulunduğu dizine cron'u çalıştıran kullanıcının yazabilmesi gerekir. 75 keyfi bir kod; amaç, kilit çakışmasını işin kendi hata kodlarından ayırmaktır. alarm.sh kodu görüp "çakışma" ya da "iş hatası" diye farklı mesaj atabilir. -n ve -w ile birlikte -E'nin sizin dağıtımınızdaki flock sürümünde bulunduğunu man flock ile kontrol edin; yukarıdaki sayfa güncel util-linux içindir.

Çakışmada işin sessizce atlanması da bir hatadır: o gece veri yüklenmemiştir. Kilit çıkış kodu alarma bağlı değilse, kilidi eklemekle sorunu görünmez hâle getirmiş olursunuz.

Cron'un sessiz kırılma noktaları

BelirtiNedenÇözüm
Dosya adındaki tarih bozuk, komut yarım çalışıyorKomuttaki kaçışsız % satır sonuna çevrilir; ilk %'ten sonrası komuta girdi olarak gider (crontab(5))date +\%F yazın ya da tarihi hesaplayan mantığı betiğe taşıyın
Hata olunca kimse haberdar olmuyorMAILTO boş bırakılmışsa posta gönderilmez; tanımsızsa çıktı crontab sahibine giderMAILTO'yu yazın, ayrıca log dosyasına yönlendirin ve çıkış koduyla alarm bağlayın
Betik [[ ... ]] gibi bash söz diziminde hata veriyorVarsayılan kabuk /bin/shCrontab'a SHELL=/bin/bash ekleyin ya da komutu bash ile çalışan betiğe taşıyın
cronie'de (RHEL, Fedora, Alma) yaz saati geçişinde iş ya hiç çalışmadı ya da iki kez çalıştıVar olmayan saat eşleşmez; iki kez yaşanan saat eşleşen işi iki kez çalıştırır (crontab(5))İşi geçişin yaşandığı saatlerin dışına alın; cronie'de tablo saatini CRON_TZ ile bilinçli seçin. Yükleme tekrar çalıştırılabilir olsun (aşağıdaki SQL) ve kilit kalsın
Debian/Ubuntu cron'da geçiş gecesi iş geç çalıştı ya da tekrarlanan saatte çalışmadıİleri alınan saatte atlanan saatteki işler geçişten hemen sonra çalışır; 3 saatten kısa geri alınan saatte tekrarlanan saate düşen işler yeniden çalışmaz. Yalnızca belirli saate bağlı işler etkilenir, joker (*) kullananlar yeni saate göre çalışır (cron(8))Aynı çözüm: işi geçiş saatlerinin dışına alın, yükleme tekrar çalıştırılabilir olsun, kilit kalsın. Debian crontab(5) CRON_TZ'den söz etmez; orada belgelenmiş bir çözüm olarak önermiyoruz

Elle çalışıp cron'da çalışmayan bir komutla karşılaşırsanız, betiğin içinde komutların tam yolunu yazın ve hatayı log dosyasına yönlendirin.

Orkestratörsüz bir adım ötesi: systemd zamanlayıcı

Aynı sunucuda kalacak ama kaçan çalışmanın telafisini istiyorsanız iki dosya yeter. /etc/systemd/system/etl-satis.service:

[Unit]
Description=Gunluk satis yuklemesi

[Service]
Type=oneshot
User=etl
ExecStart=/opt/etl/yukle.sh

/etc/systemd/system/etl-satis.timer:

[Unit]
Description=Satis yuklemesi zamanlayicisi

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
AccuracySec=1min
RandomizedDelaySec=300

[Install]
WantedBy=timers.target

Etkinleştirmek için systemctl daemon-reload sonrası systemctl enable --now etl-satis.timer çalıştırılır. systemd.timer(5) belgesine göre Persistent= yalnız OnCalendar= ile etkilidir, AccuracySec= varsayılanı 1 dakikadır, RandomizedDelaySec= zamanlamayı 0 ile verilen süre arasında rastgele geciktirir (varsayılan 0). Birçok zamanlayıcı aynı saate denk geliyorsa bu gecikme başlangıçları yayar. Zamanlayıcı servisi tetiklediğinde servis zaten çalışıyorsa yeniden başlatılmaz; ek flock bu yolda gerekmeyebilir, ama aynı betiği elle de çalıştırıyorsanız yine işe yarar.

Airflow'a geçerken: tek DAG

Aşağıdaki örnek Airflow 3.3.2 belgesindeki içe aktarma yollarına göredir. Her gün 00:00 UTC'de çalışır, bir önceki günün veri aralığını işler ve aralığın tarihlerini bir sonraki bölümdeki SQL'e verir. schedule="@daily" yerine veri aralıklı zamanlayıcı açıkça seçildi; nedeni aşağıdaki tabloda.

import subprocess
from datetime import timedelta

import pendulum
from airflow.sdk import CronDataIntervalTimetable, dag, task


@dag(
    schedule=CronDataIntervalTimetable("0 0 * * *", timezone="UTC"),
    start_date=pendulum.datetime(2026, 10, 1, tz="UTC"),
    catchup=False,            # geçmişi kendiliğinden doldurma; açıkça yazılı
    max_active_runs=1,        # aynı anda tek çalışma
    default_args={"retries": 2, "retry_delay": timedelta(minutes=5)},
)
def gunluk_satis_yukle():
    @task
    def yukle(*, data_interval_start, data_interval_end):
        bas = data_interval_start.date().isoformat()
        bit = data_interval_end.date().isoformat()
        subprocess.run(
            ["psql", "-v", "ON_ERROR_STOP=1",
             "-v", f"bas={bas}", "-v", f"bit={bit}",
             "-f", "/opt/etl/yukle.sql"],
            check=True,
        )

    yukle()


gunluk_satis_yukle()

Parametrelerin anlamı belgelerde şöyle geçer: max_active_runs eşzamanlı aktif çalışmayı sınırlar, retries ve retry_delay görev belgesinde tanımlıdır, data_interval_start ve data_interval_end şablon başvurusunda yer alır ve @task fonksiyonuna anahtar sözcüklü argüman olarak verilebilir (TaskFlow öğreticisi). catchup'ın varsayılanı sürüme ve yapılandırmaya göre değiştiği için örnekte değeri açıkça yazılıdır. Bağlantı bilgisi koda girmez; psql bunu ortam değişkenlerinden okur.

Veri aralıklı zamanlayıcıda data_interval_end çalışmanın tetiklendiği andır, data_interval_start aralığın başıdır; tetikleme zamanlayıcısında ise ikisi aynı değerdir (zamanlayıcı belgesi). Airflow 3'te schedule içindeki düz cron dizesi varsayılan olarak tetikleme zamanlayıcısına çevrilir; @daily gibi kısayolların nasıl çözüldüğü belgede açık olmadığı için örnek kısayola dayanmaz. İkisi aynı olursa bas ile bit eşitlenir ve sonraki bölümdeki SQL boş bir aralık siler. Veri aralıklı zamanlayıcıda çalışma kendi aralığından sonra başlar ve logical_date aralığın başlangıcını gösterir, çalışmanın yapıldığı anı değil (DAG çalışmaları belgesi). Bu yüzden "bugünün" çalışması dünün verisini yükler; panelde şaşırtıcı görünür ama doğru işleyiş budur.

Airflow 2 ile 3 arasındaki farklar

KonuAirflow 2.xAirflow 3
Çalışma anahtar sözcüğüexecution_datelogical_date; execution_date bağlam değişkenlerinden kaldırıldı
Geçmişi doldurma varsayılanıcatchup_by_default varsayılanı TrueYükseltme belgesine göre catchup_by_default artık varsayılan olarak False
schedule içindeki düz cron dizesiVeri aralıklı zamanlayıcı (CronDataIntervalTimetable)Varsayılan olarak tetikleme zamanlayıcısı (CronTriggerTimetable); yükseltme belgesi create_cron_data_intervals varsayılanının False olduğunu söyler
İçe aktarmafrom airflow.decorators import dag, task; DAG için airflow.models.dag.DAGfrom airflow.sdk import dag, task; DAG için airflow.sdk.DAG
Zamanlama parametresiSürümünüzün belgesine bakınDAG sınıfı belgesinde schedule bulunur, schedule_interval parametresi yer almaz
Standart operatörlerÇekirdek paketteapache-airflow-providers-standard paketine ayrıldı

Kaynaklar: Airflow 3'e yükseltme belgesi ve Task SDK DAG başvurusu. Yukarıdaki kod Airflow 3 içindir; 2.x'te yalnız içe aktarma satırı değil, bağlam anahtarları da değişir, o sürümün belgesinden denetleyin.

Airflow'un ne olduğunu bilmiyorsanız önce Airflow rehberine, zamanlama kavramı için zamanlama ve orkestrasyon rehberine bakın.

Hangisini seçerseniz seçin: yüklemeyi yeniden çalıştırılabilir yapın

Üç yolda da bir iş yarıda kesilebilir, elle yeniden çalıştırılabilir, otomatik denenebilir ve zamanlama iki kez tetiklenebilir. Yükleme "sona ekle" biçimindeyse her tekrar satırları ikiler. Çözüm, bir tarih aralığını aynı işlemde silip yeniden yazmaktır; aynı aralık kaç kez çalışırsa çalışsın sonuç aynı kalır.

/opt/etl/yukle.sql (PostgreSQL, psql değişkenleriyle; tablo ve sütun adları örnektir):

BEGIN;

DELETE FROM dwh.satis_gunluk
WHERE satis_tarihi >= :'bas'::date
  AND satis_tarihi <  :'bit'::date;

INSERT INTO dwh.satis_gunluk (satis_tarihi, magaza_id, adet, tutar)
SELECT s.satis_tarihi, s.magaza_id, SUM(s.adet), SUM(s.tutar)
FROM stg.satis s
WHERE s.satis_tarihi >= :'bas'::date
  AND s.satis_tarihi <  :'bit'::date
GROUP BY s.satis_tarihi, s.magaza_id;

COMMIT;

ON_ERROR_STOP=1 ile çalışan psql, ilk hatada durur; bağlantı kapanınca bitmemiş işlem geri alınır, yani silme tek başına kalıcı olmaz.

Cron ve systemd için çağıran betik /opt/etl/yukle.sh: argüman verilmezse dünü yükler, verilirse o günü yeniden yükler. date -d GNU date söz dizimidir.

#!/bin/sh
bas="${1:-$(date -d yesterday +%F)}"
bit=$(date -d "$bas + 1 day" +%F)
psql -v ON_ERROR_STOP=1 -v bas="$bas" -v bit="$bit" -f /opt/etl/yukle.sql

Geçmişi doldurmak için /opt/etl/yukle.sh 2026-10-05 yeter. Airflow'da aynı aralığı temizleyip yeniden çalıştırmak aynı SQL'i aynı tarihlerle çalıştırır. Yalnızca değişen kayıtları çekmek ve kaçan kayıtları yakalamak ayrı bir konudur; onun için artımlı yüklemede kayıp kayıt yazısına bakın. Kavramın kendisi için ETL rehberi yeterlidir.

Orkestratöre geçmeden önce altı soru

  • İşler birbirini bekliyor mu, yani B'nin A'nın başarısına bağlı olduğu en az bir zincir var mı?
  • Geçmiş günleri yeniden doldurmak istisna mı, yoksa ayda birkaç kez yaptığınız bir iş mi?
  • Hata olunca bakılacak yer tek ve herkesin görebildiği bir ekran mı olmalı?
  • Zamanlama tanımı kod gözden geçirmesinden ve sürüm kontrolünden geçmeli mi? Crontab sunucuda duran bir dosyadır.
  • Airflow'u kuracak, yükseltecek ve izleyecek biri var mı? Bu emeği karşılayacak bir işiniz var mı?
  • Mevcut işler tekrar çalıştırılabilir mi? Değilse orkestratör sorunu çözmez, yalnızca ikilenmeyi otomatikleştirir.

Karar verirken şöyle bir yol izleyin:

  • İşler bağımsız, tek sunucuda ve yeniden doldurma nadirse: cron ile flock ve çıkış koduna bağlı alarm.
  • Aynı sunucuda kalıyor ama kaçan çalışmanın telafisi lazımsa: Persistent=true ile systemd zamanlayıcı.
  • Zincir bağımlılığı, rutin yeniden doldurma ve ortak izleme gerekiyorsa: Airflow. Kurulum ve işletim desteği düşünüyorsanız Apache Airflow danışmanlık sayfası var.
Bu yazıyı paylaşın LinkedIn'de paylaş

Sıradaki yazılar

Burada anlatılana benzer bir işiniz mi var?

Yazıların arkasındaki ekibe doğrudan yazabilirsiniz.

Bizimle iletişime geçin

Demo Talebi

Çözümlerimizi yakından tanımak için formu doldurun.

person
mail
phone
notes