DevOps & Skalierung

Ansible-Playbooks für die CaptchaAI-Worker-Bereitstellung

Sobald mehr als eine Handvoll Server CAPTCHAs löst, wird die Konfiguration per SSH von Hand zur Fehlerquelle: Jeder Worker driftet in eine leicht andere Version, Konfigurationsdateien laufen auseinander, und jedes Update bedeutet Ausfallzeit. Ansible beseitigt genau diese Drift. Sie beschreiben den gewünschten Zustand Ihrer Worker-Flotte einmal deklarativ – ein Inventar, eine Rolle und drei kompakte Playbooks – und rollen ihn reproduzierbar auf 3 oder 300 Server aus, inklusive Rolling Updates ohne Downtime.

Kurz zur Rollenteilung: Terraform erzeugt die Server, Ansible bringt sie in den betriebsbereiten Zustand. In der Praxis greifen beide ineinander; dieser Leitfaden konzentriert sich auf den Ansible-Teil und zeigt das vollständige Setup – von der Projektstruktur über die Rolle captcha-worker bis zu Health-Checks gegen die CaptchaAI-API.

Projektstruktur des Ansible-Repositorys

Ein sauberes Layout trennt Inventar, wiederverwendbare Rolle und die ausführbaren Playbooks. Die Rolle kapselt alles, was auf jedem Worker gleich ist; das Inventar hält die umgebungsspezifischen Unterschiede zwischen Staging und Produktion.

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

Inventar: Staging und Produktion trennen

Das Inventar listet Ihre Hosts und setzt pro Umgebung die Gruppenvariablen. In der Produktion laufen drei Worker mit höherer Nebenläufigkeit und knapperem Poll-Intervall; Staging fährt mit einer Instanz, ausführlichem Logging und einem Release-Kandidaten. Ob Hetzner, netcup oder AWS die zugrunde liegenden VPS bereitstellt, ist dabei zweitrangig – Ansible spricht jeden Host über SSH an.

# 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"

Ein wichtiger Punkt für die Kapazitätsplanung: captchaai_concurrency beschreibt, wie viele CAPTCHAs ein Worker gleichzeitig in Bearbeitung hält. Drei Produktions-Worker mit je 20 ergeben 60 gleichzeitige Lösungen – und CaptchaAI rechnet Thread-basiert ab, nicht pro Lösung. Für 60 parallele Threads reicht der Tarif ADVANCE (90 $/Monat, 50 Threads) nicht mehr; hier passt PREMIUM (170 $/Monat, 100 Threads). Staging mit 5 gleichzeitigen Lösungen ist bereits von BASIC (15 $/Monat, 5 Threads) abgedeckt. Skalieren Sie die Nebenläufigkeit im Inventar also stets im Einklang mit Ihrer Thread-Zuteilung.

Die Rolle „captcha-worker“

Standardvariablen der Rolle

Die Defaults definieren einen konservativen Ausgangszustand, den das Inventar pro Umgebung überschreibt. So bleibt die Rolle allgemeingültig, während die konkreten Werte dort liegen, wo sie hingehören.

# 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

Tasks: Worker installieren und starten

Die Task-Datei arbeitet einen klaren Ablauf ab: dedizierten Systembenutzer und Arbeitsverzeichnis anlegen, Python samt virtueller Umgebung und Abhängigkeiten installieren, dann Anwendung, Konfiguration und systemd-Unit ausrollen und den Dienst starten. Die notify-Direktiven stoßen bei Änderungen die passenden Handler an, sodass nur bei tatsächlichem Bedarf neu gestartet wird.

# 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

Templates für Konfiguration und systemd-Service

Zwei Jinja2-Templates rendern die worker-spezifische Konfiguration und die systemd-Unit. Die Konfigurationsdatei liegt mit Modus 0600 beim Worker-Benutzer, weil sie Laufzeitparameter enthält; der API-Schlüssel kommt separat über eine Environment-Variable in den Dienst und landet nie auf der Platte im Klartext.

# 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

Die drei Härtungsdirektiven NoNewPrivileges, ProtectSystem=strict und ReadWritePaths schränken den Dienst auf das Nötigste ein – ein sinnvoller Standard, wenn der Worker rund um die Uhr auf einem geteilten Host läuft.

Handler für Reload und Neustart

Handler laufen genau einmal am Ende eines Durchlaufs, egal wie oft sie benachrichtigt wurden. Das vermeidet unnötige Neustarts, wenn mehrere Tasks dieselbe Unit anfassen.

# 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

Die drei Playbooks

Deploy-Playbook: Erstausrollung

Das Deploy-Playbook fragt den API-Schlüssel interaktiv ab (vars_prompt), prüft per Ping die Erreichbarkeit, wendet die Rolle an und meldet am Ende den systemd-Status jedes Hosts. Für automatisierte Läufe – etwa aus einer GitLab-CI-Pipeline – ersetzen Sie vars_prompt durch eine mit Ansible Vault verschlüsselte Variable.

# 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 }}"

Rolling-Update ohne Ausfallzeit

Der Kern für unterbrechungsfreie Updates ist serial: 1: Ansible aktualisiert genau einen Host nach dem anderen. Jeder Worker wird zuerst geleert (drain.py lässt laufende Lösungen zu Ende bringen), gestoppt, neu bespielt und erst dann wieder in Betrieb genommen. Der abschließende Health-Check mit until/retries gibt den Host erst frei, wenn /health mit Status 200 antwortet. max_fail_percentage: 0 stoppt den Rollout beim ersten Fehler, bevor die halbe Flotte betroffen ist.

# 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 }}"

Health-Check-Playbook

Dieses Playbook prüft zwei Ebenen: den lokalen systemd-Status jedes Workers und – von der Kontrollmaschine aus, einmalig – die Erreichbarkeit der CaptchaAI-API über getbalance. So sehen Sie in einem Durchlauf, ob die Dienste laufen und ob das Guthaben noch reicht.

# 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 }}

Playbooks ausführen

Jedes Playbook wird gegen ein Inventar ausgeführt. Testen Sie neue Versionen zuerst gegen Staging, bevor Sie das Rolling-Update in Produktion anstoßen; mit --limit grenzen Sie einen Lauf auf einzelne Hosts ein.

# 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

Fehlerbehebung

Problem Ursache Lösung
Host „unreachable“ SSH-Schlüssel nicht hinterlegt Schlüssel verteilen: ssh-copy-id user@host, dann Erreichbarkeit per ping-Task prüfen
Dienst startet nicht API-Schlüssel als Environment-Variable fehlt vars_prompt prüfen oder den Schlüssel mit Ansible Vault einbinden
Rolling-Update bleibt hängen Health-Check auf /health schlägt fehl Logs mit journalctl -u captcha-worker sichten; retries/delay erhöhen
Konfiguration wird nicht übernommen Handler wurde nicht ausgelöst Mit --force-handlers laufen lassen oder changed_when: true ergänzen

Häufige Fragen

Wie aktualisiere ich alle Worker ohne Ausfallzeit?

Mit dem Rolling-Update-Playbook und serial: 1. Ansible nimmt jeweils einen Host aus dem Betrieb, leert ihn per drain.py, spielt die neue Version ein und gibt ihn erst nach einem erfolgreichen Health-Check wieder frei. max_fail_percentage: 0 bricht den Rollout beim ersten Fehler ab.

Wie hinterlege ich den API-Schlüssel sicher?

Über Ansible Vault: ansible-vault encrypt_string 'ihr-api-key' --name 'captchaai_api_key'. Referenzieren Sie die verschlüsselte Variable in den Group-Vars des Inventars. So liegt der Schlüssel weder im Klartext im Repository noch im Shell-Verlauf, und CI-Läufe erhalten ihn über eine Vault-Passwortdatei.

Kann ich Staging und Produktion mit denselben Playbooks betreiben?

Ja. Die Playbooks und die Rolle bleiben identisch; nur das Inventar wechselt. Über -i inventory/staging.yml bzw. -i inventory/production.yml steuern Sie Hostliste, Nebenläufigkeit und Log-Level – ohne eine Zeile der Rolle anzupassen.

Welcher CaptchaAI-Plan passt zu meiner Worker-Flotte?

Das hängt an der Summe Ihrer captchaai_concurrency-Werte, denn CaptchaAI rechnet Thread-basiert mit unbegrenzten Lösungen pro Thread ab. Drei Worker mit je 20 gleichzeitigen Lösungen brauchen 60 Threads – hier greift PREMIUM (170 $/Monat, 100 Threads). Kleinere Flotten kommen mit STANDARD (30 $/Monat, 15 Threads) oder ADVANCE (90 $/Monat, 50 Threads) aus. Die aktuellen Tarife finden Sie unter captchaai.com/pricing.

Läuft das Setup auch auf Hetzner, netcup oder in Docker?

Ja. Die Rolle setzt nur einen erreichbaren Linux-Host mit apt und systemd voraus – ob dieser bei Hetzner, netcup oder AWS läuft, ist gleichgültig. Für Container ersetzen Sie die systemd-Tasks durch das Modul community.docker.docker_container; Ansible verwaltet dann den Container-Lebenszyklus statt eines Dienstes.

Verwandte Leitfäden

Kommentare sind für diesen Artikel deaktiviert.