playbook রান সবুজে শেষ হয়, WireGuard ইউনিট সক্রিয়, হ্যান্ডশেক কাজ করে, তবু নতুন peer কিছুতে পৌঁছায় না। সার্ভার গতকালের কনফিগারেশনে চলছে অথচ playbook সফলতা জানাচ্ছে—এটা এক বিশেষ ধরনের বিরক্তি, কারণ আউটপুটে কিছুই ভুল নয়। Ansible ঠিক যা বলা হয়েছিল তাই করেছে: ইউনিট চালু আছে নিশ্চিত করেছে, আর ইউনিট আগেই চালু ছিল। নতুন করে লেখা কনফিগারেশন ফাইল প্রয়োগ করা আলাদা কাজ, এবং তা কেবল তখনই ঘটে যখন কিছু সেবাটিকে সেটি আবার পড়তে বলে।

দ্বিতীয় রান আসলে কী জানায় তা দেখুন

পরিবর্তিত কনফিগারেশন রেখেই playbook দুবার চালান এবং সারাংশ মিলিয়ে দেখুন। দ্বিতীয় রানে ইউনিট টাস্কটি changed নয়, ok দেখানো উচিত, কারণ আগে থেকেই চলা সেবাকে ডিজাইন অনুযায়ী অক্ষত রাখা হয়। এটাই সঠিকভাবে কাজ করা idempotence, এবং নতুন ফাইল কখনও কার্নেলে না পৌঁছানোর কারণও এটাই।

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 এন্ট্রি এবং যেকোনো post-up হুক।

তাই হালনাগাদ ফাইলে নতুন peer, ভিন্ন ঠিকানা বা বদলানো রাউটিং নির্দেশ থাকতে পারে, অথচ লাইভ ইন্টারফেস পুরোনো সেটই পরিবেশন করে চলেছে। হ্যান্ডশেক তবু সফল হয়, কারণ লোড করা কী ও এন্ডপয়েন্ট এখনও বৈধ। ঠিক এ কারণেই ব্যর্থতাটা চোখে পড়ে না, এবং লোকেরা এক ঘণ্টা DNS-এর পেছনে দৌড়ানোর পর টাইমস্ট্যাম্প দেখে।

আরও দুটি মডিউল অপশনকে প্রায়ই সমাধান ভাবা হয়। daemon_reload systemd-কে ইউনিট ফাইল আবার পড়তে দেয়, যা WireGuard কনফিগারেশন ফাইলের সঙ্গে সম্পর্কহীন। state: reloaded ইউনিটকে তার reload অ্যাকশন চালাতে বলে, আর সেটি আপনার প্রয়োজন মেটায় কি না তা নির্ভর করে ইউনিটটির উপরই। কোনোটিই চলমান প্রসেসটি আসলে কোন ইনপুট পড়েছে তা জানার বিকল্প নয়।

ফাইল ইনস্টল করুন, তারপর হ্যান্ডলারকে জানান

idempotence রেখেও পরিবর্তন প্রয়োগ করার ধরনটি সাধারণ: যে টাস্ক ফাইলটি লেখে সে একটি হ্যান্ডলারকে জানায়, এবং হ্যান্ডলার কেবল তখনই চলে যখন ফাইলটি সত্যিই বদলেছে। কোনো সম্পাদনা ছাড়া দ্বিতীয় রান কিছুই করে না, আর আপনি চানও তাই।

- 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

হ্যান্ডলারগুলো প্লের শেষে চলে, যে টাস্কগুলো তাদের জানিয়েছে তার পরে, তাই একই প্লের পরের কোনো টাস্ক এখনও পুরোনো ইন্টারফেস দেখতে পারে। ক্রম গুরুত্বপূর্ণ হলে, নতুন কনফিগারেশনের উপর নির্ভর করা টাস্কের আগে meta: flush_handlers দিয়ে হ্যান্ডলারগুলো স্পষ্টভাবে ফ্লাশ করুন।

ইউনিটের reload অ্যাকশন যাচাই না করা পর্যন্ত reload-এর বদলে restart ব্যবহার করুন। আপস্ট্রিম wg-quick ইউনিট একটি সমর্থন করে এবং WireGuard কনফিগারেশন সিঙ্ক করে, কিন্তু ঠিকানা, রুট, DNS বা হুক সেটআপ আবার চালায় না। তাই রুট নির্দেশ যোগ করার পর reload সফল দেখায় অথচ কিছুই বদলায় না।

লাইভ ডিভাইসের সঙ্গে ফাইল মিলিয়ে দেখুন

এই ধরনের বাগ বন্ধ করার পরীক্ষা হলো কার্নেল কী ধরে আছে আর ফাইল কী বলে—এই দুইয়ের তুলনা। দুটি কমান্ড তুলনাযোগ্য আউটপুট দেয়, আর পার্থক্যটাই drift।

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 তালিকায় পার্থক্য মানে ফাইলটি কখনও প্রয়োগ হয়নি। কেবল সেই কী বা এন্ডপয়েন্টে পার্থক্য, যেগুলো বদলাবে বলে আপনি আশা করেছিলেন, মানে কিছু একটা এটিকে আংশিক প্রয়োগ করেছে। কোনো পার্থক্য না থাকলে কনফিগারেশন হালনাগাদ এবং ত্রুটি অন্যত্র, যা নিজেই একটি কাজের ফল।

restart-এর খরচটি সচেতনভাবে দিন

ইন্টারফেস restart করলে টানেল ভেঙে যায়, আর সেই টানেল যদি আপনার প্রশাসনিক সেশন বহন করে, তবে restart সেই সেশনই শেষ করে দেয় যা এটি চেয়েছিল। হ্যান্ডলারকে একটি পরিবর্তন-উইন্ডোর পেছনে রাখুন, বা এমন ম্যানেজমেন্ট পথ থেকে playbook চালান যা ইন্টারফেস restart-এর উপর নির্ভর করে না।

এই খরচের কারণেই মানুষ reload-এর দিকে ঝোঁকে। এটি যুক্তিসঙ্গত প্রবৃত্তি, এবং reload আসলে কোন পরিবর্তন প্রয়োগ করে তা জানলে এটি সঠিক হয়ে ওঠে। আপস্ট্রিম wg-quick ইউনিটে peer ও কী পরিবর্তনের জন্য এটি যথেষ্ট; ঠিকানা, রাউটিং ও হুকের জন্য নয়। একবার এটি পরিষ্কার করে নেওয়া পরিবর্তন-উইন্ডোর মধ্যে তা আবিষ্কার করার চেয়ে সস্তা।

systemd_service মডিউলের ডকুমেন্টেশন started, restarted ও reloaded-এর পার্থক্য স্পষ্ট করে বলে, আর সবুজ সারাংশ থেকে অনুমান করার বদলে একবার পড়ে নেওয়া ভালো। এই পুরো আলোচনা ওই একটি পার্থক্য থেকেই আসে।

সম্পর্কিত নিবন্ধ