把 CaptchaAI 验证码识别服务部署到 Docker 里,核心就三件事:API Key 不写死进镜像、单个 worker 顶不住就用 Compose 横向扩容、生产环境用多阶段构建把镜像做小。本文从最简单的 Dockerfile 讲到 Redis 队列多 worker,代码可直接抄进项目里用。
基础镜像该怎么写
验证码识别对运行环境很敏感——本地能跑、CI 里报错,多半是 Python 或依赖版本没锁死。打包成镜像,就能让每台机器跑出一样的结果。下面是一个够用的基础镜像:
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:
requests>=2.31.0
国内网络访问 PyPI 有时不太稳定,构建镜像时可以在 pip install 后面加一个国内镜像源,比如清华 TUNA:pip install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt,能明显缩短构建耗时。
求解器脚本:调用 CaptchaAI 接口
脚本逻辑很直接:把任务提交给 in.php,拿到 task id 后每 5 秒轮询 res.php,直到拿到 token 或超时。CaptchaAI 支持 reCAPTCHA v2/v3、Turnstile、GeeTest v3、图片/九宫格验证码等类型,下面以 reCAPTCHA v2 为例:
# 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]}...")
拿到 token 之后,直接把它塞进目标页面对应的表单字段,正常提交表单即可,不需要额外处理。
构建镜像并启动容器
镜像写好后,本地先构建再跑一次,确认能拿到 token:
# 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
CAPTCHAAI_KEY 通过 -e 在运行时注入,不出现在镜像的任何一层——下一节的多阶段构建同样要保持这一点。
多阶段构建:给生产镜像瘦身
本地调试的镜像够用就行,但生产环境要更小的体积、非 root 用户运行。多阶段构建把安装和运行拆成两个阶段,最终镜像不会留下构建工具链:
# 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"]
useradd --create-home solver 加 USER solver,让容器进程以非 root 身份运行——多数安全扫描工具会把"以 root 运行"标成高风险项。
用 Docker Compose 起多个 worker
单个容器吞吐有上限,量上去了就该用 Compose 横向扩容。下面这份配置同时起了 4 个求解 worker、一个 Redis,和 4 个消费队列的 worker:
# 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
副本数最好参考 CaptchaAI 套餐的线程数:BASIC($15/月,5 线程)配 4 个 worker 差不多够用;量上来了换 ADVANCE($90/月,50 线程),副本数同步往上调。
基于 Redis 队列的 worker 脚本
如果任务是持续从队列消费(比如上游爬虫、批量任务往 Redis 塞任务),用队列 worker 模式更合适——只管取任务、求解、写回结果,扩容加副本数就行:
# 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()
脚本用的是最基础的 blpop 阻塞读取,需要优先级或失败重试可换成 Celery、RQ,逻辑不用大改。
API Key 怎么管理:环境变量与 .env
无论单容器还是 Compose 多 worker,API Key 都应在运行时通过环境变量注入,.env 文件不进 Git 仓库:
# .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
常见故障排查
| 问题 | 原因 | 处理方式 |
|---|---|---|
| 容器启动后立刻退出 | 没有传入 CAPTCHAAI_KEY | 加上 -e CAPTCHAAI_KEY=... |
| DNS 解析失败 | 容器没有网络访问权限 | 检查 Docker 网络配置 |
| 内存占用过高 | 并发请求数过多 | 限制容器内存并控制并发数 |
| API Key 出现在镜像里 | Key 写死在 Dockerfile 里 | 改用环境变量或 Docker secrets |
常见问题
requirements.txt 只锁了 requests 的版本,够用吗?
对这个脚本足够——它只靠 requests 发请求。同一镜像里如果还跑 Selenium、Playwright,建议锁到具体小版本,避免 CI 因依赖自动升级出现不可复现的构建。
API Key 能不能直接写死在 Dockerfile 里?
不建议。写进 Dockerfile 的值会留在镜像层历史里,之后删掉也能被 docker history 翻出来。始终用 -e CAPTCHAAI_KEY=... 或 Docker secrets 在运行时传入。
docker pull 拉基础镜像很慢,有办法吗?
给 Docker daemon 配置国内 registry 加速地址,或把 python:3.11-slim 换成团队自维护的私有镜像,首次拉取快不少。
worker 副本数要不要跟着 CaptchaAI 套餐的线程数走?
建议大致对齐。比如 ADVANCE($90/月,50 线程)配 8~10 个 worker 通常就能把并发打满;副本开太多,任务反而在线程层面排队等待,没有实际收益。
相关阅读
把你的验证码识别服务容器化——立即使用 CaptchaAI。