ריצת ה-playbook מסתיימת בירוק, יחידת WireGuard פעילה, ה-handshake עובד, וה-peer החדש עדיין לא מגיע לשום מקום. לראות playbook מדווח על הצלחה בזמן שהשרת רץ עם התצורה של אתמול זה מתסכל במיוחד, כי שום דבר בפלט אינו שגוי. Ansible עשה בדיוק את מה שהתבקש: וידא שהיחידה מופעלת, והיא כבר הייתה מופעלת. החלת קובץ תצורה שנכתב מחדש היא משימה אחרת, והיא קורית רק אם משהו אומר לשירות לקרוא אותו שוב.

ראו מה ריצה שנייה באמת מדווחת

הריצו את ה-playbook פעמיים כשהתצורה ששונתה במקומה והשוו את הסיכום. הריצה השנייה אמורה להראות את משימת היחידה כ-ok ולא כ-changed, כי שירות שכבר פועל נשאר בשקט לפי התכנון. זו idempotence שעובדת נכון, וגם הסיבה שהקובץ החדש לא מגיע לעולם ל-kernel.

הוסיפו ריצת בדיקה עם פלט diff כדי לראות אילו משימות היו נוגעות במשהו בכלל.

ansible-playbook -i inventory site.yml --check --diff
systemctl show wg-quick@wg0 -p ActiveEnterTimestamp -p NRestarts
stat -c '%y %n' /etc/wireguard/wg0.conf

שלושת חותמות הזמן מספרות את הסיפור לפני שתקראו שורת לוג אחת. אם קובץ התצורה חדש מהרגע שבו היחידה הפכה לפעילה, הממשק הפעיל נבנה מקובץ ישן יותר, וכל תסמין בהמשך נובע מכך.

זו אינה תקלה ב-Ansible. אותה התנהגות מופיעה בכל כלי לניהול תצורה שמפריד בין «ודא שזה פועל» לבין «ודא שזה פועל עם זה». ההבחנה נראית קפדנית מדי עד שהיא עולה לכם אחר צהריים.

מופעל הוא מצב, לא טעינה מחדש

state: started מצהיר על תנאי לגבי היחידה. הוא לא מבטיח שהשירות יקרא שוב את הקלטים שלו, והוא לא יכול לדעת שקובץ שהוא מעולם לא פתח השתנה. בפריסת wg-quick הקלטים כוללים את תצורת הממשק, הכתובות, המסלולים, רשומות ה-DNS וכל hook מסוג post-up.

לכן קובץ מעודכן יכול להכיל peer חדש, כתובת אחרת או הוראת ניתוב שהשתנתה בזמן שהממשק החי ממשיך לשרת את הסט הישן. ה-handshake עדיין מצליח כי המפתחות ונקודות הקצה שנטענו עדיין תקפים. זה בדיוק מה שמקשה לראות את התקלה, ולכן אנשים רודפים אחרי DNS במשך שעה לפני שהם בודקים חותמת זמן.

שתי אפשרויות אחרות של המודול נחשבות לעתים קרובות לפתרון. daemon_reload גורם ל-systemd לקרוא מחדש קבצי יחידה, וזה לא רלוונטי לקובץ תצורה של WireGuard. state: reloaded מבקש מהיחידה להריץ את פעולת הטעינה מחדש שלה, והאם היא עושה את מה שצריך תלוי כולו ביחידה. אף אחת מהן אינה מחליפה את הידיעה אילו קלטים התהליך הפעיל קרא באמת.

התקינו את הקובץ ואז הודיעו ל-handler

התבנית ששומרת על idempotence ובכל זאת מיישמת שינויים היא הרגילה: המשימה שכותבת את הקובץ מודיעה ל-handler, וה-handler רץ רק כשהקובץ באמת השתנה. ריצה שנייה בלי עריכות לא עושה דבר, וזה מה שאתם רוצים.

- name: write the interface configuration
  ansible.builtin.template:
    src: wg0.conf.j2
    dest: /etc/wireguard/wg0.conf
    owner: root
    mode: "0600"
  notify: apply wg0

- name: ensure the interface unit is enabled
  ansible.builtin.systemd_service:
    name: wg-quick@wg0
    enabled: true
    state: started

- name: apply wg0
  listen: apply wg0
  ansible.builtin.systemd_service:
    name: wg-quick@wg0
    state: restarted

ה-handlers רצים בסוף ה-play, אחרי המשימות שהודיעו להם, ולכן משימה מאוחרת יותר באותו play עשויה עדיין לראות את הממשק הישן. אם הסדר חשוב, רוקנו את ה-handlers במפורש עם meta: flush_handlers לפני המשימה שתלויה בתצורה החדשה.

השתמשו ב-restart ולא ב-reload, אלא אם בדקתם את פעולת הטעינה מחדש של היחידה. יחידת wg-quick במעלה הזרם תומכת באחת כזו והיא מסנכרנת את תצורת WireGuard, אבל היא לא מריצה מחדש הגדרת כתובות, מסלולים, DNS או hooks. לכן reload אחרי הוספת הוראת מסלול נראה מוצלח ולא משנה דבר.

השוו את ההתקן החי לקובץ

הבדיקה שסוגרת את סוג התקלות הזה היא השוואה בין מה שה-kernel מחזיק לבין מה שהקובץ אומר. שתי פקודות מפיקות פלט בר-השוואה, וההפרש הוא הסטייה.

sudo wg showconf wg0
sudo wg-quick strip wg0
sudo diff <(sudo wg showconf wg0) <(sudo wg-quick strip wg0)

wg showconf מדפיס את ההתקן החי, כולל המפתח הפרטי, אז התייחסו לפלט הזה כאל סוד ואל תדביקו אותו בכרטיס. wg-quick strip מדפיס את הקובץ בלי המפתחות הייחודיים ל-wg-quick, ולכן השניים בכלל ניתנים להשוואה.

הבדל ברשימת ה-peers אומר שהקובץ מעולם לא יושם. הבדל מוגבל למפתחות או לנקודות קצה שציפיתם שישתנו אומר שמשהו יישם אותו חלקית. אין הבדל כלל אומר שהתצורה מעודכנת והתקלה במקום אחר, וזה כשלעצמו תוצאה שימושית.

שלמו את מחיר האתחול בכוונה

אתחול הממשק מפיל את המנהרה, ואם המנהרה הזו נושאת את סשן הניהול שלכם, האתחול מסיים את הסשן שביקש אותו. שימו את ה-handler מאחורי חלון שינוי, או הריצו את ה-playbook מנתיב ניהול שאינו תלוי באתחול הממשק.

המחיר הוא הסיבה שאנשים פונים ל-reload. זה אינסטינקט הגיוני, והוא הופך לנכון ברגע שאתם יודעים אילו שינויים reload באמת מיישם. לשינויי peers ומפתחות ביחידת wg-quick במעלה הזרם זה מספיק; לכתובות, לניתוב ול-hooks לא. לסדר את זה פעם אחת זול יותר מלגלות את זה במהלך חלון שינוי.

התיעוד של מודול systemd_service מסביר את ההבדל בין started, restarted ו-reloaded, וכדאי לקרוא אותו פעם אחת במקום להסיק מסיכום ירוק. כל מה שבמדריך הזה נובע מאותה הבחנה אחת.

מאמרים קשורים