เมื่อ Ansible playbook ไม่ทำงานตามที่คาดหวัง ขั้นตอนแรกคือเพิ่ม verbosity ด้วย flag -v เพื่อดู output ละเอียด และใช้ debug module เพื่อตรวจสอบค่า variables ณ จุดที่ต้องการ — เครื่องมือเหล่านี้ช่วยลดเวลา troubleshoot จากชั่วโมงเหลือไม่กี่นาที
บทความนี้อธิบาย verbosity levels ของ Ansible (-v ถึง -vvvv), การใช้ debug module แสดงค่า variables, assert module สำหรับ validate conditions, register กับ failed_when/changed_when, –check และ –diff สำหรับ dry-run, รวมถึง –start-at-task และ –step สำหรับ debug ทีละ task
Verbosity Levels: -v ถึง -vvvv
Ansible มี verbosity 4 ระดับ — แต่ละระดับเพิ่มข้อมูลใน output มากขึ้น ใช้ระดับที่เหมาะสมกับปัญหาที่ต้องการ debug
# ระดับ verbosity และข้อมูลที่ได้เพิ่มเติม
# -v (verbose) — แสดง task results ละเอียดขึ้น
ansible-playbook site.yml -v
# -vv — แสดง file paths, task inputs
ansible-playbook site.yml -vv
# -vvv — แสดง SSH connection info, module arguments
ansible-playbook site.yml -vvv
# ใช้บ่อยที่สุดสำหรับ debug — เห็นทุกอย่างที่ Ansible ส่งไป
# -vvvv — แสดง SSH debug output (network-level)
ansible-playbook site.yml -vvvv
# ใช้เมื่อ connection มีปัญหา (SSH key, proxy, timeout)
# ตัวอย่าง output จาก -vvv เมื่อ task ล้มเหลว:
# TASK [Install nginx] *****
# fatal: [web01]: FAILED! => {
# "changed": false,
# "msg": "No package matching 'nginx' is available",
# "rc": 1,
# "stderr": "E: Unable to locate package nginx",
# "stderr_lines": ["E: Unable to locate package nginx"],
# "stdout": "",
# "stdout_lines": []
# }
# รัน ad-hoc command ด้วย verbosity
ansible webservers -m ping -vvv
debug Module — แสดงค่า Variables
debug module แสดงค่า variables หรือข้อความในระหว่างรัน playbook — ใช้ตรวจสอบว่า variable มีค่าอะไร, task register ได้ผลอะไร, และ condition ที่ใช้ใน when ประเมินผลเป็น true/false
# แสดงค่า variable พื้นฐาน
- name: Debug app version
ansible.builtin.debug:
msg: "App version is {{ app_version }}"
# แสดง variable โดยตรง (ไม่ต้องใส่ msg)
- name: Show all inventory variables
ansible.builtin.debug:
var: hostvars[inventory_hostname]
# แสดง registered variable หลัง task รัน
- name: Check nginx status
ansible.builtin.command: systemctl status nginx
register: nginx_status
ignore_errors: true
- name: Debug nginx status output
ansible.builtin.debug:
var: nginx_status
verbosity: 2 # แสดงเฉพาะเมื่อใช้ -vv หรือสูงกว่า
# แสดงเฉพาะส่วนที่สนใจ
- name: Show nginx return code
ansible.builtin.debug:
msg: "nginx rc={{ nginx_status.rc }}, running={{ nginx_status.rc == 0 }}"
# แสดง multiple variables
- name: Debug deployment info
ansible.builtin.debug:
msg:
- "Host: {{ inventory_hostname }}"
- "User: {{ ansible_user }}"
- "OS: {{ ansible_distribution }} {{ ansible_distribution_version }}"
- "App: {{ app_version }}"
register และ failed_when / changed_when
register เก็บผลลัพธ์ของ task ไว้ใน variable — ใช้ร่วมกับ failed_when และ changed_when เพื่อกำหนดเงื่อนไขของ task status แทนที่จะใช้ return code อย่างเดียว
# register ผลลัพธ์และตรวจสอบ
- name: Check if service exists
ansible.builtin.command: systemctl is-active myapp
register: service_check
ignore_errors: true
# ใช้ result ใน when condition
- name: Start service if not running
ansible.builtin.systemd:
name: myapp
state: started
when: service_check.rc != 0
# failed_when — กำหนดว่า task ล้มเหลวเมื่อไหร่
- name: Run database migration
ansible.builtin.command: python manage.py migrate
register: migration_result
failed_when:
- migration_result.rc != 0
- "'No migrations to apply' not in migration_result.stdout"
# ไม่ fail ถ้า error เป็น "No migrations to apply"
# changed_when — กำหนดว่า task "changed" เมื่อไหร่
- name: Check pending updates
ansible.builtin.command: apt list --upgradable
register: update_list
changed_when: "'upgradable' in update_list.stdout"
# task จะ "changed" เฉพาะเมื่อมี packages รอ update
# ดู structure ของ registered variable
- name: Show result structure
ansible.builtin.debug:
msg:
- "rc: {{ migration_result.rc }}"
- "stdout: {{ migration_result.stdout }}"
- "stderr: {{ migration_result.stderr }}"
- "changed: {{ migration_result.changed }}"
- "failed: {{ migration_result.failed }}"
assert Module — Validate Conditions
assert module หยุด playbook ทันทีเมื่อเงื่อนไขไม่ตรง — ใช้ validate ค่า variables, ตรวจสอบ prerequisites, และ enforce assumptions ก่อนรัน tasks ที่สำคัญ
# assert เงื่อนไขก่อนรัน playbook
- name: Validate required variables
ansible.builtin.assert:
that:
- app_version is defined
- app_version | length > 0
- db_password is defined
fail_msg: "Required variables are not set: app_version and db_password are required"
success_msg: "All required variables are present"
# assert หลายเงื่อนไขพร้อมกัน
- name: Validate disk space
ansible.builtin.assert:
that:
- ansible_mounts | selectattr('mount', 'equalto', '/') | list | first | json_query('size_available') > 1073741824
fail_msg: "Insufficient disk space on / (need at least 1GB free)"
# assert OS version
- name: Check supported OS
ansible.builtin.assert:
that:
- ansible_distribution in ['Ubuntu', 'Debian']
- ansible_distribution_major_version | int >= 20
fail_msg: "This playbook requires Ubuntu/Debian 20+ (got {{ ansible_distribution }} {{ ansible_distribution_version }})"
# assert หลัง task สำคัญ
- name: Deploy application
ansible.builtin.command: ./deploy.sh
register: deploy_result
- name: Assert deployment succeeded
ansible.builtin.assert:
that:
- deploy_result.rc == 0
- "'Successfully deployed' in deploy_result.stdout"
fail_msg: "Deployment failed: {{ deploy_result.stderr }}"
–check และ –diff: Dry-Run Mode
–check รัน playbook โดยไม่เปลี่ยนแปลงอะไร — บอกว่า task จะ “changed” หรือ “ok” ถ้ารันจริง, ส่วน –diff แสดงความแตกต่างของไฟล์ที่จะเปลี่ยน เหมาะสำหรับตรวจสอบ template output ก่อน apply
# Dry-run — ไม่เปลี่ยนแปลงอะไร แค่แสดงว่าจะเกิดอะไร
ansible-playbook site.yml --check
# --diff — แสดง diff ของไฟล์ที่จะเปลี่ยน (template, copy, lineinfile)
ansible-playbook site.yml --diff
# ใช้ทั้งคู่พร้อมกัน (แนะนำ)
ansible-playbook site.yml --check --diff
# ตัวอย่าง output จาก --diff:
# TASK [Update nginx config] *****
# --- before: /etc/nginx/nginx.conf
# +++ after: /etc/nginx/nginx.conf
# @@ -5,7 +5,7 @@
# worker_connections 768;
# - # multi_accept on;
# + multi_accept on;
# หมายเหตุ: บาง modules ไม่รองรับ --check
# เช่น command/shell — จะ skip แล้วรายงานว่า "skipped" เสมอ
# ใช้ check_mode: false บน task ที่ต้องรันเสมอแม้ในโหมด check
- name: Always run this task
ansible.builtin.command: echo "test"
check_mode: false
# ใช้ ansible_check_mode variable
- name: Show check mode status
ansible.builtin.debug:
msg: "Running in check mode: {{ ansible_check_mode }}"
Tags — รัน Tasks เฉพาะที่ต้องการ
Tags ช่วยจำกัด scope การรัน — แทนที่จะรัน playbook ทั้งหมด สามารถรันเฉพาะ tasks ที่ tag ด้วย –tags เพื่อ debug เฉพาะส่วนที่มีปัญหา หรือ skip tasks ที่ใช้เวลานานด้วย –skip-tags
# กำหนด tags บน tasks
- name: Install packages
ansible.builtin.apt:
name: "{{ item }}"
state: present
loop: [nginx, postgresql, redis]
tags:
- packages
- install
- name: Configure nginx
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
tags:
- config
- nginx
- name: Deploy application
ansible.builtin.copy:
src: app/
dest: /opt/myapp/
tags:
- deploy
- app
# รันเฉพาะ tasks ที่มี tag 'config'
ansible-playbook site.yml --tags config
# รันหลาย tags
ansible-playbook site.yml --tags "config,deploy"
# Skip tags ที่ใช้เวลานาน
ansible-playbook site.yml --skip-tags packages
# Tags พิเศษ:
# --tags always — รัน tasks ที่ tag always เสมอ (แม้ใช้ --tags อื่น)
# --tags never — tasks ที่ tag never จะไม่รัน เว้นแต่ระบุ --tags never
# --list-tags — แสดง tags ทั้งหมดในไฟล์
ansible-playbook site.yml --list-tags
–start-at-task และ –step
–start-at-task ข้าม tasks ก่อนหน้าแล้วเริ่มรันจาก task ที่ระบุ — ใช้เมื่อ playbook ล้มเหลวตรงกลางและต้องการ resume ส่วน –step ถามยืนยันก่อนรันทุก task
# เริ่มรันจาก task ที่ระบุ (ข้าม tasks ก่อนหน้า)
ansible-playbook site.yml --start-at-task "Deploy application"
# ใช้ชื่อ task แบบ exact match (case-sensitive)
ansible-playbook site.yml --start-at-task "Configure nginx"
# --step — ถามยืนยันก่อนรันทุก task
ansible-playbook site.yml --step
# Perform task: Install packages (y/n/c)?
# y = ทำ task นี้
# n = ข้าม task นี้
# c = continue ทั้งหมดโดยไม่ถามอีก
# รวม --start-at-task กับ --step เพื่อ debug ตั้งแต่จุดที่ต้องการ
ansible-playbook site.yml --start-at-task "Deploy application" --step
# --list-tasks — แสดง tasks ทั้งหมดก่อนรัน
ansible-playbook site.yml --list-tasks
# PLAY [Deploy web stack]
# tasks:
# Install packages TAGS: [packages]
# Configure nginx TAGS: [config, nginx]
# Deploy application TAGS: [deploy, app]
# Start services TAGS: []
Debugging Connection Issues
ปัญหา connection เป็นสาเหตุหลักที่ playbook ล้มเหลว — ใช้ ad-hoc commands และ ansible -m ping เพื่อตรวจสอบ connectivity ก่อน และ ansible_facts เพื่อ verify ข้อมูล host
# ทดสอบ connection ก่อนรัน playbook
ansible all -m ping -i inventory/production/hosts.ini
# ตรวจสอบ SSH ด้วย verbosity
ansible web01 -m ping -vvvv
# แสดง facts ของ host
ansible web01 -m ansible.builtin.setup
ansible web01 -m ansible.builtin.setup -a "filter=ansible_distribution*"
# ทดสอบ become (sudo)
ansible web01 -m ansible.builtin.command -a "id" --become
# debug ansible configuration
ansible --version
ansible-config dump --only-changed # แสดงเฉพาะค่าที่เปลี่ยนจาก default
# ตรวจสอบ inventory parsing
ansible-inventory -i inventory/ --list
ansible-inventory -i inventory/ --host web01
# ANSIBLE_DEBUG environment variable — แสดง debug ละเอียดมาก
ANSIBLE_DEBUG=1 ansible-playbook site.yml 2>&1 | head -100
สรุป
Ansible มีเครื่องมือ debug ครบถ้วน — เริ่มด้วย -vvv เพื่อดู output ละเอียด, ใช้ debug module ตรวจสอบค่า variables ณ จุดที่ต้องการ, assert module enforce conditions ก่อน tasks สำคัญ, –check –diff สำหรับ dry-run ก่อน apply จริง และ –start-at-task –step สำหรับ debug ทีละ task
หลักการ debug: เพิ่ม verbosity ก่อนเสมอ (-vvv), register ผลลัพธ์ tasks ที่สงสัยแล้วใช้ debug แสดง, ใช้ –check ก่อน apply จริงในทุก production deployment — เครื่องมือเหล่านี้ใช้ร่วมกันได้และไม่มีค่าใช้จ่ายเพิ่มเติม

