DevOps và Mở Rộng

Playbook Ansible để triển khai CaptchaAI Worker

Khi fleet worker CaptchaAI lớn hơn vài máy, SSH tay vào từng host để đổi concurrency hay khởi động lại service sớm thành nút thắt vận hành. Playbook Ansible dưới đây giải quyết đúng việc đó: một role cài worker, template đồng bộ cấu hình, và một playbook rolling update không cần dừng dịch vụ. Terraform dựng hạ tầng; Ansible cấu hình những gì chạy bên trong.

Khi nào bạn cần playbook này

Cân nhắc chuyển sang Ansible khi:

  • Bạn quản lý từ 3 worker trở lên và SSH tay vào từng máy để đổi captchaai_concurrency chiếm nhiều thời gian hơn viết playbook.
  • Staging và production lệch cấu hình vì ai đó sửa tay một host rồi quên đồng bộ.
  • Bạn cần nâng cấp captcha_worker.py mà không được dừng cả fleet cùng lúc.
  • Đội bạn đã dùng Ansible cho hạ tầng khác và muốn quản lý worker theo cùng quy trình.

Fleet một host thì chạy trực tiếp qua systemd đơn giản hơn.

Cấu trúc dự án

Role, playbook và inventory tách riêng thư mục để dễ tái sử dụng:

  • inventory/ — khai báo host theo môi trường (production, staging) cùng biến riêng cho từng nhóm.
  • roles/captcha-worker/ — toàn bộ logic cài đặt: tasks, template cấu hình, handler restart, biến mặc định.
  • playbooks/ — ba kịch bản chạy: triển khai lần đầu, rolling update, health check.
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

Ví dụ thực tế: mở rộng fleet cho một đội QA outsourcing

Tình huống điển hình ở các công ty outsourcing tại TP.HCM hoặc Hà Nội: đội automation bắt đầu với một worker cho một client, rồi nhận thêm hai client mới cần chạy song song trên staging trước khi lên production. Thay vì SSH tay vào từng host, họ thêm entry host vào inventory/production.yml, giữ nguyên role captcha-worker, rồi chạy lại deploy.yml — cấu hình tự đồng bộ xuống cả fleet. captchaai_concurrency khai báo trong inventory nên khớp đúng số thread đã mua: ba worker song song với tổng 15 thread cần gói STANDARD ($30/tháng, 15 thread), không nhồi vào gói BASIC ($15/tháng, 5 thread) rồi gặp lỗi giới hạn thread giữa batch của client.

Inventory: khai báo máy chủ theo môi trường

File inventory nhóm host theo môi trường. Production chạy concurrency cao hơn, log level warning:

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

Staging dùng concurrency thấp hơn, log level debug để soi lỗi trước khi lên production:

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

Role: captcha-worker

Biến mặc định

Các giá trị mặc định — override được qua inventory ở trên:

# 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: các bước cài đặt worker

Role chạy tuần tự: tạo user, dựng virtualenv, cài dependency, rồi deploy code và cấu hình systemd:

# 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: mẫu cấu hình và service

Template render các biến trên thành config.yaml thật trên từng host:

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

Unit systemd chạy worker qua virtualenv, tự khởi động lại khi crash (Restart=always):

# 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

Handlers: kích hoạt khi có thay đổi

Handler chỉ chạy khi task tương ứng báo changed:

# 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

Bảo mật khi lưu API key và cấu hình

captchaai_api_key đi qua ba lớp: vars_prompt lúc chạy deploy.yml, file config.yaml quyền 0600 chỉ user captcha đọc được, và biến môi trường CAPTCHAAI_API_KEY trong unit systemd. Với production, mã hóa key bằng Ansible Vault và tham chiếu biến đã mã hóa trong group_vars thay vì nhập tay mỗi lần.

Vài điểm nên kiểm tra định kỳ:

  • ansible.cfg không commit mật khẩu SSH dạng plaintext.
  • ReadWritePaths trong unit systemd chỉ trỏ vào worker_dir.
  • Xoay API key qua dashboard CaptchaAI, cập nhật Vault rồi chạy lại rolling-update.yml để áp dụng không downtime.

Playbook: deploy, rolling update và health check

Deploy: triển khai lần đầu

Playbook hỏi API key qua vars_prompt, ping host, áp role captcha-worker, rồi kiểm tra service:

# 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: cập nhật không downtime

serial: 1 giới hạn Ansible chỉ đụng một host mỗi lượt: drain, dừng, deploy bản mới, khởi động lại rồi chờ health check 200:

# 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: xác minh sau khi deploy

Playbook này gọi API không cần quyền root (become: false ở mức play) — kiểm tra service systemd rồi gọi endpoint getbalance để xác nhận API key còn hoạt động:

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

Các lệnh chạy playbook

Luôn chạy staging trước, dùng --limit khi chỉ cần vài 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

Khắc phục sự cố thường gặp

Vấn đề Nguyên nhân Cách xử lý
Host báo "unreachable" Chưa cấu hình SSH key Thêm SSH key: ssh-copy-id user@host
Service không khởi động được Thiếu biến env chứa API key Kiểm tra vars_prompt hoặc chuyển sang Ansible Vault
Rolling update bị kẹt giữa chừng Health check không pass Xem journalctl -u captcha-worker; tăng số retries trong playbook
Cấu hình mới không được áp dụng Handler không được trigger Chạy với --force-handlers hoặc thêm changed_when: true vào task

Mẹo phòng ngừa:

  • Chạy ansible-playbook --check trước khi apply thật trên production.
  • Giữ staging-worker-1 chạy worker_version mới nhất trước production ít nhất một ngày.
  • Log level debug trên staging giúp đọc lỗi API rõ hơn warning trên production.

Câu hỏi thường gặp

Lưu API key CaptchaAI trong Ansible bằng cách nào cho an toàn?

Dùng Ansible Vault: ansible-vault encrypt_string 'your-api-key' --name 'captchaai_api_key'. Tham chiếu biến đã mã hóa trong inventory hoặc group_vars.

Playbook rolling update có làm gián đoạn worker đang chạy không?

Không, với serial: 1 trong rolling-update.yml: mỗi lượt chỉ một host bị drain, cập nhật, khởi động lại; các host còn lại vẫn giải CAPTCHA bình thường.

Nên đặt captchaai_concurrency bao nhiêu cho mỗi worker?

Khớp số thread bạn đã mua, không đặt tùy ý. Gói BASIC ($15/tháng, 5 thread) vừa cho một worker captchaai_concurrency: 5; ba worker song song với 15 thread cần gói STANDARD ($30/tháng, 15 thread).

Có triển khai captcha-worker trong Docker container thay vì systemd được không?

Có. Thay task ansible.builtin.systemd bằng module community.docker.docker_container — Ansible quản lý vòng đời container, phần còn lại giữ nguyên.

Làm sao biết fleet đang chạy ổn định mà không cần SSH vào từng host?

Chạy health-check.yml — playbook tổng hợp trạng thái systemd và số dư API của cả fleet vào một báo cáo. Đặt vào cron trên host điều phối để cảnh báo sớm thay vì phát hiện lỗi khi client báo CAPTCHA không giải được.

Bước tiếp theo

Đưa playbook này vào fleet worker: lấy API key CaptchaAI, chạy deploy.yml lần đầu, rolling-update.yml cho lần sau.

Hướng dẫn liên quan:

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