手动配置验证码工作人员无法扩展。 Terraform 允许您将整个验证码解决基础设施定义为代码 - 对其进行版本控制、审查、跨环境复制,并在完成后将其拆除。
建筑学
terraform/
├── main.tf # Provider config
├── variables.tf # Input variables
├── outputs.tf # Output values
├── modules/
│ └── captcha-worker/
│ ├── main.tf # ECS/EC2 resources
│ ├── variables.tf # Module inputs
│ └── outputs.tf # Module outputs
├── environments/
│ ├── dev.tfvars
│ ├── staging.tfvars
│ └── production.tfvars
核心 Terraform 配置
提供者和后端
# main.tf
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "my-terraform-state"
key = "captcha-workers/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
provider "aws" {
region = var.aws_region
}
变量
# variables.tf
variable "aws_region" {
description = "AWS region for deployment"
type = string
default = "us-east-1"
}
variable "environment" {
description = "Environment name (dev, staging, production)"
type = string
}
variable "worker_count" {
description = "Number of CAPTCHA solving workers"
type = number
default = 3
}
variable "worker_cpu" {
description = "CPU units for each worker (1024 = 1 vCPU)"
type = number
default = 512
}
variable "worker_memory" {
description = "Memory in MB for each worker"
type = number
default = 1024
}
variable "max_workers" {
description = "Maximum workers for auto-scaling"
type = number
default = 10
}
variable "captchaai_concurrency" {
description = "Concurrent CAPTCHA tasks per worker"
type = number
default = 10
}
保密管理
# secrets.tf — Store API key in AWS Secrets Manager
resource "aws_secretsmanager_secret" "captchaai_api_key" {
name = "${var.environment}/captchaai-api-key"
description = "CaptchaAI API key for CAPTCHA solving workers"
}
# Reference secret in ECS task (never in plain text)
data "aws_secretsmanager_secret_version" "captchaai_api_key" {
secret_id = aws_secretsmanager_secret.captchaai_api_key.id
}
ECS工作集群
# ecs.tf — Fargate-based CAPTCHA workers
resource "aws_ecs_cluster" "captcha" {
name = "captcha-workers-${var.environment}"
setting {
name = "containerInsights"
value = "enabled"
}
}
resource "aws_ecs_task_definition" "captcha_worker" {
family = "captcha-worker-${var.environment}"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = var.worker_cpu
memory = var.worker_memory
execution_role_arn = aws_iam_role.ecs_execution.arn
task_role_arn = aws_iam_role.ecs_task.arn
container_definitions = jsonencode([
{
name = "captcha-worker"
image = "${aws_ecr_repository.captcha_worker.repository_url}:latest"
environment = [
{ name = "CAPTCHAAI_CONCURRENCY", value = tostring(var.captchaai_concurrency) },
{ name = "CAPTCHAAI_POLL_INTERVAL", value = "5" },
{ name = "ENVIRONMENT", value = var.environment },
]
secrets = [
{
name = "CAPTCHAAI_API_KEY"
valueFrom = aws_secretsmanager_secret.captchaai_api_key.arn
}
]
logConfiguration = {
logDriver = "awslogs"
options = {
"awslogs-group" = aws_cloudwatch_log_group.captcha.name
"awslogs-region" = var.aws_region
"awslogs-stream-prefix" = "worker"
}
}
}
])
}
resource "aws_ecs_service" "captcha_worker" {
name = "captcha-workers"
cluster = aws_ecs_cluster.captcha.id
task_definition = aws_ecs_task_definition.captcha_worker.arn
desired_count = var.worker_count
launch_type = "FARGATE"
network_configuration {
subnets = var.private_subnets
security_groups = [aws_security_group.captcha_worker.id]
}
}
自动缩放
# autoscaling.tf
resource "aws_appautoscaling_target" "captcha" {
max_capacity = var.max_workers
min_capacity = var.worker_count
resource_id = "service/${aws_ecs_cluster.captcha.name}/${aws_ecs_service.captcha_worker.name}"
scalable_dimension = "ecs:service:DesiredCount"
service_namespace = "ecs"
}
# Scale up when queue is deep
resource "aws_appautoscaling_policy" "scale_up" {
name = "captcha-scale-up"
policy_type = "StepScaling"
resource_id = aws_appautoscaling_target.captcha.resource_id
scalable_dimension = aws_appautoscaling_target.captcha.scalable_dimension
service_namespace = aws_appautoscaling_target.captcha.service_namespace
step_scaling_policy_configuration {
adjustment_type = "ChangeInCapacity"
cooldown = 120
step_adjustment {
scaling_adjustment = 2
metric_interval_lower_bound = 0
}
}
}
# Scale down when idle
resource "aws_appautoscaling_policy" "scale_down" {
name = "captcha-scale-down"
policy_type = "StepScaling"
resource_id = aws_appautoscaling_target.captcha.resource_id
scalable_dimension = aws_appautoscaling_target.captcha.scalable_dimension
service_namespace = aws_appautoscaling_target.captcha.service_namespace
step_scaling_policy_configuration {
adjustment_type = "ChangeInCapacity"
cooldown = 300
step_adjustment {
scaling_adjustment = -1
metric_interval_upper_bound = 0
}
}
}
每个环境变量
# environments/dev.tfvars
environment = "dev"
worker_count = 1
max_workers = 3
worker_cpu = 256
worker_memory = 512
captchaai_concurrency = 3
# environments/production.tfvars
environment = "production"
worker_count = 5
max_workers = 20
worker_cpu = 1024
worker_memory = 2048
captchaai_concurrency = 20
工人申请代码
"""captcha_worker.py — The container runs this."""
import os
import time
import signal
import requests
API_KEY = os.environ["CAPTCHAAI_API_KEY"]
CONCURRENCY = int(os.environ.get("CAPTCHAAI_CONCURRENCY", "10"))
POLL_INTERVAL = int(os.environ.get("CAPTCHAAI_POLL_INTERVAL", "5"))
running = True
def shutdown_handler(signum, frame):
global running
print("Graceful shutdown initiated")
running = False
signal.signal(signal.SIGTERM, shutdown_handler)
signal.signal(signal.SIGINT, shutdown_handler)
session = requests.Session()
def solve_captcha(sitekey, pageurl):
resp = session.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"json": 1
})
data = resp.json()
if data.get("status") != 1:
return {"error": data.get("request")}
captcha_id = data["request"]
for _ in range(60):
time.sleep(POLL_INTERVAL)
result = session.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": captcha_id, "json": 1
}).json()
if result.get("status") == 1:
return {"solution": result["request"]}
if result.get("request") != "CAPCHA_NOT_READY":
return {"error": result.get("request")}
return {"error": "TIMEOUT"}
# Main loop — pull tasks from SQS or Redis
print(f"Worker started: concurrency={CONCURRENCY}")
while running:
# Pull tasks from your queue here
time.sleep(1)
print("Worker shutdown complete")
部署命令
# Initialize
terraform init
# Plan for production
terraform plan -var-file=environments/production.tfvars
# Apply
terraform apply -var-file=environments/production.tfvars
# Destroy (dev cleanup)
terraform destroy -var-file=environments/dev.tfvars
故障排除
| 问题 | 原因 | 处理方式 |
|---|---|---|
| 部署时未发现秘密 | 秘密尚未填充 | 在 terraform apply 之前创建秘密值 |
| 工人们一开始就崩溃了 | 缺少环境变量或图像错误 | 检查CloudWatch日志;验证 ECR 图像标签 |
| 自动缩放未触发 | 缺少 CloudWatch 警报或指标错误 | 验证扩展策略中的警报 ARN |
| 状态锁定错误 | 先前的申请已中断 | 解锁:terraform force-unlock <lock-id> |
常问问题
对于 CAPTCHA 工作人员,我应该使用 Fargate 还是 EC2?
Fargate 实现简单性和自动扩展。 EC2 用于稳定状态下的成本优化(预留实例)。大多数团队从 Fargate 开始,并将大量工作负载转移到 EC2。
如何应对多种环境?
每个环境使用 .tfvars 文件和单独的状态文件。使用工作区或单独的状态后端来隔离 dev/staging/production.
我可以将 Terraform 与 GCP 或 Azure 结合使用吗?
是的。将 AWS 提供商和资源替换为其 GCP(Cloud Run、GKE)或 Azure(容器实例、AKS)等效项。模块结构保持不变。
下一步
编码您的验证码基础设施 –”获取您的 CaptchaAI API 密钥并使用 Terraform 进行部署。
相关指南: