Ansible รัน tasks บน hosts จำนวนมากได้เร็วหรือช้าขึ้นอยู่กับการตั้งค่า — ค่า default ของ Ansible อนุรักษ์นิยมและใช้ SSH connections มากกว่าที่จำเป็น การ tune ค่าเพียงไม่กี่จุดสามารถลดเวลา execution จาก 30 นาทีเหลือ 5 นาทีโดยไม่ต้องเปลี่ยน playbook
บทความนี้อธิบายการเพิ่ม forks สำหรับ parallel execution, เปิด pipelining ลด SSH overhead, ใช้ fact caching ไม่ต้อง gather facts ซ้ำ, async tasks สำหรับ long-running operations, strategy: free, และ profile_tasks callback สำหรับหาจุดที่ช้าที่สุด
Forks — Parallel Execution
forks กำหนดจำนวน hosts ที่ Ansible รัน task พร้อมกัน — ค่า default คือ 5 ซึ่งช้ามากสำหรับ infrastructure ขนาดใหญ่ เพิ่มเป็น 20-50 ขึ้นอยู่กับ resource ของ control node
# ansible.cfg — เพิ่ม forks สำหรับ parallel execution
[defaults]
forks = 20 # default คือ 5, เพิ่มได้ถึง 50+ ตาม CPU/RAM
# หรือระบุใน command line
ansible-playbook site.yml -f 20
# ผลกระทบ:
# forks=5 : รัน 100 hosts ใน 20 รอบ (100/5)
# forks=20 : รัน 100 hosts ใน 5 รอบ (100/20)
# forks=50 : รัน 100 hosts ใน 2 รอบ
# serial — จำกัด hosts ต่อ batch ใน play (แตกต่างจาก forks)
- name: Rolling deployment
hosts: webservers
serial: 5 # รัน 5 hosts ต่อ batch (ไม่ขนานกันระหว่าง batch)
tasks:
- name: Deploy app
...
# serial กับ percentage
serial: "20%" # 20% ของ hosts ต่อ batch
# serial กับ list (escalating batches)
serial:
- 1 # batch แรก: 1 host
- 5 # batch ที่สอง: 5 hosts
- "50%" # batch ที่สาม: 50% ที่เหลือ
SSH Pipelining — ลด Connection Overhead
SSH Pipelining ลดจำนวน SSH connections ที่ต้องเปิดต่อ task — แทนที่จะเปิด connection ใหม่ทุกครั้ง Ansible ส่ง module ผ่าน connection เดิม ลด overhead ได้มากโดยเฉพาะเมื่อมี tasks จำนวนมาก
# ansible.cfg — เปิด SSH pipelining
[ssh_connection]
pipelining = True
# ข้อกำหนด: ต้อง disable requiretty ใน /etc/sudoers บน target hosts
# เพิ่มใน /etc/sudoers หรือ /etc/sudoers.d/ansible:
# Defaults:ansible !requiretty
# ทำผ่าน Ansible task:
- name: Disable requiretty for ansible user
ansible.builtin.lineinfile:
path: /etc/sudoers.d/ansible
line: "Defaults:ansible !requiretty"
create: true
validate: "visudo -cf %s"
# ControlMaster — reuse SSH connections
[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o ControlPath=/tmp/ansible-ssh-%r@%h:%p
# ผลลัพธ์: task ที่ใช้เวลา 30s อาจลดเหลือ 15s จาก SSH overhead ที่หายไป
Fact Caching — ไม่ Gather Facts ซ้ำ
Gather facts เป็นขั้นตอนที่ช้ามาก — Ansible ต้องเชื่อมต่อและดึงข้อมูล host facts ก่อนทุก play การ cache facts ช่วยให้รัน playbook ครั้งถัดไปข้ามขั้นตอนนี้ได้ถ้า facts ยังไม่ expire
# ansible.cfg — เปิด fact caching ด้วย jsonfile backend
[defaults]
gathering = smart # smart = ใช้ cache ถ้ามี, gather ถ้าไม่มี/หมดอายุ
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_fact_cache
fact_caching_timeout = 3600 # cache อยู่ได้ 1 ชั่วโมง
# หรือใช้ Redis backend (สำหรับ multi-node control)
fact_caching = redis
fact_caching_connection = localhost:6379:0
# ต้องติดตั้ง: pip install redis
# ปิด fact gathering บน play ที่ไม่ต้องการ facts
- name: Deploy app (no facts needed)
hosts: webservers
gather_facts: false
tasks:
- name: Copy application
ansible.builtin.copy:
src: app/
dest: /opt/app/
# gather_facts เฉพาะ subset ที่ต้องการ
- name: Play with minimal facts
hosts: webservers
gather_facts: true
gather_subset:
- "!all" # ไม่เก็บ facts ทั้งหมด
- "!min" # ไม่เก็บ minimal set
- network # เก็บเฉพาะ network facts
- hardware # เก็บเฉพาะ hardware facts
async และ poll — Non-blocking Tasks
async รัน task แบบ background โดยไม่รอ response — เหมาะสำหรับ tasks ที่ใช้เวลานาน เช่น yum update, package compilation, หรือ database migration ที่ไม่ต้องการ block tasks ถัดไป
# async task — รันใน background, ไม่รอผลทันที
- name: Run long database migration
ansible.builtin.command: python manage.py migrate
async: 600 # timeout สูงสุด 600 วินาที
poll: 0 # ไม่รอ, ทำ task ถัดไปได้เลย
register: migration_job
# ทำ tasks อื่นระหว่างรอ
- name: Update static assets
ansible.builtin.command: python manage.py collectstatic --no-input
# รอผลลัพธ์ของ async task
- name: Wait for migration to complete
ansible.builtin.async_status:
jid: "{{ migration_job.ansible_job_id }}"
register: job_result
until: job_result.finished
retries: 30
delay: 20
# รัน tasks แบบ parallel บน host เดียว
# (ปกติ tasks รันทีละ task ต่อ host)
- name: Download package A (async)
ansible.builtin.get_url:
url: https://example.com/package-a.tar.gz
dest: /tmp/package-a.tar.gz
async: 120
poll: 0
register: download_a
- name: Download package B (async)
ansible.builtin.get_url:
url: https://example.com/package-b.tar.gz
dest: /tmp/package-b.tar.gz
async: 120
poll: 0
register: download_b
- name: Wait for all downloads
ansible.builtin.async_status:
jid: "{{ item.ansible_job_id }}"
loop: ["{{ download_a }}", "{{ download_b }}"]
register: downloads
until: downloads.finished
retries: 12
delay: 10
Strategy: free — ไม่รอ Slowest Host
Strategy default ของ Ansible คือ “linear” — รอให้ทุก host ทำ task เดียวกันเสร็จก่อนไป task ถัดไป Strategy “free” ให้แต่ละ host รัน tasks ต่อไปได้ทันทีโดยไม่รอ host อื่น
# strategy: free — แต่ละ host รันเร็วที่สุดที่ทำได้
- name: Deploy to all servers
hosts: webservers
strategy: free # ไม่รอ host อื่น
tasks:
- name: Install packages
ansible.builtin.apt:
name: nginx
state: present
- name: Start service
ansible.builtin.systemd:
name: nginx
state: started
# strategy: host_pinned — คล้าย free แต่ไม่เกิน forks ต่อ host
- name: Deploy
hosts: webservers
strategy: host_pinned
# กำหนด default strategy ใน ansible.cfg
[defaults]
strategy = free
# เปรียบเทียบ linear vs free กับ 3 hosts:
# linear: H1-task1, H2-task1, H3-task1, [wait all], H1-task2, H2-task2...
# free: H1-task1, H1-task2, H1-task3 (H1 เร็วกว่า H2,H3 ไม่ต้องรอ)
profile_tasks — หา Bottleneck
profile_tasks callback แสดงเวลาที่ใช้ต่อ task — ใช้หา tasks ที่ช้าที่สุดเพื่อ optimize ก่อน แทนที่จะ guess สิ่งที่ต้อง optimize
# ansible.cfg — เปิด profile_tasks callback
[defaults]
callbacks_enabled = profile_tasks, profile_roles, timer
# หรือใช้ ANSIBLE_CALLBACKS_ENABLED environment variable
ANSIBLE_CALLBACKS_ENABLED=profile_tasks ansible-playbook site.yml
# ตัวอย่าง output หลัง playbook รันเสร็จ:
# Thursday 16 April 2026 06:00:00 +0700 (0:00:00.050) 0:02:35.123
# ========================================================
# Install packages --------------------------------------- 45.23s
# Run database migrations -------------------------------- 32.10s
# Pull docker images ------------------------------------- 28.50s
# Configure nginx ---------------------------------------- 5.23s
# Start services ----------------------------------------- 2.10s
# timer callback — แสดงเวลารวมท้าย playbook
# profile_roles — แสดงเวลาต่อ role
# ดู task timing เฉพาะ task ที่สนใจ
- name: Measure package installation time
ansible.builtin.apt:
name: "{{ packages }}"
state: present
register: install_result
- name: Show installation time
ansible.builtin.debug:
msg: "Installation took {{ install_result.delta }}"
Mitogen — Drop-in Accelerator
Mitogen เป็น third-party strategy plugin ที่เร่งความเร็ว Ansible อย่างมีนัยสำคัญ — ใช้ Python multiplexing แทน SSH ทำให้ tasks รันเร็วขึ้น 1.5-7x โดยไม่ต้องเปลี่ยน playbook
# ติดตั้ง Mitogen
pip install mitogen --break-system-packages
# ตรวจสอบ path ของ Mitogen
python3 -c "import mitogen; print(mitogen.__file__)"
# /usr/local/lib/python3.10/dist-packages/mitogen/__init__.py
# ansible.cfg — ใช้ Mitogen strategy
[defaults]
strategy_plugins = /usr/local/lib/python3.10/dist-packages/ansible_mitogen/plugins/strategy
strategy = mitogen_linear # แทน linear ปกติ
# หรือใช้ mitogen_free
strategy = mitogen_free # แทน free ปกติ
# ข้อควรระวัง Mitogen:
# - ไม่รองรับ connection plugins บางตัว (เช่น network devices)
# - อาจ conflict กับบาง modules
# - ตรวจสอบ compatibility กับ Ansible version ก่อนใช้ใน production
# - ทดสอบด้วย --check ก่อนเสมอ
# เปรียบเทียบ benchmark (ตัวอย่าง 50 hosts, 20 tasks):
# standard linear : ~8 นาที
# mitogen_linear : ~2 นาที (เร็วขึ้น ~4x)
Configuration Optimization รวม
ตัวอย่าง ansible.cfg ที่ optimize ทุกตัวเลือกหลักพร้อมกัน — เหมาะสำหรับ production environment ที่ต้องการ performance สูงสุด
# ansible.cfg — production-optimized configuration
[defaults]
# Parallel execution
forks = 20
# Fact caching
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts_cache
fact_caching_timeout = 86400 # 24 ชั่วโมง
# Callbacks สำหรับ profiling
callbacks_enabled = profile_tasks, timer
# Reduce output noise
display_skipped_hosts = false
display_ok_hosts = false # ซ่อน OK tasks (แสดงเฉพาะ changed/failed)
# Retry files
retry_files_enabled = false # ไม่สร้าง .retry files
[ssh_connection]
# SSH optimization
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o ControlPath=/tmp/ansible-ssh-%r@%h:%p
control_path_dir = /tmp/ansible-ssh
# Connection timeout
timeout = 30
# ผลลัพธ์ที่คาดหวัง:
# playbook 100 hosts, 50 tasks:
# ก่อน optimize : ~25 นาที
# หลัง optimize : ~5-8 นาที (ลดลง 3-5x)
สรุป
Performance tuning ใน Ansible เริ่มที่ forks (เพิ่มจาก 5 เป็น 20+) และ SSH pipelining — สองตัวนี้ให้ผลมากที่สุดโดยไม่ต้องเปลี่ยน playbook เลย จากนั้นเปิด fact caching ลด gather facts overhead, ใช้ async tasks สำหรับ long-running operations, strategy: free เพื่อไม่รอ slow hosts และ profile_tasks หา bottleneck
ลำดับการ optimize: วัดก่อน (profile_tasks) → หา bottleneck → แก้ที่ใหญ่ก่อน (forks, pipelining, fact caching) → วัดอีกครั้ง — อย่า optimize สิ่งที่ไม่ช้า

