Конвейер решения CAPTCHA может «просесть» посреди ночи: вырастет время ответа, начнёт распухать очередь задач, баланс тихо уйдёт в ноль — а вы узнаете об этом только утром, из жалоб пользователей. Поймать проблему раньше них можно только одним способом: снимать метрики с воркера в реальном времени. Prometheus собирает эти метрики, Grafana превращает их в дашборд, а правила оповещений будят дежурного до того, как упадёт конверсия.
Какие метрики стоит собирать
Шести метрик достаточно для первого дашборда: три счётчика дают долю успешных решений и разбивку ошибок, гистограмма — распределение времени ответа, а два датчика — состояние очереди и баланса.
| Метрика | Тип | Что показывает |
|---|---|---|
captcha_solves_total |
Счётчик | Всего попыток решения |
captcha_solves_success |
Счётчик | Успешные решения |
captcha_solves_errors |
Счётчик | Неудачные решения (по типу ошибки) |
captcha_solve_duration |
Гистограмма | Распределение времени решения |
captcha_balance |
Датчик | Текущий остаток на счёте |
captcha_queue_length |
Датчик | Задачи, ожидающие в очереди |
Остальное — пропускная способность по типам CAPTCHA, латентность до ocr.captchaai.com — добавляйте по мере роста нагрузки, начинать стоит именно с этих шести.
Экспортер метрик на Python
Ниже — минимальный экспортер: обёртка над вызовами in.php/res.php, которая пишет значения в prometheus_client и поднимает HTTP-сервер /metrics на порту 8000. Дальше Prometheus сам приходит и опрашивает этот порт по расписанию.
# metrics.py
import time
import requests
from prometheus_client import (
Counter, Histogram, Gauge, start_http_server,
)
# Define metrics
SOLVES_TOTAL = Counter(
"captcha_solves_total",
"Total CAPTCHA solve attempts",
["method"],
)
SOLVES_SUCCESS = Counter(
"captcha_solves_success",
"Successful CAPTCHA solves",
["method"],
)
SOLVES_ERRORS = Counter(
"captcha_solves_errors",
"Failed CAPTCHA solves",
["method", "error_code"],
)
SOLVE_DURATION = Histogram(
"captcha_solve_duration_seconds",
"CAPTCHA solve duration in seconds",
["method"],
buckets=[5, 10, 15, 20, 30, 45, 60, 90, 120],
)
BALANCE = Gauge(
"captcha_balance_usd",
"Current CaptchaAI account balance in USD",
)
QUEUE_LENGTH = Gauge(
"captcha_queue_length",
"Number of pending CAPTCHA tasks",
)
class InstrumentedSolver:
"""Solver with Prometheus metric instrumentation."""
def __init__(self, api_key):
self.api_key = api_key
self.base = "https://ocr.captchaai.com"
def solve(self, method, **params):
"""Solve CAPTCHA with metric collection."""
SOLVES_TOTAL.labels(method=method).inc()
start = time.time()
try:
token = self._do_solve(method, params)
duration = time.time() - start
SOLVES_SUCCESS.labels(method=method).inc()
SOLVE_DURATION.labels(method=method).observe(duration)
return token
except Exception as e:
error_code = str(e)[:30]
SOLVES_ERRORS.labels(
method=method, error_code=error_code,
).inc()
raise
def update_balance(self):
"""Fetch and update balance metric."""
resp = requests.get(f"{self.base}/res.php", params={
"key": self.api_key,
"action": "getbalance",
"json": 1,
}, timeout=15)
balance = float(resp.json()["request"])
BALANCE.set(balance)
return balance
def _do_solve(self, method, params, timeout=120):
data = {"key": self.api_key, "method": method, "json": 1}
data.update(params)
resp = requests.post(
f"{self.base}/in.php", data=data, timeout=30,
)
result = resp.json()
if result.get("status") != 1:
raise RuntimeError(result.get("request"))
task_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": 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(data["request"])
raise TimeoutError("Solve timeout")
# Start metrics server on port 8000
start_http_server(8000)
print("Metrics server running on :8000/metrics")
Обратите внимание: _do_solve считает и успешные, и неуспешные попытки. Это важно, чтобы captcha_solves_total действительно был знаменателем для доли успешных решений, а не только суммой удачных вызовов.
Конфигурация Prometheus
Укажите адрес воркера в scrape_configs — Prometheus подключится к порту 8000 и будет опрашивать /metrics раз в 10 секунд:
# prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: "captcha-solver"
static_configs:
- targets: ["solver-app:8000"]
scrape_interval: 10s
Docker Compose стек
Для локальной проверки или staging удобнее поднять весь стек одной командой — воркер, Prometheus и Grafana в одном docker-compose-файле:
# docker-compose.yml
version: "3.8"
services:
solver:
build: .
environment:
- CAPTCHAAI_KEY=${CAPTCHAAI_KEY}
ports:
- "8000:8000"
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana-data:/var/lib/grafana
volumes:
grafana-data:
После docker compose up Grafana доступна на :3000 (логин и пароль по умолчанию — admin/admin), Prometheus — на :9090.
Запросы для дашборда Grafana
Из этих пяти PromQL-запросов собирается стартовый дашборд.
Доля успешных решений (PromQL)
rate(captcha_solves_success[5m])
/ rate(captcha_solves_total[5m]) * 100
Среднее время решения
rate(captcha_solve_duration_seconds_sum[5m])
/ rate(captcha_solve_duration_seconds_count[5m])
Ошибки по типу
sum by (error_code) (
rate(captcha_solves_errors[5m])
)
Баланс во времени
captcha_balance_usd
Время решения, перцентиль P95
histogram_quantile(0.95,
rate(captcha_solve_duration_seconds_bucket[5m])
)
Правила оповещений
Три правила закрывают базовый SLA:
- заканчивающийся баланс — предупреждение, пока есть время пополнить счёт;
- всплеск ошибок — сигнал о проблеме на стороне сети, прокси или самой CAPTCHA;
- деградация времени ответа — ранний признак того, что P95 подбирается к пользовательскому таймауту.
# alert_rules.yml
groups:
- name: captcha-alerts
rules:
- alert: LowBalance
expr: captcha_balance_usd < 5
for: 5m
labels:
severity: warning
annotations:
summary: "CaptchaAI balance below $5"
- alert: HighErrorRate
expr: |
rate(captcha_solves_errors[5m])
/ rate(captcha_solves_total[5m]) > 0.1
for: 10m
labels:
severity: critical
annotations:
summary: "CAPTCHA error rate above 10%"
- alert: SlowSolveTime
expr: |
histogram_quantile(0.95,
rate(captcha_solve_duration_seconds_bucket[5m])
) > 60
for: 15m
labels:
severity: warning
annotations:
summary: "P95 solve time exceeds 60s"
При интеграции с Alertmanager эти же правила легко направить не только на email, но и в Telegram или Slack — команды, работающие в вечерних и ночных часовых поясах СНГ, обычно заводят именно туда, чтобы не пропустить инцидент до утра.
Практический сценарий: распределённая команда
Команда, разнесённая между Алматы, Минском и Белградом, обслуживает парсер, который решает reCAPTCHA v2 и Cloudflare Turnstile через API CaptchaAI. Воркеры развёрнуты в европейском облачном регионе — так латентность до ocr.captchaai.com предсказуема и не зависит от того, из какого города дежурит инженер в конкретную ночь. В такой схеме датчик captcha_queue_length обычно становится первым сигналом: если очередь растёт быстрее, чем убывает баланс, не хватает потоков, а не денег, и решение — поднять план, а не паниковать.
Отдельная гигиеническая привычка для логов и меток Prometheus: не пишите в них pageurl или sitekey целевых страниц без необходимости. Если туда случайно попадут персональные данные пользователей, это уже вопрос 152-ФЗ «О персональных данных», а не только наблюдаемости, — собирайте в метриках только то, что действительно нужно для мониторинга.
Что стоит проверить перед тем, как объявлять дашборд готовым:
- метки
method/error_codeне содержат значений с высокой кардинальностью (например, сырых ID задач); - дашборд открывается и без доступа к конкретному воркеру — по общим меткам, а не по имени хоста;
- у каждого правила оповещения есть понятное дежурному описание причины, а не только номер ошибки.
Диагностика типичных проблем
Большинство инцидентов с этим стеком сводятся к четырём причинам — ниже они собраны в одну таблицу, чтобы не искать их по логам вручную.
| Проблема | Причина | Решение |
|---|---|---|
На /metrics нет данных |
Сервер не запущен | Вызовите start_http_server(8000) |
Prometheus показывает target как down |
Неверный адрес или порт | Проверьте сеть и порт контейнера в Docker |
| В Grafana нет данных | Prometheus не добавлен как источник данных | Добавьте data source Prometheus в Grafana |
| Метрики обнуляются при перезапуске | Ожидаемое поведение — счётчик сбрасывается | Используйте rate(), а не «сырые» значения счётчиков |
Часто задаваемые вопросы
Насколько сильно Prometheus замедляет воркер?
Практически никак: prometheus_client добавляет заметно меньше 1 мс на операцию с метрикой, а опрос раз в 10–15 секунд не создаёт ощутимой нагрузки даже на небольшом VPS.
Как мониторить несколько API-ключей CaptchaAI одновременно?
Добавьте метку key (или account) к каждой метрике и запускайте отдельный экспортер на аккаунт — либо один процесс с несколькими инстансами InstrumentedSolver. В Grafana достаточно группировать запросы по этой метке: агрегация по всем ключам считается автоматически.
Как отправлять оповещения в Telegram, а не только на email?
Настройте у Alertmanager ресивер webhook и подключите к нему Telegram-бот-шлюз (например, alertmanager-telegram-bot). Сами правила из раздела про оповещения не меняются — меняется только то, куда Alertmanager доставляет уведомление.
Сколько хранить историю метрик?
Локальный Prometheus по умолчанию хранит данные около 15 дней — этого достаточно для разбора конкретного инцидента. Для долгосрочных трендов (сравнение месяц к месяцу) подключите remote_write в Grafana Cloud, Mimir или VictoriaMetrics; конфигурация самого экспортера при этом не меняется.
С каких порогов начать в правилах оповещений?
Значения из примера в этой статье — рабочая отправная точка, а не догма: баланс ниже $5, доля ошибок выше 10 % за 10 минут и P95 времени решения выше 60 секунд. Подстройте их под свой трафик через одну-две недели наблюдения за реальными графиками, а не сразу «под ноль» — слишком чувствительные пороги быстро превращаются в шум, который дежурный инженер начинает игнорировать.
Связанные материалы
Наблюдаемость — это не разовая настройка, а рабочая привычка: подключите CaptchaAI к своему Prometheus уже сегодня и ловите проблемы раньше пользователей.