সোয়াপ ফাইল না থাকায় যেভাবে আমাদের সার্ভার জমে গিয়েছিল
২৪ সেপ্টেম্বর আমাদের একটি প্রোডাকশন সার্ভার সাড়া দেওয়া বন্ধ করে দেয়। লোড ব্যালান্সার ওই সার্ভারটিকে unhealthy হিসেবে চিহ্নিত করে এবং আমাদের remote-command টুল সেখানে পৌঁছাতে পারেনি। কিন্তু কিছুই ক্র্যাশ করেনি, আর মেমরি বেশি ব্যবহারের কারণে কোনো প্রসেসও মারা যায়নি। সার্ভারটি থ্র্যাশিং করছিল: swap স্পেস না থাকায় এবং কন্টেইনারগুলোকে মেশিনের আসল মেমরির চেয়ে বেশি ব্যবহারের সুযোগ দেওয়ায়, Linux তার চলার জন্য দরকারি ফাইলগুলো সরিয়ে আবার পড়তে সময় কাটাচ্ছিল, ফলে প্রায় কোনো কাজই এগোচ্ছিল না। প্রতিটি সার্ভারে ২ GB-র একটি swap file যোগ করে সমস্যাটি ঠিক হয়। তবে এর চেয়েও কৌতূহলোদ্দীপক শিক্ষা ছিল health check নিয়ে।
“আটকে যাওয়া” দেখতে কেমন ছিল
- ওই সার্ভারে লোড ব্যালান্সারের health check টাইমআউট হয়ে যাচ্ছিল।
- AWS Systems Manager-এর মাধ্যমে পাঠানো কমান্ডগুলো “Pending” অবস্থায় আটকে থাকছিল, কখনও চলছিল না।
- লগে কোনো out-of-memory kill ছিল না। কোনো প্রসেস মারা যায়নি।
এর একটু আগেই আমরা প্রোডাকশনকে ২ GB RAM-এর ছোট ARM সার্ভারে নিয়ে গিয়েছিলাম। আমাদের staging সার্ভার, যেটি আরও ছোট, একটি deploy-এর সময় container image আনপ্যাক করার সময় একইভাবে জমে গিয়েছিল।
কোনো memory error কেন ছিল না?
Linux কেবল তখনই একটি প্রসেস মারে, যখন আসলেই পুনরুদ্ধারযোগ্য মেমরি শেষ হয়ে যায়। তার আগে সে page cache থেকে মেমরি নিয়ে নেয়। এটি হলো RAM-এ রাখা ফাইলের কপি, যার মধ্যে প্রতিটি চলমান প্রোগ্রামের কোডও থাকে।
আমাদের সার্ভারে কন্টেইনারগুলোর মোট memory limit ছিল প্রায় ৩.৩ GB, অথচ মেশিনে ব্যবহারযোগ্য ছিল প্রায় ১.৮ GB। সাধারণত সবাই একসঙ্গে তাদের পুরো limit ব্যবহার করে না। কিন্তু যখন করেছিল, কার্নেল ক্যাশ থেকে প্রোগ্রামের কোড সরিয়ে জায়গা করছিল, আবার সেই কোড চালানোর মুহূর্তেই ডিস্ক থেকে পড়তে হচ্ছিল। এই চক্রটিই হলো থ্র্যাশিং, যা একটি মেশিনকে প্রায় সম্পূর্ণ অসাড় করে দিতে পারে, অথচ আনুষ্ঠানিকভাবে মেমরি শেষ হয়ে যায় না। ফলে কিছুই মারা যায় না, আবার কিছু পুনরুদ্ধারও হয় না।
Swap না থাকলে বরাদ্দ করা কিন্তু কম ব্যবহৃত মেমরি রাখার কোনো জায়গা কার্নেলের থাকে না। তাই তাকে ক্যাশ থেকেই সেটি নিতে হয়।
সমাধান
সার্ভার বুটস্ট্র্যাপ স্ক্রিপ্ট এখন প্রতিবার সার্ভার বুট বা মেরামতের সময় একটি ২ GB swap file তৈরি করে, যার swappiness সেটিং কম (১০)। কম swappiness মানে কার্নেল এখনও কাজের মেমরি RAM-এ রাখতে বেশি পছন্দ করে, তবে খুব কম ব্যবহৃত পেজগুলো ডিস্কে সরে যেতে পারে, ফলে প্রোগ্রামের কোড জোর করে ক্যাশ থেকে বের করতে হয় না। আমরা এটি প্রোডাকশনের দুটি সার্ভার ও staging-এ লাইভ প্রয়োগ করেছি। এছাড়া প্রতিটি প্রোডাকশন deploy-এ deploy pipeline বুটস্ট্র্যাপ স্ক্রিপ্টটি আবার প্রকাশ করে, যাতে প্রতিস্থাপিত সার্ভারও এটি পায়।
যে অংশটি আমাদের বেশি ভাবিয়েছিল: অসুস্থ সার্ভারটি রয়ে গিয়েছিল
আমাদের server group ব্যবহার করত EC2 health checks — “মেশিনটি চালু আছে কি?” — এবং load balancer health checks — “এটি কি অনুরোধের উত্তর দিচ্ছে?” — নয়। ফলে লোড ব্যালান্সার যে সার্ভারটিকে ছেড়ে দিয়েছিল, group-এর কাছে সেটি তখনও সুস্থ ছিল। সেটি কখনও প্রতিস্থাপিত হতো না। প্রোডাকশন নিঃশব্দে একটি সার্ভারেই চলতে থাকত, যতক্ষণ না কেউ টের পেয়ে অন্যটি রিবুট করত।
Group-কে load balancer health check-এ বদলে দেওয়াই স্পষ্ট সমাধান মনে হয়, কিন্তু আমাদের জন্য এটি সহজ নয়। একটি deploy-এর সময় সার্ভারগুলো ইচ্ছাকৃতভাবে তাদের health endpoint থেকে একটি error দেয়, যাতে balancer সেগুলো থেকে ট্র্যাফিক সরিয়ে নেয় (দেখুন কীভাবে আমরা deploy-এ শূন্য 502 পেয়েছি)। যে group এই সংকেতের ভিত্তিতে সার্ভার প্রতিস্থাপন করে, সে প্রতিটি deploy-এর মাঝখানে সার্ভার বদলে ফেলত।
তাই আপাতত আমরা আমাদের deploy checklist-এ একটি ধাপ যোগ করেছি: প্রতিটি deploy-এর পর শুধু deploy job পাস হয়েছে কি না নয়, load balancer-এ প্রতিটি target-এর health দেখতে হবে। কোনো সার্ভারে কমান্ড “Pending” অবস্থায় আটকে থাকলে বুঝতে হবে সেটি থ্র্যাশ করছে। তখন সেটিকে রিবুট করুন, কারণ সেটি আগেই রোটেশনের বাইরে চলে গেছে।
এ থেকে আমরা যা নিয়েছি
- “ক্র্যাশ না করা” মানেই “সুস্থ” নয়। থ্র্যাশিং কোনো error তৈরি করে না, শুধু এমন চরম ধীরগতি আনে যে মেশিন সাড়া দেওয়াই বন্ধ করে দেয়।
- ছোট সার্ভারেরও swap দরকার। এমনকি অল্প হলেও, কম swappiness-সহ, কার্নেলের কাছে কম ব্যবহৃত মেমরি রাখার একটি জায়গা থাকে।
- মেশিনের চেয়ে বেশি memory limit একটি বাজি। কখনও সেটি হেরে যায়। কখন বাজি ধরছেন, তা জানা রাখুন।
- আপনার autoscaling কোন health check-কে বিশ্বাস করে, তা জানুন। যে health check শুধু জিজ্ঞেস করে “VM কি চালু?”, সে কাজ বন্ধ করে দেওয়া সার্ভারকে কখনও প্রতিস্থাপন করবে না।