DevOps & Scaling

Docker + CaptchaAI: Konteynerli CAPTCHA Çözümü

CAPTCHA çözüm mantığınızı tek bir Docker imajında paketlerseniz aynı kod geliştirme makinenizde, CI'da ve üretimde birebir aynı davranır — "bende çalışıyordu" tartışması biter. Bu rehber yalın bir Dockerfile'dan başlar, çok aşamalı üretim build'ine ilerler ve Redis kuyruğuyla çok worker'lı bir CaptchaAI kurulumuyla biter; API anahtarını hiçbir noktada imaja gömmeden, worker sayısını CaptchaAI thread planınıza göre ölçekleyerek.

Konteynerleştirmenin çözücü iş akışına üç somut faydası var:

  • Tutarlılık: Python sürümü ve bağımlılıklar imaja sabitlenir; ortamlar arası "farklı davranış" sürprizleri ortadan kalkar.
  • Ölçeklenebilirlik: aynı imajı replica sayısını artırarak yatayda büyütürsünüz, kodu tekrar yazmazsınız.
  • Temiz dağıtım: tek bir artefakt build edilir, test edilir ve üretime alınır — geri alma da aynı ölçüde kolaydır.

Yalın bir Dockerfile ile başlayın

Başlangıç noktası sade: Python 3.11 slim imajı, bağımlılıkları kuran tek bir katman ve çalışma zamanında verilen bir API anahtarı. Anahtarı ENV ile boş bırakıp konteyneri çalıştırırken enjekte etmek, imajı paylaşılabilir tutmanın en temiz yoludur.

FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY solver.py .

# API key passed at runtime, not baked into image
ENV CAPTCHAAI_KEY=""

CMD ["python", "solver.py"]

requirements.txt dosyası tek satırdan ibaret — HTTP istekleri için requests:

requests>=2.31.0

Çözücü scriptini yazın

solver.py, reCAPTCHA v2 için in.php uç noktasına bir görev gönderir, ardından res.php üzerinden token dönene kadar sonucu sorgular. Aynı desen Turnstile, GeeTest v3 ve diğer desteklenen türler için de geçerlidir — yalnızca method değeri ve parametreler değişir.

# solver.py
import os
import sys
import requests
import time


def solve_recaptcha(api_key, site_key, page_url):
    """Solve reCAPTCHA v2 using CaptchaAI."""
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": api_key,
        "method": "userrecaptcha",
        "googlekey": site_key,
        "pageurl": page_url,
        "json": 1,
    }, timeout=30)
    result = resp.json()

    if result.get("status") != 1:
        raise RuntimeError(f"Submit error: {result.get('request')}")

    task_id = result["request"]

    # Poll for result
    for _ in range(24):  # 120s max
        time.sleep(5)
        resp = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": api_key,
            "action": "get",
            "id": task_id,
            "json": 1,
        }, timeout=15)
        data = resp.json()
        if data["request"] != "CAPCHA_NOT_READY":
            if data.get("status") == 1:
                return data["request"]
            raise RuntimeError(f"Solve error: {data['request']}")

    raise TimeoutError("Solve timeout")


if __name__ == "__main__":
    api_key = os.environ.get("CAPTCHAAI_KEY")
    if not api_key:
        print("Error: CAPTCHAAI_KEY environment variable required")
        sys.exit(1)

    site_key = os.environ.get("SITE_KEY", "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-")
    page_url = os.environ.get("PAGE_URL", "https://example.com")

    token = solve_recaptcha(api_key, site_key, page_url)
    print(f"Token: {token[:50]}...")

İmajı oluşturun ve çalıştırın

İmajı derleyin, ardından API anahtarınızı -e bayrağıyla geçerek çalıştırın. YOUR_API_KEY yer tutucusunu kendi anahtarınızla değiştirmeyi unutmayın:

# Build
docker build -t captchaai-solver .

# Run with API key from environment
docker run --rm \
  -e CAPTCHAAI_KEY="YOUR_API_KEY" \
  -e SITE_KEY="TARGET_SITE_KEY" \
  -e PAGE_URL="https://example.com" \
  captchaai-solver

Üretim için çok aşamalı build

Üretimde imaj boyutunu küçültmek ve yüzeyi daraltmak için çok aşamalı (multi-stage) build kullanın. Bağımlılıklar ayrı bir katmanda kurulur, çalışma zamanı imajı yalnızca gereken dosyaları taşır ve konteyner root olmayan bir kullanıcıyla çalışır. Bu yaklaşım üç kazanç sağlar:

  • Derleme araçları nihai imaja sızmaz, boyut belirgin şekilde küçülür.
  • root olmayan solver kullanıcısı konteyner ayrıcalıklarını daraltır.
  • PYTHONUNBUFFERED logların anında akmasını sağlar, böylece worker davranışını gerçek zamanlı izlersiniz.
# Build stage
FROM python:3.11-slim AS builder

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --target=/app/deps -r requirements.txt

# Runtime stage
FROM python:3.11-slim

# Run as non-root
RUN useradd --create-home solver
USER solver

WORKDIR /home/solver/app

COPY --from=builder /app/deps /home/solver/app/deps
COPY solver.py .

ENV PYTHONPATH=/home/solver/app/deps
ENV PYTHONUNBUFFERED=1

CMD ["python", "solver.py"]

Docker Compose ile worker'ları ölçekleyin

Tek bir konteyner çoğu iş için yeterlidir; hacim arttığında Docker Compose ile yatay ölçekleyin. Aşağıdaki kurulum çözücü worker'larını, bir Redis kuyruğunu ve kuyruktan görev çeken worker'ları birlikte ayağa kaldırır:

# docker-compose.yml
version: "3.8"

services:
  solver-worker:
    build: .
    environment:

      - CAPTCHAAI_KEY=${CAPTCHAAI_KEY}
    restart: unless-stopped
    deploy:
      replicas: 4
      resources:
        limits:
          memory: 256M
          cpus: "0.25"

  redis:
    image: redis:7-alpine
    ports:

      - "6379:6379"

  queue-worker:
    build:
      context: .
      dockerfile: Dockerfile.worker
    environment:

      - CAPTCHAAI_KEY=${CAPTCHAAI_KEY}
      - REDIS_URL=redis://redis:6379
    depends_on:

      - redis
    deploy:
      replicas: 4

Kurulumda üç servis birlikte çalışır:

  • solver-worker — doğrudan tetiklenen tek seferlik çözüm işleri için.
  • redis — görev kuyruğu ve sonuç deposu.
  • queue-worker — kuyruktan sürekli görev çeken kalıcı worker'lar.

Kritik nokta şu: replicas sayınız ile eşzamanlı istek toplamınız, CaptchaAI planınızın thread sayısını aşmamalı. Küçük bir QA akışı için BASIC ($15/ay, 5 thread) yeterliyken, e-ticaret ve fintech tarafında sürekli çalışan otomasyonlar genelde ADVANCE ($90/ay, 50 thread) seviyesinde rahat eder. Fiyatların USD ve thread bazlı olması, TL'deki kur oynaklığından etkilenmeden aylık maliyeti öngörülebilir kılar — Türkiye'deki ekipler için somut bir avantaj.


Redis kuyruğuyla worker

Kuyruk tabanlı worker, Redis'ten görevleri blpop ile bloklayarak çeker, çözer ve sonucu bir hash'e yazar. Bu desen, worker'ları durdurmadan ölçekleyip işi birden fazla replica arasında dağıtmanıza olanak tanır:

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


def process_task(api_key, task_data):
    """Process a single CAPTCHA task from the queue."""
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": api_key,
        "method": task_data["method"],
        "json": 1,
        **task_data["params"],
    }, timeout=30)
    result = resp.json()

    if result.get("status") != 1:
        return {"error": result.get("request")}

    task_id = result["request"]

    for _ in range(24):
        time.sleep(5)
        resp = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": api_key, "action": "get",
            "id": task_id, "json": 1,
        }, timeout=15)
        data = resp.json()
        if data["request"] != "CAPCHA_NOT_READY":
            if data.get("status") == 1:
                return {"token": data["request"]}
            return {"error": data["request"]}

    return {"error": "timeout"}


def main():
    api_key = os.environ["CAPTCHAAI_KEY"]
    redis_url = os.environ.get("REDIS_URL", "redis://localhost:6379")
    r = redis.from_url(redis_url)

    print("Worker started, waiting for tasks...")
    while True:
        _, raw = r.blpop("captcha:tasks")
        task = json.loads(raw)
        task_id = task.get("id", "unknown")

        print(f"Processing task {task_id}...")
        result = process_task(api_key, task)

        r.hset("captcha:results", task_id, json.dumps(result))
        print(f"Task {task_id} done: {'ok' if 'token' in result else 'error'}")


if __name__ == "__main__":
    main()

Ortam değişkenleri ve secret yönetimi

API anahtarını asla imaja veya Git'e koymayın. Geliştirmede .env dosyası pratiktir; üretimde Docker secret veya Kubernetes secret tercih edin:

# .env file (never commit to Git)
CAPTCHAAI_KEY=your_api_key_here

# .gitignore
echo ".env" >> .gitignore

# Run with .env file
docker compose --env-file .env up -d

# Scale workers
docker compose up -d --scale queue-worker=8

Üretimde nelere dikkat etmeli

Çok replica'lı bir kurulumda güvenilirliği belirleyen ayrıntılar koddan çok işletim tarafındadır:

  • İdempotent görevler: Bir worker çökerse görev yeniden işlenebilmeli; kuyruk anahtarını çözüm tamamlanmadan silmeyin.
  • Zaman aşımı: res.php sorgusu 120 saniye içinde sonuç döndürmezse görevi başarısız sayın — örnekteki 24 × 5 saniyelik döngü tam olarak bunu yapar.
  • Yeniden deneme: Geçici hatalarda üstel geri çekilme (exponential backoff) ile tekrar deneyin; sabit aralıkla yeniden denemek kuyruğu tıkar.
  • Loglama: Her worker'ın task_id bazlı log basması, çok replica'lı ortamda hata ayıklamayı ölçülebilir hale getirir.

Sorun giderme

Sorun Sebep Düzeltme
Konteyner hemen kapanıyor CAPTCHAAI_KEY eksik -e CAPTCHAAI_KEY=... ile anahtarı geçin
DNS çözümlemesi başarısız Ağ erişimi yok Docker ağ ayarlarını kontrol edin
Bellek kullanımı çok yüksek Aşırı eşzamanlı istek Konteyner belleğini ve eşzamanlılığı sınırlayın
İmajda görünen API anahtarı Anahtar Dockerfile'a gömülmüş Ortam değişkeni veya secret kullanın

Sık sorulan sorular

Docker Compose replica sayısını CaptchaAI thread sayısıyla nasıl eşleştiririm?

Eşzamanlı çalışan replica ve istek toplamınız, planınızın thread sayısını geçmemeli. Örneğin ADVANCE ($90/ay, 50 thread) planında aynı anda en çok 50 CAPTCHA çözümü havada tutabilirsiniz; daha fazla replica bu tavanı yükseltmez, yalnızca kuyrukta bekleme yaratır.

Konteyner başına ne kadar bellek ayırmalıyım?

Yalın bir requests tabanlı çözücü için 256M genelde fazlasıyla yeter — Compose örneğindeki limits değeri buradan geliyor. Bir konteynerdeki eşzamanlılığı artırdıkça belleği kademeli yükseltin ve gerçek kullanımı ölçerek ayarlayın.

API anahtarını imaja gömmek neden riskli?

İmaj kayıtlara push edildiğinde veya paylaşıldığında anahtar da onunla gider ve katman geçmişinden geri okunabilir. Anahtarı her zaman çalışma zamanında ortam değişkeni ya da secret olarak verin.

Kubernetes'e geçmeden Docker Compose yeterli mi?

Tek makinede birkaç düzine worker için Compose yeterlidir. Çok düğümlü ölçekleme, otomatik yeniden başlatma ve sağlık kontrolleri gerektiğinde Kubernetes iş kuyruklarına geçmek mantıklı olur.


İlgili kılavuzlar


Çözüm mantığınızı tek imajda paketleyin, worker'larınızı thread planınıza göre ölçekleyin — CaptchaAI'yi edinin.

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