DevOps & Scaling

Playbooks Ansible para deploy de workers CaptchaAI

Depois que a frota de workers CaptchaAI passa de um punhado de servidores, editar cada host na mão vira gargalo. A solução é dividir responsabilidades: o Terraform cria os servidores, o Ansible cuida do que roda dentro deles. Este guia traz um projeto Ansible completo para workers CaptchaAI — inventário por ambiente, role reutilizável, deploy, rolling update sem downtime, healthcheck e dimensionamento da frota.

Pré-requisitos antes de rodar os playbooks

Confirme estes pontos antes do primeiro ansible-playbook:

  • Nó de controle com Ansible 2.14+ e Python 3 instalados.
  • Acesso SSH por chave a todos os hosts da frota (ssh-copy-id user@host).
  • Hosts Debian/Ubuntu com apt disponível — a role usa ansible.builtin.apt; adapte para dnf/yum em distribuições RHEL.
  • Uma chave de API válida da CaptchaAI, pronta para ser criptografada com o Ansible Vault.

Estrutura do projeto Ansible

ansible/
├── inventory/
│   ├── production.yml
│   └── staging.yml
├── roles/
│   └── captcha-worker/
│       ├── tasks/
│       │   └── main.yml
│       ├── templates/
│       │   ├── captcha-worker.service.j2
│       │   └── config.yaml.j2
│       ├── handlers/
│       │   └── main.yml
│       └── defaults/
│           └── main.yml
├── playbooks/
│   ├── deploy.yml
│   ├── rolling-update.yml
│   └── health-check.yml
└── ansible.cfg

Inventário: produção e staging

Dois arquivos de inventário separam claramente staging e produção, cada um com sua própria concorrência (threads) e nível de log:

# inventory/production.yml
all:
  children:
    captcha_workers:
      hosts:
        worker-1:
          ansible_host: 10.0.1.10
        worker-2:
          ansible_host: 10.0.1.11
        worker-3:
          ansible_host: 10.0.1.12
      vars:
        captchaai_concurrency: 20
        captchaai_poll_interval: 3
        captchaai_log_level: warning
        worker_version: "1.3.0"
# inventory/staging.yml
all:
  children:
    captcha_workers:
      hosts:
        staging-worker-1:
          ansible_host: 10.0.2.10
      vars:
        captchaai_concurrency: 5
        captchaai_poll_interval: 5
        captchaai_log_level: debug
        worker_version: "1.4.0-rc1"

Dica de latência: na AWS, prefira hosts na região sa-east-1 (São Paulo) — o RTT até o endpoint da CaptchaAI cai bastante, o que importa quando captchaai_poll_interval está baixo.

Role: captcha-worker

Variáveis padrão

# roles/captcha-worker/defaults/main.yml
captchaai_concurrency: 10
captchaai_poll_interval: 5
captchaai_log_level: info
captchaai_timeout: 300
captchaai_retries: 3
worker_version: "latest"
worker_user: captcha
worker_dir: /opt/captcha-worker
worker_venv: /opt/captcha-worker/venv

Tarefas

# roles/captcha-worker/tasks/main.yml
---

- name: Create worker user
  ansible.builtin.user:
    name: "{{ worker_user }}"
    system: true
    shell: /usr/sbin/nologin
    home: "{{ worker_dir }}"

- name: Create worker directory
  ansible.builtin.file:
    path: "{{ worker_dir }}"
    state: directory
    owner: "{{ worker_user }}"
    mode: "0755"

- name: Install system dependencies
  ansible.builtin.apt:
    name:

      - python3
      - python3-venv
      - python3-pip
    state: present
    update_cache: true

- name: Create Python virtual environment
  ansible.builtin.command:
    cmd: python3 -m venv {{ worker_venv }}
    creates: "{{ worker_venv }}/bin/activate"

- name: Install Python dependencies
  ansible.builtin.pip:
    name:

      - requests>=2.31.0
      - pyyaml>=6.0
    virtualenv: "{{ worker_venv }}"

- name: Deploy worker application
  ansible.builtin.copy:
    src: captcha_worker.py
    dest: "{{ worker_dir }}/captcha_worker.py"
    owner: "{{ worker_user }}"
    mode: "0644"
  notify: restart captcha-worker

- name: Deploy configuration
  ansible.builtin.template:
    src: config.yaml.j2
    dest: "{{ worker_dir }}/config.yaml"
    owner: "{{ worker_user }}"
    mode: "0600"
  notify: restart captcha-worker

- name: Deploy systemd service
  ansible.builtin.template:
    src: captcha-worker.service.j2
    dest: /etc/systemd/system/captcha-worker.service
    mode: "0644"
  notify:

    - reload systemd
    - restart captcha-worker

- name: Enable and start service
  ansible.builtin.systemd:
    name: captcha-worker
    enabled: true
    state: started

Modelos

# roles/captcha-worker/templates/config.yaml.j2
# CaptchaAI Worker Configuration
# Managed by Ansible — do not edit manually
concurrency: {{ captchaai_concurrency }}
poll_interval: {{ captchaai_poll_interval }}
timeout: {{ captchaai_timeout }}
retries: {{ captchaai_retries }}
log_level: {{ captchaai_log_level }}
# roles/captcha-worker/templates/captcha-worker.service.j2
[Unit]
Description=CaptchaAI CAPTCHA Solving Worker
After=network.target
Wants=network-online.target

[Service]
Type=simple
User={{ worker_user }}
WorkingDirectory={{ worker_dir }}
ExecStart={{ worker_venv }}/bin/python {{ worker_dir }}/captcha_worker.py
Environment=CAPTCHAAI_API_KEY={{ captchaai_api_key }}
Restart=always
RestartSec=10
TimeoutStopSec=30

# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths={{ worker_dir }}

[Install]
WantedBy=multi-user.target

Manipuladores

# roles/captcha-worker/handlers/main.yml
---

- name: reload systemd
  ansible.builtin.systemd:
    daemon_reload: true

- name: restart captcha-worker
  ansible.builtin.systemd:
    name: captcha-worker
    state: restarted

Três playbooks cobrem o ciclo de vida do worker: implantar do zero, atualizar em produção sem downtime e checar a saúde da frota — cada um independente, rode só o que precisar.

Playbook de deploy: do zero ao worker rodando

O deploy.yml monta o worker em um host novo: pede a chave de API, aplica a role captcha-worker e confirma que o serviço está active.

# playbooks/deploy.yml
---

- name: Deploy CaptchaAI Workers
  hosts: captcha_workers
  become: true
  vars_prompt:

    - name: captchaai_api_key
      prompt: "Enter CaptchaAI API key"
      private: true

  pre_tasks:

    - name: Verify connectivity
      ansible.builtin.ping:

  roles:

    - captcha-worker

  post_tasks:

    - name: Wait for worker to start
      ansible.builtin.wait_for:
        port: 8080
        timeout: 30
      ignore_errors: true

    - name: Check worker status
      ansible.builtin.systemd:
        name: captcha-worker
      register: worker_status

    - name: Report status
      ansible.builtin.debug:
        msg: "Worker {{ inventory_hostname }}: {{ worker_status.status.ActiveState }}"

Atualização contínua (rolling update) sem downtime

O rolling-update.yml usa serial: 1: atualiza um host de cada vez, drena as tarefas antes de parar o serviço e só avança quando o healthcheck responde 200. Se um host falhar, max_fail_percentage: 0 interrompe o rollout na hora.

# playbooks/rolling-update.yml
---

- name: Rolling Update CaptchaAI Workers
  hosts: captcha_workers
  become: true
  serial: 1   # Update one host at a time
  max_fail_percentage: 0

  tasks:

    - name: Drain current tasks
      ansible.builtin.command:
        cmd: "{{ worker_venv }}/bin/python {{ worker_dir }}/drain.py"
      timeout: 120
      ignore_errors: true

    - name: Stop worker
      ansible.builtin.systemd:
        name: captcha-worker
        state: stopped

    - name: Deploy new version
      ansible.builtin.copy:
        src: "captcha_worker.py"
        dest: "{{ worker_dir }}/captcha_worker.py"
        owner: "{{ worker_user }}"
        mode: "0644"

    - name: Update dependencies
      ansible.builtin.pip:
        requirements: "{{ worker_dir }}/requirements.txt"
        virtualenv: "{{ worker_venv }}"

    - name: Start worker
      ansible.builtin.systemd:
        name: captcha-worker
        state: started

    - name: Verify worker health
      ansible.builtin.uri:
        url: "http://localhost:8080/health"
        return_content: true
      register: health
      until: health.status == 200
      retries: 6
      delay: 10

    - name: Report update result
      ansible.builtin.debug:
        msg: "{{ inventory_hostname }} updated — {{ health.content }}"

Verificação de saúde da frota

O health-check.yml não altera nada, só lê: confere o serviço systemd em cada host e, uma vez (run_once: true), consulta o saldo via getbalance para confirmar que a chave configurada ainda é válida.

# playbooks/health-check.yml
---

- name: Check CaptchaAI Worker Health
  hosts: captcha_workers
  become: false
  gather_facts: false

  tasks:

    - name: Check systemd service
      ansible.builtin.systemd:
        name: captcha-worker
      register: service_status
      become: true

    - name: Check API connectivity
      ansible.builtin.uri:
        url: "https://ocr.captchaai.com/res.php?key={{ captchaai_api_key }}&action=getbalance&json=1"
        return_content: true
      register: api_check
      delegate_to: localhost
      run_once: true

    - name: Summary
      ansible.builtin.debug:
        msg: |
          Host: {{ inventory_hostname }}
          Service: {{ service_status.status.ActiveState }}
          API Balance: {{ (api_check.content | from_json).request }}

Segurança dos workers: Vault e hardening do systemd

A role já aplica várias camadas de segurança por padrão — vale entender cada uma antes de ir para produção. worker_user roda sem shell (/usr/sbin/nologin), config.yaml sai com mode: "0600", e NoNewPrivileges=true + ProtectSystem=strict no systemd limitam o que o processo toca no sistema de arquivos.

  • Nunca commite a chave de API em texto puro no repositório do playbook.
  • Prefira ansible-vault encrypt_string a vars_prompt em pipelines automatizados.
  • Rotacione a chave periodicamente pelo painel da CaptchaAI e atualize o Vault.
  • Restrinja o acesso de leitura ao arquivo .vault_pass, se usar um.
  • Audite o journalctl -u captcha-worker após cada rotação.

Nunca deixe a chave de API em texto puro em um playbook versionado — sempre via Ansible Vault ou injetada por uma variável de ambiente no momento do deploy.

Observabilidade da frota

O health-check.yml cobre o básico; numa frota maior, vale registrar estes sinais em um lugar central em vez de rodar o playbook manualmente:

Sinal Onde aparece O que verificar
Estado do serviço systemctl status captcha-worker / journalctl -u captcha-worker Reinícios frequentes indicam crash loop
Saldo da API getbalance no res.php Saldo caindo rápido pode indicar concorrência maior que o esperado
Latência de polling Logs do captcha_worker.py Tempo entre envio e resposta acima do normal para o tipo de CAPTCHA

Com Prometheus já rodando, exportar essas métricas via node_exporter textfile collector, alimentado pelo próprio health-check.yml, é mais simples do que integrar um agente novo.

Dimensionando a frota: threads por plano CaptchaAI

A CaptchaAI cobra por thread simultânea, não por solve — cada plano inclui solves ilimitados dentro das threads contratadas. O que importa para dimensionar é o pico de workers ativos ao mesmo tempo, não o total de servidores.

Tamanho da frota Plano recomendado Threads incluídas
1–3 workers, baixo volume BASIC (US$ 15/mês) 5
Staging + produção pequena STANDARD (US$ 30/mês) 15
Produção com múltiplos workers ADVANCE (US$ 90/mês) 50
Frota grande, alta concorrência ENTERPRISE (US$ 300/mês) 200

Ajuste captchaai_concurrency no inventário conforme o plano contratado.

Comandos de execução

Comandos prontos para rodar os playbooks em staging, em produção ou limitados a um único host:

# Deploy to staging
ansible-playbook -i inventory/staging.yml playbooks/deploy.yml

# Rolling update in production
ansible-playbook -i inventory/production.yml playbooks/rolling-update.yml

# Health check
ansible-playbook -i inventory/production.yml playbooks/health-check.yml

# Limit to specific hosts
ansible-playbook -i inventory/production.yml playbooks/deploy.yml --limit worker-1

Solução de problemas comuns

Os problemas mais frequentes ao rodar esses playbooks — e como resolver cada um:

Problema Causa Correção
Host "Unreachable" Chave SSH não configurada Adicione a chave: ssh-copy-id user@host
Serviço não inicia Variável de ambiente da chave de API ausente Verifique o vars_prompt ou use o Ansible Vault
Rolling update travado Falha na verificação de integridade Verifique journalctl -u captcha-worker; aumente retries/delay
Configuração não aplicada Handler não acionado Rode com --force-handlers ou adicione changed_when: true
Health check retorna erro de conexão Porta 8080 ainda não exposta, serviço subindo Aumente retries/delay no wait_for; confira o log do worker
getbalance retorna erro na resposta captchaai_api_key inválida, expirada ou mal escapada no Vault Gere uma chave nova no painel da CaptchaAI e atualize o valor no Vault

Quando nada acima resolve, siga esta ordem antes de abrir uma issue interna:

  1. Rode health-check.yml isolado para descartar problema de infraestrutura.
  2. Confira journalctl -u captcha-worker -n 100 no host afetado.
  3. Rode o playbook de deploy com -vvv para ver exatamente qual task falhou.

Perguntas frequentes

Como armazeno a chave de API do CaptchaAI com segurança no Ansible?

Use o Ansible Vault: ansible-vault encrypt_string 'sua-chave-de-api' --name 'captchaai_api_key'. Guarde o valor criptografado no inventário ou nas group vars — nunca em texto puro no playbook.

Quantas threads eu configuro em captchaai_concurrency?

Nunca ultrapasse as threads do plano contratado (BASIC tem 5, STANDARD tem 15, ADVANCE tem 50). Configurar acima do limite não acelera nada, só enfileira no host.

Dá para usar o mesmo playbook de deploy com Docker em vez de systemd?

Sim. Basta trocar as tasks do systemd pelo módulo community.docker.docker_container; o Ansible passa a gerenciar o ciclo de vida do contêiner em vez do serviço.

Vale a pena rodar o health-check.yml antes de um rolling update?

Sim. Confirma que a frota já está saudável antes de mexer nela.

Como limito um deploy a um único host da frota?

Use a flag --limit: ansible-playbook -i inventory/production.yml playbooks/deploy.yml --limit worker-1.

Próximos passos

Escale sua frota de workers sem editar servidor por servidor: crie sua conta e obtenha a chave de API da CaptchaAI e implante com os playbooks Ansible deste guia.

Guias relacionados:

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