اجرای 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

سه برچسب زمانی داستان را قبل از خواندن یک خط log می‌گویند. اگر فایل پیکربندی از لحظه‌ای که واحد فعال شد جدیدتر باشد، رابط در حال اجرا از فایل قدیمی‌تری ساخته شده و هر نشانه بعدی از همان‌جا می‌آید.

این نقص Ansible نیست. همین رفتار در هر ابزار مدیریت پیکربندی دیده می‌شود که «مطمئن شو اجرا می‌شود» را از «مطمئن شو این را اجرا می‌کند» جدا می‌کند. این تفاوت تا وقتی یک بعدازظهر را نگیرد، موشکافانه به نظر می‌رسد.

راه‌اندازی یک حالت است، نه بارگذاری مجدد

state: started شرطی درباره واحد را تأیید می‌کند. وعده نمی‌دهد که سرویس ورودی‌هایش را دوباره بخواند و نمی‌تواند بداند فایلی که هرگز باز نکرده تغییر کرده است. در استقرار wg-quick ورودی‌ها شامل پیکربندی رابط، آدرس‌ها، مسیرها، رکوردهای DNS و هر hook پس از بالا آمدن است.

به همین دلیل فایل به‌روزشده می‌تواند 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

handlerها در پایان play اجرا می‌شوند، پس از وظایفی که به آن‌ها اطلاع داده‌اند، بنابراین وظیفه‌ای در ادامه همان play ممکن است هنوز رابط قدیمی را ببیند. اگر ترتیب مهم است، handlerها را صریحاً با meta: flush_handlers پیش از وظیفه‌ای که به پیکربندی جدید وابسته است خالی کنید.

به‌جای reload از restart استفاده کنید مگر اینکه عمل بارگذاری مجدد واحد را بررسی کرده باشید. واحد wg-quick بالادست یکی را پشتیبانی می‌کند و پیکربندی WireGuard را هم‌گام می‌کند، اما تنظیم آدرس‌ها، مسیرها، DNS یا hookها را دوباره اجرا نمی‌کند. بنابراین 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 چاپ می‌کند و به همین دلیل این دو اصلاً قابل مقایسه‌اند.

تفاوت در فهرست peer یعنی فایل هرگز اعمال نشده است. تفاوت محدود به کلیدها یا نقطه‌های پایانی که انتظار تغییرشان را داشتید یعنی چیزی آن را جزئی اعمال کرده است. نبود تفاوت یعنی پیکربندی به‌روز است و عیب جای دیگری است، که خودش نتیجه مفیدی است.

هزینه راه‌اندازی مجدد را آگاهانه بپردازید

راه‌اندازی مجدد رابط تونل را می‌اندازد، و اگر آن تونل نشست مدیریتی شما را حمل کند، راه‌اندازی مجدد همان نشستی را که آن را خواسته پایان می‌دهد. handler را پشت پنجره تغییر بگذارید یا playbook را از مسیر مدیریتی‌ای اجرا کنید که به راه‌اندازی مجدد رابط وابسته نیست.

هزینه همان دلیلی است که مردم به reload چنگ می‌زنند. این غریزه معقولی است و وقتی بدانید reload کدام تغییرات را واقعاً اعمال می‌کند درست می‌شود. برای تغییرات peer و کلید در واحد wg-quick بالادست کافی است؛ برای آدرس‌دهی، مسیریابی و hookها نه. یک بار روشن کردن این موضوع ارزان‌تر از کشف آن در پنجره تغییر است.

مستندات ماژول systemd_service تفاوت started، restarted و reloaded را توضیح می‌دهد و ارزش یک بار خواندن را دارد به‌جای حدس زدن از یک خلاصه سبز. همه چیز در این مسیر از همان یک تفاوت می‌آید.

مقالات مرتبط