Ansible Debugging: -vvv Flag และ Debug Module สำหรับ Troubleshoot

เมื่อ 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 — เครื่องมือเหล่านี้ใช้ร่วมกันได้และไม่มีค่าใช้จ่ายเพิ่มเติม