সরাসরি পর্বে যান
পর্ব ৫৪ · ৬২-এর মধ্যে · মডিউল ৯

Cloud আর container নেটওয়ার্কিং — VPC, Docker আর Kubernetes

Cloud & Container Networking — VPC, Subnets, NAT Gateway, Security Groups vs NACLs, Docker Bridge & Port Publishing, Kubernetes CNI, Services, kube-proxy, NetworkPolicy & Service Mesh

Taught in Bangla, this lesson covers cloud and container networking: what a VPC is, subnets, route tables, internet and NAT gateways, security groups versus network ACLs, VPC peering and CIDR planning, container namespaces, veth pairs and bridges, Docker network modes and the real path of port publishing, the Kubernetes network model with Pods, CNI, Services and kube-proxy, CoreDNS, Ingress, NetworkPolicy, service mesh and overlay MTU troubleshooting.

এই পর্বে যা শিখবেন

  • VPC, availability zone, public/private subnet, route table, internet gateway, NAT gateway আর elastic IP — কে কোন কাজ করে
  • Security group আর network ACL-এর পার্থক্য, আর একটি simulator দিয়ে পাঁচটি flow-এর সিদ্ধান্ত যাচাই
  • VPC peering, transit gateway, private endpoint, VPN আর ঠিকানা না মেলানোর (overlap এড়ানোর) CIDR পরিকল্পনা
  • Docker-এর ভেতরের নেটওয়ার্ক: network namespace, veth, docker0, নেটওয়ার্ক mode আর -p 8080:80-এর আসল পথ
  • Kubernetes-এর Pod নেটওয়ার্ক, CNI, Service, kube-proxy, CoreDNS, Ingress, NetworkPolicy, service mesh, overlay-এর MTU আর সমস্যা খোঁজার পদ্ধতি

১ · Cloud-এ নিজের নেটওয়ার্ক — VPC আসলে কী

ধরুন আপনার প্রতিষ্ঠান একটি অনলাইন দোকান চালায়। আগে নিজের অফিসে সার্ভার রাখতে হতো: switch কেনা, তার টানা, router বসানো, firewall কেনা। এখন একটি cloud প্রদানকারীর (AWS, Microsoft Azure, Google Cloud…) পাতায় কয়েকটি ক্লিক বা কয়েক লাইন কনফিগারেশন — আর মিনিটের মধ্যে আপনার নিজের একটি নেটওয়ার্ক তৈরি। এর নাম VPC (Virtual Private Cloud; Azure-এ বলে VNet).

VPC-তে তিনটি জিনিস পুরোপুরি আপনার হাতে: ঠিকানার পরিসর (সাধারণত একটি private CIDR ব্লক, যেমন 10.20.0.0/16), সেই পরিসরকে কীভাবে subnet-এ ভাগ করবেন, আর কোন ট্রাফিক কোন পথে যাবে (route table). অথচ আসল তার, switch আর router আপনি কখনো দেখেন না। একই ভবনে, একই rack-এ হয়তো আরেকটি প্রতিষ্ঠানের VPC-ও চলছে — একই 10.20.0.0/16 ঠিকানা নিয়ে! তবু কেউ কারও packet দেখে না।

ঢাকার একটি বড় আবাসিক প্রকল্প ভাবুন — একই ডেভেলপারের জমিতে অনেকগুলো আলাদা ভবন, প্রতিটির নিজের গেট, নিজের দারোয়ান। প্রতিটি ভবনে “ফ্ল্যাট ৩-বি” আছে, কারণ ফ্ল্যাট নম্বর শুধু ভবনের ভেতরে অর্থবহ। নিচের রাস্তা, পানির লাইন, বিদ্যুতের লাইন সবার জন্য একটাই — ডেভেলপার সেটা চালায়। VPC = আপনার ভবন, ফ্ল্যাট নম্বর = private IP, ডেভেলপারের রাস্তা আর লাইন = প্রদানকারীর আসল নেটওয়ার্ক (underlay). ভবনের গেট আর দারোয়ান কোথায় বসবে, কাকে ঢুকতে দেবে — সেটা ঠিক করেন আপনি।

নিচে কী চলছে — overlay আর SDN

একই ঠিকানা দুটি গ্রাহকের দুটি VPC-তে থাকতে পারে কারণ VPC একটি overlay network. আপনার VM যে packet পাঠায় (উৎস 10.20.0.10), hypervisor-এর ভেতরের একটি সফটওয়্যার switch সেটিকে ধরে, দেখে এটি কোন গ্রাহকের কোন VPC-র, তারপর পুরো packet-টি আরেকটি বাইরের header-এ মুড়ে (encapsulation) প্রদানকারীর নিজস্ব underlay নেটওয়ার্কে গন্তব্য VM-এর আসল সার্ভারে পাঠায়। মোড়কের ভেতরে একটি গ্রাহক-চিহ্ন থাকে — অনেকটা পর্ব ১৩-এ দেখা VXLAN-এর ২৪ bit-এর VNI-র মতো। কোন VM কোন সার্ভারে, কোন VPC-র route কী — এই তথ্য সব hypervisor-এ বসিয়ে দেয় একটি কেন্দ্রীয় controller. এটাই SDN-এর ধারণা (পর্ব ৫৩)।

Amazon ২০০৬ সালে EC2 চালু করে একটি সমতল, সবার সাথে ভাগ করা নেটওয়ার্কে — প্রতিটি VM পেত প্রদানকারীর বাছাই করা একটি ঠিকানা, নিজের পরিসর বাছার সুযোগ ছিল না (পরে এর নাম হয় “EC2-Classic”). ২০০৯ সালে Amazon VPC আনে: নিজের ঠিকানার পরিসর, subnet, route table আর অফিসের সাথে VPN. পরে প্রতিটি নতুন অ্যাকাউন্টে একটি “default VPC” দেওয়া শুরু হয় (AWS-এ এর পরিসর 172.31.0.0/16), আর EC2-Classic কয়েক বছর আগে পুরোপুরি অবসরে গেছে। Google Cloud আর Azure-ও একই ধারণায় নিজের নকশা বানিয়েছে।

এই পর্বে নামগুলো AWS-এর রাখা হলো, কারণ সেগুলো সবচেয়ে প্রচলিত। কিন্তু ধারণা প্রায় সব প্রদানকারীতে এক — শুধু নাম, সীমা আর ডিফল্ট আচরণ আলাদা। কাজে নামার আগে সবসময় নিজের প্রদানকারীর ডকুমেন্টেশন দেখবেন।

ধারণাAWSAzureGoogle Cloud
নিজের private নেটওয়ার্কVPC (একটি region-এ)Virtual Network, VNet (একটি region-এ)VPC — global; subnet প্রতি region-এ
Subnet-এর বিস্তারএকটি availability zoneপুরো region (zone জুড়ে)পুরো region (zone জুড়ে)
যন্ত্রের পাশের stateful firewallSecurity groupNetwork security group (NSG) — subnet বা NIC-এVPC firewall rules / policies (tag বা service account দিয়ে)
Subnet-এর stateless নিয়মNetwork ACLআলাদা কিছু নেই; NSG-ই subnet-এ বসেআলাদা কিছু নেই
Private subnet থেকে বাইরে যাওয়াNAT gatewayNAT GatewayCloud NAT

২ · Subnet, route table আর gateway — public আর private subnet

একটি cloud region (যেমন সিঙ্গাপুর বা মুম্বাই) কয়েকটি availability zone (AZ)-এ ভাগ করা — প্রতিটি AZ এক বা একাধিক আলাদা data center, আলাদা বিদ্যুৎ আর ঠান্ডা করার ব্যবস্থা, কিন্তু নিজেদের মধ্যে দ্রুত সংযোগ। একটি AZ-এ আগুন লাগলে বা বিদ্যুৎ গেলে অন্যগুলো চলে। তাই ভালো নকশায় একই কাজের সার্ভার অন্তত দুটি, সাধারণত তিনটি AZ-এ ছড়িয়ে রাখা হয়। AWS-এ প্রতিটি subnet ঠিক একটি AZ-এর ভেতরে থাকে, তাই “তিন AZ × দুই স্তর = ছয়টি subnet” খুব চেনা নকশা।

চারটি জিনিস, একটি নিয়ম

Route table

প্রতিটি subnet একটি route table-এর সাথে যুক্ত। প্রতিটি table-এ একটি local route থাকে (পুরো VPC-র CIDR, যেমন 10.20.0.0/16 → local) — তাই VPC-র ভেতরের সব subnet নিজে থেকেই একে অপরের কাছে পৌঁছায়। বাকি সিদ্ধান্ত longest prefix match-এ।

পথের নকশা

Internet gateway

Internet gateway হলো VPC-র সাথে ইন্টারনেটের দরজা। যে VM-এর একটি public IPv4 আছে, তার জন্য এটি এক-এক ঠিকানা বদল করে: বাইরে public, ভেতরে private. VM নিজের ভেতরে শুধু private ঠিকানাই দেখে।

igw

NAT gateway

NAT gateway হলো public subnet-এ বসা একটি পরিচালিত (managed) NAT. Private subnet-এর যন্ত্রগুলো এর মাধ্যমে বাইরে যায় (আপডেট নামানো, API ডাকা), কিন্তু বাইরে থেকে নতুন সংযোগ ঢুকতে পারে না — পর্ব ১৯-এর PAT-এর হুবহু আচরণ।

শুধু বাইরে যাওয়া

Elastic / public IP

সাধারণ public IP VM বন্ধ-চালু করলে বদলে যেতে পারে। Elastic IP আপনার অ্যাকাউন্টে স্থায়ীভাবে বরাদ্দ একটি public ঠিকানা, যা এক VM থেকে আরেকটিতে সরানো যায়। (IPv4 ঠিকানা দুর্লভ, তাই প্রদানকারীরা এর জন্য আলাদা টাকা নেয়।)

স্থায়ী ঠিকানা
মূল ধারণা

“Public subnet” কোনো বিশেষ ধরনের subnet নয় — এটি শুধু একটি route. যে subnet-এর route table-এ 0.0.0.0/0 → igw আছে, সেটি public subnet; যেখানে 0.0.0.0/0 → nat-gw বা কোনো default route-ই নেই, সেটি private subnet. ডেটাবেস, ভেতরের API, cache — এগুলো private subnet-এ; শুধু load balancer, NAT gateway আর একান্ত দরকারি কিছু public subnet-এ।

AWS প্রতিটি subnet-এর পাঁচটি ঠিকানা নিজের কাজে রেখে দেয়: প্রথম চারটি (network ঠিকানা, VPC router, DNS, ভবিষ্যতের জন্য একটি) আর শেষটি। তাই একটি /24 subnet-এ ব্যবহারযোগ্য ২৫৬ − ৫ = ২৫১টি ঠিকানা। অন্য প্রদানকারীর সংখ্যা আলাদা হতে পারে (Google Cloud সাধারণত চারটি রাখে)। এবার private subnet-এর একটি ডেটাবেস সার্ভার কীভাবে আপডেট নামায়, আর কেন বাইরের কেউ তাকে ছুঁতে পারে না — ধাপে ধাপে দেখুন।

  1. db (private-a)10.20.48.20public IP নেই
  2. NAT gatewayEIP 203.0.113.50public-a-তে
  3. আপডেট সার্ভার198.51.100.80port 443
  4. dbউত্তর পেলNACL + SG পার
  5. অচেনা inbound:5432 → DROPখাতায় সারি নেই
Packet আর সিদ্ধান্ত
কোথায়
উৎস → গন্তব্য
Route / নিয়ম
ফল
  1. db আপডেট নামাতে চায়। গন্তব্য 198.51.100.80:443 VPC-র 10.20.0.0/16-এ পড়ে না, তাই private subnet-এর route table-এর একমাত্র মিল 0.0.0.0/0 → nat-gw. db-র কোনো public IP নেই — সে বাইরের ঠিকানা জানেই না।
  2. NAT gateway উৎস বদলায়। উৎস 10.20.48.20:43210 মুছে নিজের Elastic IP 203.0.113.50 আর নিজের বাছাই করা একটি port (এখানে 1034) বসায়, আর খাতায় লেখে “1034 ⇄ 10.20.48.20:43210 ⇄ 198.51.100.80:443”. এটি পর্ব ১৯-এর SNAT, শুধু প্রদানকারী চালায়।
  3. Public subnet-এর route দিয়ে বাইরে। NAT gateway public subnet-এ বসে, তাই তার route table-এ 0.0.0.0/0 → igw. Internet gateway packet-টি ইন্টারনেটে ছাড়ে। সার্ভার উৎস দেখে 203.0.113.50:1034 — ভেতরের ঠিকানা কখনো বাইরে যায় না।
  4. উত্তর ফেরে একই পথে। গন্তব্য 203.0.113.50:1034 — NAT gateway খাতা মিলিয়ে গন্তব্য বদলে 10.20.48.20:43210 করে। Private subnet-এর network ACL stateless, তাই ফিরতি port-এর জন্য inbound নিয়ম লাগবে (rule 110: 1024–65535). db-র security group stateful — নিজের পাঠানো অনুরোধের উত্তর আপনাআপনি ঢুকতে দেয়।
  5. বাইরে থেকে কেউ db-তে ঢুকতে চাইলে? db-র private ঠিকানা ইন্টারনেটে route হয় না, আর তার public IP নেই, তাই internet gateway-এর কিছু বদলানোর নেই। NAT gateway-এর ঠিকানায় port 5432-এ পাঠালেও খাতায় এমন সারি নেই — DROP. NAT gateway নকশাগতভাবে শুধু ভেতর থেকে শুরু হওয়া কথোপকথনের উত্তর ফেরত দেয়; port forwarding-এর কোনো সুযোগই রাখে না।

খরচের ফাঁদ: পরিচালিত NAT gateway-এর জন্য সাধারণত ঘণ্টা হিসাবে আর প্রতি GB প্রক্রিয়াজাত ডেটার জন্য আলাদা টাকা লাগে। Private subnet-এর সার্ভার যদি একই প্রদানকারীর storage থেকে বিশাল ডেটা টানে, পুরোটা NAT gateway দিয়ে গেলে বিল অবাক করার মতো বড় হতে পারে। এজন্য অংশ ৪-এর private endpoint ব্যবহার করা হয়। দাম আর নিয়ম প্রদানকারী আর region ভেদে বদলায় — নিজের প্রদানকারীর মূল্যতালিকা দেখবেন।

৩ · Security group আর network ACL — দুই স্তরের পাহারা

পর্ব ৪৯-এ firewall-এর দুই রূপ দেখেছেন — stateful আর stateless. Cloud-এ দুটিই আছে, দুই জায়গায়:

বিষয়Security group (SG)Network ACL (NACL)
কোথায় বসেপ্রতিটি VM-এর network interface-এপুরো subnet-এর সীমানায়
অবস্থা মনে রাখে?হ্যাঁ — অনুমোদিত অনুরোধের উত্তর আপনাআপনি ফেরেনা — দুই দিকের জন্য আলাদা নিয়ম
নিয়মের ধরনশুধু allow; কোনো নিয়ম না মিললে বন্ধনম্বরযুক্ত allow আর deny; ছোট নম্বর আগে, প্রথম মিলেই থামা; শেষে * deny
উৎস হিসেবে কী লেখা যায়CIDR, অথবা আরেকটি SG (“যে-ই sg-web-এর সদস্য”)শুধু CIDR
সাধারণ ডিফল্ট (AWS)নতুন SG: inbound কিছু নেই, outbound সব খোলাDefault NACL সব খোলা; নতুন বানানো NACL সব বন্ধ

SG-র সবচেয়ে কাজের বৈশিষ্ট্য হলো আরেকটি SG-কে উৎস হিসেবে লেখা: “db-তে port 5432 খোলা শুধু sg-web-এর সদস্যদের জন্য”. ওয়েব সার্ভার বাড়ুক-কমুক, ঠিকানা বদলাক — নিয়ম একই থাকে। এটি ঠিকানা-ভিত্তিক থেকে পরিচয়-ভিত্তিক নিয়মের দিকে একটি পদক্ষেপ, যা Kubernetes-এর NetworkPolicy-তে (অংশ ৮) আরও এগিয়েছে।

একটি VPC-র সিদ্ধান্ত নিজে যাচাই

নিচের simulator অংশ ২-এর VPC: web (10.20.0.10, public-a, public IP 203.0.113.10, SG: 443 সবার জন্য) আর db (10.20.48.20, private-a, SG: 5432 শুধু sg-web থেকে)। Public subnet-এর NACL সব খোলা; private subnet-এর NACL কড়া: inbound-এ 5432 শুধু VPC থেকে আর ফিরতি ephemeral port, outbound-এ 443 আর VPC-র দিকে ফিরতি port. পাঁচটি flow-এর প্রতিটি ধাপের সিদ্ধান্ত আর কারণ ছাপা হয়।

// AWS-style VPC 10.20.0.0/16: route tables, stateful security groups, stateless ordered NACLs.
const n = s => s.split(".").reduce((a, o) => a * 256 + +o, 0);
const inC = (ip, c) => { const [b, l] = c.split("/"), m = 2 ** (32 - l); return Math.floor(n(ip) / m) === Math.floor(n(b) / m); };
const RT = { pub: [["10.20.0.0/16", "local"], ["0.0.0.0/0", "igw"]], priv: [["10.20.0.0/16", "local"], ["0.0.0.0/0", "nat-gw"]] };
const HOST = { "10.20.0.10": { name: "web", sub: "pub", sg: "web", eip: "203.0.113.10" },
               "10.20.48.20": { name: "db", sub: "priv", sg: "db" } };
const SG = { web: [[443, "0.0.0.0/0"]], db: [[5432, "sg-web"]] };          // inbound allows; outbound: all
const NACL = { pub: { in: [[100, 0, 65535, "0.0.0.0/0"]], out: [[100, 0, 65535, "0.0.0.0/0"]] },
  priv: { in: [[100, 5432, 5432, "10.20.0.0/16"], [110, 1024, 65535, "0.0.0.0/0"]], out: [[100, 443, 443, "0.0.0.0/0"], [110, 1024, 65535, "10.20.0.0/16"]] } };
const route = (sub, dst) => RT[sub].filter(r => inC(dst, r[0])).sort((a, b) => b[0].split("/")[1] - a[0].split("/")[1])[0]; // longest prefix
const nacl = (sub, dir, port, peer) => { const r = NACL[sub][dir].find(([, lo, hi, c]) => port >= lo && port <= hi && inC(peer, c));
  return r ? [true, `NACL ${sub}/${dir} rule ${r[0]} allow`] : [false, `NACL ${sub}/${dir} rule * DENY`]; };
const sg = (h, port, src) => { const r = SG[h.sg].find(([p, s]) => p === port && (s === "sg-" + HOST[src]?.sg || (!s.startsWith("sg") && inC(src, s))));
  return r ? [true, `SG ${h.sg} allows ${port} from ${r[1]}`] : [false, `SG ${h.sg}: no rule -> DROP`]; };
function flow(title, src, sp, dst, dp) {
  console.log(`\n# ${title}: ${src}:${sp} -> ${dst}:${dp}`); let S = HOST[src], D = Object.entries(HOST).find(([ip, h]) => ip === dst || h.eip === dst), checks = [];
  if (!S) {                                                    // from the internet
    if (dst === "203.0.113.50") return console.log("  NAT GW 203.0.113.50: no entry for this flow (it only answers flows started inside) -> DROP");
    const [ip, h] = D; console.log(`  IGW: public ${dst} -> private ${ip} (${h.name})`);
    checks = [nacl(h.sub, "in", dp, src), sg(h, dp, src), nacl(h.sub, "out", sp, src)];
  } else {
    const r = route(S.sub, dst); console.log(`  route table ${S.sub}: ${dst} matches ${r[0]} -> ${r[1]}`);
    checks.push(nacl(S.sub, "out", dp, dst), [true, `SG ${S.sg} outbound: allow all`]);
    if (r[1] === "local") { const h = HOST[dst];
      checks.push(nacl(h.sub, "in", dp, src), sg(h, dp, src), nacl(h.sub, "out", sp, src), nacl(S.sub, "in", sp, dst));
    } else if (r[1] === "nat-gw") checks.push([true, "NAT GW: SNAT src -> 203.0.113.50 (+ its own port), remembers the flow"], nacl(S.sub, "in", sp, dst));
  }
  for (const [ok, why] of checks) { console.log("  " + (ok ? "ok   " : "FAIL ") + why); if (!ok) return console.log("  => BLOCKED"); }
  console.log("  => ALLOWED (reply: SG remembers the flow; NACL return rules checked above)");
}
flow("internet -> web", "198.51.100.7", 50123, "203.0.113.10", 443);
flow("web -> db", "10.20.0.10", 41000, "10.20.48.20", 5432);
flow("db -> internet (OS updates)", "10.20.48.20", 43210, "198.51.100.80", 443);
flow("internet -> web on 22", "198.51.100.7", 50124, "203.0.113.10", 22);
flow("internet -> db via NAT GW IP", "198.51.100.7", 50125, "203.0.113.50", 5432);

আউটপুট:

# internet -> web: 198.51.100.7:50123 -> 203.0.113.10:443
  IGW: public 203.0.113.10 -> private 10.20.0.10 (web)
  ok   NACL pub/in rule 100 allow
  ok   SG web allows 443 from 0.0.0.0/0
  ok   NACL pub/out rule 100 allow
  => ALLOWED (reply: SG remembers the flow; NACL return rules checked above)

# web -> db: 10.20.0.10:41000 -> 10.20.48.20:5432
  route table pub: 10.20.48.20 matches 10.20.0.0/16 -> local
  ok   NACL pub/out rule 100 allow
  ok   SG web outbound: allow all
  ok   NACL priv/in rule 100 allow
  ok   SG db allows 5432 from sg-web
  ok   NACL priv/out rule 110 allow
  ok   NACL pub/in rule 100 allow
  => ALLOWED (reply: SG remembers the flow; NACL return rules checked above)

# db -> internet (OS updates): 10.20.48.20:43210 -> 198.51.100.80:443
  route table priv: 198.51.100.80 matches 0.0.0.0/0 -> nat-gw
  ok   NACL priv/out rule 100 allow
  ok   SG db outbound: allow all
  ok   NAT GW: SNAT src -> 203.0.113.50 (+ its own port), remembers the flow
  ok   NACL priv/in rule 110 allow
  => ALLOWED (reply: SG remembers the flow; NACL return rules checked above)

# internet -> web on 22: 198.51.100.7:50124 -> 203.0.113.10:22
  IGW: public 203.0.113.10 -> private 10.20.0.10 (web)
  ok   NACL pub/in rule 100 allow
  FAIL SG web: no rule -> DROP
  => BLOCKED

# internet -> db via NAT GW IP: 198.51.100.7:50125 -> 203.0.113.50:5432
  NAT GW 203.0.113.50: no entry for this flow (it only answers flows started inside) -> DROP

১ — ইন্টারনেট থেকে web: internet gateway public 203.0.113.10-কে private 10.20.0.10-এ বদলায়; public NACL সব খোলা; SG-তে 443 সবার জন্য অনুমোদিত — ALLOWED. ২ — web থেকে db: route table-এ 10.20.0.0/16 → local মিলেছে, তাই packet VPC-র ভেতরেই থাকে। মোট ছয়টি পরীক্ষা: যাওয়ার পথে দুই subnet-এর NACL, web-এর outbound SG আর db-র SG (“5432 from sg-web” — web সেই SG-র সদস্য), ফেরার পথে আবার দুই NACL. খেয়াল করুন private/out-এ rule 110 লাগল — উত্তর যায় web-এর ephemeral port 41000-এ, আর stateless NACL নিজে তা জানে না।

৩ — db থেকে ইন্টারনেট: route 0.0.0.0/0 → nat-gw; NAT gateway SNAT করে; ফিরতি packet-এর জন্য private/in-এর rule 110 (1024–65535) দরকার — অনুশীলন ২-এ দেখবেন ওটা মুছলে কী হয়। ৪ — ইন্টারনেট থেকে web-এর port 22: NACL পার হলো, কিন্তু SG-তে 22-এর কোনো নিয়ম নেই — BLOCKED. দুই স্তরের যেকোনো একটি না বললেই packet বাতিল। ৫ — NAT gateway-এর ঠিকানা দিয়ে db-তে: সরাসরি DROP, কারণ NAT gateway শুধু ভেতর থেকে শুরু হওয়া flow-এর উত্তর ফেরায়। (এই simulator শেখার জন্য সরল: আসল cloud-এ আরও ধাপ আছে, যেমন NAT gateway যে public subnet-এ বসে তার NACL আর route table, আর নিয়মের সংখ্যার সীমা।)

৪ · VPC-কে বাইরের সাথে জোড়া — আর CIDR পরিকল্পনা

একটি VPC একা থাকে না। একই প্রতিষ্ঠানের আরেকটি VPC, ঢাকার অফিস, চট্টগ্রামের শাখা, প্রদানকারীর নিজস্ব সেবা (storage, ডেটাবেস) — সবার সাথে যোগ লাগে। প্রধান উপায়গুলো:

  • VPC peering: দুটি VPC-র মধ্যে সরাসরি private সংযোগ; দুই পাশের route table-এ অপর পাশের CIDR যোগ করতে হয়। এটি transitive নয়: A–B আর B–C peering থাকলেও A থেকে C-তে যাওয়া যায় না। দুটি VPC-র CIDR মিলে গেলে (overlap) peering-ই করা যায় না।
  • Transit gateway: অনেক VPC আর অফিসের সংযোগের কেন্দ্রীয় hub — বাসের রুটের একটি বড় টার্মিনালের মতো, যেখানে সব রুট এসে মেলে। দশটি VPC-কে জোড়ায় জোড়ায় peering করলে ১০ × ৯ ÷ ২ = ৪৫টি সংযোগ লাগত; hub-এ লাগে ১০টি।
  • Private endpoint: প্রদানকারীর সেবা (যেমন object storage) বা অন্য প্রতিষ্ঠানের সেবায় ইন্টারনেট বা NAT gateway ছাড়াই, VPC-র ভেতরের একটি private ঠিকানা দিয়ে পৌঁছানো (AWS-এ PrivateLink/VPC endpoint, Azure-এ Private Endpoint, Google-এ Private Service Connect).
  • Site-to-site VPN আর ডেডিকেটেড সংযোগ: অফিসের router আর cloud-এর VPN gateway-এর মধ্যে IPsec tunnel (পর্ব ৪৯) — ইন্টারনেটের ওপর দিয়ে, এনক্রিপ্টেড। আরও স্থির গতি আর latency চাইলে ভাড়া করা ব্যক্তিগত লাইন: AWS Direct Connect, Azure ExpressRoute, Google Cloud Interconnect.
  • Cloud load balancer: public subnet-এ বসে বাইরের ট্রাফিক নেয়, private subnet-এর সার্ভারগুলোতে ভাগ করে দেয় — L4 (TCP/UDP) বা L7 (HTTP) স্তরে। Load balancing-এর algorithm আর L4/L7-এর পার্থক্য পর্ব ৫২-এ।

CIDR পরিকল্পনা — আগে ভাবলে পরে কাঁদতে হয় না

VPC-র CIDR একবার বাছলে পরে বদলানো কঠিন (কিছু প্রদানকারী অতিরিক্ত CIDR যোগ করতে দেয়, কিন্তু পুরোনোটা সরানো ঝামেলার)। সবচেয়ে বড় ভুল হলো সবাই মিলে একই চেনা পরিসর বাছা — 10.0.0.0/16 বা 192.168.0.0/16. পরে যখন অফিসের নেটওয়ার্ক বা আরেকটি প্রতিষ্ঠানের VPC-র সাথে জুড়তে হয়, ঠিকানা মিলে যায় আর routing অসম্ভব হয়ে পড়ে: একই ঠিকানা দুই জায়গায় থাকলে router কোন দিকে পাঠাবে? পর্ব ১৭-এর subnetting এখানে সরাসরি কাজে লাগে।

উদাহরণ: একটি 10.20.0.0/16 VPC, তিনটি AZ, প্রতিটিতে একটি public আর একটি private subnet, প্রত্যেকটি /20. হিসাব: /16 থেকে /20 মানে ৪ bit ধার, তাই ২৪ = ১৬টি /20 হতে পারে, প্রতিটিতে ২১২ = ৪,০৯৬ ঠিকানা। তৃতীয় octet-এ block size ১৬, তাই subnet-গুলো শুরু হয় 10.20.0.0, 10.20.16.0, 10.20.32.0… থেকে। ছয়টি নিলে দশটি ফাঁকা থাকে ভবিষ্যতের জন্য। নিচের planner এটি করে, আর তিনটি নেটওয়ার্কের সাথে overlap পরীক্ষা করে।

// Plan /20 subnets for a 10.20.0.0/16 VPC across 3 AZs, then check overlaps with other networks.
const n = s => s.split(".").reduce((a, o) => a * 256 + +o, 0);
const ip = x => [24, 16, 8, 0].map(b => Math.floor(x / 2 ** b) % 256).join(".");
const range = c => { const [b, l] = c.split("/"), size = 2 ** (32 - l); return [n(b), n(b) + size - 1, size]; };
const VPC = "10.20.0.0/16", NEW = 20, AZS = ["a", "b", "c"];
let [next, vpcEnd] = range(VPC); const plan = [];
for (const tier of ["public", "private"]) for (const az of AZS) {
  const cidr = `${ip(next)}/${NEW}`, [lo, hi, size] = range(cidr);
  plan.push({ name: `${tier}-${az}`, cidr, lo, hi, size }); next = hi + 1;
}
console.log("subnet     cidr            first .. last                   usable*");
for (const s of plan)
  console.log(`${s.name.padEnd(10)} ${s.cidr.padEnd(15)} ${(ip(s.lo) + " .. " + ip(s.hi)).padEnd(31)} ${s.size - 5}`);
const left = vpcEnd - next + 1;
console.log(`used ${plan.length} x /${NEW} = ${plan.length * 2 ** (32 - NEW)} addrs; free: ${ip(next)} .. ${ip(vpcEnd)} = ${left} (${left / 2 ** (32 - NEW)} more /${NEW})`);
const overlap = (a, b) => { const [a1, a2] = range(a), [b1, b2] = range(b); return a1 <= b2 && b1 <= a2; };
const others = { "Dhaka office": "10.10.0.0/16", "Ctg branch": "10.20.200.0/22", "partner VPC": "10.0.0.0/8" };
console.log("\noverlap check against " + VPC);
for (const [who, c] of Object.entries(others)) {
  const hits = plan.filter(s => overlap(s.cidr, c)).map(s => s.name);
  const v = overlap(VPC, c) ? "OVERLAP" + (hits.length ? " with " + hits.join(",") : " (inside VPC range, no subnet yet)") : "ok";
  console.log(`  ${who.padEnd(13)} ${c.padEnd(15)} ${v}`);
}
console.log("* usable = size - 5 (AWS reserves 5 per subnet; other clouds differ)");

আউটপুট:

subnet     cidr            first .. last                   usable*
public-a   10.20.0.0/20    10.20.0.0 .. 10.20.15.255       4091
public-b   10.20.16.0/20   10.20.16.0 .. 10.20.31.255      4091
public-c   10.20.32.0/20   10.20.32.0 .. 10.20.47.255      4091
private-a  10.20.48.0/20   10.20.48.0 .. 10.20.63.255      4091
private-b  10.20.64.0/20   10.20.64.0 .. 10.20.79.255      4091
private-c  10.20.80.0/20   10.20.80.0 .. 10.20.95.255      4091
used 6 x /20 = 24576 addrs; free: 10.20.96.0 .. 10.20.255.255 = 40960 (10 more /20)

overlap check against 10.20.0.0/16
  Dhaka office  10.10.0.0/16    ok
  Ctg branch    10.20.200.0/22  OVERLAP (inside VPC range, no subnet yet)
  partner VPC   10.0.0.0/8      OVERLAP with public-a,public-b,public-c,private-a,private-b,private-c
* usable = size - 5 (AWS reserves 5 per subnet; other clouds differ)

ছয়টি subnet পরপর বসেছে, প্রতিটি ৪,০৯৬ ঠিকানার, ব্যবহারযোগ্য ৪,০৯১ (AWS-এর হিসাবে ৫টি বাদ)। মোট ৬ × ৪,০৯৬ = ২৪,৫৭৬ ঠিকানা গেল, বাকি 10.20.96.0 থেকে 10.20.255.255 = ৪০,৯৬০ = আরও ১০টি /20. ঢাকা অফিসের 10.10.0.0/16 সম্পূর্ণ আলাদা — ok. চট্টগ্রাম শাখার 10.20.200.0/22 কোনো subnet-এর সাথে এখনো মেলেনি, তবু এটি VPC-র পরিসরের ভেতরে — আর VPC-র local route পুরো 10.20.0.0/16 ঢেকে রাখে, তাই শাখার দিকে যাওয়া packet সাধারণত VPC ছেড়েই বেরোবে না। “Partner VPC” 10.0.0.0/8 পুরো VPC-কেই গিলে ফেলে। Overlap পরীক্ষার সূত্রটি সরল: দুটি পরিসর [a1, a2] আর [b1, b2] মেলে যদি a1 ≤ b2 আর b1 ≤ a2.

৫ · Container-এর নেটওয়ার্ক — namespace, veth আর bridge

এবার এক যন্ত্রের ভেতরে নামি। একটি container আসলে একটি সাধারণ Linux প্রসেস, যাকে kernel আলাদা “চশমা” পরিয়ে দিয়েছে — নিজের প্রসেস-তালিকা, নিজের ফাইল-সিস্টেম, আর নেটওয়ার্কের জন্য নিজের network namespace: নিজের interface, নিজের IP, নিজের route table, নিজের firewall নিয়ম আর socket. Namespace আর cgroup-এর পুরো গল্প অপারেটিং সিস্টেম কোর্সের Container — namespace আর cgroup পর্বে; এখানে শুধু নেটওয়ার্কের দিকটা।

নতুন network namespace জন্মায় শুধু একটি বন্ধ lo নিয়ে — বাইরের সাথে কোনো যোগ নেই। যোগ দিতে তিনটি ইট:

  1. veth জোড়া: একটি ভার্চুয়াল তারের দুই মাথা। এক মাথা container-এর namespace-এ (ভেতরে নাম eth0), অন্য মাথা host-এ (নাম vethXXXX). এক মাথায় যে frame ঢোকে, অন্য মাথা দিয়ে বেরোয়।
  2. Linux bridge: host-এর ভেতরে একটি সফটওয়্যার switch (Docker-এ নাম docker0). সব container-এর host-পাশের veth মাথা এতে লাগানো, তাই একই bridge-এর container-রা পর্ব ১২-এর switch-এর মতো MAC শিখে একে অপরের সাথে কথা বলে। Bridge-এর নিজেরও একটি IP (172.17.0.1) — container-দের default gateway.
  3. Routing আর NAT: host-এর kernel 172.17.0.0/16-এর packet বাইরের interface-এ forward করে, আর বেরোনোর সময় masquerade করে উৎস বদলে host-এর ঠিকানা বসায় — ঠিক বাসার router-এর মতো (পর্ব ১৯)।

একটি বড় অফিস-ভবন ভাবুন। প্রতিটি কক্ষ (container) নিজের দরজা আর ভেতরের এক্সটেনশন নম্বর নিয়ে আলাদা। প্রতিটি কক্ষ থেকে একটি তার (veth) গেছে নিচতলার রিসেপশনের PABX বোর্ডে (bridge). কক্ষে কক্ষে কথা হয় বোর্ড দিয়ে; বাইরে কল যায় রিসেপশন দিয়ে (NAT), আর বাইরের কল কোনো কক্ষে আসে শুধু যদি রিসেপশনে আগে থেকে লেখা থাকে “অমুক নম্বরে ফোন এলে ৩০৪ নম্বর কক্ষে দেবেন” — সেটাই port publishing.

এই কোর্সের কোড যে container-এ চলে, তার নেটওয়ার্ক

এই পাতার bash কোড যে সার্ভারে চলে, সেটি নিজেই একটি rootless container. নিচের স্ক্রিপ্ট শুধু পড়ে: আমরা কোন network namespace-এ, কী interface আছে, সেগুলো কোন ধরনের, আর একজন সাধারণ ব্যবহারকারী নতুন network namespace বানাতে পারে কি না। শেষ ধাপে একটি ফেলে-দেওয়া নতুন namespace-এর ভেতরে একটি veth জোড়া বানিয়ে দেখা হয় — host-এর কিছুই বদলায় না।

echo "== 1. which network namespace am I in? =="
echo "  net : $(readlink /proc/self/ns/net)   user: $(readlink /proc/self/ns/user)   uid=$(id -u)"
echo "== 2. interfaces and addresses =="
if command -v ip >/dev/null; then ip -br link; ip -br -4 addr
else for d in /sys/class/net/*; do echo "  ${d##*/} $(cat "$d/address")"; done; fi
echo "== 3. routing table =="
command -v ip >/dev/null && ip route || tail -n +2 /proc/net/route
echo "== 4. what kind of interface is each one? =="
for d in /sys/class/net/*; do
  n=${d##*/}; kind=other
  [ "$n" = lo ] && kind=loopback
  [ -e "$d/tun_flags" ] && kind="tun/tap: a program on the host holds the other end"
  [ -d "$d/bridge" ] && kind=bridge
  [ "$(cat "$d/iflink")" != "$(cat "$d/ifindex")" ] && kind="$kind, peer ifindex=$(cat "$d/iflink") (veth-like)"
  echo "  $n: $kind  mtu=$(cat "$d/mtu")"
done
echo "== 5. packet counters (/proc/net/dev) =="
tail -n +3 /proc/net/dev | while read -r line; do
  set -- ${line/:/ }; echo "  $1 rx_packets=$3 tx_packets=${11}"
done
echo "== 6. may an ordinary user create a NEW network namespace? =="
command -v unshare >/dev/null || { echo "  unshare নেই"; exit 0; }
out=$(unshare -n true 2>&1) && echo "  unshare -n : allowed" || echo "  unshare -n : denied -> ${out#unshare: }"
inner='echo "  inside: $(readlink /proc/self/ns/net)"; ip -br link
  ip link add v-host type veth peer name v-ctr 2>&1 && echo "  veth pair created:" && ip -br link | grep v-'
out=$(unshare -rn sh -c "$inner" 2>&1) && echo "  unshare -rn: allowed (via a user namespace)" || echo "  unshare -rn: denied"
echo "$out"

আসল রানের আউটপুট (namespace নম্বর, MAC আর packet counter প্রতিটি রানে আলাদা হতে পারে):

== 1. which network namespace am I in? ==
  net : net:[4026532747]   user: user:[4026532740]   uid=999
== 2. interfaces and addresses ==
lo               UNKNOWN        00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP>
tap0             UNKNOWN        52:6d:2f:a4:1c:c0 <BROADCAST,UP,LOWER_UP>
lo               UNKNOWN        127.0.0.1/8
tap0             UNKNOWN        10.0.2.100/24
== 3. routing table ==
default via 10.0.2.2 dev tap0
10.0.2.0/24 dev tap0 proto kernel scope link src 10.0.2.100
== 4. what kind of interface is each one? ==
  lo: loopback  mtu=65536
  tap0: tun/tap: a program on the host holds the other end  mtu=65520
== 5. packet counters (/proc/net/dev) ==
  lo rx_packets=0 tx_packets=0
  tap0 rx_packets=1 tx_packets=3
== 6. may an ordinary user create a NEW network namespace? ==
  unshare -n : denied -> unshare failed: Operation not permitted
  unshare -rn: allowed (via a user namespace)
  inside: net:[4026532824]
lo               DOWN           00:00:00:00:00:00 <LOOPBACK>
  veth pair created:
v-ctr@v-host     DOWN           fa:26:ae:36:0e:50 <BROADCAST,MULTICAST,M-DOWN>
v-host@v-ctr     DOWN           8e:f2:37:17:c4:32 <BROADCAST,MULTICAST,M-DOWN>

১ — namespace: /proc/self/ns/net একটি বিশেষ লিংক; বন্ধনীর সংখ্যাটি namespace-এর পরিচয় (inode নম্বর)। দুটি প্রসেসের এই সংখ্যা এক হলে তারা একই নেটওয়ার্ক-জগতে। আমরা uid 999 — root নই। ২–৩ — interface আর route: শুধু lo আর tap0, ঠিকানা 10.0.2.100/24, default gateway 10.0.2.2. Host-এর আসল network card এখান থেকে অদৃশ্য — ঠিক একটি container-এর যা দেখার কথা।

৪ — ধরন: tap0-এর জন্য /sys/class/net/tap0/tun_flags আছে, অর্থাৎ এটি একটি tun/tap যন্ত্র: এর অপর মাথা কোনো তার নয়, host-এর একটি সাধারণ প্রোগ্রাম, যে packet নিয়ে বাইরে পাঠায় (rootless নেটওয়ার্কিংয়ের প্রচলিত ব্যবস্থা, যেমন slirp4netns বা pasta). কোনো veth বা bridge নেই, কারণ root ছাড়া host-এ bridge বানানো যায় না। MTU ৬৫,৫২০ — packet কোনো আসল তারে যায় না বলে বড় রাখা যায়। ৫ — counter: container চালুর সময়ের কয়েকটি packet মাত্র; বাইরে ইন্টারনেট নেই।

৬ — নতুন namespace: সরাসরি unshare -n — Operation not permitted, কারণ এর জন্য CAP_SYS_ADMIN ক্ষমতা লাগে। কিন্তু unshare -rn (আগে একটি user namespace বানিয়ে নিজেকে তার ভেতরের “root” করা) চলেছে: ভেতরের namespace-এর নম্বর আলাদা, সেখানে শুধু একটি DOWN অবস্থার lo. সেই নিজস্ব জগতে আমরা v-host/v-ctr নামের veth জোড়াও বানাতে পেরেছি — নামের @ চিহ্ন দেখায় কোনটি কার জোড়া। Docker ঠিক এটাই করে, তবে এক মাথা container-এর namespace-এ সরিয়ে দেয় আর অন্য মাথা docker0-এ লাগায়। স্ক্রিপ্ট শেষ হতেই ফেলে-দেওয়া namespace আর veth দুটোই মুছে যায়। (কোন ব্যবস্থায় user namespace চালু থাকবে, সেটা distro আর নিরাপত্তা-নীতির ওপর নির্ভর করে।)

৬ · Docker-এর নেটওয়ার্ক mode আর -p 8080:80-এর আসল পথ

Docker একটি container চালানোর সময় তার নেটওয়ার্ক কয়েক রকম হতে পারে (--network দিয়ে বাছা হয়):

Modeকী হয়কখন
bridge (ডিফল্ট)নিজের namespace, veth, docker0 বা নিজের বানানো bridge, বাইরে masqueradeপ্রায় সব সাধারণ কাজ
hostআলাদা network namespace-ই নেই; host-এর interface আর port সরাসরি ব্যবহারসর্বোচ্চ গতি বা অনেক port লাগলে; বিচ্ছিন্নতা কম
noneশুধু lo, বাইরের কোনো যোগ নেইনেটওয়ার্ক লাগে না এমন কাজ, বাড়তি নিরাপত্তা
overlayকয়েকটি host জুড়ে একটি virtual নেটওয়ার্ক, নিচে VXLANDocker Swarm-এর মতো একাধিক যন্ত্রে
macvlanContainer নিজের MAC নিয়ে সরাসরি host-এর LAN-এ বসে, আলাদা IP পায়পুরোনো ব্যবস্থা যেখানে প্রতিটি সেবার LAN-এ নিজের ঠিকানা চাই; host নিজে সাধারণত সরাসরি এদের সাথে কথা বলতে পারে না

ডিফল্ট bridge-এ container পায় 172.17.0.0/16-এর একটি ঠিকানা — যা বাইরের কেউ দেখে না। তাহলে container-এর ভেতরের ওয়েব সার্ভারে পৌঁছানো যায় কীভাবে? docker run -p 8080:80 nginx লিখলে Docker host-এর netfilter-এ (iptables বা নতুন সংস্করণে কখনো nftables) একটি DNAT নিয়ম বসায়: “host-এর TCP 8080-এ যা আসবে, গন্তব্য বদলে 172.17.0.2:80”. এটি পর্ব ১৯-এর port forwarding, শুধু router-এর জায়গায় আপনার নিজের যন্ত্র। একে Docker-এর ভাষায় বলে port publishing.

  1. ল্যাপটপ192.168.0.50port 51000
  2. host-এর netfilterDNAT → 172.17.0.2:80PREROUTING
  3. docker0 bridge172.17.0.1veth → eth0
  4. nginx container172.17.0.2:80নিজের namespace
  5. conntrackউল্টো বদলউৎস → host:8080
Packet-এর ঠিকানা আর host-এর সিদ্ধান্ত
কোথায়
উৎস
গন্তব্য
নিয়ম / table
  1. ল্যাপটপ host-এর port 8080-এ সংযোগ চায়। Host-এর LAN ঠিকানা 192.168.0.10. ল্যাপটপ container-এর অস্তিত্বই জানে না।
  2. Routing-এর আগেই গন্তব্য বদল। nat table-এর PREROUTING থেকে Docker-এর DOCKER chain-এ গিয়ে নিয়ম মিলল: গন্তব্য 172.17.0.2:80. conntrack এই কথোপকথনের দুই দিকের চেহারা মনে রাখল — পরের packet-গুলো আর নিয়ম ঘাঁটবে না।
  3. Routing আর অনুমতি। নতুন গন্তব্য 172.17.0.0/16, যা docker0-এর সাথে সরাসরি যুক্ত — তাই packet forward হবে, আর filter table-এর FORWARD-এ Docker-এর বসানো নিয়ম এই container-এর port 80-কে অনুমতি দেয়।
  4. Bridge থেকে veth দিয়ে container-এ। docker0 switch-এর মতো MAC দেখে সঠিক vethXXXX-এ দেয়; অপর মাথা container-এর ভেতরের eth0. nginx দেখে উৎস ল্যাপটপের আসল ঠিকানা 192.168.0.50:51000 — log-এ সঠিক ঠিকানা ওঠে।
  5. উত্তর ফেরে। nginx উত্তর দেয় 192.168.0.50-কে, default gateway 172.17.0.1 দিয়ে। Host-এ conntrack দেখে এটি চেনা কথোপকথনের ফিরতি packet.
  6. উল্টো বদল — ল্যাপটপ খুশি। উৎস 172.17.0.2:80 বদলে 192.168.0.10:8080 — ল্যাপটপ যেখানে পাঠিয়েছিল ঠিক সেখান থেকেই উত্তর পেল। Container নিজে বাইরে কিছু চাইলে উল্টো ঘটনা ঘটে: POSTROUTING-এ masquerade উৎসকে host-এর ঠিকানায় বদলায়।
$ docker run -d --name web -p 8080:80 nginx
$ sudo iptables -t nat -S DOCKER
-N DOCKER
-A DOCKER -i docker0 -j RETURN
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 8080 -j DNAT --to-destination 172.17.0.2:80
$ sudo iptables -t nat -S POSTROUTING | grep 172.17
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
$ ip -br link | grep -E 'docker0|veth'
docker0          UP             02:42:5e:1a:7c:01 <BROADCAST,MULTICAST,UP,LOWER_UP>
veth3f2a1b9@if2  UP             9a:51:0c:3e:22:7d <BROADCAST,MULTICAST,UP,LOWER_UP>

নাম দিয়ে খোঁজা — user-defined bridge আর embedded DNS

ডিফল্ট docker0-এ container-রা একে অপরকে শুধু IP দিয়ে চেনে। কিন্তু নিজে একটি নেটওয়ার্ক বানালে (docker network create shop) আর দুটি container তাতে রাখলে, একটি অন্যটিকে নাম দিয়ে ডাকতে পারে — db:5432. কারণ user-defined নেটওয়ার্কে Docker প্রতিটি container-এ একটি ছোট embedded DNS সার্ভার দেয় (সাধারণত 127.0.0.11-এ), যা container-এর নামকে তার বর্তমান IP-তে রূপান্তর করে, আর বাকি নাম বাইরের resolver-এ পাঠায়। এই নেটওয়ার্কের container-রা নিজেদের মধ্যে সরাসরি bridge দিয়ে কথা বলে — NAT ছাড়া, port publish ছাড়া; বাইরের দুনিয়া থেকে শুধু publish করা port দেখা যায়। আলাদা দুটি user-defined নেটওয়ার্কের container-রা ডিফল্টভাবে একে অপরকে দেখে না — একটি সহজ বিভাজন। (খুঁটিনাটি Docker সংস্করণ ভেদে বদলায়; DNS নিয়ে বিস্তারিত পর্ব ৩৫-এ।)

৭ · Kubernetes-এর নেটওয়ার্ক মডেল — Pod, CNI আর Service

একটি যন্ত্রে Docker যথেষ্ট। কিন্তু শত শত যন্ত্রে হাজারো container চালাতে, নষ্ট হলে আবার চালু করতে, চাপ বাড়লে সংখ্যা বাড়াতে লাগে একটি orchestrator — আজ সবচেয়ে প্রচলিত Kubernetes (Google-এর ভেতরের Borg ব্যবস্থার অভিজ্ঞতা থেকে, ২০১৪ সালে উন্মুক্ত হয়)। এর ক্ষুদ্রতম একক Pod: এক বা একাধিক container, যারা একটি network namespace ভাগ করে — তাই একই Pod-এর container-রা একে অপরকে localhost-এ পায়।

তিনটি নিয়ম

Kubernetes নিজে নেটওয়ার্ক বানায় না; সে শুধু একটি মডেল দাবি করে:

  1. প্রতিটি Pod-এর নিজের IP — Docker-এর মতো host-এর port ভাগাভাগি নয়। দুটি Pod একই port 8080 ব্যবহার করলেও কোনো সংঘাত নেই।
  2. যেকোনো node-এর যেকোনো Pod অন্য যেকোনো Pod-এর সাথে NAT ছাড়া কথা বলে — প্রাপক যে উৎস-ঠিকানা দেখে, সেটি প্রেরক Pod-এর আসল ঠিকানা।
  3. একটি node-এর এজেন্টরা (যেমন kubelet) সেই node-এর সব Pod-এ পৌঁছাতে পারে।

এই মডেল বাস্তবায়ন করে CNI (Container Network Interface) plugin: Pod তৈরির সময় kubelet-এর নির্দেশে container runtime plugin-কে ডাকে, plugin namespace-এ interface বসায়, ঠিকানা দেয়, route লেখে। প্রচলিত কয়েকটি (প্রতিটি অনেক mode-এ চলে; নিচে সবচেয়ে চেনা রূপ):

  • Flannel: প্রতিটি node একটি Pod-subnet পায় (যেমন 10.244.1.0/24); অন্য node-এর Pod-এ যাওয়া packet VXLAN-এ মুড়ে node-এর ঠিকানায় পাঠানো হয় — সরল overlay.
  • Calico: প্রায়ই overlay ছাড়াই — প্রতিটি node BGP দিয়ে অন্যদের জানায় “10.244.2.0/24 আমার কাছে”, আর underlay router-রা সরাসরি route করে। Underlay সেটা না মানলে IP-in-IP বা VXLAN-ও চালাতে পারে। NetworkPolicy প্রয়োগে বিশেষ জোর।
  • Cilium: Linux kernel-এর eBPF দিয়ে packet-এর পথে ছোট প্রোগ্রাম বসিয়ে forwarding, load balancing আর নীতি প্রয়োগ — অনেক ক্ষেত্রে iptables-এর লম্বা নিয়ম-তালিকা এড়িয়ে।
  • Cloud-এর নিজস্ব CNI: যেমন AWS-এর VPC CNI — Pod সরাসরি VPC-র ঠিকানা পায়, overlay লাগে না, তবে VPC-র ঠিকানা দ্রুত ফুরাতে পারে (অংশ ৪-এর পরিকল্পনা তখন আরও জরুরি)।

Service — বদলাতে থাকা Pod-এর সামনে একটি স্থির ঠিকানা

Pod মরে, নতুন জন্মায়, ঠিকানা বদলায়। তাই অন্যরা সরাসরি Pod-এর IP-তে নির্ভর করতে পারে না। Service একটি স্থির নাম আর ঠিকানা দেয়, আর label দিয়ে ঠিক করে পেছনের কোন Pod-গুলো এর “endpoint”. তিন ধরন:

  • ClusterIP (ডিফল্ট): cluster-এর ভেতরে একটি virtual ঠিকানা, যেমন 10.96.0.20. প্রচলিত iptables mode-এ কোনো interface-এ এই ঠিকানা বসানো নেই, কেউ ping-এর উত্তর দেয় না — এটি শুধু নিয়মে আছে (IPVS mode-এ ঠিকানাটি একটি dummy interface-এ বসানো থাকে, তাই সেখানে ping-এর উত্তর আসতে পারে)।
  • NodePort: প্রতিটি node-এর একটি port (ডিফল্ট পরিসর 30000–32767) খুলে দেয়, যা ClusterIP-তে পৌঁছায়।
  • LoadBalancer: cloud-এ চললে প্রদানকারীর একটি load balancer চেয়ে নেয়, যা NodePort বা সরাসরি Pod-এ ট্রাফিক পাঠায়।

ClusterIP-কে “জীবন্ত” করে kube-proxy — প্রতিটি node-এ চলে, Service আর endpoint-এর তালিকা দেখে, আর node-এর netfilter-এ নিয়ম লেখে: “গন্তব্য 10.96.0.20:80 হলে এই endpoint-গুলোর যেকোনো একটিতে DNAT হবে”. প্রচলিত iptables mode-এ বাছাই হয় এলোমেলোভাবে (random); IPVS mode-এ round robin-সহ কয়েকটি algorithm থাকে; নতুন সংস্করণে nftables mode-ও এসেছে। Cilium-এর মতো plugin kube-proxy-কে পুরো বাদ দিয়ে eBPF দিয়ে একই কাজ করতে পারে। নিচের stepper দেখায় এক node-এর Pod থেকে আরেক node-এর Pod-এ Service-এর মাধ্যমে একটি packet-এর যাত্রা (Flannel-ধাঁচের VXLAN overlay ধরে)।

  1. web Pod (node A)10.244.1.9→ 10.96.0.20:80
  2. node A: kube-proxy-র নিয়মDNAT → 10.244.2.8:8080conntrack
  3. VXLAN মোড়ক192.168.10.11 → .12UDP 8472
  4. api Pod (node B)10.244.2.8:8080উৎস অপরিবর্তিত
  5. node A: উল্টো বদলউৎস → 10.96.0.20:80web Pod খুশি
ভেতরের packet, বাইরের মোড়ক আর সিদ্ধান্ত
কোথায়
ভেতরের উৎস → গন্তব্য
বাইরের মোড়ক
সিদ্ধান্ত
  1. web Pod Service-এ সংযোগ চায়। নাম api.shop.svc.cluster.local CoreDNS আগেই ClusterIP 10.96.0.20-এ রূপান্তর করেছে (অংশ ৮)। Packet Pod-এর eth0 থেকে veth-এর অপর মাথা দিয়ে node A-র মূল namespace-এ আসে।
  2. Node A-তেই Service-এর রহস্য খোলে। kube-proxy-র লেখা নিয়ম গন্তব্য 10.96.0.20:80 চিনে তিনটি ready endpoint-এর একটি এলোমেলোভাবে বাছে — এবার node B-র 10.244.2.8:8080 — আর DNAT করে। conntrack সারি রাখে। উৎস বদলায় না।
  3. অন্য node-এর Pod — তাই মোড়ক। Node A-র route table বলে 10.244.2.0/24 আছে flannel.1 (VXLAN interface) দিয়ে node B-তে। পুরো ভেতরের frame একটি বাইরের UDP packet-এ মোড়া হয়: উৎস node A 192.168.10.11, গন্তব্য node B 192.168.10.12, UDP port 8472 (Flannel-এর ডিফল্ট — Linux-এর পুরোনো VXLAN port; IANA-র মান 4789), VNI 1.
  4. Underlay কিছুই বোঝে না। Data center-এর switch আর router দেখে শুধু node থেকে node-এ একটি সাধারণ UDP packet. Pod-এর ঠিকানা তাদের route table-এ নেই — দরকারও নেই। দাম: প্রতিটি packet ৫০ byte বড় (অংশ ৯-এ MTU-র হিসাব)।
  5. Node B মোড়ক খোলে। flannel.1 মোড়ক সরিয়ে ভেতরের packet দেয়; route 10.244.2.0/24 dev cni0 দিয়ে bridge থেকে veth হয়ে api Pod-এ। api Pod উৎস দেখে 10.244.1.9 — Pod-এর আসল ঠিকানা, কোনো NAT নেই, ঠিক Kubernetes-এর দ্বিতীয় নিয়মের মতো।
  6. উত্তর, আর উল্টো বদল node A-তে। api Pod উত্তর পাঠায় 10.244.1.9-কে; একইভাবে মোড়কে চড়ে node A-তে আসে। node A-র conntrack সারি দেখে উৎস 10.244.2.8:8080 বদলে 10.96.0.20:80 করে — web Pod যেখানে পাঠিয়েছিল, সেখান থেকেই উত্তর পেল। এজন্যই DNAT প্রেরকের node-এ হওয়া জরুরি: ফিরতি পথও সেখান দিয়ে যায়।

এলোমেলো বাছাই আসলে সমান ভাগ — একটি ছোট প্রমাণ

iptables-এ “n-টির যেকোনো একটি সমান সম্ভাবনায় বাছাই” সরাসরি লেখা যায় না; লেখা যায় শুধু “এই নিয়ম p সম্ভাবনায় মেলে”. তাই kube-proxy n-টি endpoint-এর জন্য ক্রমানুসারে নিয়ম লেখে, যেখানে i-তম নিয়ম (i = ০, ১, …, n − ১) মেলে সম্ভাবনা 1/(n − i)-তে, আর শেষটি নিশ্চিতভাবে (1/1). Endpoint k বাছাই হতে হলে আগের k-টি নিয়ম না মিলে k-তমটি মিলতে হবে:

P(k) = [ (n−1)/n × (n−2)/(n−1) × … × (n−k)/(n−k+1) ] × 1/(n−k) = (n−k)/n × 1/(n−k) = 1/n
গুণফলটি “টেলিস্কোপ” হয়ে কেটে যায় — প্রতিটি endpoint ঠিক 1/n সম্ভাবনা পায়

তিনটি endpoint-এ তাই নিয়ম তিনটির সম্ভাবনা ০.৩৩৩, ০.৫, ১. খেয়াল করুন এই বাছাই হয় প্রতিটি নতুন সংযোগের প্রথম packet-এ; পরের packet-গুলো conntrack-এর সারি অনুসরণ করে। তাই একটি দীর্ঘ HTTP/2 বা gRPC সংযোগ সবসময় একটি Pod-এই আটকে থাকে — L4 load balancing-এর একটি পরিচিত সীমা।

৮ · নাম, L7 আর নিয়ম — CoreDNS, Ingress আর NetworkPolicy

CoreDNS আর Service-এর নাম

Cluster-এর ভেতরে DNS চালায় CoreDNS (নিজেও একটি Service-এর পেছনে কয়েকটি Pod). প্রতিটি Service একটি নাম পায়: <service>.<namespace>.svc.cluster.local — যেমন api.shop.svc.cluster.local (শেষের cluster.local ডিফল্ট, বদলানো যায়)। Pod-এর /etc/resolv.conf-এ CoreDNS-এর ClusterIP আর কয়েকটি search domain বসানো থাকে, তাই একই namespace থেকে শুধু api আর অন্য namespace থেকে api.shop লিখলেই চলে। (search domain অনেক হওয়ায় বাইরের নাম খুঁজতে কয়েকটি বাড়তি প্রশ্ন যায় — ndots সেটিংয়ের পরিচিত একটি পারফরম্যান্স-বিষয়। DNS-এর ভিত্তি পর্ব ৩৫-এ।)

L7-এর দরজা — Ingress আর Gateway API

ClusterIP আর NodePort হলো L4: শুধু ঠিকানা আর port দেখে। কিন্তু একই public ঠিকানায় shop.example.com/api এক Service-এ আর /images আরেকটিতে পাঠাতে, TLS শেষ করতে, header দেখে সিদ্ধান্ত নিতে লাগে একটি L7 reverse proxy. Kubernetes-এ এর জন্য Ingress resource আর একটি Ingress controller (যেমন NGINX-ভিত্তিক বা Envoy-ভিত্তিক) ব্যবহার হতো; নতুন এবং আরও সমৃদ্ধ মান Gateway API, যেখানে কাঠামো বসানো (Gateway) আর route লেখা (HTTPRoute) আলাদা দলের হাতে রাখা যায়। L7 load balancing-এর ভেতরের কথা পর্ব ৫২-এ।

NetworkPolicy — Pod-এর firewall, আর সবচেয়ে প্রচলিত ফাঁদ

ডিফল্টভাবে Kubernetes-এ সব Pod সব Pod-এর সাথে কথা বলতে পারে — একটি সমতল নেটওয়ার্ক। NetworkPolicy label দিয়ে নিয়ম বসায়। নিয়মটা মনে রাখার মতো:

মূল নিয়ম

কোনো Pod যতক্ষণ কোনো policy-তে বাছাই (select) হয়নি, ততক্ষণ সব খোলা। একবার কোনো policy তাকে Ingress দিকের জন্য বাছাই করলে, সেই দিকের সব ট্রাফিক বন্ধ — শুধু policy-গুলোতে যা স্পষ্টভাবে অনুমোদিত, তা ছাড়া। একাধিক policy থাকলে তাদের allow-গুলো যোগ হয় (union); NetworkPolicy-তে “deny” নিয়ম বলে কিছু নেই। Egress-এর জন্যও একই কথা, আলাদাভাবে।

ফাঁদটা হলো: “db-তে শুধু api ঢুকুক” লিখে কেউ ভাবেন বাকি সব আগের মতো আছে — অথচ db-র monitoring agent, backup job সবাই এখন আটকে গেল। উল্টো ফাঁদ: কোনো policy না লিখে ভাবা “Kubernetes নিরাপদ”. আরেকটি বিষয়: NetworkPolicy প্রয়োগ করে CNI plugin; যে plugin সমর্থন করে না (যেমন শুধু Flannel), সেখানে policy লিখলেও কোনো প্রভাব পড়ে না — কোনো ত্রুটিও দেখায় না।

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: db-from-api
  namespace: shop
spec:
  podSelector:
    matchLabels:
      app: db            # এই Pod-গুলো এখন "selected"
  policyTypes: ["Ingress"]
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: api       # একই namespace-এর api Pod-গুলো
    ports:
    - protocol: TCP
      port: 5432

নিচের simulator দুটি অংশে: প্রথমে kube-proxy-র iptables-ধাঁচের ClusterIP — তিনটি endpoint, ৩০০টি নতুন সংযোগ, তারপর একটি Pod readiness পরীক্ষায় ব্যর্থ হয়ে endpoint-তালিকা থেকে বাদ; দ্বিতীয়ত উপরের YAML-এর মতো একটি policy দিয়ে চারটি সংযোগের সিদ্ধান্ত। এলোমেলো সংখ্যা একটি নির্দিষ্ট seed থেকে, তাই প্রতিবার একই ফল।

// kube-proxy (iptables mode) style ClusterIP DNAT + a NetworkPolicy check. Seeded => same output every run.
let seed = 54; const rnd = () => (seed = (seed * 16807) % 2147483647) / 2147483647;   // Park-Miller
const svc = { name: "api", vip: "10.96.0.20:80", eps: [
  { ip: "10.244.1.5:8080", node: "A", ready: true }, { ip: "10.244.2.7:8080", node: "B", ready: true },
  { ip: "10.244.2.8:8080", node: "B", ready: true }] };
function rules() {                       // rule i matches with probability 1/(n-i): uniform overall
  const ready = svc.eps.filter(e => e.ready);
  return ready.map((e, i) => ({ to: e.ip, p: 1 / (ready.length - i) }));
}
function connect(k) {                    // only the FIRST packet of a connection walks the rules
  const R = rules(), counts = Object.fromEntries(R.map(r => [r.to, 0]));
  console.log(`rules for ${svc.vip}: ` + R.map(r => `${r.to} p=${r.p.toFixed(3)}`).join(" | "));
  for (let c = 0; c < k; c++) { const hit = R.find(r => rnd() < r.p); counts[hit.to]++; }
  console.log(`  ${k} new connections -> DNAT:`, JSON.stringify(counts));
}
connect(300);
svc.eps[1].ready = false;                // readiness probe fails: endpoint leaves the EndpointSlice
console.log("pod 10.244.2.7 not ready -> removed from endpoints, rules rewritten");
connect(300);
// NetworkPolicy: once a pod is selected by a policy for Ingress, everything not listed is denied.
const pods = { web: { app: "web" }, api: { app: "api" }, db: { app: "db" } };
const policies = [{ name: "db-from-api", select: { app: "db" }, from: { app: "api" }, port: 5432 }];
const match = (sel, labels) => Object.entries(sel).every(([k, v]) => labels[k] === v);
function ingress(src, dst, port) {
  const sel = policies.filter(p => match(p.select, pods[dst]));
  if (!sel.length) return console.log(`${src} -> ${dst}:${port}  ALLOW (no policy selects ${dst}: default allow)`);
  const ok = sel.find(p => match(p.from, pods[src]) && p.port === port);
  console.log(`${src} -> ${dst}:${port}  ${ok ? "ALLOW by " + ok.name : "DENY (" + dst + " is selected; nothing allows this)"}`);
}
console.log("");
ingress("api", "db", 5432); ingress("web", "db", 5432); ingress("api", "db", 22); ingress("web", "api", 8080);

আউটপুট:

rules for 10.96.0.20:80: 10.244.1.5:8080 p=0.333 | 10.244.2.7:8080 p=0.500 | 10.244.2.8:8080 p=1.000
  300 new connections -> DNAT: {"10.244.1.5:8080":106,"10.244.2.7:8080":97,"10.244.2.8:8080":97}
pod 10.244.2.7 not ready -> removed from endpoints, rules rewritten
rules for 10.96.0.20:80: 10.244.1.5:8080 p=0.500 | 10.244.2.8:8080 p=1.000
  300 new connections -> DNAT: {"10.244.1.5:8080":149,"10.244.2.8:8080":151}

api -> db:5432  ALLOW by db-from-api
web -> db:5432  DENY (db is selected; nothing allows this)
api -> db:22  DENY (db is selected; nothing allows this)
web -> api:8080  ALLOW (no policy selects api: default allow)

ClusterIP: নিয়মের সম্ভাবনা ০.৩৩৩, ০.৫০০, ১.০০০ — অংশ ৭-এর প্রমাণ অনুযায়ী প্রত্যেকের ভাগ ১/৩. ৩০০ সংযোগে ১০৬, ৯৭, ৯৭ — প্রত্যাশিত ১০০-র কাছাকাছি; এলোমেলো বাছাইয়ে ছোট বিচ্যুতি স্বাভাবিক। তারপর 10.244.2.7-এর readiness probe ব্যর্থ হলো: Kubernetes তাকে endpoint-তালিকা (EndpointSlice) থেকে সরাল, kube-proxy নিয়ম নতুন করে লিখল — এখন দুটি নিয়ম, ০.৫ আর ১. নতুন ৩০০ সংযোগ ভাগ হলো ১৪৯ আর ১৫১-এ; অসুস্থ Pod একটিও পেল না। Pod-টি মরেনি, শুধু “ট্রাফিক নেওয়ার জন্য প্রস্তুত নয়” — সুস্থ হলে আবার তালিকায় ফিরবে।

NetworkPolicy: api → db:5432 অনুমোদিত (policy-র from আর port দুটোই মিলেছে)। web → db:5432 বন্ধ — web-এর কোনো নিষেধ লেখা নেই, তবু বন্ধ, কারণ db এখন “selected”. api → db:22-ও বন্ধ, কারণ port মেলেনি। আর web → api:8080 খোলা, কারণ কোনো policy api-কে বাছাই করেনি — ডিফল্ট allow. মূল নিয়মের চারটি দিকই এখানে দেখা গেল।

৯ · Service mesh, overlay-এর MTU আর সমস্যা খোঁজা

Service mesh — প্রতিটি Pod-এর পাশে একজন দারোয়ান

Microservice-এ শত শত সেবা একে অপরকে ডাকে। প্রতিটির কোডে retry, timeout, এনক্রিপশন আর মাপজোখ আলাদা করে লেখা কঠিন। Service mesh এগুলো কোডের বাইরে আনে: প্রতিটি Pod-এ একটি sidecar proxy (প্রায়ই Envoy) বসানো হয়, আর Pod-এর সব ট্রাফিক netfilter-এর নিয়মে সেই proxy-র মধ্য দিয়ে যায়। দুই proxy নিজেদের মধ্যে mTLS চালায় (পর্ব ৪৮) — প্রতিটি সেবা certificate দেখিয়ে নিজের পরিচয় প্রমাণ করে, আর ট্রাফিক এনক্রিপ্টেড থাকে, এমনকি ভেতরের নেটওয়ার্কেও। নীতিও লেখা যায় সেবার পরিচয় দিয়ে: “শুধু orders সেবা payments-কে ডাকতে পারবে”. Istio আর Linkerd সবচেয়ে পরিচিত। দাম: প্রতিটি Pod-এ বাড়তি memory আর CPU, প্রতিটি অনুরোধে দুটি বাড়তি proxy-ধাপ, আর পরিচালনার জটিলতা। এজন্য সম্প্রতি sidecar ছাড়া নকশা এসেছে — যেমন Istio-র “ambient” mode, যেখানে প্রতিটি node-এ একটি ভাগ করা L4 proxy আর দরকার হলে আলাদা L7 proxy থাকে; এর পরিপক্বতা আর খুঁটিনাটি সংস্করণভেদে বদলাচ্ছে।

Overlay-এর MTU — চুপচাপ হারিয়ে যাওয়া বড় packet

অংশ ৭-এর stepper-এ দেখেছেন, VXLAN প্রতিটি packet-এ ৫০ byte যোগ করে: ভেতরের Ethernet header ১৪ + VXLAN ৮ + UDP ৮ + বাইরের IPv4 ২০ = ৫০ (পর্ব ১৩)। Underlay-এর MTU যদি ১,৫০০ হয়, তবে ভেতরের IP packet সর্বোচ্চ ১,৫০০ − ৫০ = ১,৪৫০ byte হতে পারে — এজন্যই Flannel-এর মতো VXLAN overlay-এ Pod-এর MTU সাধারণত ১,৪৫০. Underlay IPv6 হলে বাইরের header ৪০ byte, মোট ৭০, Pod-এর MTU ১,৪৩০. Pod-এর MTU ভুল করে ১,৫০০ থাকলে কী হয়? ছোট packet (ping, DNS, TCP handshake) দিব্যি চলে, কিন্তু বড় packet মোড়কের পর ১,৫৫০ হয়ে underlay-এ আটকায়। DF bit থাকলে ICMP “fragmentation needed” ফেরার কথা — কিন্তু সেটি কোনো firewall বা security group আটকালে প্রেরক কিছুই জানে না: সংযোগ খোলে, তারপর বড় response-এ ঝুলে থাকে। এই “PMTUD black hole”-এর গল্প পর্ব ৪৯-এর VPN অংশেও দেখেছেন। অনেক cloud-এ VPC-র ভেতরে jumbo frame (প্রায় ৯,০০০ byte MTU) চলে, যা overlay-এর চাপ কমায় — কিন্তু VPN বা ইন্টারনেটের দিকে গেলে আবার ছোট MTU.

সমস্যা খোঁজা — স্তর ধরে ধরে

“Pod থেকে api-তে যাওয়া যাচ্ছে না” — অনুমান না করে নিচ থেকে ওপরে উঠুন: নাম ঠিক হচ্ছে কি না, Service-এর পেছনে ready endpoint আছে কি না, সরাসরি Pod IP-তে যায় কি না, policy আটকাচ্ছে কি না, বড় packet-এ আটকায় কি না। পুরো পদ্ধতিগত তদন্ত পর্ব ৫৫-এ।

# ১. নাম ঠিক হচ্ছে? (Pod-এর ভেতর থেকে; image-এ nslookup থাকতে হবে)
$ kubectl -n shop exec deploy/web -- nslookup api
Server:    10.96.0.10
Address:   10.96.0.10:53

Name:    api.shop.svc.cluster.local
Address: 10.96.0.20

# ২. Service-এর পেছনে ready endpoint আছে? খালি হলে label বা readiness-এর সমস্যা
$ kubectl -n shop get endpointslices -l kubernetes.io/service-name=api
NAME        ADDRESSTYPE   PORTS   ENDPOINTS                AGE
api-7xk2p   IPv4          8080    10.244.1.5,10.244.2.8    3d

# ৩. Service বাদ দিয়ে সরাসরি Pod IP-তে (image-এ wget থাকলে)
$ kubectl -n shop exec deploy/web -- wget -qO- -T 3 http://10.244.2.8:8080/healthz
ok

# ৪. কোন policy এই namespace-এ আছে?
$ kubectl -n shop get networkpolicy

আরও গভীরে: বড় cloud প্রদানকারীদের virtual নেটওয়ার্কের কিছু নকশা গবেষণাপত্রে প্রকাশিত হয়েছে — যেমন Google-এর Andromeda (NSDI 2018) আর Microsoft Azure-এর host-এ চলা Virtual Filtering Platform বা VFP (NSDI 2017); দুটিতেই মূল ভাবনা হলো প্রতিটি সার্ভারের hypervisor-এ একটি প্রোগ্রামযোগ্য সফটওয়্যার switch, যাকে কেন্দ্রীয় controller নিয়ম দেয়, আর প্রায়ই এই কাজ বিশেষ network card-এ সরিয়ে নেওয়া হয়। Kubernetes-এ iptables mode-এর সমস্যা হলো নিয়মগুলো একটি লম্বা তালিকা — হাজারো Service-এ প্রতিটি নতুন সংযোগে অনেক নিয়ম ঘাঁটতে হয় আর হালনাগাদও ধীর; IPVS আর nftables mode hash-ভিত্তিক খোঁজায় এবং eBPF-ভিত্তিক বিকল্পগুলো kernel-এর আরও আগের ধাপে কাজ করে এই খরচ কমায়।

পরীক্ষা ও ইন্টারভিউতে এই বিষয়ে প্রায়ই যা জিজ্ঞেস করা হয়:

  • Security group আর network ACL-এর পার্থক্য? — SG প্রতিটি যন্ত্রে, stateful, শুধু allow; NACL পুরো subnet-এ, stateless, ক্রমিক allow/deny — ফিরতি ট্রাফিকের নিয়মও লাগে।
  • Public আর private subnet কীভাবে আলাদা? — route table-এ 0.0.0.0/0 → internet gateway থাকলে public; private-এর বাইরে যাওয়া NAT gateway দিয়ে, যা বাইরে থেকে নতুন সংযোগ ঢুকতে দেয় না।
  • docker run -p 8080:80 ভেতরে কী করে? — host-এর netfilter-এ DNAT নিয়ম: host:8080 → container-IP:80; container বাইরে যায় masquerade দিয়ে, docker0 bridge আর veth হয়ে।
  • ClusterIP-তে ping কেন উত্তর দেয় না? — প্রচলিত iptables mode-এ এটি কোনো interface-এ বসানো ঠিকানা নয়; kube-proxy-র নিয়মে শুধু Service-এর port-এ DNAT হয় (IPVS mode-এ ব্যতিক্রম হতে পারে)।
  • VXLAN overlay-এ Pod-এর MTU ১,৪৫০ কেন? — মোড়কে ৫০ byte (১৪ + ৮ + ৮ + ২০) যোগ হয়; underlay ১,৫০০ হলে ভেতরের packet ১,৪৫০-এর বেশি হতে পারে না।

১০ · AI-এর যুগে cloud আর container নেটওয়ার্কিং

Terraform-এর VPC, Docker Compose-এর নেটওয়ার্ক, Kubernetes-এর YAML — AI মুহূর্তেই লিখে দেয়, আর দেখতে নিখুঁত লাগে। কিন্তু cloud-এ একটি ভুল নিয়ম মানে সরাসরি ইন্টারনেটে খোলা ডেটাবেস, আর একটি ভুল CIDR মানে মাস কয়েক পরে peering-এর অসম্ভব জট। চেনা ভুলগুলো:

AI-এর ভুলকেন ভুলকীভাবে ধরবেন
“কাজ না করলে security group-এ 0.0.0.0/0 সব port খুলে দিন”ডেটাবেস বা SSH পুরো ইন্টারনেটের সামনে; স্বয়ংক্রিয় স্ক্যানার মিনিটের মধ্যে খুঁজে পায়শুধু দরকারি port, উৎস হিসেবে আরেকটি SG বা নির্দিষ্ট CIDR; অংশ ৩-এর মতো flow ধরে যাচাই
প্রতিটি উদাহরণে VPC 10.0.0.0/16অফিস বা অন্য VPC-র সাথে overlap — পরে peering আর VPN অসম্ভবপ্রতিষ্ঠানের ঠিকানা-খাতা রাখুন; অংশ ৪-এর planner-এর মতো overlap পরীক্ষা
Subnet-এর হিসাবে ভুল (/20-এ ৪,০৯৬ ব্যবহারযোগ্য, বা ভুল সীমা)Cloud কিছু ঠিকানা রেখে দেয়; ভাষার মডেল হিসাব গুলিয়ে ফেলেনিজে bit গুনে বা ক্যালকুলেটরে মেলান (পর্ব ১৭)
AWS-এর নাম আর আচরণ সব cloud-এ সত্য বলে ধরা (যেমন “Azure-এ NACL বসান”, “GCP subnet একটি zone-এ”)প্রদানকারী ভেদে নকশা আলাদানিজের প্রদানকারীর অফিসিয়াল ডকুমেন্টেশন
“NetworkPolicy লিখলেই নিরাপদ”, বা একটি policy যোগ করে বাকি ট্রাফিক অক্ষত ভাবাCNI সমর্থন না করলে policy অকার্যকর; selected Pod-এর বাকি সব বন্ধTest namespace-এ policy বসিয়ে অনুমোদিত আর নিষিদ্ধ দুই সংযোগই চেষ্টা
পুরোনো কমান্ড (ifconfig, netstat, --link) বা সংস্করণ-না-মেলা APIআধুনিক Linux-এ ip/ss; Kubernetes API সংস্করণে বদলায়man ip, kubectl explain, নিজের cluster-এর সংস্করণের ডকুমেন্টেশন

AI-কে cloud নেটওয়ার্কের নকশা দিলে প্রেক্ষাপট আর নিরাপত্তার শর্ত স্পষ্ট করে দিন:

আমি [AWS / Azure / Google Cloud]-এ একটি ওয়েব অ্যাপের নেটওয়ার্ক নকশা করছি। আমাদের অফিসের নেটওয়ার্ক [CIDR এখানে লেখো], আর আরেকটি VPC [CIDR এখানে লেখো]। ১. এগুলোর কোনোটির সাথে overlap না করে একটি VPC CIDR প্রস্তাব করো, আর তিনটি AZ-এ public ও private subnet ভাগ করো। প্রতিটি subnet-এর পরিসর আর ব্যবহারযোগ্য ঠিকানার হিসাব ধাপে ধাপে দেখাও। ২. শুধু load balancer আর NAT gateway public subnet-এ থাকবে; ডেটাবেসে শুধু app সার্ভারের security group থেকে port 5432. ৩. প্রতিটি security group আর network ACL-এর নিয়ম টেবিলে দাও; কোথাও 0.0.0.0/0 থাকলে কেন, আলাদা করে বলো। ৪. আমার প্রদানকারীতে যে নাম আর ডিফল্ট আলাদা, সেগুলো চিহ্নিত করো; নিশ্চিত না হলে স্পষ্ট বলো। ৫. Kubernetes থাকলে Pod CIDR আর Service CIDR কোথা থেকে নেবে, আর VPC-র সাথে overlap হবে কি না, সেটাও দেখাও।

AI-এর নেটওয়ার্ক-নকশা প্রয়োগের আগে — চেকলিস্ট

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।

প্র ০১Cloud প্রদানকারীরা গ্রাহকদের আলাদা রাখতে VLAN ব্যবহার না করে কেন overlay আর SDN-এর পথে গেল?

প্রথম কারণটা সংখ্যার। 802.1Q VLAN-এর ID ১২ bit — ব্যবহারযোগ্য ৪,০৯৪টি (পর্ব ১৩)। একটি বড় cloud-এ লাখো গ্রাহক, আর প্রত্যেকের কয়েকটি করে নেটওয়ার্ক; VLAN-এর সংখ্যা প্রথম দিনেই ফুরিয়ে যেত। দ্বিতীয় কারণ গঠন: VLAN একটি layer-2 ধারণা, যা switch-এর পর switch জুড়ে ছড়াতে হয়। হাজারো rack-এর data center-এ বিশাল layer-2 domain মানে broadcast-এর ঝড়, spanning tree-র সীমা, আর একটি ভুলে বিশাল এলাকা অচল। আধুনিক data center তাই underlay-কে সরল layer-3 নেটওয়ার্ক রাখে — leaf-spine আর ECMP (পর্ব ৫২) — আর গ্রাহকের নেটওয়ার্ককে মোড়কে করে তার ওপর দিয়ে চালায়।

তৃতীয় কারণ গতি আর স্বাধীনতা। একজন গ্রাহক যখন API ডেকে নতুন subnet বা route বানান, physical switch-এ কারও কনফিগারেশন বদলানো অসম্ভব রকম ধীর আর ঝুঁকিপূর্ণ হতো। Overlay-তে বদল ঘটে শুধু সফটওয়্যারে — controller hypervisor-গুলোতে নতুন টেবিল পাঠায়। VM এক সার্ভার থেকে আরেকটিতে সরলেও তার ঠিকানা একই থাকে, কারণ ঠিকানা আর আসল অবস্থান আলাদা করা হয়েছে।

এর ঐতিহাসিক পটভূমি আকর্ষণীয়। ২০০০-এর দশকের শেষে Nicira-র মতো প্রতিষ্ঠান (২০১২ সালে VMware কিনে নেয়) hypervisor-এ সফটওয়্যার switch আর কেন্দ্রীয় controller দিয়ে network virtualization-এর ধারণা বাস্তবে আনে; Open vSwitch এই ধারার একটি উন্মুক্ত ফল। দাম আছে: প্রতিটি packet-এ মোড়কের বাড়তি byte (MTU-র ঝামেলা), hypervisor-এ CPU খরচ (যা কমাতে বিশেষ network card), আর সমস্যা খুঁজতে দুই স্তর — overlay আর underlay — দুটোই বুঝতে হয়।

মূল উপলব্ধি: ঠিকানা আর অবস্থানকে আলাদা করলে লাখো গ্রাহককে একই physical নেটওয়ার্কে নিরাপদে, দ্রুত আর স্বাধীনভাবে রাখা যায়। VLAN সমস্যাটা ছোট পরিসরে সমাধান করেছিল; overlay আর SDN সেটাকে cloud-এর মাপে নিয়ে গেছে।

প্র ০২Security group-এ উৎস হিসেবে আরেকটি security group লেখা যায়, আর Kubernetes-এর NetworkPolicy লেখা হয় label দিয়ে। IP ঠিকানা-ভিত্তিক নিয়ম থেকে পরিচয়-ভিত্তিক নিয়মের দিকে এই যাত্রা কেন?

প্রথাগত firewall-এর নিয়ম লেখা হতো ঠিকানায়: “10.1.2.0/24 থেকে 10.1.5.10-এর port 5432-এ যাওয়া যাবে”. এটা কাজ করত যখন সার্ভার বছরের পর বছর একই ঠিকানায় থাকত। Cloud আর container-এর জগতে সেই অনুমান ভেঙে গেছে। Auto scaling-এ ওয়েব সার্ভারের সংখ্যা সকালে ৪, সন্ধ্যায় ৪০; প্রতিটির ঠিকানা নতুন। ঠিকানা-ভিত্তিক নিয়ম হয় অতি চওড়া (“পুরো subnet খোলা”), নয়তো সারাক্ষণ হালনাগাদ করতে হয় — আর হালনাগাদে দেরি মানে হয় বিভ্রাট, নয় নিরাপত্তার ফুটো।

পরিচয়-ভিত্তিক নিয়ম প্রশ্নটা বদলে দেয়: “এই packet কোন ঠিকানা থেকে” নয়, “এটি কোন ভূমিকার সেবা থেকে”. Security group-এর সদস্যপদ, Kubernetes-এর label, service mesh-এর certificate-এ লেখা সেবার পরিচয় — এগুলো কাজের সাথে যায়, ঠিকানার সাথে নয়।

এটি বড় একটি দর্শনের অংশ — zero trust: নেটওয়ার্কের ভেতরে থাকাটাই বিশ্বাসের কারণ নয়, প্রতিটি সংযোগে পরিচয় যাচাই হবে। Google ২০১৪ সাল থেকে BeyondCorp নামে এই ধারণায় নিজের অভ্যন্তরীণ ব্যবস্থা নিয়ে প্রবন্ধ প্রকাশ করে। তবে ঝুঁকিও আছে: label যে-কেউ বসাতে পারলে পরিচয় জাল করা সহজ — তাই কে কোন label বা SG বসাতে পারে, সেই অনুমতি-নিয়ন্ত্রণ (RBAC, IAM) সমান গুরুত্বপূর্ণ। আর certificate-ভিত্তিক পরিচয় (mTLS) label-এর চেয়ে শক্ত, কারণ তা গোপন key-এর ওপর দাঁড়ানো।

মূল উপলব্ধি: ঠিকানা যখন ক্ষণস্থায়ী, তখন নিরাপত্তার নিয়ম স্থায়ী জিনিসে বাঁধতে হয় — ভূমিকা আর পরিচয়ে। কিন্তু সেই পরিচয় কে দিতে পারে, সেটা পাহারা দেওয়াই তখন নিরাপত্তার নতুন কেন্দ্র।

প্র ০৩Docker-এর শুরুর মডেল ছিল host-এর ঠিকানা আর port mapping, অথচ Kubernetes প্রতিটি Pod-কে নিজের IP দিল আর Pod-এ Pod-এ NAT নিষেধ করল। এই সিদ্ধান্তের পেছনের যুক্তি আর দাম কী?

Docker ২০১৩ সালে এসে container-কে জনপ্রিয় করে একটি সরল মডেলে: container পায় একটি private ঠিকানা, বাইরে থেকে পৌঁছাতে host-এর একটি port publish করতে হয়। কিন্তু অনেক যন্ত্রে সমস্যা দেখা দেয়: দুটি container একই port 80 চাইলে host-এ একটিই পাবে, তাই port বরাদ্দের হিসাব রাখতে হয়; অন্য container-কে বলতে হয় “আমাকে host X-এর port 31847-এ পাবেন” — আর অ্যাপ্লিকেশন নিজে যে ঠিকানা জানে, বাইরের লোক তাকে অন্য ঠিকানায় দেখে।

Google-এর ভেতরের Borg ব্যবস্থায় ঠিক এই অভিজ্ঞতা ছিল: সব কাজ host-এর ঠিকানা ভাগ করত, আর port বরাদ্দ ছিল scheduler-এর একটি জটিল দায়িত্ব। Kubernetes-এর নকশাকারীরা তাই সিদ্ধান্ত নেন প্রতিটি Pod নিজের ঠিকানা পাবে, যেন একটি আলাদা ছোট VM — পুরোনো অ্যাপ্লিকেশন বদল ছাড়াই চলে, নিজের পরিচিত port-এ শোনে, আর যে ঠিকানা সে নিজে দেখে, অন্যরাও তাকে সেই ঠিকানায় দেখে। NAT না থাকায় log-এ, নীতিতে আর সমস্যা খোঁজায় আসল উৎস দেখা যায়।

দাম হলো ঠিকানা আর নেটওয়ার্কের জটিলতা। হাজারো Pod মানে হাজারো ঠিকানা, আর প্রতিটি node-এর Pod-subnet-কে পুরো cluster-এ route করাতে হয় — overlay দিয়ে, BGP দিয়ে, বা cloud-এর route table দিয়ে। Cloud-এর নিজস্ব CNI-তে Pod সরাসরি VPC-র ঠিকানা নিলে VPC-র ঠিকানা দ্রুত ফুরোয়, তখন বড় ব্লক বা ঠিকানা ভাগাভাগির কৌশল লাগে। এই চাপ IPv6-কেও সামনে এনেছে: Kubernetes-এ IPv4/IPv6 dual-stack স্থিতিশীল বৈশিষ্ট্য হিসেবে এসেছে, আর IPv6-এ ঠিকানার অভাব প্রায় অপ্রাসঙ্গিক।

মূল উপলব্ধি: Kubernetes জটিলতা অ্যাপ্লিকেশন থেকে সরিয়ে নেটওয়ার্কে রেখেছে — প্রতিটি অ্যাপ সরল থাকে, কিন্তু নেটওয়ার্ককে আরও বুদ্ধিমান হতে হয়। এই বদলটাই CNI plugin-এর এক পুরো শিল্প তৈরি করেছে।

প্র ০৪Service mesh প্রতিটি সেবার মাঝে mTLS, retry আর মাপজোখ এনে দেয়। তবু অনেক দল mesh গ্রহণ করতে দ্বিধা করে, আর sidecar ছাড়া নকশা আসছে। লাভ-ক্ষতির হিসাবটা কী?

“Service mesh” শব্দটি জনপ্রিয় হয় ২০১৬-১৭ সালে, Linkerd আর পরে Istio-র হাত ধরে; Istio-র data plane হিসেবে Lyft-এর বানানো Envoy proxy ব্যবহৃত হয়। প্রতিশ্রুতি বড়: অ্যাপ্লিকেশনের কোডে হাত না দিয়েই সব সেবার মধ্যে এনক্রিপশন আর পারস্পরিক পরিচয় যাচাই (mTLS, পর্ব ৪৮), certificate-এর স্বয়ংক্রিয় নবায়ন, retry আর timeout-এর একরকম নীতি, ধাপে ধাপে নতুন সংস্করণে ট্রাফিক সরানো, আর প্রতিটি অনুরোধের latency ও ত্রুটির মাপ।

দামগুলোও বাস্তব। প্রতিটি Pod-এ একটি sidecar মানে হাজারো proxy — প্রত্যেকে memory আর CPU খায়। প্রতিটি অনুরোধ দুটি বাড়তি proxy পার হয় (প্রেরকের আর প্রাপকের), তাই latency কিছুটা বাড়ে। আর সবচেয়ে বড় খরচ মানুষের: mesh-এর নিজস্ব ধারণা, কনফিগারেশন আর ত্রুটির ধরন শিখতে হয়; একটি ভুল নীতি পুরো সিস্টেমের সব যোগাযোগ বন্ধ করতে পারে।

এই চাপ থেকেই sidecar-বিহীন নকশা: Istio-র ambient mode প্রতিটি node-এ একটি ভাগ করা L4 proxy রাখে (mTLS আর L4 নীতির জন্য), আর L7 বৈশিষ্ট্য দরকার হলে আলাদা “waypoint” proxy; Cilium eBPF দিয়ে kernel-এ কিছু কাজ সরিয়ে আনে। এগুলো সম্পদ বাঁচায়, তবে নিরাপত্তার সীমানা নিয়ে নতুন প্রশ্ন তোলে — node-এর ভাগ করা proxy অনেক Pod-এর key ধরে রাখে কি না, আর তার একটি ত্রুটির প্রভাব কতদূর। এসব নকশা এখনো দ্রুত বদলাচ্ছে।

মূল উপলব্ধি: Mesh নেটওয়ার্ককে নিরাপত্তা আর পর্যবেক্ষণের একটি প্ল্যাটফর্মে পরিণত করে, কিন্তু জটিলতা মুছে ফেলে না — শুধু সরিয়ে রাখে। প্রশ্ন সবসময়: আমাদের মাপে কোন সমস্যাটি সত্যি আছে, আর তার জন্য কতটা জটিলতা নিতে রাজি?

প্র ০৫“ছোট request চলে, বড় response আটকে যায়” — overlay নেটওয়ার্কে এই ক্লাসিক সমস্যা কেন হয়, আর overlay ছাড়া (যেমন BGP দিয়ে Pod-এর route) নকশা কখন ভালো?

লক্ষণটা প্রায় সবসময় MTU-র। Overlay প্রতিটি packet-এ মোড়ক যোগ করে — VXLAN-এ ৫০ byte, WireGuard-এর মতো এনক্রিপ্টেড tunnel-এ আরও বেশি। Pod-এর interface-এর MTU যদি underlay-এর সমান রাখা হয়, তবে পূর্ণ আকারের packet মোড়কের পর underlay-এর সীমা ছাড়ায়। TCP handshake, ছোট HTTP request, DNS — সবই ছোট, তাই চলে। কিন্তু সার্ভার যখন বড় response পাঠায়, পূর্ণ আকারের segment underlay-এ আটকায়। IPv4-এ DF bit থাকলে router ICMP “fragmentation needed” পাঠানোর কথা, আর প্রেরক তখন ছোট করে পাঠায় (PMTUD). কিন্তু সেই ICMP যদি কোনো firewall, security group বা ভুল নিয়মে আটকায়, প্রেরক কিছুই জানে না: সংযোগ খোলা, অথচ ডেটা আসে না। সমাধান: Pod-এর MTU ঠিক রাখা (অধিকাংশ CNI স্বয়ংক্রিয়ভাবে করে, কিন্তু underlay নিজেই কোনো tunnel-এর ভেতরে থাকলে ভুল হিসাব হয়), MSS clamping, আর দরকারি ICMP-কে অনুমতি দেওয়া।

Overlay ছাড়া নকশায় এই সমস্যা থাকে না। Calico-র প্রচলিত mode-এ প্রতিটি node BGP দিয়ে নিজের Pod-subnet ঘোষণা করে, আর data center-এর router সরাসরি Pod-এর ঠিকানায় route করে — কোনো মোড়ক নেই, MTU পূর্ণ, CPU-র মোড়ক-খরচ নেই, আর packet capture-এ সরাসরি Pod-এর ঠিকানা দেখা যায়। Cloud-এর নিজস্ব CNI-ও একইভাবে VPC-র route ব্যবহার করে। কিন্তু এর শর্ত হলো underlay-কে Pod-এর ঠিকানা জানতে আর মানতে হবে — নেটওয়ার্ক দলের সাথে BGP-এর সমন্বয়, router-এর route-সংখ্যার সীমা, আর cloud-এ প্রায়ই VPC-র source/destination যাচাই বন্ধ করার মতো বিশেষ ব্যবস্থা।

Overlay তাই সেখানে জেতে, যেখানে underlay আপনার নিয়ন্ত্রণে নেই বা বদলাতে চান না — যেকোনো নেটওয়ার্কে বসিয়ে দিলেই চলে। Overlay-বিহীন নকশা জেতে বড়, গতি-সংবেদনশীল cluster-এ, যেখানে নেটওয়ার্ক দল ও Kubernetes দল ঘনিষ্ঠভাবে কাজ করে।

মূল উপলব্ধি: প্রতিটি মোড়ক একটি সুবিধা কেনে (স্বাধীনতা) আর একটি দাম চাপায় (MTU, CPU, দৃশ্যমানতা)। “ছোট চলে, বড় আটকায়” দেখলেই প্রথম সন্দেহ MTU আর হারিয়ে যাওয়া ICMP.

অনুশীলন

খাতা-কলম নিন — ঠিকানা, নিয়ম আর byte গুনে হিসাব করুন।

  1. হিসাব (CIDR পরিকল্পনা): একটি VPC 10.30.0.0/16. তিনটি AZ-এ public আর private মিলিয়ে ছয়টি subnet লাগবে, প্রতিটি /19, পরপর বসানো (আগে তিনটি public, তারপর তিনটি private). (ক) ছয়টির CIDR লিখুন। (খ) প্রতিটিতে AWS-এর হিসাবে কতটি ব্যবহারযোগ্য ঠিকানা? (গ) কত ঠিকানা আর কতটি /19 ফাঁকা থাকে? (ঘ) অফিসের নেটওয়ার্ক 10.30.192.0/20 হলে সমস্যা আছে কি?

    (ক) /16 থেকে /19 = ৩ bit ধার, block size তৃতীয় octet-এ ৩২: public-a 10.30.0.0/19, public-b 10.30.32.0/19, public-c 10.30.64.0/19, private-a 10.30.96.0/19, private-b 10.30.128.0/19, private-c 10.30.160.0/19. (খ) প্রতিটি ২১৩ = ৮,১৯২ ঠিকানা, ব্যবহারযোগ্য ৮,১৯২ − ৫ = ৮,১৮৭. (গ) ব্যবহৃত ৬ × ৮,১৯২ = ৪৯,১৫২; ফাঁকা ৬৫,৫৩৬ − ৪৯,১৫২ = ১৬,৩৮৪ = 10.30.192.0 থেকে 10.30.255.255, অর্থাৎ আরও ২টি /19.

    (ঘ) হ্যাঁ। 10.30.192.0/20 কোনো subnet-এর সাথে মেলে না, কিন্তু VPC-র পরিসরের ভেতরে পড়ে — VPC-র local route পুরো 10.30.0.0/16 ঢেকে রাখে, তাই অফিসের দিকে VPN route সাধারণত কাজ করবে না, আর ভবিষ্যতে ওই ফাঁকা অংশে subnet বানানোও যাবে না। অংশ ৪-এর planner-এ VPC আর NEW বদলে চালালে ঠিক এই ছয়টি সারি, “free: 10.30.192.0 .. 10.30.255.255 = 16384 (2 more /19)” আর অফিসের জন্য “OVERLAP (inside VPC range, no subnet yet)” ছাপা হয়। ভালো সমাধান: শুরুতেই অফিস থেকে দূরের পরিসর বাছা, যেমন 10.40.0.0/16.

  2. আউটপুট অনুমান করুন: অংশ ৩-এর VPC simulator-এ private subnet-এর NACL-এর inbound তালিকা থেকে শুধু rule 110 ([110, 1024, 65535, "0.0.0.0/0"]) মুছে ফেলা হলো। “db -> internet (OS updates)” flow-এর আউটপুট কেমন হবে? “web -> db” কি বদলাবে?
    # db -> internet (OS updates): 10.20.48.20:43210 -> 198.51.100.80:443
      route table priv: 198.51.100.80 matches 0.0.0.0/0 -> nat-gw
      ok   NACL priv/out rule 100 allow
      ok   SG db outbound: allow all
      ok   NAT GW: SNAT src -> 203.0.113.50 (+ its own port), remembers the flow
      FAIL NACL priv/in rule * DENY
      => BLOCKED

    অনুরোধ বেরিয়ে যায়, NAT gateway-ও ঠিকঠাক কাজ করে, কিন্তু উত্তর (উৎস port 443, গন্তব্য db-র ephemeral port 43210) private subnet-এ ঢুকতে গিয়ে আর কোনো allow নিয়ম পায় না — rule 100 শুধু 5432 আর শুধু VPC থেকে। NACL stateless, তাই “আমিই তো অনুরোধ পাঠিয়েছিলাম” মনে রাখে না। বাস্তবে লক্ষণ: apt update বা curl ঝুলে থেকে timeout. “web -> db” বদলায় না: সেখানে inbound-এ rule 100 (5432 from VPC) মেলে, আর ফিরতি পথ যায় private-এর outbound rule 110 দিয়ে, যা অক্ষত।

  3. কমান্ড আর YAML লিখুন: namespace shop-এ app: db label-এর Pod-গুলোতে শুধু app: api থেকে TCP 5432 আসতে দিতে হবে, আর app: monitor থেকে TCP 9187 (metrics). (ক) একটি NetworkPolicy লিখুন। (খ) policy বসানোর পর যাচাইয়ের জন্য দুটি সংযোগ-পরীক্ষা কী হবে? (গ) monitor-এর অংশটি ভুলে গেলে কী ঘটত?
    # উদাহরণ — একটি policy, দুটি ingress নিয়ম (allow-গুলো যোগ হয়)
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata: { name: db-ingress, namespace: shop }
    spec:
      podSelector: { matchLabels: { app: db } }
      policyTypes: ["Ingress"]
      ingress:
      - from: [ { podSelector: { matchLabels: { app: api } } } ]
        ports: [ { protocol: TCP, port: 5432 } ]
      - from: [ { podSelector: { matchLabels: { app: monitor } } } ]
        ports: [ { protocol: TCP, port: 9187 } ]

    (খ) অনুমোদিত পথ: api Pod থেকে db-র 5432-এ সংযোগ সফল হওয়া উচিত; নিষিদ্ধ পথ: web Pod (বা api থেকে 9187) থেকে চেষ্টা ব্যর্থ বা timeout হওয়া উচিত। দুটোই পরীক্ষা জরুরি — শুধু সফলটা দেখলে policy আদৌ প্রয়োগ হচ্ছে কি না বোঝা যায় না (CNI policy সমর্থন না করলে সবই সফল হবে)। (গ) db “selected” হয়ে যাওয়ায় monitoring-এর ট্রাফিক চুপচাপ বন্ধ হয়ে যেত — dashboard-এ db “অদৃশ্য”, যা অনেক সময় দেরিতে ধরা পড়ে। এটাই “selected ⇒ default deny” ফাঁদ।

  4. হাতে হিসাব (MTU আর সম্ভাবনা): (ক) underlay MTU ১,৫০০, IPv4; VXLAN overlay-এ Pod-এর সর্বোচ্চ MTU কত? underlay IPv6 হলে? (খ) underlay-এ jumbo frame (MTU ৯,০০১) থাকলে IPv4 VXLAN-এ Pod-এর MTU কত হতে পারে? (গ) একটি Service-এর ৪টি ready endpoint; kube-proxy-র iptables-ধাঁচের নিয়মে চারটি নিয়মের সম্ভাবনা কী, আর তৃতীয় endpoint বাছাইয়ের সম্ভাবনা হিসাব করে দেখান যে তা ১/৪.

    (ক) মোড়ক = ভেতরের Ethernet ১৪ + VXLAN ৮ + UDP ৮ + বাইরের IPv4 ২০ = ৫০; ১,৫০০ − ৫০ = ১,৪৫০. IPv6 underlay-এ বাইরের header ৪০, মোড়ক ৭০; ১,৫০০ − ৭০ = ১,৪৩০. (খ) ৯,০০১ − ৫০ = ৮,৯৫১ — যদিও বাস্তবে Pod-এর MTU প্রায়ই সাধারণ ১,৫০০-এর কাছাকাছি রাখা হয়, কারণ ইন্টারনেট বা VPN-এর দিকে গেলে বড় packet আবার ছোট করতে হবে।

    (গ) n = ৪: নিয়মের সম্ভাবনা ১/৪, ১/৩, ১/২, ১. তৃতীয় endpoint (k = ২, শূন্য থেকে গোনা) বাছাই হতে প্রথম দুটি না মিলে তৃতীয়টি মিলতে হবে: (১ − ১/৪) × (১ − ১/৩) × ১/২ = ৩/৪ × ২/৩ × ১/২ = ৬/২৪ = ১/৪. প্রথমটির জন্য ১/৪, দ্বিতীয়টির ৩/৪ × ১/৩ = ১/৪, চতুর্থটির ৩/৪ × ২/৩ × ১/২ × ১ = ১/৪ — চারটিই সমান, যোগফল ১.

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমKubernetes-এ node A-র একটি Pod একটি Service-এ সংযোগ খুলছে, আর বাছাই হওয়া endpoint আছে node B-তে (VXLAN overlay). ঘটনাগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. CoreDNS Service-এর নামকে ClusterIP-তে রূপান্তর করে
  2. Node A-র netfilter-এ kube-proxy-র নিয়ম একটি ready endpoint বেছে DNAT করে
  3. Node A packet-টি VXLAN-এ মুড়ে node B-র ঠিকানায় পাঠায়
  4. Node B মোড়ক খুলে bridge আর veth দিয়ে endpoint Pod-এ দেয়
  5. উত্তর node A-তে ফিরলে conntrack উৎসকে আবার ClusterIP বানায়

একটি AWS-ধরনের VPC-র subnet-এর route table-এ আছে 10.20.0.0/16 → local আর 0.0.0.0/0 → nat-gw. এই subnet সম্পর্কে কোনটি ঠিক?

  • এটি একটি public subnet; এর যন্ত্রে বাইরে থেকে সরাসরি সংযোগ আসতে পারে
  • এটি একটি private subnet; যন্ত্রগুলো বাইরে সংযোগ খুলতে পারে, বাইরে থেকে নতুন সংযোগ আসতে পারে না
  • এই subnet-এর যন্ত্র VPC-র অন্য subnet-এ পৌঁছাতে পারে না
  • NAT gateway এই subnet-এর ভেতরেই থাকতে হবে

একটি subnet-এর network ACL-এ inbound-এ TCP 443 অনুমোদিত আর outbound-এ শুধু TCP 443 অনুমোদিত; বাকি সব deny. ভেতরের ওয়েব সার্ভারের security group-এ inbound 443 খোলা। বাইরের ব্যবহারকারীরা পাতা খুলতে পারছেন না। কারণ কী?

  • Security group উত্তর আটকাচ্ছে, কারণ outbound নিয়ম নেই
  • NACL inbound 443 আটকাচ্ছে
  • NACL stateless, তাই ব্যবহারকারীর ephemeral port-এ যাওয়া উত্তর outbound নিয়মে আটকাচ্ছে
  • Internet gateway port 443 সমর্থন করে না

একটি Linux host-এ docker run -p 8080:80 nginx চালানো হলো; container পেল 172.17.0.2. LAN-এর একটি ল্যাপটপ host-এর 192.168.0.10:8080-এ সংযোগ করলে host ভেতরে কী করে?

  • nat table-এ DNAT করে গন্তব্য 172.17.0.2:80 বানায়, তারপর docker0 আর veth দিয়ে container-এ পাঠায়
  • MASQUERADE করে উৎসকে 172.17.0.1 বানায়
  • Container সরাসরি host-এর port 8080-এ শুনছে, কোনো ঠিকানা বদল লাগে না
  • VXLAN-এ মুড়ে container-এ পাঠায়

একটি Kubernetes namespace-এ কোনো NetworkPolicy ছিল না। এরপর একটি policy যোগ করা হলো, যা app: db Pod-গুলোকে Ingress-এর জন্য বাছাই করে আর শুধু app: api থেকে TCP 5432 অনুমোদন করে। এখন কোনটি ঠিক?

  • web Pod থেকে db-তে 5432 এখনো যায়, কারণ web-কে নিষেধ করা হয়নি
  • namespace-এর সব Pod-এর inbound এখন বন্ধ
  • api থেকে db-র যেকোনো port-এ যাওয়া যায়
  • db-তে শুধু api থেকে 5432 আসে; যেসব Pod কোনো policy-তে বাছাই হয়নি, তাদের ingress আগের মতোই খোলা

একটি VXLAN overlay-এর underlay IPv4, MTU 1500. মোড়কে যোগ হয় ভেতরের Ethernet header 14, VXLAN 8, UDP 8 আর বাইরের IPv4 20 byte. Pod-এর interface-এর সর্বোচ্চ নিরাপদ MTU কত?

  • 1500
  • 1450
  • 1430
  • 1480

একটি প্রতিষ্ঠানের অফিস নেটওয়ার্ক 10.0.0.0/16, আর তারা cloud-এ VPN দিয়ে যুক্ত একটি VPC বানাতে চায়। কোন VPC CIDR সবচেয়ে ভালো পছন্দ?

  • 10.40.0.0/16
  • 10.0.0.0/16
  • 10.0.0.0/8
  • 10.0.128.0/17

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. VPC = নিজের CIDR + subnet + route table, নিচে প্রদানকারীর overlay আর SDN. Public subnet মানে 0.0.0.0/0 → igw; private subnet বাইরে যায় NAT gateway দিয়ে, বাইরে থেকে নতুন সংযোগ আসে না।
  2. Security group stateful, প্রতি যন্ত্রে, শুধু allow; network ACL stateless, প্রতি subnet-এ, ক্রমিক allow/deny — ফিরতি ephemeral port-এর নিয়ম ভুলবেন না। নাম AWS-এর; অন্য cloud-এ সমতুল্য আছে, ডিফল্ট আলাদা।
  3. CIDR আগে পরিকল্পনা করুন: অফিস, অন্য VPC, Pod আর Service CIDR — কোনোটির সাথে overlap নয়; peering transitive নয়, অনেক VPC-তে transit gateway.
  4. Container = network namespace + veth + bridge (docker0) + NAT; -p 8080:80 = DNAT নিয়ম; user-defined নেটওয়ার্কে নাম দিয়ে খোঁজা (embedded DNS).
  5. Kubernetes: প্রতি Pod-এর IP, NAT ছাড়া Pod-এ Pod-এ; CNI বাস্তবায়ন করে; ClusterIP = virtual ঠিকানা, kube-proxy-র DNAT (প্রতিটি endpoint 1/n); NetworkPolicy-তে selected ⇒ default deny; VXLAN-এ MTU ১,৪৫০.

পরের পর্বে: নেটওয়ার্ক কাজ না করলে কোথা থেকে শুরু করবেন? tcpdump আর Wireshark দিয়ে packet ধরা, স্তর ধরে ধরে পদ্ধতিগত তদন্ত, আর “ধীর”, “মাঝে মাঝে কেটে যায়”, “শুধু বড় ফাইলে আটকায়”-এর মতো অভিযোগকে প্রমাণে বদলানো।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান