DevOps & Scaling

Geniş Ölçekte CAPTCHA Çözümü için Kubernetes İş Kuyrukları

Tek bir worker süreci aynı anda yalnızca birkaç CAPTCHA çözer; iş yükü günde on binlerce göreve çıkınca tek düğüm tıkanır. Çözüm, görevleri bir Redis kuyruğuna alıp Kubernetes'te talebe göre ölçeklenen worker bölmelerine dağıtmaktır.


Mimari genel bakış

Akış tek yönlü ve gevşek bağlıdır: producer kuyruğa yazar, worker bölmeleri çeker ve CaptchaAI API'ye gönderir, sonuç ayrı bir Redis deposuna düşer. Bu ayrıştırma sayesinde her katmanı bağımsız ölçekleyebilirsiniz. Üretici trafiği ani yükseldiğinde kuyruk tampon görevi görür ve worker'lar görevleri kendi işleme hızlarında tüketir; böylece istek dalgalanmaları worker'ları çökertmek yerine kuyruk derinliğine yansır. HPA'nın ölçeklendirme kararı için okuduğu sinyal de tam olarak bu derinliktir.

Producer → Redis Queue → Worker Pods (auto-scaled) → CaptchaAI API
                              ↓
                       Results Store (Redis)

Worker dağıtımı (Deployment)

Worker'ları 3 replica ile başlatın; bu, HPA devreye girene kadar makul bir taban kapasite sağlar. API anahtarı bir Secret üzerinden CAPTCHAAI_KEY değişkenine enjekte edilir, böylece imaja gömülmez. requests değerleri zamanlayıcının bölmeyi hangi düğüme yerleştireceğini belirler; limits ise tek bir bölmenin bellek veya CPU'yu tüketip komşu bölmeleri aç bırakmasını önler. Worker çoğunlukla ağ üzerinde beklediği (CaptchaAI yanıtını sorguladığı) için CPU talebi düşük tutulmuştur — bu iş yükünde darboğaz işlemci değil, eşzamanlı istek sayısıdır.

# k8s/worker-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: captcha-worker
  labels:
    app: captcha-worker
spec:
  replicas: 3
  selector:
    matchLabels:
      app: captcha-worker
  template:
    metadata:
      labels:
        app: captcha-worker
    spec:
      containers:

        - name: worker
          image: your-registry/captcha-worker:latest
          env:

            - name: CAPTCHAAI_KEY
              valueFrom:
                secretKeyRef:
                  name: captchaai-secret
                  key: api-key

            - name: REDIS_URL
              value: "redis://redis-service:6379"
          resources:
            requests:
              memory: "128Mi"
              cpu: "100m"
            limits:
              memory: "256Mi"
              cpu: "250m"

API anahtarını Secret olarak saklama

Anahtarı imaja ya da YAML'a gömmeyin — imajı çeken herkes anahtarı görebilir. Anahtarı ayrı bir Secret'ta tutmak, onu sızdırmadan döndürmenizi (rotate) ve staging ile üretim için farklı anahtarlar kullanmanızı sağlar. Secret'ı şu komutla oluşturun:

kubectl create secret generic captchaai-secret \
  --from-literal=api-key=YOUR_API_KEY

Redis dağıtımı

Başlangıç ve orta ölçek için tek bir Redis örneği yeterlidir; kuyruk ve sonuç deposu aynı örnekte durabilir. Ancak bu tek örnek aynı zamanda tek hata noktasıdır — çökerse bekleyen görevler kaybolur. Üretimde yüksek erişilebilirlik gerekiyorsa kalıcılığı (AOF/RDB) açık, yönetilen bir Redis servisi ya da Redis Sentinel/Cluster kurulumu tercih edin.

# k8s/redis.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:

        - name: redis
          image: redis:7-alpine
          ports:

            - containerPort: 6379
          resources:
            requests:
              memory: "128Mi"
              cpu: "100m"
---
apiVersion: v1
kind: Service
metadata:
  name: redis-service
spec:
  selector:
    app: redis
  ports:

    - port: 6379

Worker kodu

Worker döngüsü, blpop ile kuyruğu bloklayarak dinler (boş kuyrukta CPU harcamaz), görevi CaptchaAI in.php uç noktasına gönderir, sonucu res.php üzerinden periyodik olarak sorgular ve tamamlanan token'ı captcha:results hash'ine yazar. Her döngü sonunda kuyruk uzunluğunu captcha:queue_length anahtarına yazar; HPA'nın harici metrik olarak okuduğu değer budur. Çözüm 120 saniyede tamamlanmazsa TimeoutError fırlatılır ve hata sonuç deposuna kaydedilir, böylece takılan bir görev worker'ı süresiz meşgul etmez.

# worker.py
import os
import json
import time
import redis
import requests


class CaptchaWorker:
    """Kubernetes worker that processes CAPTCHA tasks from Redis."""

    def __init__(self):
        self.api_key = os.environ["CAPTCHAAI_KEY"]
        self.redis = redis.from_url(
            os.environ.get("REDIS_URL", "redis://localhost:6379"),
        )
        self.base = "https://ocr.captchaai.com"

    def run(self):
        """Main worker loop."""
        hostname = os.environ.get("HOSTNAME", "unknown")
        print(f"Worker {hostname} started")

        while True:
            result = self.redis.blpop("captcha:queue", timeout=30)
            if result is None:
                continue

            _, raw = result
            task = json.loads(raw)
            task_id = task.get("id", "unknown")

            print(f"[{hostname}] Processing {task_id}")
            start = time.time()

            try:
                token = self._solve(task["method"], task["params"])
                duration = time.time() - start
                self.redis.hset("captcha:results", task_id, json.dumps({
                    "status": "success",
                    "token": token,
                    "duration": f"{duration:.1f}s",
                    "worker": hostname,
                }))
                print(f"[{hostname}] {task_id} solved in {duration:.1f}s")

            except Exception as e:
                self.redis.hset("captcha:results", task_id, json.dumps({
                    "status": "error",
                    "error": str(e),
                    "worker": hostname,
                }))
                print(f"[{hostname}] {task_id} failed: {e}")

            # Update queue length metric
            queue_len = self.redis.llen("captcha:queue")
            self.redis.set("captcha:queue_length", queue_len)

    def _solve(self, method, params, timeout=120):
        resp = requests.post(f"{self.base}/in.php", data={
            "key": self.api_key,
            "method": method,
            "json": 1,
            **params,
        }, timeout=30)
        result = resp.json()

        if result.get("status") != 1:
            raise RuntimeError(result.get("request"))

        captcha_id = result["request"]

        start = time.time()
        while time.time() - start < timeout:
            time.sleep(5)
            resp = requests.get(f"{self.base}/res.php", params={
                "key": self.api_key,
                "action": "get",
                "id": captcha_id,
                "json": 1,
            }, timeout=15)
            data = resp.json()
            if data["request"] != "CAPCHA_NOT_READY":
                if data.get("status") == 1:
                    return data["request"]
                raise RuntimeError(data["request"])

        raise TimeoutError("Solve timeout")


if __name__ == "__main__":
    CaptchaWorker().run()

HPA ile otomatik ölçeklendirme

Worker'ları CPU yerine kuyruk derinliğine göre ölçeklemek bu iş yükü için daha doğrudur: gerçek yük göstergesi bekleyen görev sayısıdır, işlemci kullanımı değil. Bölme başına ortalama 10 görev biriktiğinde HPA yeni worker ekler; kuyruk boşaldıkça minReplicas değerine (2) kadar geri iner. maxReplicas (20) tavanını, aşağıda açıklanan plan thread tavanınızla uyumlu tutun. Harici Redis metriğini HPA'ya taşımak için KEDA gibi bir metrik adaptörüne ihtiyacınız olur:

# k8s/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: captcha-worker-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: captcha-worker
  minReplicas: 2
  maxReplicas: 20
  metrics:

    - type: External
      external:
        metric:
          name: redis_queue_length
          selector:
            matchLabels:
              queue: captcha
        target:
          type: AverageValue
          averageValue: "10"

Görev üreticisi (producer)

Her göreve benzersiz kimlik atanıp rpush ile kuyruğa eklenir; get_results sonuçları kimliğe göre toplar.

import json
import uuid
import redis


def submit_tasks(redis_url, tasks):
    """Submit CAPTCHA tasks to the queue."""
    r = redis.from_url(redis_url)
    task_ids = []

    for task in tasks:
        task_id = str(uuid.uuid4())[:8]
        task["id"] = task_id
        r.rpush("captcha:queue", json.dumps(task))
        task_ids.append(task_id)

    return task_ids


def get_results(redis_url, task_ids, timeout=180):
    """Wait for and collect results."""
    r = redis.from_url(redis_url)
    results = {}
    deadline = time.time() + timeout

    while len(results) < len(task_ids) and time.time() < deadline:
        for tid in task_ids:
            if tid in results:
                continue
            raw = r.hget("captcha:results", tid)
            if raw:
                results[tid] = json.loads(raw)
        time.sleep(1)

    return results

Kapasite planlaması ve maliyet

Bölme sayısını artırmak eşzamanlı çözüm sayısını sınırsız artırmaz. Gerçek eşzamanlılık tavanı CaptchaAI planınızdaki thread sayısıdır: her thread aynı anda tek bir çözümü tutar. 100 worker bölmesi çalıştırsanız bile 5 thread'lik bir planda aynı anda yalnızca 5 çözüm ilerler, kalan bölmeler API yanıtını bekler. Bu nedenle maxReplicas değerini plan thread tavanınızın belirgin biçimde üzerine çıkarmak yalnızca boş bölme ve kaynak israfı üretir.

Bölme sayısını ve plan boyutunu birlikte planlayın. Küçük ve orta hatlar için BASIC ($15/ay, 5 thread) veya STANDARD ($30/ay, 15 thread) mantıklı bir başlangıçtır; sürekli yüksek hacim için ADVANCE ($90/ay, 50 thread) ya da PREMIUM ($170/ay, 100 thread) daha uygundur. Fiyatların USD ve thread bazlı olması, TL oynaklığından bağımsız olarak aylık maliyeti öngörülebilir kılar — kapasiteyi bütçeye bağlamak isteyen ekipler için pratik bir avantajdır.

Kubernetes kümenizde kişisel veri işleyen iş akışları varsa (örneğin e-ticaret QA senaryolarında müşteri verisi), toplanan ya da test edilen verilerin KVKK kapsamına girebileceğini unutmayın; CaptchaAI'yi yetkili QA ve veri toplama akışları içinde konumlandırın.


Sorun giderme

Ölçekli bir kurulumda en sık karşılaşacağınız sorunlar ve ilk bakılacak noktalar:

Sorun Sebep Düzeltme
Worker'lar başlamıyor Secret oluşturulmadı kubectl create secret komutunu çalıştırın
Bölmeler CrashLoopBackOff'ta Ortam değişkenleri veya Redis eksik Günlükleri kubectl logs ile inceleyin
HPA ölçeklenmiyor Özel metrikler yapılandırılmadı Metrik adaptörünü (KEDA) kurun
Kuyruk büyüyor ama işlenmiyor Worker'lar boşta veya çökmüş Bölme sağlığını kontrol edip yeniden başlatın

Sık sorulan sorular

Bu iş yükü için kaç thread'lik plan gerekir?

Kaç bölme çalıştırırsanız çalıştırın, gerçek eşzamanlılık tavanı CaptchaAI plan thread sayınızdır — her thread aynı anda tek çözümü tutar. BASIC ($15/ay, 5 thread) küçük hatlara yeter; yüksek hacim için ADVANCE ($90/ay, 50 thread) veya PREMIUM ($170/ay, 100 thread) planına geçin. Fiyatlar USD ve thread bazlı olduğundan aylık maliyet öngörülebilir kalır.

KEDA mı yoksa HPA mı kullanmalıyım?

İkisi de çalışır. KEDA, Redis kuyruk uzunluğunu yerel bir tetikleyici olarak destekler ve özel metrik adaptörü kurmaktan daha az yapılandırma ister.

Worker bölmesi çökerse görevler kaybolur mu?

blpop görevi çektiği anda bellekte tutar; bölme bitirmeden çökerse o görev kaybolabilir. Kritik işlerde, tamamlanmayanları yeniden kuyruğa alan bir onay (ack) deseni ekleyin — örneğin görevi işleme alırken ayrı bir "processing" listesine taşıyıp tamamlanınca oradan silin.

Redis örneği çökerse kuyruktaki görevler ne olur?

Tek Redis örneğinde kalıcılık kapalıysa bekleyen görevler ve sonuçlar kaybolur. Üretimde AOF kalıcılığını açın veya yüksek erişilebilirlikli/yönetilen bir Redis kullanın; Redis'i kümenizin en dayanıklı bileşeni olarak ele alın.

Kuyruk derinliğini ve worker sağlığını nasıl izlerim?

Worker her döngüde captcha:queue_length değerini günceller; bu anahtarı bir Redis exporter ile toplayıp panonuzda izleyebilirsiniz. Bölme sağlığı için kubectl get pods ve kubectl top pods, ayrıntılı hata ayıklama için kubectl logs yeterlidir.


İlgili kılavuzlar


CAPTCHA hattınızı binlerce göreve taşıyın — CaptchaAI'yi edinin.

Bu makale için yorumlar devre dışı bırakılmıştır.