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
- Tek konteyner kurulumu için Docker ile CaptchaAI
- Dağıtık işleme için Redis kuyruğu ile CaptchaAI
CAPTCHA hattınızı binlerce göreve taşıyın — CaptchaAI'yi edinin.