Celery Flower ile Görev İzleme ve Yönetim Arayüzü

Production ortamında Celery kullanmaya başladığınızda, bir süre sonra fark ediyorsunuz: görevlerin ne durumda olduğunu anlamak gerçekten zor. Terminal’de celery inspect active yazıyorsunuz, bir şeyler çıkıyor ama o an ne kadar görev birikmiş, hangisi başarısız olmuş, worker’lar sağlıklı mı çalışıyor – bunları tek tek komutlarla takip etmek hem zahmetli hem de yetersiz kalıyor. İşte tam bu noktada Flower devreye giriyor.

Flower, Celery için geliştirilmiş açık kaynaklı bir web tabanlı izleme ve yönetim aracı. Sadece “dashboard” demek yetmez; görevleri gerçek zamanlı izleyebiliyorsunuz, worker’ları yönetebiliyorsunuz, başarısız görevleri yeniden kuyruğa alabiliyorsunuz ve bunların hepsini bir tarayıcı üzerinden yapabiliyorsunuz. Bu yazıda Flower’ı production ortamına nasıl kuracağınızı, güvenli hale getireceğinizi ve anlamlı şekilde kullanacağınızı aktarıyorum.

Flower Nedir, Ne Değildir?

Önce beklentileri doğru ayarlayalım. Flower bir APM (Application Performance Monitoring) aracı değil. Datadog veya New Relic’in yerine geçmiyor. Flower, spesifik olarak Celery ekosistemi için tasarlanmış, broker üzerindeki mesajları ve worker durumlarını görselleştiren bir araç.

Flower’ın size sağladıkları:

  • Gerçek zamanlı görev izleme: Hangi görev hangi worker’da çalışıyor, ne kadar süredir devam ediyor
  • Worker yönetimi: Worker’ları uzaktan kapatabilir, havuzunu değiştirebilir, görev oranlarını görebilirsiniz
  • Görev geçmişi: Başarılı, başarısız ve bekleyen görevlerin listesi
  • Kuyruk izleme: Kuyruklarda bekleyen mesaj sayıları
  • HTTP API: Flower’ın kendi REST API’si var, bunu otomasyon için kullanabilirsiniz
  • Temel kimlik doğrulama: Basit kullanıcı adı/şifre koruması

Flower’ın size vermediği şeyler de var: uzun dönemli metrik depolama, gelişmiş alerting, trace/span düzeyinde profiling. Bunlar için Prometheus + Grafana kombinasyonunu düşünmek gerekiyor, ama bu ayrı bir konu.

Kurulum ve Temel Yapılandırma

Flower’ı projenize eklemek oldukça basit:

pip install flower

# Veya requirements.txt'e ekleyin
echo "flower==2.0.1" >> requirements.txt
pip install -r requirements.txt

Celery uygulamanızın var olduğunu varsayarak en basit haliyle Flower’ı şöyle başlatabilirsiniz:

# Celery uygulamanızla birlikte
celery -A myapp flower

# Belirli bir port ve broker adresiyle
celery -A myapp flower --port=5555 --broker=redis://localhost:6379/0

# Sadece flower binary ile (broker'ı direkt belirterek)
flower --broker=redis://localhost:6379/0 --port=5555

Burada dikkat edilmesi gereken bir nokta var: Flower, Celery uygulamanızın celery_app nesnesine erişmek zorunda. Eğer -A myapp parametresiyle çalıştırırsanız, Celery’nin tüm konfigürasyonunu (görev routing’i dahil) okuyabilir. Aksi takdirde sadece broker’a doğrudan bağlanır, bu da bazı özelliklerin eksik çalışmasına neden olabilir.

Uygulama Yapısı

Gerçek bir proje yapısında genellikle şöyle bir düzen olur:

myproject/
├── myapp/
│   ├── __init__.py
│   ├── celery.py        # Celery app tanımı
│   ├── tasks.py         # Görev tanımları
│   └── settings.py
├── requirements.txt
└── docker-compose.yml

celery.py içeriği:

# myapp/celery.py
from celery import Celery
from django.conf import settings  # Django kullanıyorsanız

app = Celery('myapp')

app.config_from_object('django.conf:settings', namespace='CELERY')

# Tüm task modüllerini otomatik keşfet
app.autodiscover_tasks()

# Celery Beat için periyodik görevler
app.conf.beat_schedule = {
    'cleanup-expired-sessions': {
        'task': 'myapp.tasks.cleanup_sessions',
        'schedule': 3600.0,  # Her saat
    },
}

Docker ile Production Kurulumu

Gerçek dünyada kimse Flower’ı doğrudan bare metal üzerinde çalıştırmıyor (ya da çalıştırmamalı). Docker Compose ile birlikte tutarlı bir ortam oluşturmak çok daha mantıklı:

# docker-compose.yml
version: '3.8'

services:
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    command: redis-server --appendonly yes

  worker:
    build: .
    command: celery -A myapp worker --loglevel=info --concurrency=4
    environment:
      - CELERY_BROKER_URL=redis://redis:6379/0
      - CELERY_RESULT_BACKEND=redis://redis:6379/1
    depends_on:
      - redis
    volumes:
      - .:/app

  flower:
    build: .
    command: celery -A myapp flower
      --broker=redis://redis:6379/0
      --port=5555
      --basic_auth=admin:guclu_sifre_buraya
      --persistent=True
      --db=/flower_data/flower.db
      --max_tasks=10000
    ports:
      - "5555:5555"
    environment:
      - CELERY_BROKER_URL=redis://redis:6379/0
    depends_on:
      - redis
    volumes:
      - flower_data:/flower_data

volumes:
  redis_data:
  flower_data:

--persistent=True ve --db parametrelerine dikkat edin. Flower varsayılan olarak görev geçmişini bellekte tutuyor, container yeniden başladığında her şey gidiyor. Persistent mode açıkken SQLite veritabanına yazıyor ve yeniden başlatmalardan sonra geçmiş korunuyor. Production’da bu şart.

--max_tasks=10000 parametresi de önemli: Flower ne kadar görev geçmişini bellekte/veritabanında saklayacağını bu değerle sınırlıyor. Yüksek trafikli sistemlerde bu değeri kontrol altında tutmazsanız memory kullanımı şişebilir.

Güvenlik Yapılandırması

Flower’ı açık internete basic auth olmadan açmak büyük bir güvenlik açığı. Bunu bir kez production’da görünce bir daha yapmıyorsunuz. Basic auth en temel koruma ama yeterli değil, reverse proxy arkasına almak gerekiyor.

Nginx konfigürasyonu:

# /etc/nginx/sites-available/flower
upstream flower_backend {
    server 127.0.0.1:5555;
}

server {
    listen 80;
    server_name flower.sirketiniz.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name flower.sirketiniz.com;

    ssl_certificate /etc/letsencrypt/live/flower.sirketiniz.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/flower.sirketiniz.com/privkey.pem;

    # Sadece ofis IP'lerinden erişim
    allow 85.XX.XX.XX;    # Ofis IP'niz
    allow 95.XX.XX.XX;    # VPN IP'niz
    deny all;

    location / {
        proxy_pass http://flower_backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket desteği (Flower real-time updates için kullanıyor)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 86400;
    }
}

IP kısıtlaması + SSL + basic auth üçlüsü production için makul bir minimum güvenlik katmanı oluşturuyor. Daha gelişmiş senaryolarda SSO (Keycloak, Okta) entegrasyonu da Flower’ın OAuth desteği sayesinde mümkün.

Flower HTTP API Kullanımı

Flower’ın en değer katan özelliklerinden biri REST API’si. Bu API’yi monitoring scriptlerinizde, CI/CD pipeline’larınızda veya kendi dashboard’larınızda kullanabilirsiniz.

# Tüm worker'ları listele
curl -s http://admin:sifre@localhost:5555/api/workers | python3 -m json.tool

# Aktif görevleri listele
curl -s http://admin:sifre@localhost:5555/api/tasks?state=ACTIVE | python3 -m json.tool

# Belirli bir görevi iptal et
curl -X POST http://admin:sifre@localhost:5555/api/task/revoke/TASK_ID_BURAYA 
  -H "Content-Type: application/json" 
  -d '{"terminate": true}'

# Worker pool boyutunu değiştir
curl -X POST http://admin:sifre@localhost:5555/api/worker/pool/grow/celery@worker1 
  -H "Content-Type: application/json" 
  -d '{"n": 2}'

# Kuyruk uzunluklarını kontrol et
curl -s http://admin:sifre@localhost:5555/api/queues/length | python3 -m json.tool

Bu API’yi kullanarak basit bir health check scripti yazabilirsiniz:

#!/usr/bin/env python3
# flower_health_check.py
import requests
import sys
import json

FLOWER_URL = "http://localhost:5555"
FLOWER_AUTH = ("admin", "sifre_buraya")
QUEUE_THRESHOLD = 1000  # Bu kadar görev birikirse alarm ver

def check_flower_health():
    sorunlar = []

    try:
        # Worker durumunu kontrol et
        response = requests.get(
            f"{FLOWER_URL}/api/workers",
            auth=FLOWER_AUTH,
            timeout=10
        )
        workers = response.json()

        aktif_worker = sum(
            1 for w in workers.values()
            if w.get('status') is True
        )

        if aktif_worker == 0:
            sorunlar.append("KRITIK: Hic aktif worker yok!")
        elif aktif_worker < 2:
            sorunlar.append(f"UYARI: Sadece {aktif_worker} worker aktif")

        # Kuyruk dolulugunu kontrol et
        queue_response = requests.get(
            f"{FLOWER_URL}/api/queues/length",
            auth=FLOWER_AUTH,
            timeout=10
        )
        queues = queue_response.json()

        for queue_name, length in queues.items():
            if length > QUEUE_THRESHOLD:
                sorunlar.append(
                    f"UYARI: '{queue_name}' kuyrugunda "
                    f"{length} bekleyen gorev var!"
                )

    except requests.exceptions.ConnectionError:
        sorunlar.append("KRITIK: Flower'a bagilanamadi!")
        return sorunlar

    return sorunlar

if __name__ == "__main__":
    sorunlar = check_flower_health()
    if sorunlar:
        for sorun in sorunlar:
            print(sorun)
        sys.exit(1)
    else:
        print("OK: Celery cluster saglikli gorunuyor")
        sys.exit(0)

Bu scripti cron’a ekleyip Slack veya PagerDuty ile entegre edebilirsiniz. Özellikle celery@ prefix’iyle başlayan worker isimlerini tanıyan monitoring sistemleri yazmak, production olaylarında hayat kurtarıcı oluyor.

Gerçek Dünya Senaryosu: Başarısız Görev Yönetimi

E-ticaret projesinde sipariş email’leri gönderen bir Celery task’ımız vardı. Bir gece SMTP sunucusu geçici olarak çöktü, yüzlerce görev FAILURE durumuna düştü. Flower olmadan bu durumu fark etmek ve recover etmek saatler alırdı. Flower’ın API’sini kullanarak şöyle bir recovery scripti yazdık:

#!/usr/bin/env python3
# recover_failed_tasks.py
"""
Belirli bir zaman aralığında başarısız olan görevleri
yeniden kuyruğa alan script.
"""
import requests
from datetime import datetime, timedelta
import time

FLOWER_URL = "http://localhost:5555"
AUTH = ("admin", "sifre_buraya")

def get_failed_tasks(task_name=None, since_hours=2):
    """Son X saatte başarısız olan görevleri getir."""
    since_timestamp = (
        datetime.now() - timedelta(hours=since_hours)
    ).timestamp()

    params = {
        "state": "FAILURE",
        "limit": 500
    }

    if task_name:
        params["taskname"] = task_name

    response = requests.get(
        f"{FLOWER_URL}/api/tasks",
        auth=AUTH,
        params=params
    )

    tasks = response.json()

    # Zaman filtrelemesi
    recent_failures = {
        task_id: task_info
        for task_id, task_info in tasks.items()
        if task_info.get('received', 0) > since_timestamp
    }

    return recent_failures

def retry_task(task_id):
    """Belirli bir görevi yeniden çalıştır."""
    response = requests.post(
        f"{FLOWER_URL}/api/task/retry/{task_id}",
        auth=AUTH
    )
    return response.status_code == 200

def main():
    print("Basarisiz gorevler kontrol ediliyor...")
    basarisiz = get_failed_tasks(
        task_name="myapp.tasks.send_order_email",
        since_hours=3
    )

    print(f"Son 3 saatte {len(basarisiz)} basarisiz gorev bulundu")

    basarili = 0
    basarisiz_retry = 0

    for task_id, task_info in basarisiz.items():
        print(f"Yeniden deneniyor: {task_id[:8]}...")

        if retry_task(task_id):
            basarili += 1
        else:
            basarisiz_retry += 1

        # Broker'ı boğmamak için küçük bekleme
        time.sleep(0.1)

    print(f"nSonuc: {basarili} gorev yeniden siraya alindi, "
          f"{basarisiz_retry} gorev yanitlamadi")

if __name__ == "__main__":
    main()

Bu tür recovery scriptleri incident sırasında değil, önceden hazırlanmalı. Flower API’sini öğrenmek için en iyi zaman, bir şeyler kötü gitmeden öncesidir.

Prometheus Entegrasyonu

Flower, /metrics endpoint’i üzerinden Prometheus formatında metrik sunabiliyor. Bu özelliği aktifleştirmek için:

celery -A myapp flower 
  --broker=redis://localhost:6379/0 
  --port=5555 
  --basic_auth=admin:sifre 
  --persistent=True 
  --db=/data/flower.db

Prometheus scrape konfigürasyonu:

# prometheus.yml
scrape_configs:
  - job_name: 'celery-flower'
    static_configs:
      - targets: ['flower:5555']
    metrics_path: '/metrics'
    basic_auth:
      username: admin
      password: sifre_buraya
    scrape_interval: 30s

Flower’ın sunduğu önemli metrikler:

  • flower_events_total: Toplam Celery event sayısı
  • flower_task_prefetch_count: Prefetch’te bekleyen görev sayısı
  • flower_workers_online: Aktif worker sayısı
  • flower_task_runtime_seconds: Görev çalışma süresi histogram’ı

Grafana’da bu metrikleri kullanarak anlamlı dashboard’lar oluşturabilirsiniz. Özellikle flower_task_runtime_seconds histogram’ı ile task sürelerindeki anomalileri erken fark etmek mümkün oluyor.

Yaygın Sorunlar ve Çözümleri

Flower worker’ları göremiyorum:

Bu genellikle event’lerin kapalı olmasından kaynaklanıyor. Worker’ları --events flag’iyle başlatmanız gerekiyor:

celery -A myapp worker --loglevel=info --events

Veya Celery konfigürasyonunuza ekleyin:

# celery.py veya settings.py
CELERY_SEND_EVENTS = True
CELERY_TRACK_STARTED = True

Flower çok fazla memory kullanıyor:

--max_tasks değerini düşürün ve persistent mode’da çalıştırın. Bellekteki görev sayısını sınırlamak önemli:

celery -A myapp flower 
  --max_tasks=5000 
  --persistent=True 
  --db=/data/flower.db

WebSocket bağlantıları kopuyor:

Nginx veya başka bir reverse proxy kullanıyorsanız, WebSocket proxy ayarlarını kontrol edin. proxy_read_timeout değeri yeterince yüksek olmalı, yoksa gerçek zamanlı güncellemeler durabilir.

RabbitMQ ile kuyruk uzunlukları görünmüyor:

RabbitMQ broker kullanıyorsanız Flower’ın management API’sine erişmesi gerekiyor:

celery -A myapp flower 
  --broker=amqp://user:pass@rabbitmq:5672// 
  --broker_api=http://user:pass@rabbitmq:15672/api/

Flower’ın Sınırları ve Alternatifler

Flower ile birkaç yıl çalıştıktan sonra şunu söyleyebilirim: küçük ve orta ölçekli Celery kurulumları için ideal. Ancak bazı ciddi sınırları var.

Görev geçmişini uzun dönem saklamak istiyorsanız Flower doğru araç değil. Kendi veritabanınıza task_postrun signal’ini kullanarak görev sonuçlarını kaydetmek çok daha sağlam bir yaklaşım. Yüksek hacimli sistemlerde Flower’ın event stream’i işlemesi darboğaz yaratabilir; bu senaryolarda celery-exporter + Prometheus + Grafana stack’ini tercih etmek daha mantıklı. Alerting konusunda da Flower size fazla bir şey sunmuyor; alert kuralları için Prometheus AlertManager veya benzer bir araç şart.

Bununla birlikte Flower’ı tamamen bir kenara bırakmak da doğru değil. Kurulumu basit, görsel arayüzü işe yarıyor, HTTP API’si esneklik sağlıyor. Ekipteki herkesin terminal açmak zorunda kalmadan görev durumunu kontrol edebilmesi ciddi bir operasyonel değer.

Sonuç

Flower, Celery ekosistemi içinde “görevlerime ne oluyor?” sorusunun en pratik cevabı. Sıfırdan production’a taşırken şu adımları öneririm: önce Docker Compose ile ayağa kaldırın, persistent mode’u açın, reverse proxy arkasına alın ve basic auth ekleyin. Sonra HTTP API’yi keşfedin ve kendi monitoring/recovery scriptlerinizi yazmaya başlayın.

İlk başta “sadece bir dashboard” gibi görünüyor ama zamanla Flower’ın API’si üzerine inşa ettiğiniz scriptler, incident müdahale sürelerinizi gerçekten kısaltıyor. Başarısız görevleri toplu retry etmek, worker havuzunu dinamik ayarlamak, kuyruk doluluk alarmları kurmak – bunların hepsi Flower’ın sunduğu altyapı üzerine kolayca inşa edilebilir.

Daha büyük bir sisteme geçiş noktası ise genellikle “Flower’dan Grafana’da göremediğim metrikler var, bunu nasıl çözerim?” sorusunu sorduğunuz an. O noktada celery-exporter veya özel Prometheus exporter yazmak mantıklı hale geliyor. Ama o köprüyü geçmeden önce Flower’ın sağladığı gözlemlenebilirlik çoğu operasyonel ihtiyacı karşılamaya yetiyor.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir