วิธีตั้งค่า ignore_unreachable ใน Ansible ให้ข้ามโฮสต์
เรียนรู้วิธีจัดการโฮสต์ที่ติดต่อไม่ได้ใน Ansible ด้วย ignore_unreachable เพื่อป้องกันไม่ให้ Playbook หยุดทำงาน พร้อมเทคนิคการใช้ serial และ max_fail_percentage อย่างถูกต้อง
โฮสต์ที่ติดต่อไม่ได้ไม่ใช่ภารกิจที่ล้มเหลว
หากต้องการเพิกเฉยต่อโฮสต์ที่ติดต่อไม่ได้ใน Ansible คุณสามารถตั้งค่า ignore_unreachable: true ซึ่งสวิตช์นี้จะทำงานตามที่ระบุ สิ่งสำคัญคือต้องทราบว่าควรใช้เมื่อใด เนื่องจาก Ansible จัดการปัญหาที่แตกต่างกันสองประการด้วยวิธีที่ต่างกัน ภารกิจที่ทำงานบนโฮสต์แล้วส่งค่าความผิดพลาดกลับมาถือเป็น ความล้มเหลว (failure) ส่วนโฮสต์ที่ Ansible ไม่สามารถเชื่อมต่อได้เลยถือว่า ติดต่อไม่ได้ (unreachable) โดย ignore_errors จะครอบคลุมเฉพาะกรณีแรกเท่านั้น ในขณะที่ ignore_unreachable จะครอบคลุมเฉพาะกรณีที่สองเท่านั้น
นี่คือความแตกต่างในสรุปผลการทำงาน (play recap)
PLAY RECAP *********************************************************************
web1 : ok=7 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
web2 : ok=0 changed=0 unreachable=1 failed=0 skipped=0 rescued=0 ignored=0Ansible เชื่อมต่อกับ web1 และรันภารกิจไป 7 รายการ web2 แสดง unreachable=1 และ failed=0 ซึ่งหมายความว่าไม่มีการรันภารกิจใดๆ บนโฮสต์นั้นเลย Ansible ไม่สามารถสร้างการเชื่อมต่อได้ จึงนำโฮสต์ดังกล่าวออกจาก play และดำเนินการส่วนที่เหลือต่อไป หาก play นั้นเป็นการติดตั้งอัปเดตความปลอดภัย เซิร์ฟเวอร์เครื่องหนึ่งของคุณจะไม่ได้รับการอัปเดตดังกล่าว
สาเหตุที่ทำให้โฮสต์ไม่สามารถเข้าถึงได้
คำว่าไม่สามารถเข้าถึงได้ (unreachable) หมายความว่าการเชื่อมต่อล้มเหลวก่อนที่โมดูลใดๆ จะเข้าถึงโฮสต์นั้นได้ จึงไม่มีผลลัพธ์จากโมดูลให้อ่าน มีเพียงข้อผิดพลาดในการเชื่อมต่อเท่านั้น ซึ่งจะปรากฏขึ้นที่งานแรกที่พยายามติดต่อกับเครื่องนั้น
fatal: [web2]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 203.0.113.20 port 22: Connection refused", "unreachable": true}ฟิลด์ msg จะระบุสาเหตุที่แท้จริง ซึ่งสาเหตุที่คุณมักจะพบมีดังนี้:
Connection refused: การเชื่อมต่อ TCP ถูกปฏิเสธ หมายความว่าไม่มีบริการใดฟังอยู่ที่พอร์ตนั้น อาจเป็นเพราะ sshd หยุดทำงาน หรือมีการย้าย SSH ไปยังพอร์ตอื่นแต่ใน inventory ของคุณยังระบุว่าเป็นพอร์ต 22Connection timed out: ไม่มีการตอบกลับใดๆ เลย อาจเป็นเพราะไฟร์วอลล์ดรอปแพ็กเก็ตเหล่านั้น หรือเซิร์ฟเวอร์ปิดอยู่ การพยายามเชื่อมต่อแต่ละครั้งจะใช้เวลาตามค่า timeout ของการเชื่อมต่อ ซึ่งโดยปกติคือ 10 วินาทีHost key verification failed.: คีย์ใน~/.ssh/known_hostsไม่ตรงกับคีย์ที่เซิร์ฟเวอร์ส่งมา การสร้าง VPS ใหม่โดยใช้ IP เดิมจะทำให้ได้ host key ใหม่ ดังนั้นเหตุการณ์นี้เป็นเรื่องปกติหลังจากการติดตั้งใหม่ แต่หากเกิดขึ้นในเวลาอื่นถือเป็นเรื่องร้ายแรงPermission denied (publickey): SSH ตอบกลับมาแต่ปฏิเสธคีย์ของคุณ พอร์ตทำงานปกติ ดังนั้นปัญหาอยู่ที่การยืนยันตัวตน ซึ่งมักเกิดจากการระบุansible_userผิด หรือคีย์ไม่ได้ถูกโหลดไว้Timeout (12s) waiting for privilege escalation prompt: การเชื่อมต่อทำงานได้ แต่becomeล้มเหลว โดยปกติคือ sudo กำลังรอรหัสผ่านแต่ไม่มีการส่งไปให้
การขาด Python interpreter เป็นสาเหตุที่หลายคนคาดว่าจะอยู่ในรายการนี้ แต่จริงๆ แล้วไม่ใช่ เพราะ SSH เชื่อมต่อได้สำเร็จแสดงว่าโฮสต์นั้นเข้าถึงได้ แต่โมดูลไม่มีสภาพแวดล้อมสำหรับรันคำสั่ง:
fatal: [db1]: FAILED! => {"changed": false, "module_stdout": "/bin/sh: 1: /usr/bin/python3: not found\r\n", "msg": "The module failed to execute correctly, you probably need to set the interpreter", "rc": 127}บรรทัดนั้นระบุว่า FAILED! และในส่วนสรุปจะนับรวมไว้ภายใต้ failed ดังนั้น ignore_unreachable จะไม่เข้าไปจัดการกับส่วนนี้ ให้ตั้งค่า ansible_python_interpreter สำหรับโฮสต์นั้น หรือติดตั้ง python3 ลงบนเครื่องดังกล่าว
วิธีละเว้นโฮสต์ที่ไม่สามารถเข้าถึงได้ใน Play
ในระดับ Task คีย์เวิร์ดนี้จะวางอยู่ข้างโมดูล:
- name: Read the package list, and do not stop if the host is down
ansible.builtin.command: dpkg -l
register: packages
changed_when: false
ignore_unreachable: trueในระดับ Play คีย์เวิร์ดนี้จะกำหนดค่าเริ่มต้นให้กับทุก Task ใน Play นั้น และ Task เดียวสามารถกำหนดค่ากลับได้:
- name: Opportunistic fleet maintenance
hosts: all
ignore_unreachable: true
tasks:
- name: This runs, cannot connect, and the play carries on
ansible.builtin.ping:
- name: This one still ends the play for a host that is down
ansible.builtin.ping:
ignore_unreachable: falseสิ่งที่เปลี่ยนแปลงเบื้องหลังเป็นเรื่องที่ควรทราบ เมื่อตั้งค่า ignore_unreachable โฮสต์จะไม่ถูกนำออกจาก Play ดังนั้นทุก Task ที่ตามมาจะพยายามเชื่อมต่อใหม่และล้มเหลวในลักษณะเดิมอีกครั้ง การพยายามแต่ละครั้งจะรอจนครบเวลา connection timeout ซึ่งปกติคือ 10 วินาที เว้นแต่คุณจะเปลี่ยนค่า timeout ใน ansible.cfg การรัน Play ที่มี 20 Task กับเซิร์ฟเวอร์ที่ตายแล้วหนึ่งเครื่อง จะเพิ่มเวลาการทำงานประมาณ 200 วินาทีและเพิ่มบรรทัดสีแดงใน Log อีก 20 บรรทัด
ดังนั้น ให้ตรวจสอบเพียงครั้งเดียว แล้วหยุดโฮสต์นั้นอย่างสะอาด:
- name: Opportunistic fleet maintenance
hosts: all
gather_facts: false
tasks:
- name: Check that the host answers before doing any work
ansible.builtin.ping:
register: reachable
ignore_unreachable: true
- name: End the play for this host if it never answered
ansible.builtin.meta: end_host
when: reachable.unreachable | default(false)
- name: Gather facts now that the connection is known good
ansible.builtin.setup:
- name: Refresh the package index
ansible.builtin.apt:
update_cache: true
become: trueวิธีนี้จะเป็นการพยายามเชื่อมต่อหนึ่งครั้งต่อโฮสต์ที่ตายแล้ว แทนที่จะเป็นหนึ่งครั้งต่อ Task โดย end_host ซึ่งถูกเพิ่มเข้ามาใน Ansible 2.8 จะยุติการทำงานของ Play สำหรับโฮสต์ปัจจุบันโดยไม่ทำเครื่องหมายว่าล้มเหลว คีย์ unreachable จะปรากฏในผลลัพธ์ที่ลงทะเบียนไว้ (registered result) ก็ต่อเมื่อการเชื่อมต่อล้มเหลวเท่านั้น ดังนั้น default(false) จึงช่วยให้เงื่อนไขยังคงถูกต้องสำหรับทุกโฮสต์ที่ตอบกลับมา การเก็บ Fact ถูกปิดในระดับ Play เพราะ Task Gathering Facts โดยนัย (implicit) อาจกลายเป็น Task ที่ไปเจอกับการเชื่อมต่อที่เสีย และคุณต้องการให้ Task นั้นเป็น Ping ของคุณเอง
ignore_unreachable เป็นคีย์เวิร์ดสำหรับ Play และ Task ควรเก็บไว้ใน Playbook ที่ผู้อ่านสามารถมองเห็นได้ แทนที่จะเก็บไว้ภายใน Role เพราะมันเป็นตัวตัดสินว่าการรันครั้งนั้นอนุญาตให้พลาดโฮสต์ใดได้บ้าง การแบ่งแยกหน้าที่ระหว่าง Playbooks และ Roles ครอบคลุมถึงเลเยอร์ที่ควรเป็นเจ้าของค่าคอนฟิกูเรชันลักษณะนี้
เหตุผลที่ ignore_errors ไม่ใช่เครื่องมือที่เหมาะสมในกรณีนี้
เอกสารประกอบของ Ansible ระบุข้อจำกัดไว้อย่างชัดเจนว่า ignore_errors "ทำงานได้เฉพาะเมื่อ task สามารถรันได้และส่งค่ากลับมาเป็น 'failed' เท่านั้น มันไม่สามารถทำให้ Ansible เพิกเฉยต่อข้อผิดพลาดประเภทตัวแปรไม่ได้กำหนด (undefined variable), การเชื่อมต่อล้มเหลว, ปัญหาการประมวลผล (เช่น แพ็กเกจหาย) หรือข้อผิดพลาดทางไวยากรณ์ได้"
ความล้มเหลวในการเชื่อมต่อจะไม่กลายเป็นผลลัพธ์ของ task ที่มี failed: true แต่มันจะมาในรูปแบบของ flag แยกต่างหาก ซึ่ง Ansible จะจัดการกับ flag นั้นก่อนเสมอ โดยโฮสต์จะถูกย้ายไปอยู่ในรายการ unreachable และถูกนำออกจาก play ทันที การใส่ ignore_errors: true ไว้ในทุก 12 task ของ play จะไม่ช่วยอะไร หากโฮสต์มีพอร์ต SSH ที่ปิดอยู่ มันก็จะหยุดทำงานตั้งแต่ task แรก นี่คือความเข้าใจผิดที่พบบ่อยที่สุดในส่วนนี้ และควรตรวจสอบ playbook เก่าของคุณด้วยการใช้ grep โดยเฉพาะ playbook ที่เขียนขึ้นในขณะที่ กำลังเรียนรู้การเขียน playbook แรกบน VPS
ตรวจสอบก่อนที่จะเพิกเฉย
การเพิกเฉยปัญหาจนกลายเป็นเรื่องถาวรจะทำให้ระบบจัดการไม่ได้ เพราะโฮสต์ที่เข้าถึงไม่ได้ก็คือโฮสต์ที่ไม่มีการแพตช์ ให้ดำเนินการตามลำดับนี้ก่อน คำสั่งทั้งหมดในที่นี้เป็นเพียงการอ่านค่าเท่านั้น
ansible web2 -i inventory.ini -m ansible.builtin.ping -oจะรันหนึ่งโมดูลกับหนึ่งโฮสต์และแสดงผลลัพธ์หนึ่งบรรทัด- เพิ่ม
-vvvvเข้าไปในคำสั่งเดิม Ansible จะแสดงคำสั่ง ssh เต็มรูปแบบที่สร้างขึ้น รวมถึงผู้ใช้ปลายทาง, พอร์ต, private key และตัวเลือกต่างๆ ที่ส่งผ่านไป - รันคำสั่ง ssh นั้นด้วยตัวคุณเองโดยใช้
-vหาก ssh ปกติไม่สามารถเข้าถึงได้ แสดงว่าปัญหาอยู่ที่ระดับต่ำกว่า Ansible และไม่มีคีย์เวิร์ดใดใน playbook ที่จะแก้ไขปัญหานี้ได้ - อ่านสตริง
msgและเปรียบเทียบกับรายการด้านบนConnection refusedและConnection timed outชี้ไปยังจุดที่ต่างกันสองจุด จุดหนึ่งคือบริการ SSH และอีกจุดคือเส้นทางเครือข่าย - สำหรับ
Host key verification failed.ให้ตรวจสอบสิ่งที่คุณจัดเก็บไว้ด้วยssh-keygen -F web2.example.comหากเซิร์ฟเวอร์ถูกสร้างใหม่ ให้ลบรายการเก่าออกด้วยssh-keygen -R web2.example.comแล้วยอมรับคีย์ใหม่หลังจากตรวจสอบกับคอนโซลของผู้ให้บริการแล้ว การตั้งค่าhost_key_checking = Falseในansible.cfgจะล้างข้อผิดพลาดและยังลบการตรวจสอบที่จะแจ้งเตือนคุณหากมีเครื่องอื่นตอบกลับมาที่ที่อยู่นั้น - สำหรับ
Permission denied (publickey)ให้ยืนยันสิ่งที่ Ansible คิดว่าควรใช้ansible-inventory -i inventory.ini --host web2จะแสดงตัวแปรที่มีผลอยู่ รวมถึงansible_userและansible_port - หาก SSH ทำงานได้แต่โมดูลไม่ทำงาน ให้ตรวจสอบ interpreter ด้วย
ansible web2 -m ansible.builtin.raw -a 'command -v python3 || echo none'โมดูลrawจะรันคำสั่งผ่าน shell และไม่จำเป็นต้องมี python บนโฮสต์ปลายทาง
หลังจากดำเนินการทั้งหมดนี้แล้ว การเพิกเฉยต่อโฮสต์จึงจะเป็นการตัดสินใจอย่างมีเหตุผล ไม่ใช่เพียงแค่ความเคยชิน
สรุปผลการทำงานจะนับโฮสต์ที่ไม่สามารถเข้าถึงได้แยกต่างหาก และโดยปกติแล้ว CI มักจะมองข้ามจุดนี้
ansible-playbook จะส่งค่า exit code เป็น 0 เมื่อสำเร็จ เป็น 2 เมื่อมีโฮสต์อย่างน้อยหนึ่งแห่งล้มเหลว และเป็น 4 เมื่อมีโฮสต์อย่างน้อยหนึ่งแห่งไม่สามารถเข้าถึงได้ ค่าทั้งสองนี้เป็น bit flag ในซอร์สโค้ด ดังนั้นการรันที่มีทั้งโฮสต์ที่ล้มเหลวและโฮสต์ที่ไม่สามารถเข้าถึงได้จะส่งค่า exit code เป็น 6 ส่วนคำสั่ง ansible ก็ส่งค่ารหัสเดียวกัน ค่าเหล่านี้ได้รับการตรวจสอบกับซอร์สโค้ดของ ansible-core ในเดือนสิงหาคม 2026
ตอนนี้ให้ตั้งค่า ignore_unreachable: true แล้วรันเพลย์บุ๊กที่มี 7 งานชุดเดิมกับโฮสต์ที่ตายแล้วตัวเดิม:
PLAY RECAP *********************************************************************
web1 : ok=7 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
web2 : ok=7 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=7web2 จะรายงาน unreachable=0 และงาน 7 งานในสถานะ ok โดยการรันจะส่งค่า exit code เป็น 0 เมื่อมีการตั้งค่าคีย์เวิร์ดนี้ Ansible จะเพิ่มตัวนับ ok และ ignored สำหรับโฮสต์นั้นแทนที่จะเป็นตัวนับที่เรียกว่า dark ซึ่งเป็นตัวนับที่แสดงในคอลัมน์ unreachable บรรทัดสีแดง UNREACHABLE! จะยังคงถูกพิมพ์ออกมา ดังนั้น log จึงยังคงแสดงความจริง ในขณะที่สรุปผลและ exit code นั้นไม่ตรงตามความเป็นจริง
งาน CI ที่รันเพลย์บุ๊กและตรวจสอบเพียงแค่ $? จะถือว่าการรันนั้นผ่าน และไม่มีส่วนใดในสรุปที่ระบุว่าเครื่องนั้นไม่เคยถูกเข้าถึงมาก่อน ให้แยกการตรวจสอบการเข้าถึงโฮสต์ออกมาเป็นขั้นตอนต่างหากก่อนเริ่มเพลย์บุ๊ก:
ansible all -i inventory.ini -m ansible.builtin.ping -oคำสั่งนี้จะพิมพ์หนึ่งบรรทัดต่อหนึ่งโฮสต์และส่งค่า exit code เป็น 4 หากมีโฮสต์ใดไม่สามารถเข้าถึงได้ ซึ่งจะทำให้ไปป์ไลน์มีจุดที่สามารถแจ้งความล้มเหลวได้ และทำให้คุณเห็นชื่อโฮสต์ใน log ทั้งนี้ ping จำเป็นต้องมี Python interpreter ที่ทำงานได้บนโฮสต์ปลายทาง ดังนั้นมันจึงพิสูจน์ได้มากกว่าแค่การเชื่อมต่อ ซึ่งมักจะเป็นสิ่งที่คุณต้องการ จากนั้นให้รันเพลย์บุ๊กด้วย ignore_unreachable เพื่อให้โฮสต์ที่ยังออนไลน์อยู่ได้รับการเปลี่ยนแปลงตามปกติ
any_errors_fatal และ max_fail_percentage ในการทำงานแบบกลุ่ม
คีย์เวิร์ดทั้งสองตัวนี้กำหนดการทำงานเมื่อเกิดข้อผิดพลาดขึ้นกับโฮสต์บางส่วนในกลุ่ม โดยทั้งสองตัวมีวิธีจัดการกับโฮสต์ที่ไม่สามารถเข้าถึงได้ (unreachable hosts) แตกต่างกัน
any_errors_fatal: true จะตอบสนองต่อโฮสต์ที่ไม่สามารถเข้าถึงได้ โดย Ansible จะทำงานใน task ปัจจุบันให้เสร็จสิ้นสำหรับโฮสต์ที่เหลือในกลุ่ม จากนั้นจึงหยุดการทำงานของ play สำหรับทุกโฮสต์ในกลุ่มนั้น ให้ใช้ตัวเลือกนี้เมื่อการทำงานจำเป็นต้องสำเร็จทั้งหมดหรือล้มเหลวทั้งหมดเท่านั้น เช่น การปรับปรุงโครงสร้างฐานข้อมูล (schema change) ที่ต้องทำพร้อมกัน
max_fail_percentage: 30 จะไม่ตอบสนองต่อโฮสต์ที่ไม่สามารถเข้าถึงได้ การตรวจสอบนี้จะคำนวณจากจำนวนโฮสต์ที่ ล้มเหลว (failed) หารด้วยขนาดของกลุ่ม โดยโฮสต์ที่ไม่สามารถเข้าถึงได้จะถูกแยกไว้ในรายการต่างหาก จึงไม่มีผลต่อการคำนวณตัวเลขดังกล่าว หากมีโฮสต์ 10 ตัวและไม่สามารถเข้าถึงได้ 4 ตัว การทำงานจะยังคงดำเนินต่อไปภายใต้ max_fail_percentage: 10 ในขณะที่หากมีโฮสต์ 2 ตัวล้มเหลวใน task หนึ่ง จะทำให้ play หยุดทำงานทันที เอกสารประกอบระบุข้อควรระวังเพิ่มเติมว่า "ค่าเปอร์เซ็นต์ที่ตั้งไว้จะต้องถูกเกินค่าที่กำหนด ไม่ใช่เท่ากับค่าที่กำหนด" ดังนั้นด้วย serial: 4 การจะหยุดทำงานหลังจากล้มเหลว 2 จาก 4 ตัว คุณต้องระบุค่าเป็น 49 ไม่ใช่ 50
มีกรณีเดียวที่โฮสต์ที่ไม่สามารถเข้าถึงได้จะทำให้การทำงานหยุดลงด้วยตัวมันเอง คือหากโฮสต์ทุกตัวในกลุ่มล้มเหลวหรือเข้าถึงไม่ได้ทั้งหมด Ansible จะไม่มีโฮสต์เหลือให้ทำงานต่อ และจะยุติการทำงานของ play ด้วย NO MORE HOSTS LEFT
serial: การปรับใช้การเปลี่ยนแปลงแบบไล่ระดับทั่วทั้งกลุ่มเซิร์ฟเวอร์
- name: Rolling nginx config update
hosts: webservers
serial: 2
max_fail_percentage: 25
tasks:
- name: Deploy the site config
ansible.builtin.template:
src: site.conf.j2
dest: /etc/nginx/conf.d/site.conf
owner: root
mode: "0644"
become: true
notify: Reload nginx
handlers:
- name: Reload nginx
ansible.builtin.service:
name: nginx
state: reloaded
become: trueserial: 2 จะรันเพลย์บุ๊กทั้งหมดกับโฮสต์สองเครื่องจนเสร็จสิ้น จากนั้นจึงเริ่มดำเนินการกับสองเครื่องถัดไป serial: "25%" จะปรับขนาดตามจำนวนของกลุ่มโฮสต์ รายการในรูปแบบ serial: [1, 5, 10] คือการกำหนดกลุ่มนำร่อง (canary): เริ่มจากโฮสต์หนึ่งเครื่อง ตามด้วยห้าเครื่อง และสิบเครื่อง โดยโฮสต์ที่เหลือจะถูกรันในชุดที่มีขนาดเท่ากับกลุ่มสุดท้าย max_fail_percentage จะถูกวัดผลต่อหนึ่งชุดการทำงาน ดังนั้นทั้งสองค่าจึงทำงานร่วมกัน หากเครื่องแรกเกิดข้อผิดพลาด การทำงานจะหยุดลงก่อนที่จะส่งผลกระทบต่อเครื่องที่สี่สิบ นี่คือสิ่งที่ทำให้ การจัดการกลุ่มเซิร์ฟเวอร์ Linux จากเครื่องควบคุมเครื่องเดียว มีความปลอดภัยในการสั่งการด้วยคำสั่งเดียว
เมื่อใดที่ควรเพิกเฉยต่อโฮสต์ที่ติดต่อไม่ได้ และเมื่อใดที่ไม่ควร
ให้เพิกเฉยต่อโฮสต์เหล่านั้นสำหรับการทำงานแบบฉวยโอกาส (opportunistic work) การรวบรวมข้อมูล (fact collection) หรือการตรวจสอบความคลาดเคลื่อนรายชั่วโมง (drift check) จะไม่สูญเสียอะไรเลยหากข้ามโฮสต์ที่ออฟไลน์ไป เพราะรอบถัดไปจะกลับมาตรวจสอบโฮสต์เหล่านั้นเอง การใช้ระดับ play ที่ ignore_unreachable: true คือคำตอบที่ถูกต้องในกรณีนี้ โดยจับคู่กับขั้นตอนการ ping เพื่อให้ชื่อโฮสต์ที่ถูกข้ามไปปรากฏในที่ที่ผู้ดูแลระบบสามารถอ่านได้
ห้ามเพิกเฉยต่อโฮสต์ที่ติดต่อไม่ได้เด็ดขาดสำหรับการรันแพตช์ความปลอดภัย (security patch run) คุณค่าของการรันแพตช์คือการรับประกันว่าทุกโฮสต์ได้รับการแก้ไขแล้ว การระงับสถานะ unreachable จะเปลี่ยนสถานะจาก "มีเซิร์ฟเวอร์หนึ่งเครื่องที่ยังคงมีความเสี่ยง" ให้กลายเป็นสรุปผลที่ดูสะอาดตา โฮสต์ที่ติดต่อไม่ได้มาเป็นเวลาสองสัปดาห์คือโฮสต์ที่มีแนวโน้มจะล้าหลังที่สุด ให้การรันนั้นจบลงด้วย exit 4 และปล่อยให้เจ้าหน้าที่เข้ามาตรวจสอบ
มีกฎข้อหนึ่งที่ใช้ได้กับทั้งสองกรณี คือให้ระงับการหยุดทำงาน แต่ห้ามระงับการบันทึกข้อมูล หากโฮสต์ถูกข้ามไป จะต้องมีส่วนใดส่วนหนึ่งแจ้งให้ทราบ ไม่ว่าจะเป็นในสรุปผล ใน log ของ CI หรือในการแจ้งเตือนของระบบ monitoring เนื่องจาก Ansible จะรับรู้ว่าโฮสต์มีตัวตนอยู่เฉพาะในช่วงเวลาไม่กี่วินาทีที่ play ทำงานกับโฮสต์นั้นเท่านั้น จึงไม่ใช่ที่ที่เหมาะสมในการตรวจสอบว่าเซิร์ฟเวอร์เครื่องใดออฟไลน์มาตั้งแต่วันอังคาร งานดังกล่าวเป็นหน้าที่ของระบบ monitoring และ Ansible playbook สำหรับติดตั้ง Zabbix จะช่วยให้คุณเห็นภาพรวมของเซิร์ฟเวอร์ทั้งหมดได้ภายในช่วงบ่ายวันเดียว
FAQ
ignore_errors และ ignore_unreachable ใน Ansible ต่างกันอย่างไร
ignore_errors: true ใช้กับงานที่รันบนโฮสต์แล้วพบข้อผิดพลาด เช่น คำสั่งส่งค่า exit code ที่ไม่ใช่ศูนย์ ส่วน ignore_unreachable: true ใช้กับโฮสต์ที่ Ansible ไม่สามารถเชื่อมต่อได้ ซึ่งหมายความว่าไม่มีโมดูลใดถูกรันเลย ทั้งสองตัวเลือกอ่านค่าจากฟิลด์ที่ต่างกันในผลลัพธ์ของงาน และไม่มีตัวใดครอบคลุมกรณีของอีกตัว เอกสารของ Ansible ระบุว่า ignore_errors "ไม่ได้ทำให้ Ansible เพิกเฉยต่อข้อผิดพลาดจากตัวแปรที่ไม่ได้กำหนด, ความล้มเหลวในการเชื่อมต่อ, ปัญหาในการประมวลผล (เช่น แพ็กเกจหาย) หรือข้อผิดพลาดทางไวยากรณ์" และการที่พอร์ต SSH ปิดอยู่ถือเป็นความล้มเหลวในการเชื่อมต่อ
ignore_unreachable ทำให้โฮสต์หายไปจากสรุปผลการรัน (play recap) หรือไม่
ในทางปฏิบัติคือใช่ เมื่อตั้งค่าคีย์เวิร์ดนี้ Ansible จะหยุดนับโฮสต์นั้นในส่วน unreachable และนับเป็น ok และ ignored แทนในแต่ละงาน ทำให้การรันจบลงด้วย exit code 0 อย่างไรก็ตาม บรรทัด fatal: [host]: UNREACHABLE! ยังคงแสดงผลอยู่ ดังนั้น log จึงยังคงถูกต้องแม้ว่าสรุปผลและ exit code จะไม่สะท้อนความจริง ให้คอยตรวจสอบคอลัมน์ ignored หรือรัน ansible all -m ansible.builtin.ping -o แยกเป็นอีกขั้นตอน เพื่อให้โฮสต์ที่ไม่สามารถเข้าถึงได้ยังคงส่งผลเป็น exit code ที่ไม่ใช่ศูนย์
ansible-playbook ส่งค่า exit code ใดกลับมาเมื่อโฮสต์ไม่สามารถเข้าถึงได้
Ansible ส่งค่า 4 กลับมา การรันที่มีโฮสต์ล้มเหลวอย่างน้อยหนึ่งเครื่องจะส่งค่า 2 กลับมา และเนื่องจากค่าทั้งสองเป็น bit flag การรันที่มีทั้งโฮสต์ที่ล้มเหลวและโฮสต์ที่เข้าถึงไม่ได้จะส่งค่า 6 กลับมา ส่วนการรันที่สมบูรณ์จะส่งค่า 0 รหัสเหล่านี้ได้รับการตรวจสอบกับซอร์สโค้ดของ ansible-core ในเดือนสิงหาคม 2026 การตั้งค่า ignore_unreachable: true จะทำให้ค่า 4 หายไป ซึ่งเป็นเหตุผลว่าทำไม pipeline ที่ตรวจสอบเฉพาะ exit code จึงไม่สามารถตรวจพบเครื่องที่ถูกข้ามไปได้
ฉันจะข้ามงานที่เหลือใน play สำหรับโฮสต์ที่ไม่ตอบสนองได้อย่างไร
ให้งานแรกเป็น ansible.builtin.ping โดยใช้ ignore_unreachable: true และ register: reachable จากนั้นตามด้วย ansible.builtin.meta: end_host ภายใต้เงื่อนไข when: reachable.unreachable | default(false) โดย end_host จะเป็นการจบการทำงานของ play สำหรับโฮสต์นั้นโดยไม่ทำเครื่องหมายว่าล้มเหลว ให้ตั้งค่า gather_facts: false ไว้ที่ระดับ play เพื่อให้การ ping เป็นงานที่ตรวจพบการเชื่อมต่อที่ขาดหายไป หากไม่มีรูปแบบนี้ โฮสต์ที่ตายแล้วจะยังคงอยู่ใน play และงานถัดไปทุกงานจะต้องรอจนกว่าจะหมดเวลาเชื่อมต่อ (timeout) อีกครั้ง
ฉันควรเพิกเฉยต่อโฮสต์ที่ไม่สามารถเข้าถึงได้ระหว่างการรันแพตช์ความปลอดภัยหรือไม่
ไม่ควร การรันแพตช์มีความสำคัญเพราะเป็นการรับประกันว่าทุกโฮสต์ได้รับการอัปเดตแล้ว การเพิกเฉยต่อโฮสต์ที่ไม่สามารถเข้าถึงได้จะทำให้การรับประกันนั้นหายไปและแทนที่ด้วยสรุปผลที่เป็นสีเขียว ควรปล่อยให้การรันจบลงด้วยค่า 4 อ่านรายชื่อโฮสต์ที่ไม่ตอบสนอง แล้วแก้ไขปัญหาเหล่านั้น การเพิกเฉยควรใช้เฉพาะกับการรันซ้ำแบบฉวยโอกาส (opportunistic runs) ที่การรันรอบถัดไปจะเก็บงานที่พลาดไปได้เอง