Tối Ưu và Chiến Lược Thực Thi
Trên một host, Ansible chạy đơn giản. Trên hàng trăm host, hai câu hỏi nảy sinh: làm sao chạy nhanh (song song), và làm sao an toàn (không sập cả fleet cùng lúc). Bài này là các kỹ thuật vận hành thực chiến để trả lời hai câu đó.
forks: chạy song song bao nhiêu host
Mặc định Ansible xử lý 5 host song song (forks = 5). Với fleet lớn, tăng lên để nhanh hơn:
# ansible.cfg
[defaults]
forks = 50
Hoặc ansible-playbook site.yml -f 50. forks là số kết nối SSH đồng thời control node mở. Tăng quá cao có thể nghẽn control node (CPU/RAM/mạng) — cân theo sức máy, thường 20–100.
strategy: linear vs free
Strategy plugin quyết định cách Ansible điều phối task qua các host:
linear (mặc định): mọi host chạy XONG task N rồi cả nhóm mới sang task N+1
→ đồng bộ, dễ hiểu; nhưng host nhanh phải đợi host chậm
free: mỗi host chạy hết các task của mình ĐỘC LẬP, không đợi nhau
→ nhanh hơn khi host không đồng đều; khó theo dõi hơn
- hosts: web
strategy: free # mỗi host chạy hết playbook của nó, không chờ
linear (mặc định) dễ suy luận và đủ cho phần lớn. free hữu ích khi các host có tốc độ rất khác nhau và task độc lập.
serial: rolling update không gián đoạn
Đây là kỹ thuật quan trọng nhất cho production. Mặc định Ansible áp thay đổi lên tất cả host cùng lúc — nguy hiểm: nếu thay đổi có lỗi, toàn bộ fleet sập đồng thời. serial chia host thành từng đợt:
- hosts: web
serial: 2 # hoặc "25%"
tasks:
- name: Deploy phiên bản mới
...
6 host, serial: 2 →
Đợt 1: host1, host2 ── deploy + kiểm tra ──┐
Đợt 2: host3, host4 ── chỉ chạy NẾU đợt 1 ok │ (rolling)
Đợt 3: host5, host6 ── ┘
Nếu một đợt lỗi (vượt ngưỡng max_fail_percentage), Ansible dừng — các host còn lại chưa bị động, dịch vụ vẫn chạy trên chúng. Kết hợp với load balancer (series Mạng Bài 11): rút host khỏi LB → deploy → kiểm tra → đưa lại LB, từng đợt → cập nhật không gián đoạn (zero-downtime). Đây là cách Ansible làm rolling deploy.
delegate_to và run_once
delegate_to chạy một task trên host khác với host đang xử lý — hữu ích khi thao tác liên quan host hiện tại nhưng phải chạy ở nơi khác:
- name: Rút host khỏi load balancer trước khi deploy
community.general.haproxy:
state: disabled
host: "{{ inventory_hostname }}"
delegate_to: "{{ load_balancer_host }}" # chạy TRÊN load balancer
run_once: true chạy task đúng một lần (trên host đầu) thay vì mọi host — cho việc chỉ cần làm một lần (chạy migration database, tạo bản ghi DNS):
- name: Chạy migration (chỉ một lần cho cả nhóm)
ansible.builtin.command: /opt/app/migrate.sh
run_once: true
Kết hợp delegate_to: localhost + run_once là mẫu phổ biến để chạy thứ gì đó trên control node một lần.
async: task chạy lâu
Task dài (cập nhật hệ thống, build) có thể vượt timeout SSH. async chạy nó nền trên host và bạn poll trạng thái:
- name: Việc lâu
ansible.builtin.command: /opt/long-job.sh
async: 600 # cho phép chạy tối đa 600s
poll: 10 # kiểm tra mỗi 10s
poll: 0 thì "fire and forget" (chạy rồi đi tiếp, kiểm tra sau bằng module async_status) — hữu ích khi khởi động nhiều việc dài song song.
Tăng tốc: fact caching và pipelining
Hai tối ưu giảm thời gian đáng kể:
- Pipelining (nhớ Bài 1): gửi module qua stdin của SSH thay vì sftp PUT riêng, giảm số vòng SSH mỗi task. Bật trong
ansible.cfg:
[ssh_connection]
pipelining = True
- Fact caching:
Gathering Facts(Bài 4) tốn thời gian mỗi lần chạy. Cache facts để tái dùng giữa các lần chạy:
[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 3600
Hoặc đơn giản hơn: gather_facts: false ở play không cần facts. Tìm task chậm bằng callback profile_tasks (Bài 12):
ANSIBLE_CALLBACKS_ENABLED=profile_tasks ansible-playbook site.yml
Gathering Facts -------------------------- 3.49s
Cài gói ----------------------------------- 2.97s
→ biết chính xác task nào ngốn thời gian để tối ưu.
tags: chạy chọn lọc
Gắn tags cho task để chạy một phần playbook (nhắc ở Bài 7):
- name: Cài gói
ansible.builtin.dnf: { name: nginx, state: present }
tags: [install]
- name: Cấu hình
ansible.builtin.copy: { ... }
tags: [config]
ansible-playbook site.yml --tags config # chỉ chạy task tag "config"
ansible-playbook site.yml --skip-tags install # chạy tất cả TRỪ tag "install"
ansible-playbook site.yml --list-tasks # xem task + tags trước khi chạy
Chạy --tags config cho thấy chỉ task gắn config chạy, task install bị bỏ. Cực tiện khi playbook dài mà bạn chỉ muốn cập nhật cấu hình (khỏi chạy lại phần cài đặt).
check mode: dry-run trước production
Nhắc lại Bài 5 vì nó thuộc nhóm vận hành an toàn: --check --diff xem playbook sẽ đổi gì mà không áp thật. Luôn --check trước khi chạy thật trên production — và --diff để thấy nội dung file sẽ thay đổi ra sao. Đây là lưới an toàn quan trọng nhất khi vận hành.
Tổng kết
Chạy Ansible quy mô lớn: forks điều khiển song song; strategy (linear đồng bộ / free độc lập); serial chia đợt cho rolling update không gián đoạn (kết hợp load balancer); delegate_to/run_once chạy task ở host khác/một lần; async cho task dài; pipelining + fact caching + gather_facts: false để tăng tốc (profile_tasks tìm task chậm); tags chạy chọn lọc; --check --diff dry-run an toàn trước production. Đây là bộ công cụ biến playbook "chạy được" thành "vận hành được trên fleet thật".
Chạy nhanh và an toàn rồi, nhưng làm sao biết role đúng trước khi đẩy lên hàng trăm host? Bài 14: kiểm thử role bài bản với Molecule.