Tài Liệu Tham Khảo

CaptchaAI trong sản xuất: Hướng dẫn quản lý cấu hình

Phần lớn sự cố CaptchaAI trên production không đến từ code mà từ cấu hình: khóa API hardcode trong source, timeout copy từ máy dev, secret lộ trong log. Cách xử lý: tách cấu hình ba lớp ưu tiên rõ ràng, đưa bí mật khỏi source control, để bật/tắt tính năng không cần deploy — gồm độ ưu tiên env var, loader Python/JavaScript, file theo môi trường, quản lý bí mật và feature flag.


Ví dụ thực tế: đội automation TP.HCM và 3 tầng môi trường

Một đội automation ở TP.HCM chạy pipeline theo dõi giá Shopee, Tiki cho nhiều khách hàng, dùng chung một worker CaptchaAI trên ba tầng dev/staging/production. Mặc định nằm trong config/captchaai.yaml, mỗi tầng ghi đè bằng file riêng, khóa API chỉ vào qua secrets manager của CI/CD — không commit vào git. Cần tăng tải đợt cao điểm, họ đổi concurrency trên production rồi reload, không build lại image.

Khi lưu dữ liệu thu thập được, nên ghi log có kiểm soát theo tinh thần Nghị định 13/2023/NĐ-CP về bảo vệ dữ liệu cá nhân — không phải tư vấn pháp lý, chỉ là lý do giữ secret tách biệt khỏi log nghiệp vụ.


Thứ tự ưu tiên: env var thắng file, file thắng mặc định

Priority (highest → lowest):

1. Environment variables     ← deployment-specific overrides
2. Config file (YAML/JSON)   ← version-controlled defaults
3. Application defaults      ← fallback values in code

Biến môi trường ghi đè file, file ghi đè mặc định trong code. Có CLI flag thì đặt trên cùng — càng gần người vận hành lúc chạy lệnh, ưu tiên càng cao.

Toàn bộ tham số cấu hình

Tham số Biến Env Mặc định Mô tả
API key CAPTCHAAI_API_KEY Bắt buộc
Submit URL CAPTCHAAI_SUBMIT_URL https://ocr.captchaai.com/in.php Endpoint gửi task
Poll URL CAPTCHAAI_POLL_URL https://ocr.captchaai.com/res.php Endpoint polling
Khoảng polling CAPTCHAAI_POLL_INTERVAL 5 Giây giữa các lần polling
Số lần polling tối đa CAPTCHAAI_MAX_POLLS 60 Trước khi timeout
Concurrency CAPTCHAAI_CONCURRENCY 10 Task song song tối đa
Timeout CAPTCHAAI_TIMEOUT 300 Giây
Proxy CAPTCHAAI_PROXY URL proxy khi giải
Callback URL CAPTCHAAI_CALLBACK_URL Webhook bất đồng bộ
Số lần thử lại CAPTCHAAI_RETRIES 3 Khi lỗi tạm thời
Log level CAPTCHAAI_LOG_LEVEL info Mức chi tiết log

Loader đọc cấu hình theo đúng thứ tự ưu tiên

Loader dưới đây tự áp dụng nguyên tắc trên: mặc định → file YAML nếu có → biến môi trường ghi đè cuối cùng.

Python

import os
import yaml
from dataclasses import dataclass, field
from pathlib import Path

@dataclass
class CaptchaAIConfig:
    api_key: str = ""
    submit_url: str = "https://ocr.captchaai.com/in.php"
    poll_url: str = "https://ocr.captchaai.com/res.php"
    poll_interval: int = 5
    max_polls: int = 60
    concurrency: int = 10
    timeout: int = 300
    proxy: str = ""
    callback_url: str = ""
    retries: int = 3
    log_level: str = "info"

    @classmethod
    def load(cls, config_path=None):
        """Load config: env vars override file, which overrides defaults."""
        config = cls()

        # Layer 2: Config file
        if config_path and Path(config_path).exists():
            with open(config_path) as f:
                file_config = yaml.safe_load(f) or {}
            for key, value in file_config.items():
                if hasattr(config, key):
                    setattr(config, key, value)

        # Layer 1: Environment variables (highest priority)
        env_map = {
            "CAPTCHAAI_API_KEY": "api_key",
            "CAPTCHAAI_SUBMIT_URL": "submit_url",
            "CAPTCHAAI_POLL_URL": "poll_url",
            "CAPTCHAAI_POLL_INTERVAL": "poll_interval",
            "CAPTCHAAI_MAX_POLLS": "max_polls",
            "CAPTCHAAI_CONCURRENCY": "concurrency",
            "CAPTCHAAI_TIMEOUT": "timeout",
            "CAPTCHAAI_PROXY": "proxy",
            "CAPTCHAAI_CALLBACK_URL": "callback_url",
            "CAPTCHAAI_RETRIES": "retries",
            "CAPTCHAAI_LOG_LEVEL": "log_level",
        }

        for env_key, attr_name in env_map.items():
            value = os.environ.get(env_key)
            if value is not None:
                # Cast to correct type
                current = getattr(config, attr_name)
                if isinstance(current, int):
                    value = int(value)
                setattr(config, attr_name, value)

        config.validate()
        return config

    def validate(self):
        if not self.api_key:
            raise ValueError("CAPTCHAAI_API_KEY is required")
        if self.poll_interval < 1:
            raise ValueError("poll_interval must be >= 1")
        if self.concurrency < 1:
            raise ValueError("concurrency must be >= 1")

# Usage
config = CaptchaAIConfig.load("config/captchaai.yaml")
print(f"Concurrency: {config.concurrency}, Timeout: {config.timeout}s")

JavaScript

const fs = require("fs");
const yaml = require("js-yaml");
const path = require("path");

class CaptchaAIConfig {
  static defaults = {
    apiKey: "",
    submitUrl: "https://ocr.captchaai.com/in.php",
    pollUrl: "https://ocr.captchaai.com/res.php",
    pollInterval: 5,
    maxPolls: 60,
    concurrency: 10,
    timeout: 300,
    proxy: "",
    callbackUrl: "",
    retries: 3,
    logLevel: "info",
  };

  static envMap = {
    CAPTCHAAI_API_KEY: "apiKey",
    CAPTCHAAI_SUBMIT_URL: "submitUrl",
    CAPTCHAAI_POLL_URL: "pollUrl",
    CAPTCHAAI_POLL_INTERVAL: { key: "pollInterval", type: "int" },
    CAPTCHAAI_MAX_POLLS: { key: "maxPolls", type: "int" },
    CAPTCHAAI_CONCURRENCY: { key: "concurrency", type: "int" },
    CAPTCHAAI_TIMEOUT: { key: "timeout", type: "int" },
    CAPTCHAAI_PROXY: "proxy",
    CAPTCHAAI_CALLBACK_URL: "callbackUrl",
    CAPTCHAAI_RETRIES: { key: "retries", type: "int" },
    CAPTCHAAI_LOG_LEVEL: "logLevel",
  };

  static load(configPath = null) {
    let config = { ...CaptchaAIConfig.defaults };

    // Layer 2: Config file
    if (configPath && fs.existsSync(configPath)) {
      const ext = path.extname(configPath);
      const raw = fs.readFileSync(configPath, "utf8");
      const fileConfig = ext === ".json" ? JSON.parse(raw) : yaml.load(raw);
      config = { ...config, ...fileConfig };
    }

    // Layer 1: Environment variables
    for (const [envKey, mapping] of Object.entries(CaptchaAIConfig.envMap)) {
      const value = process.env[envKey];
      if (value !== undefined) {
        const attrKey = typeof mapping === "string" ? mapping : mapping.key;
        const type = typeof mapping === "string" ? "string" : mapping.type;
        config[attrKey] = type === "int" ? parseInt(value, 10) : value;
      }
    }

    CaptchaAIConfig.validate(config);
    return config;
  }

  static validate(config) {
    if (!config.apiKey) throw new Error("CAPTCHAAI_API_KEY is required");
    if (config.pollInterval < 1) throw new Error("pollInterval must be >= 1");
    if (config.concurrency < 1) throw new Error("concurrency must be >= 1");
  }
}

// Usage
const config = CaptchaAIConfig.load("config/captchaai.yaml");
console.log(`Concurrency: ${config.concurrency}, Timeout: ${config.timeout}s`);

File cấu hình riêng cho từng môi trường

Giữ một file base an toàn, mỗi môi trường chỉ ghi đè phần khác biệt — như đội automation ở ví dụ trên đang làm:

# config/captchaai.yaml — base
api_key: ""  # Always set via env var
concurrency: 5
poll_interval: 5
retries: 3
log_level: info
# config/captchaai.production.yaml
concurrency: 20
poll_interval: 3
timeout: 180
log_level: warning
# config/captchaai.staging.yaml
concurrency: 3
poll_interval: 5
timeout: 300
log_level: debug

Production tăng concurrency, siết timeout; staging giữ log_level: debug để soi lỗi trước khi lên production.

Quản lý bí mật: khóa API không nằm trong file cấu hình

Không lưu khóa API trong file cấu hình hay commit vào source control, kể cả file dùng nội bộ.

Phương pháp Phù hợp nhất Ví dụ
Biến môi trường Container, CI/CD export CAPTCHAAI_API_KEY=abc123
AWS Secrets Manager Hạ tầng AWS Tự xoay vòng
HashiCorp Vault Đa cloud, on-prem Secret động, TTL
Docker secrets Docker Swarm / Compose Mount /run/secrets/
File .env (chỉ dev) Local dotenv, thêm .gitignore

Ví dụ Docker Compose

services:
  captcha-worker:
    image: captcha-worker:latest
    environment:

      - CAPTCHAAI_API_KEY=${CAPTCHAAI_API_KEY}
      - CAPTCHAAI_CONCURRENCY=15
      - CAPTCHAAI_LOG_LEVEL=warning
    env_file:

      - .env.production

${CAPTCHAAI_API_KEY} lấy từ host hoặc .env.production — khóa API không nằm trong image hay file compose đã commit.

Feature flag: bật/tắt tính năng không cần deploy lại

class FeatureFlags:
    def __init__(self):
        self.flags = {
            "use_callback": os.environ.get("FF_USE_CALLBACK", "false") == "true",
            "enable_proxy": os.environ.get("FF_ENABLE_PROXY", "true") == "true",
            "max_concurrent": int(os.environ.get("FF_MAX_CONCURRENT", "10")),
        }

    def is_enabled(self, flag):
        return self.flags.get(flag, False)

    def get(self, flag, default=None):
        return self.flags.get(flag, default)

Đổi biến môi trường và reload là chuyển được polling sang callback (use_callback) hoặc bật/tắt proxy, không cần build lại image.

Khắc phục sự cố cấu hình thường gặp

Vấn đề Nguyên nhân Cách xử lý
API key không nạp được Thiếu env var hoặc sai tên biến echo $CAPTCHAAI_API_KEY, đối chiếu bảng tham số
File cấu hình bị bỏ qua Sai đường dẫn hoặc thiếu thư viện YAML Cài pyyaml / js-yaml, kiểm tra đường dẫn
Production chạy nhầm cấu hình dev Ghi đè riêng môi trường chưa áp dụng Kiểm tra thứ tự ưu tiên, biến NODE_ENV / APP_ENV
Secret lộ trong log Log dump cả cấu hình, gồm API key Che trường nhạy cảm trước khi ghi log

Câu hỏi thường gặp

CAPTCHAAI_TIMEOUT nên đặt bao nhiêu cho production?

Theo SLA giải của loại CAPTCHA dùng nhiều nhất, cộng biên độ retry. Giữ timeout production thấp hơn staging (180s so với 300s ở trên) để phát hiện task treo nhanh hơn.

Có thể đổi CAPTCHAAI_CONCURRENCY mà không khởi động lại service không?

Có, nếu loader đọc lại biến môi trường theo từng lô task thay vì chỉ lúc khởi động — cập nhật env var rồi gửi tín hiệu reload, như đội automation ở ví dụ trên vẫn làm.

Có nên dùng secrets manager cùng lúc với file YAML không?

Có: YAML giữ giá trị không nhạy cảm (concurrency, timeout, log_level), còn CAPTCHAAI_API_KEY luôn đến từ secrets manager ở lớp ưu tiên cao nhất, không trộn vào YAML dù file không bị commit.


Bước tiếp theo

Lấy API key CaptchaAI và dựng cấu hình từ các mẫu ở trên.

Liên quan:

Os comentários estão desativados para este artigo.