এই পর্বে যা শিখবেন
- Listener, rule, target group আর health check কীভাবে জুড়ে একটি অনুরোধকে target-এ পৌঁছায়
- Host আর path routing-এর rule লেখা, আর priority-র ভুল ধরা
- TLS termination, re-encryption, certificate manager আর client-এর আসল IP
- Cross-zone balancing, sticky session, connection draining আর idle timeout হিসাব করে বোঝা
- Regional, global আর internal LB বাছা, খরচ অনুমান করা, আর log পড়ে 502 আর 504 খোঁজা
১ · Managed load balancer কেন — নিজের Nginx থেকে পরের ধাপ
পর্ব ১৭-তে autoscaling একই app-এর অনেক কপি চালাল। কিন্তু ব্যবহারকারী তো একটি ঠিকানাই চেনেন। সেই ঠিকানা থেকে অনুরোধ সুস্থ কপিগুলোর মধ্যে ভাগ করার কাজই load balancing. কাল্পনিক একটি অনলাইন বাজার অ্যাপ ধরুন, নাম bazaar. এর সামনে একটি load balancer (সংক্ষেপে LB) বসাব।
Algorithm আর তত্ত্ব এখানে আবার লিখছি না। সংক্ষেপে: L4 LB দেখে IP আর port, দ্রুত সংযোগ ভাগ করে, HTTP-এর ভেতরটা পড়ে না। L7 LB HTTP পড়ে, তাই host, path আর header দেখে সিদ্ধান্ত নেয়। Round robin, least connections আর অন্য algorithm-এর পূর্ণ আলোচনা আছে System Design · Load balancing পর্বে। এখানে প্রশ্ন একটাই: cloud-এ এটি কীভাবে চালাবেন, ঠিক করবেন আর তার দাম দেবেন।
সদরঘাটের কথা ভাবুন। যাত্রী এক গেট দিয়ে ঢোকেন। গেটের লোক জিজ্ঞেস করেন, “কোথায় যাবেন?” বরিশালের যাত্রী এক পন্টুনে, চাঁদপুরের যাত্রী আরেক পন্টুনে। প্রতিটি পন্টুনে কয়েকটি লঞ্চ। ঘাট কর্তৃপক্ষ দেখেন কোন লঞ্চ চলার মতো, আর সমস্যা হলে সেটিকে সরিয়ে দেন। গেট হলো listener, গেটের লোকের তালিকা rule, পন্টুন target group, লঞ্চ target, আর ঘাট কর্তৃপক্ষের নজরদারি health check.
পর্ব ১৪-তে আপনি একটি VM-এ Nginx দিয়ে নিজের reverse proxy চালিয়েছেন। সেটি ভালো শুরু, কিন্তু ওই VM নিজেই একটি একক ব্যর্থতার বিন্দু। Managed LB-তে সমস্যাটি বদলে যায়।
| বিষয় | VM-এ নিজের Nginx বা HAProxy | Managed cloud LB |
|---|---|---|
| OS আর software patch | আপনার | Provider-এর |
| একাধিক zone-এ চলা | নিজে জোড়া বানাতে হয় | Subnet বাছলেই সেই zone-গুলোতে চলে |
| ভার বাড়লে | আপনি বড় করবেন | Provider বাড়ায়, তবে তাৎক্ষণিক নয় |
| Rule আর config | পূর্ণ স্বাধীনতা | Provider যা দেয়, ততটুকু |
| খরচ | VM-এর ভাড়া আর আপনার সময় | ঘণ্টা আর ব্যবহার অনুযায়ী |
| ভুল config-এর দায় | আপনার | এখনো আপনার |
“Provider নিজে scale করে” কথাটি সীমাহীন নিশ্চয়তা নয়। ঈদের অফারে কয়েক মিনিটে traffic দশ গুণ হলে LB-রও বাড়তে সময় লাগতে পারে, আর কিছু অনুরোধ ব্যর্থ হতে পারে। বড় ঘটনার আগে provider-এর নির্দেশিকা দেখুন আর load test করুন। আরেকটি কথা: অনেক LB-র IP সময়ের সাথে বদলায়। তাই IP নয়, LB-র দেওয়া DNS নাম ব্যবহার করুন।
২ · চারটি অংশ — listener, rule, target group, health check
প্রতিটি provider-এর নাম আলাদা হলেও গঠন প্রায় একই। আগে নিরপেক্ষ শব্দগুলো চিনে নিন।
- Listener: একটি protocol আর port-এ শোনে, যেমন HTTPS 443. LB-র দরজা।
- Rule: এই host বা path এলে অনুরোধ কোন দলে যাবে, তার নিয়ম। কোনো rule না মিললে চলে default action.
- Target group: অনুরোধ গ্রহণকারীদের একটি দল। সদস্য হতে পারে VM, container-এর IP, এমনকি function. Autoscaling group নতুন VM-কে এখানেই যোগ করে।
- Health check: LB নিয়মিত প্রতিটি target-কে যাচাই করে। সুস্থরাই অনুরোধ পায়।
দুই zone জুড়ে internet-facing load balancer
Internet-facing LB across two availability zones, with two target groups
Subnet আর VPC-র বিস্তারিত পর্ব ২৫-এ, আর target-এ শুধু LB-কে ঢুকতে দেওয়ার firewall নিয়ম পর্ব ২৭-এ আসবে। এবার একটি অনুরোধ ধরে পুরো পথটি দেখুন।
- DNS
Aঠিকানা - Listener
:443দরজা - Rule
/api/*বাছাই - Target group
tg-apiসুস্থ দল - Target
api-2আসল কাজ
shop.example.com -> lb-demo.example.net lb-demo.example.net A 198.51.100.20 A 198.51.100.21 (অন্য zone)
client 203.0.113.10:51000 -> 198.51.100.20:443
TLS handshake, SNI = shop.example.com
certificate পাঠানো হলো, অনুরোধ খোলা হলো
GET /api/orders Host: shop.example.com rule 10 /api/admin/* মিলল না rule 20 /api/* মিলল -> tg-api
api-1 10.0.1.21:8080 unhealthy, বাদ api-2 10.0.2.21:8080 healthy, এবার এর পালা
from 10.0.1.5 (LB-র ঠিকানা) X-Forwarded-For: 203.0.113.10 HTTP/1.1 200 OK -> LB -> client
- ধাপ ১ — DNS. Browser
shop.example.comখোঁজে। উত্তরে LB-র DNS নাম আর তার কয়েকটি IP আসে। প্রতিটি zone-এ LB-র অংশ থাকায় client যেকোনো একটিতে পৌঁছায়। সংখ্যাগুলো নমুনা। - ধাপ ২ — Listener. 443 port-এর listener সংযোগ নেয় আর TLS সম্পূর্ণ করে। এখানেই অনুরোধ খোলা হয়, তাই L7 LB এখন path পড়তে পারে।
- ধাপ ৩ — Rule. Rule-গুলো priority-র ক্রমে মেলানো হয়। Rule 10 মেলেনি। Rule 20 মিলেছে, তাই পরের rule আর দেখা হয় না। সিদ্ধান্ত:
tg-api. - ধাপ ৪ — Target group.
api-1health check-এ ব্যর্থ বলে বাদ। সুস্থদের মধ্যে algorithm বাছলapi-2. - ধাপ ৫ — Target. LB নিজের ঠিকানা থেকে নতুন সংযোগ করে, আর client-এর IP header-এ জুড়ে দেয়। Target LB-কে উত্তর দেয়, LB সেটি client-কে পৌঁছায়, আর এক লাইন log লেখে।
তিন provider-এর নাম
| ধারণা | AWS | Azure | Google Cloud |
|---|---|---|---|
| L7 LB | Application Load Balancer (ALB) | Application Gateway | Application Load Balancer |
| L4 LB | Network Load Balancer (NLB) | Azure Load Balancer | Network Load Balancer |
| Target-এর দল | Target group | Backend pool | Backend service |
| Routing-এর নিয়ম | Listener rule | Routing rule, URL path map | URL map |
| Global সামনের স্তর | Global Accelerator (L4) | Front Door (L7) | Global Application LB |
নাম আর বৈশিষ্ট্য বদলায়। এই সারণি লেখার সময়ের চিত্র; বাছাইয়ের আগে provider-এর বর্তমান ডকুমেন্টেশন দেখুন। HTTP-এর কাজে সাধারণত L7, আর খুব বেশি সংযোগ, TCP বা UDP protocol, অথবা স্থির IP দরকার হলে L4 ভাবা হয়।
৩ · Host আর path routing — rule কীভাবে মেলে
একটি LB-র পেছনে অনেক service রাখার চাবি হলো rule. Host routing দেখে domain: img.example.com এক দলে, shop.example.com আরেক দলে। Path routing দেখে URL-এর পথ: /api/* এক দলে, বাকিটা অন্য দলে। দুটি একসাথেও মেলানো যায়।
AWS ALB-র rule-এর একটি উদাহরণ। ছোট priority নম্বর আগে মেলে। ARN-গুলো shell variable-এ রাখা আছে, আর আপনার account ছাড়া এটি চলবে না।
aws elbv2 create-rule \
--listener-arn "$LISTENER_ARN" \
--priority 20 \
--conditions Field=path-pattern,Values='/api/*' \
--actions Type=forward,TargetGroupArn="$API_TG_ARN"নিচের ছোট engine একই যুক্তি চালায়: rule-গুলো priority অনুযায়ী সাজানো হয়, প্রথম যেটি মেলে সেটিই শেষ কথা, আর কিছু না মিললে default. চালিয়ে শেষ তিনটি লাইন খেয়াল করুন।
// Listener rules: lowest priority number first, first match wins, else default.
const rules = [
{ pr: 10, host: "*", path: "/api/admin/*", tg: "tg-admin" },
{ pr: 20, host: "*", path: "/api/*", tg: "tg-api" },
{ pr: 30, host: "img.example.com", path: "*", tg: "tg-images" },
{ pr: 40, host: "*.example.com", path: "/shop/*", tg: "tg-shop" },
];
const DEFAULT_TG = "tg-web";
function glob(pattern, text) { // * = any characters, ? = exactly one character
const body = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&")
.replace(/\*/g, ".*").replace(/\?/g, ".");
return new RegExp("^" + body + "$").test(text);
}
function route(host, path) {
const h = host.toLowerCase(); // host names ignore case, paths do not
for (const r of [...rules].sort((a, b) => a.pr - b.pr)) {
if (glob(r.host, h) && glob(r.path, path)) return ["rule " + r.pr, r.tg];
}
return ["default", DEFAULT_TG];
}
const requests = [
["www.example.com", "/"], ["www.example.com", "/api/orders"],
["SHOP.example.com", "/api/admin/users"], ["img.example.com", "/logo.png"],
["www.example.com", "/shop/cart"], ["www.example.com", "/api"],
["www.example.com", "/API/orders"], ["other.test", "/shop/cart"],
];
for (const [host, path] of requests) {
const [why, tg] = route(host, path);
console.log((host + path).padEnd(34) + why.padEnd(9) + "-> " + tg);
}আউটপুটের প্রথম পাঁচটি লাইন প্রত্যাশিত। SHOP.example.com/api/admin/users মিলেছে rule 10-এ, কারণ host-এর বড়-ছোট হাতের অক্ষরে পার্থক্য নেই। আসল শিক্ষা শেষ তিনটিতে। www.example.com/api-এর লাইনে এসেছে default -> tg-web. প্যাটার্ন /api/*-এ পরে একটি / আছে, তাই খালি /api মেলে না। /API/orders-ও default-এ গেছে, কারণ path-এ বড়-ছোট হাতের অক্ষর আলাদা। আর other.test/shop/cart মেলেনি, কারণ host *.example.com-এর বাইরে।
আরেকটি ফাঁদ হলো ক্রম। Rule 10 আর 20 অদলবদল করলে /api/admin/users আগেই /api/*-এ মিলে যাবে, আর tg-admin কখনো কোনো অনুরোধ পাবে না। তাই নির্দিষ্ট rule আগে, সাধারণ rule পরে। এটি শুধু routing-এর ভুল নয়; admin পথে আলাদা নিরাপত্তা থাকলে সেটিও পাশ কাটানো যেতে পারে।
সব provider একইভাবে মেলায় না। ALB-তে নম্বরযুক্ত priority চলে। Google Cloud-এর URL map-এর সরল path rule-এ সাধারণত সবচেয়ে লম্বা মিলটি জেতে, আর Azure-এর URL path map-এর নিয়ম আলাদা। Nginx-এর location-এর ক্রম (পর্ব ১৪) এদের কোনোটির সাথেই হুবহু এক নয়। AI বা আগের অভিজ্ঞতা থেকে ধরে নেবেন না; ডকুমেন্টেশন দেখে পরীক্ষা করুন।
৪ · Health check — target-এর অবস্থা কীভাবে বদলায়
LB ব্যবহারকারীর হয়ে নিয়মিত একটি পরীক্ষা অনুরোধ পাঠায়, যেমন GET /healthz. উত্তরের ওপর target-এর অবস্থা বদলায়। Health check-এর তত্ত্ব, slow start আর ভালো check-এর নকশা আছে System Design · Health check অংশে। এখানে আসল সেটিংগুলো দেখব।
| সেটিং | মানে | এই পর্বে ধরে নেওয়া মান |
|---|---|---|
| Interval | কত পরপর check | 10 s |
| Timeout | কতক্ষণ উত্তরের অপেক্ষা | 5 s |
| Unhealthy threshold | পরপর কতবার ব্যর্থ হলে বাদ | 2 |
| Healthy threshold | পরপর কতবার সফল হলে ফেরত | 3 |
| Path আর সফল code | কোন পথে, কোন status-এ সফল | /healthz, 200 |
এই মানগুলো উদাহরণ; provider-এর default আলাদা হতে পারে। মূল শব্দটি হলো “পরপর”। একবার ব্যর্থ হয়ে পরেরবার সফল হলে ব্যর্থতার গণনা শূন্যে ফেরে। নিচের state machine সেটিই দেখায়। P মানে check সফল, F মানে ব্যর্থ।
// Health-check state machine: one check every 10 s.
const INTERVAL = 10, HEALTHY_T = 3, UNHEALTHY_T = 2;
function run(title, results, start) {
let state = start, okRun = 0, badRun = 0;
console.log("--- " + title + " (start: " + start + ")");
[...results].forEach((r, i) => {
if (r === "P") { okRun++; badRun = 0; } else { badRun++; okRun = 0; }
const before = state;
if (state !== "healthy" && okRun >= HEALTHY_T) state = "healthy";
if (state !== "unhealthy" && badRun >= UNHEALTHY_T) state = "unhealthy";
const changed = state !== before ? " <-- " + before + " -> " + state : "";
const t = String((i + 1) * INTERVAL).padStart(3);
console.log("t=" + t + "s " + r + " ok=" + okRun + " bad=" + badRun + changed);
});
console.log("final: " + state + ", gets traffic: " + (state === "healthy"));
}
run("new target", "PPP", "initial");
run("crash, then recovery", "PFFFPPP", "healthy");
run("flapping", "FPFPFPFP", "healthy");নতুন target t= 30s-এ initial -> healthy হয়; তার আগে অনুরোধ পায় না। দ্বিতীয় ক্ষেত্রে দুটি ব্যর্থতার পর healthy -> unhealthy, আর তিনটি সফলতার পর ফেরত। সবচেয়ে ভয়ের ক্ষেত্র তৃতীয়টি। Target একবার ব্যর্থ, একবার সফল করে চলছে, তাই bad কখনো 1-এর ওপরে ওঠে না। শেষে final: healthy, gets traffic: true. একে বলে flapping: target আধা-অসুস্থ, ব্যবহারকারীর অনেক অনুরোধ নষ্ট হচ্ছে, অথচ LB-র চোখে সে সুস্থ। তাই শুধু health check নয়, 5xx-এর হারের ওপরও alert দরকার।
এবার সময়ের হিসাব। ধরুন interval I, timeout T, unhealthy threshold U. শেষ সফল check-এর ঠিক পরে app আটকে গেলে U-সংখ্যক check পাঠাতে হয়, আর শেষটির timeout পর্যন্ত অপেক্ষা করতে হয়। তাই সবচেয়ে খারাপ ক্ষেত্রে বাদ পড়তে লাগে U × I + T সময়। আমাদের মানে 2 × 10 + 5 = 25 সেকেন্ড। নিচের ঘটনায় ৪টি target মোট ৫০০ req/s সামলাচ্ছে, তাই প্রতিটি পাচ্ছে ১২৫ req/s, আর web-2 আটকে যাচ্ছে t = 3 সেকেন্ডে।
| সময় | |
| শেষ check | |
| পরপর গণনা | |
| অবস্থা | |
| web-2 পায় (req/s) | |
| বাকি প্রতিটি পায় (req/s) | |
| নষ্ট অনুরোধ (মোট) |
- t = 0 s. সব ঠিক। ৪টি target প্রতিটি ১২৫ req/s পাচ্ছে।
web-2-এর শেষ check সফল। - t = 3 s.
web-2-এর process আটকে গেল, কিন্তু LB এখনো জানে না। তার কাছে যাওয়া অনুরোধ নষ্ট হতে শুরু করল। - t = 15 s. t = 10-এ পাঠানো check ৫ সেকেন্ডে উত্তর পায়নি।
bad=1, কিন্তু threshold 2, তাই এখনো অনুরোধ যাচ্ছে। নষ্ট হয়েছে 12 × 125 = 1500টি। - t = 25 s. t = 20-এর check-ও timeout.
bad=2, তাই অবস্থাunhealthy. নতুন অনুরোধ বন্ধ। মোট নষ্ট 22 × 125 = 2750টি। - t = 35 s. বাকি তিনটি target এখন 500 ÷ 3 ≈ 166.7 req/s নিচ্ছে। তাদের হাতে এই বাড়তি ভার নেওয়ার জায়গা থাকতে হবে। Check চলতেই থাকে,
bad=3. - t = 40 s. Process আবার চালু হয়েছে, আর check সফল।
ok=1, কিন্তু একবারে ফেরানো হয় না। - t = 50 s.
ok=2. এই ধীর ফেরা flapping থেকে বাঁচায়। - t = 60 s.
ok=3, threshold পূর্ণ, অবস্থাhealthy. আবার সবাই ১২৫ req/s. নষ্ট অনুরোধ মোট ২৭৫০, আর ব্যবহারকারীর দিক থেকে এটিই detection-এর দাম।
Interval কমালে নষ্ট কমে, কিন্তু একটি ধীর মুহূর্তেই সুস্থ target বাদ পড়তে পারে। এটি trade-off. আসল অবস্থা দেখতে তিন provider-এর কমান্ড নিচে আছে; নাম আর resource group নমুনা।
aws elbv2 describe-target-health --target-group-arn "$API_TG_ARN"
az network application-gateway show-backend-health \
--resource-group rg-demo --name agw-demo
gcloud compute backend-services get-health api-backend --globalসব target একসাথে unhealthy হলে কী হবে, সেটি আগে জেনে রাখুন। কিছু LB তখন fail open করে, অর্থাৎ সবার কাছেই অনুরোধ পাঠায়। অন্য ক্ষেত্রে 503 আসে। আচরণ provider আর সেটিং-ভেদে আলাদা।
৫ · TLS আর client-এর আসল IP
TLS-এর handshake আর certificate chain আছে Computer Networks · TLS আর HTTPS পর্বে। Cloud LB-তে প্রশ্ন হলো, TLS কোথায় শেষ হবে। তিনটি নকশা আছে।
TLS termination
LB TLS খোলে, পেছনে সাধারণ HTTP চলে। Certificate এক জায়গায়, L7 rule চলে। পেছনের পথ VPC-র ভেতরে হলেও encrypted থাকে না।
সবচেয়ে সহজRe-encryption
LB TLS খোলে, rule দেখে, তারপর target-এর সাথে নতুন TLS করে। দুটি পথই encrypted, তবে target-এও certificate লাগে আর CPU খরচ বাড়ে।
নিয়ন্ত্রিত ডেটায় প্রায়ইTLS passthrough
L4 LB সংযোগই শুধু পৌঁছে দেয়, TLS খোলে target. Private key থাকে target-এ, তবে LB path বা header দেখতে পারে না।
শেষ পর্যন্ত encryptedCertificate manager সেবা certificate নেওয়া, রাখা আর নবায়নের কাজ সহজ করে। উদাহরণ: AWS Certificate Manager, Azure Key Vault-এর সাথে Application Gateway, Google Cloud Certificate Manager. পর্ব ১৪-তে certbot দিয়ে যে কাজ নিজে করেছেন, এখানে সেটি অনেকটাই স্বয়ংক্রিয়। তবে DNS যাচাই ঠিক না থাকলে নবায়ন আটকে যেতে পারে, তাই মেয়াদের alert রাখুন। SNI-র কারণে একই listener-এ একাধিক domain-এর certificate চলে। সাধারণত port 80-র listener-এ একটিই rule থাকে: HTTPS-এ redirect.
Target কেন client-এর IP দেখে না
L7 LB নিজের ঠিকানা থেকে নতুন সংযোগ করে। তাই app-এর চোখে সব অনুরোধ আসে LB-র private IP থেকে। Rate limit, audit আর অবস্থান-ভিত্তিক কাজে এটি সমস্যা। সমাধান দুটি।
- X-Forwarded-For (L7): LB HTTP header-এ client-এর IP যোগ করে। মান হয় একটি তালিকা, যেমন
X-Forwarded-For: 198.18.0.7, 203.0.113.10. Client নিজেও এই header পাঠাতে পারে, তাই বাঁ দিকের মান বিশ্বাসযোগ্য নয়। আপনার বিশ্বস্ত LB সবচেয়ে ডানে যা যোগ করেছে, সেটিই ভরসা। মানকForwardedheader (RFC 7239, ২০১৪) একই কাজ করে, তবে X-Forwarded-For বেশি প্রচলিত। - Proxy protocol (L4): TCP সংযোগের একদম শুরুতে একটি ছোট শিরোনাম বসে, যেখানে আসল IP আর port থাকে। HTTP না হলেও চলে। দুই পক্ষকেই এটি বুঝতে হয়; target প্রস্তুত না থাকলে সেই বাড়তি bytes তার কাছে ভাঙা অনুরোধ।
কিছু L4 LB, target-এর ধরন আর সেটিং অনুযায়ী, client-এর IP অক্ষত রেখেই packet পৌঁছায়। তখন target-এর firewall-এ শুধু LB নয়, client-এর ঠিকানাও মাথায় রাখতে হয়। Nginx-এর পেছনের app-এ এই IP আনতে পর্ব ১৪-র real_ip ধরনের সেটিং লাগে।
৬ · Cross-zone balancing আর sticky session
LB-র একটি অংশ প্রতিটি zone-এ থাকে, আর DNS client-দের মোটামুটি সমান ভাগে তাদের কাছে পাঠায়। ধরুন দুই zone-এ target সমান নয়। AZ-a-তে ২টি, AZ-b-তে ৮টি। মোট ১০০০ req/s.
ঈদের আগে বাসের টিকিটের দুটি কাউন্টারের লাইন ভাবুন। গেটে যাত্রীদের সমান দুই ভাগ করা হলো। প্রথম কাউন্টারে ২ জন কর্মী, দ্বিতীয়টিতে ৮ জন। নিয়ম যদি হয় “যে লাইনে আছেন সেই কাউন্টারেই”, তাহলে প্রথম কাউন্টারের কর্মীরা হাঁপিয়ে উঠবেন, দ্বিতীয়টির কর্মীরা বসে থাকবেন। আর যদি যেকোনো কর্মী যেকোনো লাইন থেকে যাত্রী নিতে পারেন, তাহলে সবার কাজ সমান। দ্বিতীয় নিয়মটিই cross-zone balancing.
| Cross-zone | AZ-a-র প্রতিটি target | AZ-b-র প্রতিটি target | Zone পার হওয়া traffic |
|---|---|---|---|
| বন্ধ | 500 ÷ 2 = 250 req/s | 500 ÷ 8 = 62.5 req/s | 0% |
| চালু | 1000 ÷ 10 = 100 req/s | 1000 ÷ 10 = 100 req/s | 50% |
Zone পার হওয়ার ৫০% এলো এভাবে: AZ-a-র ৫০০ অনুরোধের ৮/১০ ভাগ অর্থাৎ ৪০০টি যায় AZ-b-তে, আর AZ-b-র ৫০০-এর ২/১০ ভাগ অর্থাৎ ১০০টি যায় AZ-a-তে। মোট ৫০০, মানে অর্ধেক। এবার সিমুলেশনে মিলিয়ে দেখি, যেখানে প্রতিটি অনুরোধ এলোমেলোভাবে একটি zone-এ পড়ে।
// Zone a has 2 targets, zone b has 8. 1000 req/s for 100 s.
let seed = 18;
function rand() { // mulberry32: small seeded PRNG
seed = (seed + 0x6D2B79F5) | 0;
let t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
}
const SECONDS = 100, TOTAL = 1000 * SECONDS;
const makeTargets = () => [
...Array.from({ length: 2 }, (_, i) => ({ id: "a" + (i + 1), zone: "a", n: 0 })),
...Array.from({ length: 8 }, (_, i) => ({ id: "b" + (i + 1), zone: "b", n: 0 })),
];
function simulate(crossZone) {
const targets = makeTargets(), turn = { a: 0, b: 0 };
let crossing = 0;
for (let i = 0; i < TOTAL; i++) {
const node = rand() < 0.5 ? "a" : "b"; // DNS: each LB node gets about half
const pool = crossZone ? targets : targets.filter(t => t.zone === node);
const t = pool[turn[node]++ % pool.length]; // round robin inside the pool
t.n++;
if (t.zone !== node) crossing++;
}
const rps = targets.map(t => t.n / SECONDS);
console.log("cross-zone " + (crossZone ? "ON " : "OFF"));
console.log(" a1 " + rps[0].toFixed(1) + " a2 " + rps[1].toFixed(1) +
" b1 " + rps[2].toFixed(1) + " b8 " + rps[9].toFixed(1) + " req/s");
console.log(" busiest/quietest = " + (Math.max(...rps) / Math.min(...rps)).toFixed(2));
console.log(" crossing zones = " + (100 * crossing / TOTAL).toFixed(1) + "%");
}
simulate(false);
simulate(true);বন্ধ অবস্থায় a1 249.7 a2 249.7 b1 62.6 b8 62.6 req/s আর busiest/quietest = 3.99, হাতের হিসাবের ৪ গুণের প্রায় সমান। সামান্য পার্থক্য এসেছে zone বাছাইয়ের এলোমেলোতা থেকে। চালু অবস্থায় সবাই 100.0, কিন্তু crossing zones = 50.1%. অর্থাৎ সমতার দাম হলো zone-পার traffic: কিছু latency আর কিছু ক্ষেত্রে data transfer বিল।
Default provider আর LB-র ধরন-ভেদে আলাদা। লেখার সময়ে ALB-তে এটি সাধারণত চালু, NLB-তে সাধারণত বন্ধ থাকে, আর চালু করলে zone-পার data-র খরচ লাগতে পারে। সবচেয়ে ভালো সমাধান হলো সব zone-এ target সমান রাখা। Autoscaling group-কে সব zone-এ সমানভাবে ছড়িয়ে রাখা সেই কাজেই সাহায্য করে।
Sticky session — কেন এড়ানো ভালো
Sticky session মানে একজন ব্যবহারকারীকে একই target-এ বারবার পাঠানো, সাধারণত LB-র দেওয়া cookie দিয়ে। Session যদি VM-এর memory-তে থাকে, তাহলে এটি দ্রুত সমাধান। কিন্তু দাম আছে।
- অসম ভার: কিছু ভারী ব্যবহারকারী একই target-এ আটকে থাকেন, আর নতুন target অনেকক্ষণ ফাঁকা থাকে।
- Target গেলে session হারায়: VM মরলে সেই ব্যবহারকারীর কার্ট বা login মুছে যায়।
- Scale-in আর deploy কঠিন: Target সরাতে গেলে সক্রিয় ব্যবহারকারীরা আটকে থাকেন।
ভালো পথ হলো app-কে stateless রাখা। Session যাক Redis-এর মতো বাইরের store-এ (পর্ব ২৩) বা database-এ। তখন যেকোনো target যেকোনো অনুরোধ নিতে পারে, আর autoscaling সহজ হয়। পুরোনো অ্যাপ এখনই বদলানো না গেলে sticky সাময়িক সমাধান হতে পারে, তবে সেটি ঋণ হিসেবেই লিখে রাখুন।
৭ · Draining, idle timeout, আর 502 ও 504 কোথা থেকে আসে
Connection draining — শাটার অর্ধেক নামানো
রাত দশটায় দোকান বন্ধের সময় দোকানি শাটার অর্ধেক নামান। নতুন ক্রেতা ঢোকেন না, কিন্তু ভেতরের ক্রেতারা কেনাকাটা শেষ করে বের হন। তারপর শাটার পুরো নামে। Target-কে না কেটে সরানোর নিয়মও এটাই।
Connection draining-এ (AWS-এ deregistration delay) target নতুন অনুরোধ পাওয়া বন্ধ করে, আর চলমান অনুরোধ শেষ করার সময় পায়। সময়সীমা শেষ হলে বাকি সংযোগ কাটা হয়। নিচে একটি rolling deploy, সীমা ধরে নিয়েছি ৬০ সেকেন্ড। ক্রমটি খেয়াল করুন।
| সময় | |
| v2-a অবস্থা | |
| v1-a অবস্থা | |
| v1-a নতুন অনুরোধ পায়? | |
| v1-a চলমান অনুরোধ | |
| কাটা পড়া অনুরোধ |
- t = 0 s. নতুন version-এর
v2-atarget group-এ যোগ হলো। অবস্থাinitial, তাই এখনো অনুরোধ পায় না। পুরোনোv1-a৪০টি অনুরোধ সামলাচ্ছে। - t = 30 s. তিনটি check পার করে
v2-ahealthy. এখন দুটি target-ই অনুরোধ পাচ্ছে। পুরোনোটি সরানোর আগে নতুনটির সুস্থতা নিশ্চিত হলো। - t = 31 s.
v1-a-কে target group থেকে সরানোর আদেশ। অবস্থাdraining. নতুন অনুরোধ বন্ধ, কিন্তু process চলছে আর ৪০টি অনুরোধ শেষ করছে। - t = 45 s. ছোট অনুরোধগুলো শেষ, বাকি ৬টি।
- t = 80 s. থেকে গেছে শুধু একটি বড় ফাইল upload. সীমা শেষ হতে বাকি ১১ সেকেন্ড।
- t = 91 s. ৬০ সেকেন্ড পূর্ণ। LB বাকি ১টি সংযোগ কেটে দিল। সেই ব্যবহারকারীকে আবার চেষ্টা করতে হবে। সব অনুরোধ আগেই শেষ হলে অনেক LB সীমার আগেই কাজ শেষ করে।
- t = 92 s. এবার process-কে
SIGTERM, আর সে graceful shutdown করে (পর্ব ১১)। ক্রম: LB আগে, app পরে। উল্টো হলে LB মৃত process-এ অনুরোধ পাঠাত।
সময়সীমা হওয়া উচিত আপনার সবচেয়ে লম্বা স্বাভাবিক অনুরোধের চেয়ে একটু বেশি। খুব বড় রাখলে deploy ধীর হয়; ছোট API-র জন্য কয়েক মিনিট অপ্রয়োজনীয় হতে পারে। Autoscaling-এর scale-in-এও একই নিয়ম, আর rolling, blue-green আর canary কৌশল আসবে পর্ব ৫৯-এ।
Idle timeout আর লুকানো 502
LB আর target-এর মাঝে সংযোগ পুনর্ব্যবহার হয়, যাকে বলে keep-alive. কেউ সংযোগ ফাঁকা রাখলে একসময় বন্ধ করে দেয়। Idle timeout হলো সেই ফাঁকা থাকার সীমা। LB-র নিজের একটি সীমা আছে, target-এরও আছে। ফাঁদটি এখানে।
app আগে বন্ধ করে, LB একই মুহূর্তে পাঠায় → 502
৫ সেকেন্ড ফাঁকা থাকার পর app সংযোগটি বন্ধ করছে। ঠিক সেই মুহূর্তে LB ভাবছে সংযোগ ভালো আছে, আর তাতে অনুরোধ পাঠাচ্ছে। উত্তরে আসে RST, আর ব্যবহারকারী পান 502. এটি হয় মাঝেমধ্যে, ভার কম থাকলেও। আপাত কোনো কারণ দেখা যায় না। নিয়ম হলো target-এর keep-alive LB-র idle timeout-এর চেয়ে বড় রাখা, যাতে সংযোগ বন্ধের সিদ্ধান্ত LB-ই নেয়।
| Code | সাধারণ মানে | প্রথমে দেখবেন |
|---|---|---|
| 500 | App নিজেই error দিয়েছে; LB শুধু পৌঁছে দিয়েছে | App-এর log |
| 502 | Target ভাঙা উত্তর দিয়েছে বা সংযোগ কেটেছে | Keep-alive, crash, TLS-এর অমিল |
| 503 | পাঠানোর মতো target নেই, বা LB-র ক্ষমতায় টান | Healthy target-এর সংখ্যা |
| 504 | Target সময়ের মধ্যে উত্তর দেয়নি বা সংযোগই হয়নি | ধীর query, firewall, timeout |
এই অর্থগুলো সাধারণ; provider-ভেদে সূক্ষ্ম পার্থক্য আছে। Nginx-এর 504-এর যে যুক্তি পর্ব ১৪-তে দেখেছেন, এখানেও তা-ই, শুধু proxy-টি এখন managed.
504 দেখেই idle timeout বাড়াবেন না। ৫ মিনিটের রিপোর্ট HTTP অনুরোধে চালানো নকশার সমস্যা। কাজটিকে queue-তে পাঠিয়ে ব্যবহারকারীকে “চলছে” জানানো ভালো (পর্ব ৭০)। আর timeout বাড়ালে ধীর অনুরোধ জমে সংযোগ আটকে রাখে।
WebSocket আর gRPC
WebSocket একটি HTTP অনুরোধ দিয়ে শুরু হয়ে দীর্ঘ সময় খোলা থাকে। বড় L7 LB-গুলো সাধারণত এটি সমর্থন করে, কিন্তু সংযোগ ফাঁকা থাকলে idle timeout তাকে কাটে। তাই app নিয়মিত ping পাঠায়, সময়সীমার চেয়ে ছোট ব্যবধানে। আর deploy-এর সময় draining সীমায় খোলা সংযোগ কাটা পড়ে, তাই client-এ reconnect থাকতে হয়।
gRPC চলে HTTP/2-তে। L7 LB-কে এটি পেছনের দিকেও বুঝতে হয়, আর সব LB-তে সমর্থন সমান নয়; provider-এর ডকুমেন্টেশন দেখুন। একটি দীর্ঘজীবী সংযোগে অনেক অনুরোধ চলে, তাই L4 LB ভাগ করে সংযোগ, অনুরোধ নয়। এতে ভার অসম হয়, যা বিস্তারিত আছে System Design · L4 বনাম L7 অংশে।
৮ · Regional, global আর internal load balancer
বেশিরভাগ LB regional: একটি region-এর zone-গুলোতে ভার ভাগ করে। Region-ই বিপদে পড়লে এটি বাঁচাতে পারে না। Global load balancing ব্যবহারকারীকে কাছের বা সুস্থ region-এ পাঠায়। পথ দুটি।
| পথ | কীভাবে | সুবিধা | সীমা |
|---|---|---|---|
| DNS-ভিত্তিক (পর্ব ২৯) | DNS প্রশ্নে আলাদা region-এর ঠিকানা দেয় | সহজ, সস্তা | TTL-এর কারণে failover ধীর |
| Anycast-ভিত্তিক | একই IP অনেক জায়গা থেকে ঘোষণা হয়; provider-এর network কাছের edge-এ নেয় | দ্রুত failover, একটি ঠিকানা | দামি, provider-নির্ভর |
উদাহরণ: Google Cloud-এর global Application Load Balancer একটি anycast IP দেয়। Azure Front Door global L7 সেবা। AWS-এ ALB আর NLB regional, আর Global Accelerator অথবা DNS তাদের সামনে বসে। Region নকশার যুক্তি আছে পর্ব ০৫-এ, আর VRRP ও anycast-এর তত্ত্ব System Design-এর এই অংশে।
Internal load balancer
সব LB ইন্টারনেটের দিকে নয়। Internal LB-র ঠিকানা private, শুধু VPC বা জোড়া network থেকে পৌঁছানো যায়। সাধারণ নকশা: বাইরের LB → web স্তর → internal LB → api স্তর। এতে api-র autoscaling-এ web-এর কিছু বদলাতে হয় না, শুধু একটি স্থির DNS নাম থাকে। Private হলেই নিরাপদ নয়; এখানেও TLS আর firewall নিয়ম ভাবতে হবে। Kubernetes-এ একই ধারণা ফিরে আসবে পর্ব ৪৫ আর ৪৬-এ।
৯ · খরচ আর log — বিল আর debugging
LB-র বিল সাধারণত কয়েকটি মাত্রায় হয়: চলার ঘণ্টা, ব্যবহারের একক (AWS-এ LCU, Azure-এর Application Gateway-তে capacity unit), প্রক্রিয়াকৃত data, আর zone-পার বা বাইরে যাওয়া traffic. কোনো কোনো মডেলে নতুন সংযোগ, সক্রিয় সংযোগ, bytes আর rule মূল্যায়নের মধ্যে যেটি সবচেয়ে বড়, সেটির ওপর একক গোনা হয়। নিচের দাম পুরোপুরি ধরে নেওয়া; আসল দাম provider-এর pricing পাতায় দেখুন।
| অংশ | ধরে নেওয়া | ৩০ দিনের হিসাব |
|---|---|---|
| চলার ঘণ্টা | $0.03/ঘণ্টা | 720 × 0.03 = $21.60 |
| ব্যবহারের একক | গড়ে 4 একক, $0.008/একক-ঘণ্টা | 4 × 720 × 0.008 = $23.04 |
| Zone-পার traffic | 2000 GB-র 50%, $0.01/GB | 1000 × 0.01 = $10.00 |
| মোট | $54.64 |
এখান থেকে একটি কার্যকর শিক্ষা। ১০টি ছোট service-এর প্রতিটির আলাদা LB হলে শুধু ঘণ্টার খরচই 10 × $21.60 = $216. একটি LB-তে host আর path rule দিয়ে ভাগ করলে সেই অংশ $21.60-এই থাকে। তবে এক LB-তে সব রাখলে একটি ভুল rule সবাইকে ছোঁয়, অর্থাৎ blast radius বড় হয়। বিস্তারিত বিল পর্ব ৭২-এ।
LB অনুরোধ না পেলেও ঘণ্টার বিল চলে। নিজের account-এ পরীক্ষা ঐচ্ছিক, আর তাতে টাকা লাগতে পারে। করলে কাজ শেষে LB মুছুন: aws elbv2 delete-load-balancer --load-balancer-arn "$LB_ARN", আর দেখুন পেছনের VM আর অব্যবহৃত public IP-ও সরানো হয়েছে কি না। এই কমান্ড চালু service-ও মুছে দেয়, তাই ARN দুবার মিলিয়ে নিন।
LB-র log — প্রথম সাক্ষী
LB-র access log বলে অনুরোধটি কোন target-এ গেল, কত সময় লাগল, আর দুই পক্ষের status কী। অনেক provider-এ এটি নিজে চালু করতে হয়, আর object storage-এ জমার খরচ আলাদা। সবচেয়ে কাজের জোড়াটি হলো LB-র status আর target-এর status. দুটি মিললে উত্তরটি app-এর। Target-এর ঘরে - থাকলে LB target থেকে উত্তর পায়নি। নিচের log সরলীকৃত; আসল ফরম্যাটে আরও অনেক কলাম থাকে।
set -euo pipefail
# Simplified LB access log. Columns:
# 1 time 2 client 3 target 4 lb_sec 5 target_sec 6 lb_status 7 target_status 8 method 9 path
log=$(mktemp); trap 'rm -f "$log"' EXIT
cat > "$log" <<'LOG'
10:00:01 203.0.113.10:51000 10.0.1.11:8080 0.001 0.042 200 200 GET /api/orders
10:00:01 203.0.113.11:51001 10.0.2.21:8080 0.001 -1 502 - GET /api/orders
10:00:02 203.0.113.12:51002 10.0.1.12:8080 0.001 0.051 200 200 GET /api/orders
10:00:02 203.0.113.13:51003 10.0.2.21:8080 0.001 -1 502 - POST /api/pay
10:00:03 203.0.113.14:51004 10.0.1.11:8080 0.001 0.310 500 500 POST /api/pay
10:00:03 203.0.113.15:51005 10.0.2.22:8080 0.001 0.047 200 200 GET /shop/cart
10:00:04 203.0.113.16:51006 10.0.2.21:8080 0.001 -1 502 - GET /shop/cart
10:00:05 203.0.113.17:51007 10.0.1.12:8080 0.001 -1 504 - GET /api/report
10:00:36 203.0.113.18:51008 10.0.1.12:8080 0.001 -1 504 - GET /api/report
10:01:06 203.0.113.19:51009 10.0.1.12:8080 0.001 28.400 200 200 GET /api/report
10:01:07 203.0.113.20:51010 - -1 -1 503 - GET /
LOG
echo "== requests by lb_status and target_status =="
awk '{print $6, $7}' "$log" | sort | uniq -c
echo "== errors made by the LB, by target =="
awk '$6 >= 500 && $7 == "-" {print $6, $3}' "$log" | sort | uniq -c | sort -k1,1nr -k2,2n
echo "== slowest answers from a target =="
awk '$5 >= 0 {print $5, $3, $9}' "$log" | sort -k1,1nr | head -3প্রথম সারণিতে 1 500 500 মানে LB আর target দুজনেই 500, অর্থাৎ বাগটি app-এর। কিন্তু 3 502 - আর 2 504 --এ target কোনো উত্তর দেয়নি। দ্বিতীয় সারণি ঠিকানা ধরিয়ে দেয়: 3 502 10.0.2.21:8080, মানে সব 502 একটিই target থেকে, তাই সন্দেহ ওই VM-এর ওপর। 2 504 10.0.1.12:8080 আর তৃতীয় সারণির 28.400 10.0.1.12:8080 /api/report বলছে /api/report খুব ধীর, আর দুটি অনুরোধ সময়সীমায় কাটা পড়েছে। 1 503 --এ target-এর ঘরও -, মানে সেই মুহূর্তে কোনো target বাছাই-ই হয়নি। একটি log থেকেই তিনটি আলাদা সমস্যা আলাদা হলো।
আরও গভীরে: বড় managed LB-র ভেতরটি সাধারণ server-এর ওপর চলা software. Google-এর Maglev (NSDI ২০১৬) আর Microsoft-এর Ananta (SIGCOMM ২০১৩) পেপারে দেখা যায়, router ECMP দিয়ে অনেক LB machine-এ packet ছড়িয়ে দেয়, আর consistent hashing একই সংযোগকে একই backend-এ রাখে। তাই LB-র “এক ঠিকানা” আসলে অনেক machine. ব্যবহারকারীর সংযোগ কত সহজে বাঁচে, তা provider-এর নকশার ওপর নির্ভর করে।
পরীক্ষা ও ইন্টারভিউতে:
- ALB আর NLB-র পার্থক্য? ALB L7, HTTP পড়ে rule চালায়। NLB L4, IP আর port দেখে দ্রুত সংযোগ ভাগ করে।
- Target unhealthy হতে কত সময় লাগে? সর্বোচ্চ প্রায় U × I + T, যেমন 2 × 10 + 5 = 25 সেকেন্ড।
- 2 আর 8 target, cross-zone বন্ধ, প্রতিটি কত ভাগ? ২৫% বনাম ৬.২৫%; চালু করলে সবাই ১০%.
- মাঝেমধ্যে 502 কেন? প্রায়ই target-এর keep-alive LB-র idle timeout-এর চেয়ে ছোট।
- Deploy-এ ব্যবহারকারী কাটা পড়ে কেন? Draining ছাড়া process থামানো হচ্ছে, অথবা LB-র আগে app থামছে।
১০ · AI-এর যুগে load balancer-এর config যাচাই
AI কয়েক সেকেন্ডে LB-র Terraform বা CLI লিখে দিতে পারে। কিন্তু এখানকার ভুল চোখে পড়ে না, কারণ জিনিসটি “চলে”। সমস্যা ধরা পড়ে ভিড়ে, deploy-এর সময়ে, অথবা বিলে।
| AI-এর ভুল | বিপদ | যাচাই |
|---|---|---|
Target-এর firewall-এ 0.0.0.0/0 | LB-কে পাশ কাটিয়ে app-এ ঢোকা যায় | শুধু LB থেকে আসা traffic খোলা কি না |
| Private key config ফাইলে | Secret repository-তে চলে যায় | Certificate manager বা secret store (পর্ব ৩৩) |
| অস্তিত্বহীন argument বা পুরোনো annotation | Apply ব্যর্থ বা নীরবে উপেক্ষিত | Provider-এর ডকুমেন্টেশন, terraform plan |
Health check /-এ, threshold ১ | Flapping বা ভারী পাতা বারবার চলা | হালকা /healthz, যুক্তিসঙ্গত threshold |
| 504-এর সমাধান হিসেবে timeout ৩৬০০ s | আসল ধীরতা লুকায় | আগে কারণ, পরে সেটিং |
| প্রতি service-এ আলাদা LB | নীরবে বিল বাড়ে | খরচ হিসাব, budget alert |
| সতর্কতা ছাড়া মুছে নতুন করে বানানো | DNS আর সেবা ভাঙে | Plan-এ destroy আছে কি না পড়া |
AI-কে শুধু “LB বানিয়ে দাও” না বলে পরিবেশ, সীমা আর যাচাইয়ের দায় বলে দিন:
AI-এর দেওয়া LB config হাতে পেলে — যাচাইয়ের চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন।
প্র ০১দামি hardware load balancer-এর যুগ থেকে managed cloud LB-তে পৃথিবী এলো কীভাবে? এতে কী হারালাম?
২০০০-এর দশকের data center-এ LB ছিল দামি বিশেষ যন্ত্র। সাধারণত দুটি কেনা হতো, একটি active আর একটি standby. ক্ষমতা বাড়াতে নতুন যন্ত্র কিনতে হতো, যা সপ্তাহ বা মাসের ব্যাপার। মালিক ছিল network দল, আর একটি rule বদলাতে প্রায়ই ticket লাগত।
AWS ২০০৯ সালে Elastic Load Balancing আনে, CloudWatch আর Auto Scaling-এর সাথে। এতে LB একটি API call হয়ে গেল। পরে ২০১৬ সালে ALB আর ২০১৭ সালে NLB এসে L7 আর L4-এর কাজ আলাদা করে। ভেতরের কৌশলটিও বদলায়। Google-এর Maglev পেপার (NSDI ২০১৬) দেখায়, সাধারণ server-এ চলা software বিশেষ যন্ত্রের জায়গা নিতে পারে। Router ECMP দিয়ে packet অনেক machine-এ ছড়ায়, আর machine যোগ করেই ক্ষমতা বাড়ে। Microsoft-এর Ananta (SIGCOMM ২০১৩)-ও এই পথের আরেকটি প্রকাশিত উদাহরণ।
লাভ স্পষ্ট: মিনিটে তৈরি, zone জুড়ে চলা, patch-এর দায় নেই, আর ব্যবহার অনুযায়ী বিল। একটি ছোট দলও এখন এমন ব্যবস্থা পায়, যা আগে শুধু বড় প্রতিষ্ঠান কিনতে পারত। কিন্তু কিছু হারায়ও। আপনি ভেতরের machine দেখতে পান না, তাই tcpdump চালাতে পারেন না। Provider যে সেটিং দেয় তার বাইরে যেতে পারেন না। LB-র ত্রুটি হলে অপেক্ষা ছাড়া পথ কম। আর rule, annotation, log-এর ফরম্যাট provider-নির্ভর, তাই নড়াচড়া কঠিন হয়, যাকে বলা হয় vendor lock-in. অনেক দল তাই দুই স্তর রাখে: সামনে managed LB, পেছনে নিজের Nginx বা Envoy, যেখানে সূক্ষ্ম নিয়ন্ত্রণ দরকার।
মূল উপলব্ধি: Managed সেবা দায় মুছে দেয় না, দায়ের সীমারেখা সরায়। আপনি hardware আর patch ছেড়ে দিয়ে নিয়ন্ত্রণ আর দৃশ্যমানতার কিছুটা দামে সময় কেনেন।
প্র ০২Health check কতটা গভীর হওয়া উচিত? /healthz-এর ভেতরে database যাচাই করলে কী হয়?
প্রথমে মনে হয়, যত গভীর তত ভালো। App চলছে কিন্তু database-এ পৌঁছাতে পারছে না, তাহলে তাকে অসুস্থ বলাই তো ঠিক। সমস্যা হলো database সবার এক। Database এক মিনিটের জন্য ধীর হলে সব target একসাথে check-এ ব্যর্থ হয়। LB সবাইকে বাদ দেয়। ফলে একটি আংশিক সমস্যা পুরো বিভ্রাটে বদলায়। যে ব্যবহারকারী cache থেকে পাতা পেতে পারতেন, তিনিও পান 503.
উল্টো দিকে একেবারে অগভীর check, যা শুধু process বেঁচে আছে কি না দেখে, বিপজ্জনক অন্যভাবে। ভুল config নিয়ে উঠে আসা target সুস্থ দেখায়, আর সব অনুরোধ ব্যর্থ করে। পর্ব ১৭-র autoscaling-এর সাথে জুড়লে আরেক বিপদ: ধীর target বাদ পড়ে, ভার বাড়ে, আরও target ধীর হয়, আর শৃঙ্খলটি নিজেই ছড়ায়।
Amazon Builders' Library-র “Implementing health checks” লেখায় এই টানাপোড়েন প্রকাশ্যে আলোচিত। সেখানে স্থানীয় সমস্যা আর নির্ভরতার সমস্যা আলাদা করার, আর সব target একসাথে ব্যর্থ দেখালে fail open করার যুক্তি আছে। ব্যবহারিক নকশা সাধারণত এমন: LB-র check দেখে শুধু এই process অনুরোধ নিতে পারে কি না, যেমন thread pool ফাঁকা কি না আর config ঠিক আছে কি না। Database-এর সমস্যা ধরে আলাদা deep check, যা alert দেয় কিন্তু target বাদ দেয় না।
দ্বিতীয় নিরাপত্তা জাল হলো check-কে সস্তা রাখা। ভারী query চালানো check নিজেই ভার। ৫০টি target-এ একাধিক LB node থেকে check এলে সংখ্যাটি বড় হয়। আর /healthz বাইরের দিকে খোলা রাখলে অভ্যন্তরীণ তথ্য ফাঁস হতে পারে, তাই উত্তর ছোট রাখুন।
মূল উপলব্ধি: Health check-এর প্রশ্ন হলো “এই target-কে সরালে ব্যবহারকারীর ভালো হবে কি?” যে সমস্যা target সরিয়ে সমাধান হয় না, সেটি health check-এর বিষয় নয়, alert-এর বিষয়।
প্র ০৩একটি অ্যাপ X-Forwarded-For-এর প্রথম IP দেখে rate limit করে। এতে সমস্যা কোথায়, আর ঠিক পথ কী?
HTTP header অনুরোধকারীর নিজের লেখা জিনিস। Proxy-র কাজ শুধু তালিকার শেষে নতুন মান জুড়ে দেওয়া। তাই client নিজে যা পাঠিয়েছে, তা তালিকার বাঁ দিকে থেকে যায়। অ্যাপ প্রথম মানটি বিশ্বাস করলে সেটি আসলে client-এর দাবি, প্রমাণ নয়। ফলে rate limit আর IP-ভিত্তিক allow list ভুল ঠিকানার ওপর চলে, আর audit log-এ ভুল তথ্য জমে।
ঠিক পথের মূল কথা হলো বিশ্বাসের সীমা ঠিক করা। আপনি জানেন আপনার সামনে কয়টি বিশ্বস্ত proxy আছে। ধরুন শুধু একটি LB. তাহলে তালিকার সবচেয়ে ডানের মানটি LB নিজে যোগ করেছে, আর সেটিই আসল সংযোগের ঠিকানা। CDN আর LB দুটি থাকলে ডান দিক থেকে দ্বিতীয়টি। Nginx-এর real_ip module আর অনেক web framework-এর “trusted proxy” সেটিং ঠিক এই কাজই করে। কিছু managed LB বাইরে থেকে আসা header মুছে ফেলার বা বদলানোর সেটিংও দেয়।
এই নিয়ম তখনই খাটে, যদি target-এ শুধু LB-ই ঢুকতে পারে। Target-এর firewall খোলা থাকলে কেউ সরাসরি এসে যেকোনো header পাঠাতে পারে। তাই পর্ব ২৭-এর security group আর এই নিয়ম একসাথে কাজ করে।
মান নির্ধারণের চেষ্টাও হয়েছে। RFC 7239 (২০১৪) Forwarded header নির্ধারণ করে, তবে ব্যবহারে X-Forwarded-For আজও বেশি দেখা যায়। আর TCP স্তরের জন্য HAProxy-র দলের proxy protocol ২০১০ সালের দিকে আসে। সেখানে তথ্যটি সংযোগের শুরুতে LB বসায়, তাই HTTP header-এর এই সমস্যা হয় না, তবে target-কে তা বুঝতে আর শুধু LB-র কাছ থেকেই গ্রহণ করতে হয়।
মূল উপলব্ধি: নিরাপত্তায় প্রশ্নটি “এই তথ্য কী বলছে” নয়, “এই তথ্য কে লিখেছে”। আপনার নিয়ন্ত্রণের বাইরের কেউ লিখতে পারলে সেটি শুধু দাবি।
প্র ০৪দিনে কয়েকটি মাত্র 502 আসে, ভারের সাথে সম্পর্ক নেই, app-এর log পরিষ্কার। কীভাবে খুঁজবেন?
এই লক্ষণটি বহু দলের চেনা। App-এর log-এ কিছু নেই, কারণ অনুরোধটি app পর্যন্ত পৌঁছায়ইনি। সংযোগ বন্ধের মুহূর্তে সেটি হারিয়ে গেছে।
প্রথম ধাপ LB-র log. দেখুন target-এর status ঘর - কি না, target-এর সময় কত, আর 502-গুলো একটি target-এ জমে কি না। সব target-এ ছড়ানো থাকলে সন্দেহ যায় config-এর দিকে, একটিতে জমলে সেই VM-এর দিকে। এরপর ঘটনার আগে সংযোগটি কতক্ষণ ফাঁকা ছিল ভাবুন। কম traffic-এর সময়ে সংযোগ বেশি ফাঁকা থাকে, তাই রাতে বা সকালে এই 502 বেশি দেখা যেতে পারে। এটি একটি জোরালো ইঙ্গিত।
দ্বিতীয় ধাপ দুই পাশের timeout মেলানো। অনেক app server-এর default keep-alive খুব ছোট। যেমন Node.js-এর http server-এ keepAliveTimeout-এর default অনেক version ধরে ৫ সেকেন্ড, আর Gunicorn-এ keepalive-এর default ২ সেকেন্ড; আপনার version-এ যাচাই করে নিন। LB-র idle timeout সাধারণত এর চেয়ে অনেক বড়, যেমন ৬০ সেকেন্ড। তাহলে app আগে সংযোগ বন্ধ করে। TCP-তে FIN পৌঁছাতে সময় লাগে, আর সেই ফাঁকে LB একই সংযোগে অনুরোধ পাঠাতে পারে। Target তখন RST দেয়, LB 502 দেখায়।
সমাধান: target-এর keep-alive LB-র timeout-এর চেয়ে কয়েক সেকেন্ড বড়। Node.js-এ headersTimeout-এর সাথে এর সম্পর্কও দেখতে হয়। পরিবর্তনের পর log-এ 502-র হার আগে-পরে মিলিয়ে দেখুন। প্রমাণ ছাড়া ধরে নেবেন না। দরকার হলে target-এ tcpdump দিয়ে RST দেখা যায়, যদিও এটি শুধু পরীক্ষার পরিবেশে বা অনুমতি নিয়ে করবেন।
একই বিভ্রান্তি draining ছাড়া deploy আর হঠাৎ crash-এও হয়, তাই সময় মিলিয়ে দেখুন 502 deploy-এর সাথে আসছে কি না।
মূল উপলব্ধি: দুটি ঠিকঠাক component-এর মাঝের সীমানাতেও বাগ থাকে। Timeout সবসময় জোড়ায় পড়ুন: কে আগে ছাড়ে, আর তখন অন্যজন কী ভাবে।
প্র ০৫LB-তে TLS শেষ করলে VPC-র ভেতরে plain HTTP চলে। এটি কি যথেষ্ট নিরাপদ?
এক সময় উত্তর ছিল “হ্যাঁ”। ধারণাটি ছিল দুর্গের মতো: বাইরের দেওয়াল শক্ত, ভেতরে সবাই বিশ্বস্ত। ২০১৩ সালে Snowden-এর ফাঁস করা নথির ভিত্তিতে Washington Post-এ প্রকাশিত প্রতিবেদনে একটি হাতে আঁকা নকশা আলোচনায় আসে, যেখানে Google-এর সামনের সার্ভারে TLS যোগ ও বিয়োগের জায়গাটি চিহ্নিত ছিল, আর সেই প্রতিবেদন অনুযায়ী data center-এর মধ্যের যোগাযোগ encrypted ছিল না। এরপর Google আর অন্য বড় কোম্পানি ভেতরের পথও encrypted করার কাজ জোরদার করে। এই ধরনের ঘটনা zero trust চিন্তাকে এগিয়ে দেয়: network-এর ভেতরে আছে বলেই বিশ্বাস নয়।
আজ কি VPC-র ভেতরের HTTP অনিরাপদ? উত্তর ঝুঁকির ওপর নির্ভর করে। Provider-এর network বিচ্ছিন্ন, আর কিছু provider ভেতরের traffic নিজেরাই encrypted করে; সেটি provider আর instance-এর ধরন-ভেদে আলাদা। কিন্তু একই VPC-তে কোনো VM আক্রান্ত হলে, বা ভুল firewall থাকলে, plain HTTP-র ভেতরের cookie আর token ঝুঁকিতে পড়ে। স্বাস্থ্য বা আর্থিক ডেটার মতো নিয়ন্ত্রিত ক্ষেত্রে অনেক মানদণ্ড transit-এ encryption চায়। তখন re-encryption বা passthrough ভাবতে হয়।
Re-encryption-এর দাম সামান্য CPU আর certificate সামলানোর ঝামেলা। অনেক LB পেছনের certificate কড়াভাবে যাচাই করে না, তাই target-এ private CA বা নিজস্ব certificate চলতে পারে; তবে এটি provider-ভেদে আলাদা, আর যাচাই না হলে পরিচয়ের নিশ্চয়তা কমে। আধুনিক পথে service mesh দুই service-এর মাঝে mTLS স্বয়ংক্রিয় করে।
আবার passthrough-এর দাম হলো LB আর WAF ভেতরটা দেখতে পারে না, তাই path routing আর অনুরোধ-স্তরের সুরক্ষা হারায়। কোনো পথই সব দিক থেকে সেরা নয়।
মূল উপলব্ধি: প্রশ্নটি “network কি বিশ্বস্ত” নয়, “কোন ডেটা, কার থেকে, কতটা ঝুঁকিতে”। Encryption-এর সীমা ঠিক করুন ঝুঁকি ধরে, আর সিদ্ধান্তটি লিখে রাখুন।
অনুশীলন
কোনো cloud account লাগবে না। হিসাব, পড়া আর ভাবাই আসল কাজ।
-
হিসাব করুন: তিনটি zone-এ যথাক্রমে ২, ৩ আর ৫টি target. মোট ১২০০ req/s, আর DNS তিনটি zone-এ সমান ভাগ করে। (ক) Cross-zone বন্ধ থাকলে প্রতিটি zone-এর এক একটি target কত পায়? (খ) চালু থাকলে কত? (গ) চালু থাকলে কত req/s zone পার হয়?
প্রতিটি zone পায় 1200 ÷ 3 = 400 req/s. (ক) 400 ÷ 2 = 200, 400 ÷ 3 ≈ 133.3 আর 400 ÷ 5 = 80 req/s. (খ) 1200 ÷ 10 = 120 req/s. (গ) প্রতিটি LB অংশ নিজের zone-এর বাইরে পাঠায় যথাক্রমে ৮/১০, ৭/১০ আর ৫/১০ ভাগ। মোট 400 × (0.8 + 0.7 + 0.5) = 800 req/s, অর্থাৎ প্রায় ৬৬.৭%.
-
ফল আগে বলুন: একটি target শুরুতে healthy. Interval 15 s, healthy threshold 2, unhealthy threshold 3. প্রথম check থেকে ফলগুলো:
F F P F F F P P. প্রথম check t = 15 s-এ। কখন target বাদ পড়বে, কখন ফিরবে, আর শেষে অবস্থা কী?প্রথম দুটি F-এ
bad=2, কিন্তু threshold 3, আর তৃতীয় check P হওয়ায় গণনা শূন্য। এরপর ৪, ৫ আর ৬ নম্বর check পরপর F, তাই ৬ নম্বরে, অর্থাৎ t = 90 s-এ unhealthy. ৭ আর ৮ নম্বর P, তাই ৮ নম্বরে, অর্থাৎ t = 120 s-এ healthy. শেষ অবস্থা: healthy, অনুরোধ পাচ্ছে। -
নকশা সমালোচনা: একটি দলের ১২টি service-এর প্রতিটির আলাদা internet-facing LB. সবগুলোতে sticky session চালু। Health check
/-এ, যা database থেকে হোমপেজ বানায়। App-এর keep-alive ৫ সেকেন্ড, LB-র idle timeout ৬০ সেকেন্ড। Target public subnet-এ, firewall-এ port 8080 সবার জন্য খোলা। কী কী বদলাবেন?- LB-র সংখ্যা: বাইরের service-গুলো এক বা অল্প কয়েকটি LB-তে host আর path rule দিয়ে আনুন, আর ভেতরের service-এর জন্য internal LB. খরচ আর আক্রমণের পৃষ্ঠ দুটিই কমে। Blast radius-এর ভারসাম্য মাথায় রাখুন।
- Sticky: Session বাইরের store-এ নিন, তারপর sticky বন্ধ করুন।
- Health check: হালকা
/healthz, database ছাড়া। - Keep-alive: ৬০-এর চেয়ে বড় করুন, যেমন ৬৫ সেকেন্ড।
- নেটওয়ার্ক: Target private subnet-এ, আর port 8080 শুধু LB-র security group থেকে।
-
কমান্ড লিখুন:
lb_log.sh-এর log-এ প্রতিটি status আর path-এর জোড়ায় কতগুলো 5xx এসেছে, তা বের করার একটিawkলাইন লিখুন, সবচেয়ে বেশি আগে। কোন জোড়া সবার ওপরে থাকবে?awk '$6 >= 500 {print $6, $9}' "$log" | sort | uniq -c | sort -k1,1nr৬ নম্বর কলাম LB-র status, ৯ নম্বর path. সবার ওপরে আসে
2 504 /api/report. বাকি পাঁচটি লাইনের প্রতিটির গণনা ১, যেমন1 500 /api/payআর1 502 /api/pay. একই path-এর দুটি আলাদা code আলাদা গল্প বলছে: একটি app-এর বাগ, অন্যটি একটি খারাপ target.
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমLoad balancer-এর পেছনে একটি target বদলানোর ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- নতুন target-কে target group-এ যোগ করা
- নতুন target health check পার করে healthy হওয়া
- পুরোনো target-কে সরানোর আদেশ, অবস্থা draining
- চলমান অনুরোধ শেষ হওয়া বা সময়সীমা পূর্ণ হওয়া
- পুরোনো process-কে SIGTERM পাঠানো
দুটি zone-এ ২টি আর ৮টি target. DNS দুই zone-এ সমান ভাগ করে, আর cross-zone balancing বন্ধ। মোট ১০০০ req/s হলে ছোট zone-এর প্রতিটি target কত পাবে?
- ১০০ req/s
- ২৫০ req/s
- ৬২.৫ req/s
- ৫০০ req/s
Listener-এ rule আছে: priority 10 /api/* → tg-api, priority 20 /api/admin/* → tg-admin, আর default → tg-web. ছোট নম্বর আগে মেলে, প্রথম মিলই চূড়ান্ত। /api/admin/users কোথায় যাবে?
- tg-admin
- tg-web
- tg-api
- 404 error
একটি LB-র health check interval 10 s, timeout 5 s, unhealthy threshold 3. একটি target শেষ সফল check-এর ঠিক পরে আটকে গেল। সবচেয়ে খারাপ ক্ষেত্রে বাদ পড়তে প্রায় কত সময় লাগবে?
- 5 সেকেন্ড
- 10 সেকেন্ড
- 30 সেকেন্ড
- 35 সেকেন্ড
LB-র idle timeout 60 s, আর target-এর keep-alive 5 s. দিনে অল্প কিছু 502 আসে, আর app-এর log-এ কিছু নেই। সবচেয়ে সম্ভাব্য কারণ ও প্রতিকার কী?
- Target আগে সংযোগ বন্ধ করছে; target-এর keep-alive ৬০ সেকেন্ডের বেশি করা
- Target কম; আরও VM যোগ করা
- Certificate-এর মেয়াদ শেষ; নবায়ন করা
- LB-র idle timeout ৫০ সেকেন্ড করা
L7 load balancer-এর পেছনের app-এর দৃষ্টিতে client-এর IP নিরাপদে জানার ভালো উপায় কোনটি, যদি সামনে শুধু একটি বিশ্বস্ত LB থাকে?
- TCP সংযোগের source IP পড়া
- X-Forwarded-For-এর সবচেয়ে ডানের মান পড়া
- X-Forwarded-For-এর প্রথম মান পড়া
- একটি cookie-তে IP রাখা
Sticky session এড়ানোর প্রধান কারণ কোনটি?
- এটি HTTPS-এ কাজ করে না
- Cookie অনেক network খরচ করে
- ভার অসম হয়, আর target চলে গেলে তার ভেতরের session হারায়
- এতে health check বন্ধ হয়ে যায়
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন, অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- Listener → rule → target group → target, আর health check ঠিক করে কে অনুরোধ পাবে।
- Rule-এ নির্দিষ্টটি আগে;
/api/*খালি/api-কে মেলায় না। - বাদ পড়ার সর্বোচ্চ সময় প্রায় U × I + T; এই সময়ের অনুরোধই detection-এর দাম।
- Zone-এ target সমান রাখুন; cross-zone সমতা আনে, কিন্তু zone-পার traffic বাড়ায়।
- Target-এর keep-alive > LB-র idle timeout, আর deploy-এ LB আগে, app পরে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ১৯ · কোন compute কখন — VM, container, PaaS আর FaaS-এর তুলনাপরবর্তী পর্বকাজের ধরন দেখে compute বাছাইয়ের একটি মাপকাঠি।
- ক্লাউড কম্পিউটিং ও DevOps · কোর্স হোম৮১টি পর্বপুরো কোর্সের রোডম্যাপ।
- System Design · Load balancingগভীরে যানL4 আর L7-এর তত্ত্ব, algorithm আর power of two choices.
- Computer Networks · TLS আর HTTPSগভীরে যানHandshake, certificate chain আর এর পেছনের cryptography.
- পর্ব ১৪ · Nginx আর reverse proxyপেছনে ফিরে দেখুনএকই কাজ নিজের VM-এ, proxy_pass থেকে certbot পর্যন্ত।
- System Design · Deployment আর releaseপাশের ট্র্যাকনিরাপদ release-এর কৌশল।