DevOps & Scaling

Déployer des workers CaptchaAI avec des playbooks Ansible

Sur une seule machine, un worker CaptchaAI s'installe à la main en quelques minutes. Sur une flotte de dix ou vingt serveurs, cette approche manuelle devient la première source de dérive : versions qui divergent, fichiers de service désynchronisés, mises à jour appliquées à moitié. Ansible répond exactement à ce problème : un rôle versionné décrit l'état voulu de chaque worker, et vous le rejouez à l'identique sur tout le parc.

Ce guide construit un rôle captcha-worker complet, un inventaire qui sépare production et préproduction, puis trois playbooks :

  • Déploiement initial — bootstrap complet de la flotte.
  • Mise à jour progressive — nouvelle version sans coupure.
  • Contrôle de santé — état du service et de l'API.

L'ensemble reste idempotent : rejouer le rôle sur un hôte déjà conforme ne change rien.

Arborescence du projet Ansible

L'arborescence cible suit les conventions Ansible : un rôle réutilisable sous roles/, les inventaires par environnement, et les playbooks qui orchestrent le tout.

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

Inventaire : séparer production et préproduction

L'inventaire distingue vos environnements : la production tourne avec une forte concurrence et des logs réduits ; la préproduction reste bavarde pour le diagnostic. C'est aussi ici que vous épinglez la version de worker par groupe d'hôtes, ce qui rend un rollback aussi simple qu'un changement de variable.

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

Aligner la concurrence sur vos threads CaptchaAI

La variable captchaai_concurrency fixe le nombre de résolutions traitées en parallèle par un hôte. Calez-la sur les threads de votre offre :

  • BASIC ($15/mois, 5 threads) : une concurrence de 20 serait disproportionnée.
  • ADVANCE ($90/mois, 50 threads) : absorbe sans peine une concurrence élevée.

Réduire les logs en production (warning) évite aussi que des logs verbeux capturent des fragments de requêtes : dans un cadre RGPD, journalisez le strict nécessaire.

Le rôle captcha-worker

Le rôle captcha-worker regroupe tout ce qu'un serveur doit contenir pour exécuter un worker : utilisateur système dédié, environnement Python isolé, service systemd et sa configuration. Le découper en defaults, tasks, templates et handlers garde chaque responsabilité à sa place.

Variables par défaut du rôle

Ces valeurs s'appliquent tant que l'inventaire ne les surcharge pas. Elles décrivent un worker prudent : concurrence modérée, journalisation en info, trois tentatives.

# 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

Tâches d'installation et de service

Les tâches s'exécutent dans l'ordre : utilisateur, répertoire, dépendances système et Python, déploiement du code et de la configuration, activation du service. Chaque tâche est idempotente, et grâce aux notify, le service n'est redémarré que si un fichier a réellement changé.

# 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

Ce rôle pose le socle de déploiements répétables : même utilisateur, même arborescence, même service, quel que soit le serveur.

Templates de configuration et service systemd

Deux templates Jinja2 produisent les fichiers finaux à partir des variables :

  • config.yaml.j2 génère la configuration du worker.
  • captcha-worker.service.j2 décrit le service systemd, avec un durcissement de sécurité (NoNewPrivileges, ProtectSystem=strict) qui limite ce que le processus peut toucher sur l'hôte.
# 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

La clé API n'est jamais écrite en clair : elle arrive par la variable captchaai_api_key, injectée à l'exécution ou lue depuis Ansible Vault (voir la FAQ).

Handlers de rechargement

Les handlers ne se déclenchent que sur notification : un changement de configuration ou de code recharge systemd puis redémarre le worker — jamais à chaque exécution, seulement quand quelque chose a réellement bougé.

# 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

Les playbooks

Trois playbooks couvrent le cycle de vie de la flotte : premier déploiement, mise à jour progressive et surveillance.

Déploiement initial de la flotte

Le playbook de déploiement demande la clé API de façon interactive (vars_prompt), vérifie la connectivité, applique le rôle, puis confirme que le service est actif. C'est celui à lancer pour un bootstrap ou un redéploiement complet.

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

Pour une exécution non interactive (CI/CD), remplacez vars_prompt par une variable chiffrée dans Vault.

Mise à jour progressive (rolling update)

C'est le playbook le plus important en production. serial: 1 force Ansible à traiter un hôte à la fois : il draine les tâches en cours, arrête le worker, déploie la nouvelle version, redémarre, puis attend que le contrôle de santé réponde 200 avant de passer au suivant.

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

Avec max_fail_percentage: 0, la moindre erreur stoppe le déploiement : une version défectueuse n'atteint jamais le reste de la flotte. C'est la différence entre une mise à jour prudente et une panne générale.

Contrôle de santé de la flotte

Le contrôle de santé combine deux vérifications : l'état du service systemd sur chaque hôte et une interrogation de l'API CaptchaAI depuis la machine de contrôle, pour confirmer que le solde est lisible. Lancez-le en routine ou après chaque déploiement.

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

Commandes ansible-playbook courantes

Les opérations du quotidien restent simples : un playbook par intention.

# 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

Dépannage

Problème Cause probable Correctif
Hôte injoignable (« unreachable ») Clé SSH absente ou mauvais utilisateur Ajoutez la clé avec ssh-copy-id user@host et vérifiez ansible_host
Le service ne démarre pas Variable captchaai_api_key absente Fournissez-la via vars_prompt ou Ansible Vault
Rolling update bloqué sur un hôte Le contrôle de santé ne renvoie jamais 200 Consultez journalctl -u captcha-worker et augmentez retries/delay
La nouvelle config n'est pas appliquée Le handler n'a pas été notifié Relancez avec --force-handlers ou vérifiez la détection de changement
Concurrence incohérente entre hôtes Variable surchargée dans l'inventaire Vérifiez group_vars/host_vars et réalignez sur vos threads CaptchaAI

FAQ

Comment chiffrer la clé API avec Ansible Vault ?

Chiffrez-la avec ansible-vault encrypt_string 'votre-cle-api' --name 'captchaai_api_key', puis référencez la variable dans group_vars ou l'inventaire. Elle n'apparaît jamais en clair dans le dépôt et le playbook la déchiffre à l'exécution avec --ask-vault-pass.

Comment déployer sur un seul worker sans toucher au reste ?

Ajoutez --limit worker-1 à la commande ansible-playbook : Ansible n'exécute les tâches que sur l'hôte ciblé, idéal pour valider une version avant de la généraliser.

Que faire si un rolling update reste bloqué sur un hôte ?

Avec serial: 1 et max_fail_percentage: 0, un échec de contrôle de santé arrête volontairement le déploiement. Diagnostiquez l'hôte via journalctl -u captcha-worker, corrigez, puis relancez : grâce à l'idempotence, les hôtes déjà à jour ne sont pas retouchés.

Ansible peut-il déployer des workers sur OVHcloud ou Scaleway ?

Oui. Ansible ne dépend pas du fournisseur : dès qu'un serveur est joignable en SSH — chez OVHcloud, Scaleway ou dans une région AWS comme eu-west-3 (Paris) — le même rôle s'applique. En général, Terraform crée les serveurs et Ansible les configure.

À quelle fréquence relancer le playbook de configuration ?

Aussi souvent que vous le souhaitez : le rôle est idempotent. Le rejouer sur un parc conforme ne provoque aucun changement ni redémarrage. Beaucoup d'équipes le planifient pour corriger toute dérive de configuration.

Pour aller plus loin

Si vous exploitez déjà plusieurs workers, récupérez votre clé API CaptchaAI et formalisez vos déploiements dans des playbooks versionnés plutôt que dans des opérations SSH manuelles : vous y gagnez reproductibilité, auditabilité et rollbacks propres.

Guides associés :

Les commentaires sont désactivés pour cet article.