এই পর্বে যা শিখবেন
- Isolation-এর পুরো বর্ণালি — প্রতিটি ধাপে কী ভাগ হয়, কে সীমানা টানে, আর gVisor, Kata ও Firecracker কোথায় বসে
- Isolation, startup, মেমরি, density, পারফরম্যান্স আর attack surface-এর তুলনা — আর একটি host-এ কয়টি instance ধরে, তা হিসাব করা
- Public cloud কেন container-কে VM-এর ভেতরে রাখে — এই পাতার রানারের নিজের স্তরগুলো খুঁজে দেখা
- Serverless-এর cold ও warm পথ, scale to zero, খরচের হিসাব, আর WebAssembly ও V8 isolate
- Cloud-OS-এর ধারণা, Kubernetes-কে “cluster-এর OS” হিসেবে দেখা, আর তিনটি বাস্তব দৃশ্যপটে প্ল্যাটফর্ম বাছাই
১ · একটি প্রশ্ন, অনেক উত্তর — isolation-এর বর্ণালি
পর্ব ৫০-এ দেখেছেন hypervisor কীভাবে একটি যন্ত্রে কয়েকটি পূর্ণ OS চালায়, আর পর্ব ৫১-এ দেখেছেন container কীভাবে namespace আর cgroup দিয়ে একটি kernel-এর ভেতরেই আলাদা ঘর বানায়। এই পর্বে নতুন যন্ত্র শেখা নয়, বাছাই শেখা। আপনি একটি ওয়েব অ্যাপ লিখেছেন। এখন সেটি কোথায় চালাবেন — একটি সাধারণ প্রসেস হিসেবে, container-এ, VM-এ, নাকি কোনো serverless প্ল্যাটফর্মে? উত্তর দিতে প্রথমে একটি প্রশ্ন করুন: আমার প্রোগ্রাম পাশের প্রোগ্রামের সাথে কী কী ভাগ করছে?
ঢাকায় থাকার জায়গা খোঁজার কথা ভাবুন। মেসের একটি বিছানায় রান্নাঘর, বাথরুম, এমনকি ঘরও ভাগ করতে হয় — সস্তা, কিন্তু পাশের জনের অভ্যাস আপনার জীবনে ঢুকে পড়ে। একটি ফ্ল্যাটে নিজের দরজা আছে, কিন্তু ভবনের পানির ট্যাংক, লিফট আর দারোয়ান সবার। আলাদা বাড়িতে প্রায় কিছুই ভাগ নেই — নিরাপদ ও স্বাধীন, কিন্তু খরচ আর রক্ষণাবেক্ষণ সবচেয়ে বেশি। Process হলো মেসের বিছানা, container হলো একই ভবনের ফ্ল্যাট (ভবন = kernel), VM হলো একই জমিতে আলাদা বাড়ি (জমি = হার্ডওয়্যার, সীমানা-প্রাচীর = hypervisor), আর bare metal হলো নিজের জমিতে নিজের বাড়ি।
যে দেয়াল দুই প্রোগ্রামকে আলাদা রাখে, তাকে বলি isolation boundary. দেয়ালটি যত নিচের স্তরে (হার্ডওয়্যারের কাছে) টানা হয়, ভাগ-করা অংশ তত কম, আর ভাগ-করা অংশের বাগ দিয়ে পাশের ঘরে ঢোকার পথও তত কম। নিচের ডায়াগ্রামে বর্ণালির সাতটি ধাপ এক এক করে দেখুন — প্রতিটি ধাপে কী ভাগ হয়, আর সীমানা কে টানে।
- Process
fork/execসব ভাগ - Container
runc, crunkernel ভাগ - gVisor
runscuser-space kernel - Kata
pod = VMনিজস্ব kernel - microVM
Firecrackerন্যূনতম ডিভাইস - পূর্ণ VM
KVM + QEMUপূর্ণ virtual PC - Bare metal
dedicatedকিছুই ভাগ নয়
| কী ভাগ হয় | |
| সীমানা কে টানে | |
| নিজস্ব kernel? | |
| চালু হতে (মোটামুটি) | |
| উদাহরণ |
- Process. সবচেয়ে হালকা। প্রতিটি প্রসেসের নিজের virtual address space আছে (পর্ব ৩২), আর Linux-এর UID-অনুমতি (পর্ব ৪৭) ফাইল আলাদা রাখে। কিন্তু kernel, পুরো file system, নেটওয়ার্ক আর প্রসেস-তালিকা সবার। একজন অন্যজনকে
ps-এ দেখতে পায়। এক মালিকের নিজের প্রোগ্রামের জন্য যথেষ্ট; অচেনা গ্রাহকের কোডের জন্য একেবারেই নয়। - Container. namespace দৃশ্য আলাদা করে, cgroup সম্পদের সীমা টানে, seccomp আর capability ছাঁটা (পর্ব ৪৯) ক্ষমতা কমায়। কিন্তু সব container একই host kernel-এর syscall দরজায় কথা বলে — কয়েকশো syscall, কয়েক কোটি লাইনের kernel. Kernel-এ একটি বাগ মানে সম্ভাব্যভাবে সব container-এর সীমানা ভাঙা।
- gVisor. Google-এর এই প্রকল্প (২০১৮ সালে উন্মুক্ত) host kernel-এর সামনে একটি দ্বিতীয় kernel বসায়, যা লেখা Go ভাষায় আর চলে user space-এ — নাম Sentry. অ্যাপের syscall Sentry ধরে নিজেই সামলায়, আর host kernel-এর কাছে যায় অনেক ছোট একটি syscall-তালিকা দিয়ে। দাম: syscall-ভারী কাজে (প্রচুর ছোট ফাইল I/O, নেটওয়ার্ক) পারফরম্যান্স কমে, আর কিছু Linux আচরণ পুরোপুরি মেলে না।
- Kata Containers. বাইরে থেকে দেখতে container — একই image, একই Kubernetes কমান্ড। ভেতরে প্রতিটি pod চলে একটি হালকা VM-এ, নিজের guest kernel নিয়ে। ২০১৭ সালে Intel-এর Clear Containers আর Hyper-এর runV মিলে এই প্রকল্প শুরু হয়। সীমানা এখন CPU-র hardware virtualization; host kernel-এর বাগ guest থেকে সরাসরি নাগালে নেই।
- microVM. একটি VM, যার virtual হার্ডওয়্যার ইচ্ছা করে প্রায় নেই: BIOS নেই, PCI bus নেই, শুধু কয়েকটি virtio ডিভাইস (ডিস্ক, নেটওয়ার্ক), একটি serial console. AWS ২০১৮ সালে Firecracker উন্মুক্ত করে, Rust-এ লেখা, KVM-এর ওপর। AWS-এর দাবি অনুযায়ী একটি microVM প্রায় ১২৫ ms-এ kernel বুট করতে পারে আর প্রতি microVM-এ VMM-এর বাড়তি মেমরি ৫ MiB-এর কম — পরিবেশভেদে সংখ্যা আলাদা হবে।
- পূর্ণ VM. একটি সম্পূর্ণ virtual PC: firmware, PCI ডিভাইস, পূর্ণ distro, systemd আর তার সার্ভিস। যেকোনো OS চলে (Windows, BSD, পুরোনো Linux), নিজের kernel module বসানো যায়। দাম: প্রতি VM-এ শত শত MiB মেমরি আর বুটে দশ সেকেন্ড বা তার বেশি।
- Bare metal. হার্ডওয়্যার পুরোটা আপনার। পাশের ভাড়াটে নেই, তাই noisy neighbor বা ভাগ-করা CPU cache-এর side channel-এর ভয়ও নেই; সর্বোচ্চ পারফরম্যান্স, নিজের hypervisor-ও চালাতে পারেন। দাম: কম নমনীয়তা — একটি যন্ত্র কিনতে বা ভাড়া নিতে আর চালু করতে মিনিট থেকে দিন।
Isolation একটি সুইচ নয়, একটি বর্ণালি। প্রতিটি ধাপে আপনি একটি জিনিস কিনছেন (শক্ত সীমানা) আর তিনটি জিনিস দিয়ে দিচ্ছেন (startup সময়, প্রতি instance-এর মেমরি, আর পারফরম্যান্সের কিছুটা)। সঠিক উত্তর নির্ভর করে কাকে কার থেকে আলাদা করছেন তার ওপর: নিজের দুটি সার্ভিস, নাকি দুই অচেনা গ্রাহক।
২ · মাঝের ধাপগুলো — gVisor, Kata আর Firecracker কীভাবে কাজ করে
Container আর VM-এর মাঝখানের জায়গাটি গত এক দশকে সবচেয়ে বেশি উদ্ভাবন দেখেছে, কারণ ক্লাউড-কোম্পানিগুলো একসাথে দুটি জিনিস চেয়েছে: container-এর গতি আর VM-এর সীমানা। তিনটি পথ তিন রকম কৌশল নিয়েছে। নিচের বিবরণ সরলীকৃত; প্রতিটি প্রকল্প সক্রিয়ভাবে বদলাচ্ছে, তাই বিস্তারিত নকশার জন্য তাদের অফিসিয়াল ডকুমেন্টেশন দেখুন।
কৌশল: kernel-কে নতুন করে লেখা, কিন্তু user space-এ। একটি সাধারণ container-এ অ্যাপ openat ডাকলে সরাসরি host kernel-এর কোড চলে। gVisor-এ (OCI runtime-এর নাম runsc) সেই syscall ধরা হয় আর পাঠানো হয় Sentry-র কাছে — Sentry নিজেই file system-এর দৃশ্য, প্রসেস, signal, socket সামলায়, অনেকটা একটি ছোট Linux-এর মতো। ফাইলের আসল কাজ করে আলাদা একটি সহকারী প্রসেস (Gofer, বা নতুন সংস্করণে আরও সরাসরি পথ)। Sentry নিজে seccomp দিয়ে খুব ছোট syscall-তালিকায় বন্দি। ফলে আক্রমণকারীকে আগে Sentry-র বাগ, তারপর সেই ছোট দরজা দিয়ে host kernel-এর বাগ খুঁজতে হয় — দুই স্তর। syscall ধরার পদ্ধতি (“platform”) কয়েক রকম: শুরুতে ptrace আর KVM, পরে আরও দ্রুত systrap. দাম: প্রতিটি syscall-এ বাড়তি পথ, তাই I/O-ভারী কাজ ধীর হতে পারে।
কৌশল: container-এর চেহারা, VM-এর দেহ। Kubernetes বা containerd যখন একটি pod চালাতে বলে, Kata একটি হালকা VM চালু করে (QEMU, Cloud Hypervisor বা Firecracker দিয়ে), তার ভেতরে একটি ছোট guest kernel আর একটি agent প্রসেস, আর সেই agent VM-এর ভেতরে সাধারণ container চালায়। ফলে pod-এর সব container এক guest kernel ভাগ করে, কিন্তু অন্য pod বা host-এর kernel ভাগ করে না। Image, YAML, kubectl — সব একই থাকে; Kubernetes-এ RuntimeClass দিয়ে নির্দিষ্ট pod-কে Kata-তে পাঠানো যায়। দাম: প্রতি pod-এ একটি guest kernel-এর মেমরি আর বুটের সময়, আর host-এর ডিস্ক বা ডিভাইস VM-এর ভেতরে পৌঁছাতে বাড়তি স্তর।
কৌশল: VM-কে ছোট করা। QEMU-র মতো সাধারণ VMM কয়েক দশকের পুরোনো হার্ডওয়্যার পর্যন্ত অনুকরণ করে — floppy, USB, sound card — প্রতিটি ডিভাইস মানে বাড়তি কোড আর বাড়তি attack surface. Firecracker সেগুলো ফেলে দেয়: একটি virtio-net, একটি virtio-block, একটি serial console আর রিসেটের জন্য সামান্য কিছু। ChromeOS-এর crosvm থেকে ধারণা নিয়ে Rust-এ লেখা, যাতে মেমরি-নিরাপত্তার বাগ কম থাকে। প্রতিটি Firecracker প্রসেস চলে একটি “jailer”-এর ভেতরে — আলাদা namespace, cgroup, chroot আর কড়া seccomp filter — অর্থাৎ VMM নিজেও sandbox-এ, defense in depth (পর্ব ৪৯). AWS জানায় এটি Lambda আর Fargate চালায়, আর এক host-এ সেকেন্ডে বহু microVM তৈরি করা যায়। দাম: শুধু Linux guest-এর জন্য উপযোগী, GPU বা বিশেষ ডিভাইস সাধারণত নেই।
তিনটির কোনোটিই নতুন মৌলিক যন্ত্র আনেনি: gVisor হলো syscall interposition আর seccomp, Kata হলো পর্ব ৫০-এর hardware virtualization, Firecracker হলো minimalism — পর্ব ৬-এর microkernel-চিন্তার মতো, বিশ্বস্ত কোড যত ছোট, তত কম বাগ। নিরাপত্তা-প্রকৌশলে এই বিশ্বস্ত অংশকে বলে trusted computing base.
৩ · তুলনার টেবিল — কী কিনছেন, কী দিচ্ছেন
নিচের টেবিল মাত্রার ক্রম (order of magnitude) দেখায়, নির্দিষ্ট সংখ্যা নয়। আসল মান নির্ভর করে image-এর আকার, guest OS, ডিস্ক, hypervisor আর কনফিগারেশনের ওপর — নিজের পরিবেশে মেপে দেখুন।
| মাপকাঠি | Container | gVisor / Kata | microVM | পূর্ণ VM | Bare metal |
|---|---|---|---|---|---|
| Isolation-এর শক্তি | মাঝারি — kernel ভাগ | বেশি | বেশি | বেশি | সর্বোচ্চ |
| চালু হতে | ১০০ ms – ১ s | প্রায় ১ s-এর ঘরে | ১০০ ms – ১ s | দশ সেকেন্ড – মিনিট | মিনিট |
| প্রতি instance-এ বাড়তি মেমরি | কয়েক MiB | দশ MiB-এর ঘরে | কয়েক থেকে দশ MiB-এর ঘরে | শত MiB-এর ঘরে | — (পুরো যন্ত্র) |
| এক host-এ সংখ্যা (density) | শত থেকে হাজার | শত | শত থেকে হাজার | দশ | ১ |
| CPU পারফরম্যান্স | প্রায় native | CPU-তে প্রায় native; syscall/I/O-তে কম (gVisor) | প্রায় native | প্রায় native (hardware সাহায্যে) | native |
| Kernel বদলানোর স্বাধীনতা | নেই — host-এর kernel | gVisor: নেই; Kata: guest kernel | নিজের guest kernel | পূর্ণ — যেকোনো OS | পূর্ণ |
| Image-এর আকার | কয়েক MiB থেকে শত MiB | container image-ই | kernel + rootfs, দশ MiB-এর ঘরে | GiB-এর ঘরে | পুরো OS ইনস্টল |
| Attack surface (প্রতিবেশীর দিক থেকে) | পুরো kernel-এর syscall | ছোট syscall-তালিকা / VM সীমানা | ছোট VMM + virtio | VMM-এর বড় device-অনুকরণ | প্রায় নেই (firmware, নেটওয়ার্ক) |
সংখ্যা মুখস্থ করার চেয়ে হাতে হিসাব করা বেশি শিক্ষণীয়। ধরুন আপনার কাছে ৬৪ GiB RAM-এর একটি সার্ভার। প্রতিটি instance-এর বাড়তি মেমরি (guest kernel, guest OS-এর সার্ভিস, VMM প্রসেস বা runtime shim) কত, তার ওপর নির্ভর করে কতগুলো অ্যাপ বসানো যায়। নিচের ক্যালকুলেটরের সংখ্যাগুলো আনুমানিক — স্পষ্টভাবে অনুমান হিসেবে লেখা, যাতে আপনি নিজের মাপ বসিয়ে আবার চালাতে পারেন:
// How many instances fit on one 64 GiB host? Overheads are rough, illustrative assumptions (MiB).
const HOST_MIB = 64 * 1024, RESERVED_MIB = 4 * 1024; // host OS + hypervisor/agent keep 4 GiB
const usable = HOST_MIB - RESERVED_MIB;
const kinds = [ // per-instance overhead on top of the app itself
{ name: 'full VM', guest: 400, vmm: 30, start: 'tens of s' }, // guest kernel + distro services; QEMU-like VMM
{ name: 'microVM', guest: 40, vmm: 5, start: '0.1-1 s' }, // tiny guest kernel; Firecracker-like VMM
{ name: 'gVisor container', guest: 30, vmm: 0, start: 'under 1 s' }, // user-space kernel (Sentry) per sandbox
{ name: 'container', guest: 0, vmm: 5, start: 'under 1 s' }, // shared host kernel; runtime shim process
{ name: 'process', guest: 0, vmm: 0, start: 'ms' },
];
for (const app of [128, 1024]) { // the app's own working memory
console.log(`--- app needs ${app} MiB; usable host memory ${usable} MiB ---`);
console.log('kind overhead fits overhead share start (typical order)');
for (const k of kinds) {
const over = k.guest + k.vmm, fit = Math.floor(usable / (app + over));
console.log(`${k.name.padEnd(17)} ${String(over).padStart(5)} MiB ${String(fit).padStart(5)}` +
` ${(100 * over / (app + over)).toFixed(0).padStart(10)} % ${k.start}`);
}
}একটি আসল রানের আউটপুট:
--- app needs 128 MiB; usable host memory 61440 MiB ---
kind overhead fits overhead share start (typical order)
full VM 430 MiB 110 77 % tens of s
microVM 45 MiB 355 26 % 0.1-1 s
gVisor container 30 MiB 388 19 % under 1 s
container 5 MiB 461 4 % under 1 s
process 0 MiB 480 0 % ms
--- app needs 1024 MiB; usable host memory 61440 MiB ---
kind overhead fits overhead share start (typical order)
full VM 430 MiB 42 30 % tens of s
microVM 45 MiB 57 4 % 0.1-1 s
gVisor container 30 MiB 58 3 % under 1 s
container 5 MiB 59 0 % under 1 s
process 0 MiB 60 0 % ms
দুটি শিক্ষা। এক, অ্যাপ ছোট হলে (১২৮ MiB) পূর্ণ VM-এর বাড়তি অংশই মোট মেমরির ৭৭% — একই যন্ত্রে container-এ ৪৬১টি, VM-এ মাত্র ১১০টি। Serverless প্ল্যাটফর্মে হাজার হাজার ছোট ফাংশন চলে, তাই সেখানে প্রতি instance-এর বাড়তি MiB সরাসরি টাকা। এজন্যই microVM-এর জন্ম: ৪৫ MiB-এর অনুমানে ৩৫৫টি, VM-এর তিনগুণের বেশি। দুই, অ্যাপ বড় হলে (১ GiB) পার্থক্য ছোট হয়ে আসে — ৪২ বনাম ৫৯। বড়, দীর্ঘক্ষণ চলা ডেটাবেস সার্ভারের জন্য VM-এর বাড়তি খরচ তেমন বড় কথা নয়। বাস্তবে hypervisor একই রকম মেমরি-পাতা জোড়া লাগিয়ে (যেমন Linux-এর KSM) বা ballooning দিয়ে কিছুটা ফেরত পায়, আর CPU বা নেটওয়ার্কও সীমা হতে পারে — ক্যালকুলেটর শুধু মেমরি দেখছে।
৪ · Multi-tenancy — কেন cloud container-কে VM-এর ভেতরে রাখে
একটি cloud প্রতিষ্ঠান একই ভৌত সার্ভারে হাজারো গ্রাহকের কোড চালায় — এই ব্যবস্থার নাম multi-tenancy (বহু ভাড়াটে)। এখানে প্রতিবেশী শুধু অচেনা নয়, কেউ কেউ ইচ্ছা করেই আক্রমণকারী হতে পারে: মাসে কয়েক ডলারে একটি অ্যাকাউন্ট খুলে সীমানা ভাঙার চেষ্টা করা সহজ। তাই প্রশ্ন দাঁড়ায়: প্রতিবেশীর থেকে আমাকে আলাদা করছে কোন দেয়াল, আর সেই দেয়াল কতটা পুরু?
সাধারণ container-এ দেয়াল হলো Linux kernel নিজেই। Kernel বিশাল, প্রতি বছর তাতে নিরাপত্তা-বাগ পাওয়া যায়, আর container থেকে kernel-এর প্রায় সব syscall নাগালে (seccomp কিছুটা ছাঁটে)। ২০১৯ সালে প্রকাশিত runc-এর একটি ত্রুটি (CVE-2019-5736) দেখিয়েছিল, নির্দিষ্ট পরিস্থিতিতে container-এর ভেতরের ক্ষতিকর প্রোগ্রাম host-এর runc ফাইলটিই বদলে দিতে পারত। এ ধরনের ঘটনা ঠিক হয়, কিন্তু শিক্ষাটি থাকে: বিশ্বস্ত দলের নিজের সার্ভিস আলাদা করতে container যথেষ্ট; শত্রুভাবাপন্ন হতে পারে এমন প্রতিবেশীর জন্য আরেকটি দেয়াল চাই।
তাই প্রধান public cloud-গুলো একটি স্তরবিন্যাস ব্যবহার করে: ভাড়াটের container চলে সেই ভাড়াটের নিজের VM বা microVM-এর ভেতরে। Managed Kubernetes-এর worker node-গুলো আসলে আপনার অ্যাকাউন্টের VM; অন্য গ্রাহকের pod আপনার kernel-এ চলে না। ভেতরের container দেয় সুবিধা (image, দ্রুত deploy), বাইরের VM দেয় সীমানা। তবু কিছু ভাগ থেকে যায়: প্রতিবেশীর CPU-ব্যবহার আপনার latency বাড়ালে তা noisy neighbor সমস্যা, আর একই CPU core-এর cache ভাগ করলে Spectre-ধরনের side channel-এর ঝুঁকি — তাই কিছু প্রদানকারী এক core-এর দুই hyperthread দুই ভাড়াটেকে দেন না বলে জানান।
নিজের চোখে দেখুন। এই পাতার কোড-রানার নিজেই এই নকশার একটি উদাহরণ। নিচের স্ক্রিপ্ট শুধু পড়ে, কিছু বদলায় না:
echo "== layer 1: is this kernel running inside a VM? =="
grep -m1 -o -w hypervisor /proc/cpuinfo || echo "no hypervisor flag"
grep -m1 'model name' /proc/cpuinfo
echo "kernel: $(uname -r) CPUs seen: $(nproc) $(grep MemTotal /proc/meminfo)"
echo "== layer 2: is this shell inside a container? =="
awk '$2 == "/" {print "root filesystem:", $1, "type", $3}' /proc/mounts
ps -o pid,ppid,comm -p 1,$$
echo "PID 1 says: $(tr '\0' '\n' < /proc/1/environ | grep '^container=')"
echo "marker file: $(ls /run/.containerenv 2>/dev/null || echo none)"
echo "== layer 3: which cgroup, and what limits? =="
echo "cgroup: $(cat /proc/self/cgroup)"
for f in memory.max pids.max cpu.max; do
echo "$f = $(cat /sys/fs/cgroup/$f 2>/dev/null || echo not-visible)"
done
echo "== namespaces of this shell (inode numbers) =="
for ns in pid mnt net uts user; do printf '%s ' "$(readlink /proc/self/ns/$ns)"; done; echoএকটি আসল রানের আউটপুট (namespace-এর নম্বরগুলো প্রতিবার আলাদা হবে):
== layer 1: is this kernel running inside a VM? ==
hypervisor
model name : AMD EPYC-Rome Processor
kernel: 6.8.0-137-generic CPUs seen: 2 MemTotal: 2008912 kB
== layer 2: is this shell inside a container? ==
root filesystem: overlay type overlay
PID PPID COMMAND
1 0 podman-init
2 1 bash
PID 1 says: container=podman
marker file: /run/.containerenv
== layer 3: which cgroup, and what limits? ==
cgroup: 0::/
memory.max = max
pids.max = 2048
cpu.max = max 100000
== namespaces of this shell (inode numbers) ==
pid:[4026533158] mnt:[4026533154] net:[4026533093] uts:[4026533156] user:[4026532740]
স্তরগুলো বাইরে থেকে ভেতরে পড়ুন। স্তর ১ — VM: /proc/cpuinfo-এ hypervisor flag আছে। এটি CPUID নির্দেশের একটি bit, যা hypervisor guest-কে দেখায় — অর্থাৎ যে Linux kernel (6.8.0-…-generic, একটি Ubuntu-ধরনের kernel) এই কোড চালাচ্ছে, সে নিজেই একটি VM-এ। CPU-র নাম AMD EPYC-Rome Processor-ও সম্ভবত hypervisor-এর দেওয়া virtual CPU model-এর নাম; মাত্র ২টি CPU আর প্রায় ২ GB RAM — একটি ছোট cloud VM. স্তর ২ — container: root file system overlay (container image-এর স্তরগুলো, পর্ব ৫১), PID ১ হলো podman-init, আর আমাদের bash-এর PID ২ — নিজের PID namespace-এ আমরা প্রায় একা। PID ১-এর environment-এ container=podman আর /run/.containerenv ফাইল Podman-এর চিহ্ন। স্তর ৩ — cgroup: cgroup path শুধু /, কারণ cgroup namespace আমাদের নিজের ডালটিকেই মূল হিসেবে দেখায়। pids.max = 2048 — একটি fork bomb ২০৪৮-এ থামবে; cpu.max = max 100000 মানে এই স্তরে CPU-র কোটা নেই (period ১০০ ms). memory.max = max দেখে ভাববেন না সীমা নেই — সীমা ওপরের কোনো স্তরে থাকতে পারে, যা namespace-এর ভেতর থেকে দেখা যায় না। খেয়াল করুন, MemTotal container-এর নয়, VM-এর — /proc/meminfo namespace মানে না, তাই container-এর ভেতরের অনেক প্রোগ্রাম ভুল করে ভাবে পুরো যন্ত্রের মেমরি তার।
এই চিহ্নগুলো প্রমাণ নয়, ইঙ্গিত। Hypervisor flag লুকানো যায়, container-এর চিহ্ন runtime-ভেদে আলাদা (/.dockerenv, /run/.containerenv, cgroup path), আর কিছু sandbox (যেমন gVisor) নিজের বানানো /proc দেখায়। systemd-ওয়ালা সাধারণ Linux-এ systemd-detect-virt একটি সুবিধাজনক যাচাই। Windows-এ Task Manager-এর CPU পাতায় “Virtual machine: Yes” লেখা দেখা যায়; PowerShell-এ (Get-CimInstance Win32_ComputerSystem).HypervisorPresent.
৫ · Unikernel — OS-কে অ্যাপের মাপে কেটে ফেলা
পর্ব ৬-এ OS-এর গঠনের আলোচনায় unikernel-এর পরিচয় পেয়েছেন। এবার বর্ণালিতে তার জায়গা দেখুন। microVM-এর ভেতরেও একটি সাধারণ Linux kernel চলে, যার বেশিরভাগ অংশ (বহু ব্যবহারকারী, shell, শত ধরনের driver) একটি ওয়েব-ফাংশনের কোনো কাজে লাগে না। Unikernel প্রশ্ন করে: অ্যাপের সাথে শুধু দরকারি OS-অংশ লাইব্রেরির মতো compile করে একটি image বানালে কেমন হয়? Cambridge-এর MirageOS (OCaml ভাষায়, ২০১৩ সালের গবেষণাপত্র) আর পরের Unikraft (Linux Foundation-এর Xen প্রকল্পের অংশ হিসেবে শুরু, ২০২১ সালে গবেষণাপত্র) এর পরিচিত উদাহরণ।
প্রতিশ্রুতি: image কয়েক MiB, গবেষণায় বুট মিলিসেকেন্ডের ঘরে, আর attack surface খুব ছোট — shell-ই নেই। বাধা: প্রচলিত debug-টুল চলে না, অ্যাপ প্রায়ই নতুন করে build করতে হয়, আর Linux-এর সব আচরণ মেলে না। তাই এটি এখনো মূলত বিশেষায়িত কাজে দেখা যায়।
৬ · Serverless আর FaaS — ঘটনা এলে চলে, না এলে শূন্য
Serverless নামটি বিভ্রান্তিকর: সার্ভার অবশ্যই আছে, শুধু আপনি তা দেখেন না, বেছে নেন না, patch করেন না। সবচেয়ে পরিচিত রূপ FaaS (Function as a Service): আপনি একটি ফাংশন লেখেন, আর প্ল্যাটফর্ম ঠিক করে কখন, কোথায়, কয়টি কপিতে সেটি চলবে। AWS Lambda ২০১৪ সালে এই ধারণা জনপ্রিয় করে; পরে Google Cloud Functions, Azure Functions আর অন্যরা আসে। মূল বৈশিষ্ট্যগুলো:
- Event-driven: ফাংশন চলে কোনো ঘটনায় — HTTP অনুরোধ, একটি ফাইল আপলোড, queue-তে নতুন বার্তা, নির্দিষ্ট সময় (cron-এর মতো)।
- Scale to zero: অনুরোধ না থাকলে কোনো instance চলে না, আর spike এলে প্ল্যাটফর্ম নিজে শত শত instance বাড়ায়।
- Pay-per-use: দাম সাধারণত অনুরোধের সংখ্যা আর “মেমরি × চলার সময়” (GB-second) দিয়ে, অলস সময়ের জন্য নয়।
- সময়-সীমা: প্রতিটি কল একটি নির্দিষ্ট সময়ের বেশি চলতে পারে না — প্রদানকারী আর প্ল্যানভেদে আলাদা; যেমন লেখার সময় AWS Lambda-র সর্বোচ্চ ১৫ মিনিট। লম্বা কাজ ভেঙে queue বা workflow সার্ভিসে দিতে হয়।
- Stateless: একটি কল থেকে পরের কলে instance-এর মেমরি বা লোকাল ডিস্কে কিছু থাকবে, এমন ভরসা করা যায় না। Session, ফাইল, ফলাফল — সব থাকে বাইরের সার্ভিসে: ডেটাবেস (দেখুন DBMS কোর্সের cloud database পর্ব), object storage, cache.
Cold start আর warm start
Scale to zero-র উল্টো পিঠ: শূন্য থেকে প্রথম অনুরোধ এলে কাউকে instance বানাতে হয়। এই বাড়তি দেরিই cold start. আগের অনুরোধ শেষ করে অপেক্ষায় থাকা instance পেলে সেটি warm start, শুধু ফাংশনের নিজের কাজের সময়। নিচের ডায়াগ্রামে একটি ফলাফল-দেখার ফাংশনে দুই পথের সময় ধাপে ধাপে দেখুন। সময়গুলো উদাহরণ — runtime, প্যাকেজের আকার আর প্রদানকারীভেদে অনেক বদলায়।
- Gateway
HTTPSঅনুরোধ আসে - Router
warm আছে?instance খোঁজে - microVM
bootনতুন sandbox - Runtime + init
import, connectএকবারের কাজ - Handler
handler(event)আসল কাজ
| ঘড়ি (প্রথম অনুরোধ থেকে) | |
| কী ঘটছে | |
| Instance-এর অবস্থা | |
| অপেক্ষায় থাকা instance |
- অনুরোধ এল। একজন পরীক্ষার্থী রোল নম্বর দিয়ে ফলাফল চাইলেন। Gateway TLS সংযোগ সামলায়, অনুমতি যাচাই করে, আর ঠিক করে কোন ফাংশন এই অনুরোধ পাবে। ঘড়ি চালু।
- Router-এর প্রশ্ন: অলস instance আছে? এই ফাংশনের জন্য এখন কোনো instance চলছে না — হয় কখনো চলেনি, নয়তো অনেকক্ষণ অনুরোধ না থাকায় সরিয়ে দেওয়া হয়েছে। সিদ্ধান্ত: cold start. বাকি সব ধাপ এই একটি অনুরোধের ঘাড়ে।
- নতুন sandbox. Placement-ব্যবস্থা একটি worker host বেছে নেয়, যেখানে জায়গা আছে আর ফাংশনের কোড ক্যাশে থাকতে পারে। সেখানে একটি microVM বুট হয় — Firecracker-এর ক্ষেত্রে AWS-এর দাবি প্রায় ১২৫ ms; এখানে ১০ থেকে ১৩৫ ms.
- Runtime চালু। Guest-এর ভেতরে ভাষার runtime ওঠে — Node.js, Python বা JVM — আর ফাংশনের কোড লোড হয়। বড় প্যাকেজ বা JVM-এর মতো ভারী runtime-এ এই ধাপ সবচেয়ে লম্বা হতে পারে।
- আপনার init কোড। handler-এর বাইরে লেখা কোড একবারই চলে: library import, config পড়া, ডেটাবেস-সংযোগ খোলা। এখানে ৪০৫ ms — cold start-এর বড় অংশ প্রায়ই প্ল্যাটফর্মের নয়, আপনার নিজের। ভারী library কম ইমপোর্ট করলে এটি ছোট হয়।
- Handler আর উত্তর। আসল কাজ মাত্র ৪৫ ms — ডেটাবেস থেকে ফলাফল এনে উত্তর পাঠানো। মোট ৮৫০ ms, যার ৮০০ ms cold start-এর দাম। Instance এখন অলস, কিন্তু warm: মেমরিতে runtime, import করা library আর খোলা DB-সংযোগ সব আছে।
- Warm পথ। দুই সেকেন্ড পর আরেকজন ফলাফল চাইলেন। Router অলস instance পেয়ে সরাসরি handler ডাকে: ৫ ms + ৪৫ ms = মোট ৫০ ms. একই কোড, ১৭ গুণ দ্রুত। বেশি ট্রাফিকে বেশিরভাগ অনুরোধই এই পথে যায়।
- Scale to zero. অনেকক্ষণ (এখানে ধরা হলো ১০ মিনিট; প্রদানকারীরা সাধারণত নির্দিষ্ট সময়ের প্রতিশ্রুতি দেন না) অনুরোধ না এলে instance সরিয়ে দেওয়া হয়। খরচ শূন্য, কিন্তু পরের অনুরোধ আবার ধাপ ২ থেকে cold. প্রতিকারও আছে: কিছু instance সবসময় তৈরি রাখা (provisioned concurrency, যার জন্য অলস সময়েও টাকা লাগে), বা ধাপ ৩–৫-এর পরের অবস্থার একটি snapshot থেকে instance ফিরিয়ে আনা।
শেষ কৌশলটির একটি পরিচিত উদাহরণ AWS Lambda-র SnapStart: ফাংশন প্রকাশের সময় init শেষ হওয়া microVM-এর মেমরি আর ডিস্কের ছবি রাখা হয়, আর cold start-এ নতুন করে বুট না করে সেই ছবি থেকে ফিরিয়ে আনা হয়। ২০২২ সালে Java-র জন্য চালু হয়, পরে আরও কিছু runtime-এ — কোন runtime-এ, কোন শর্তে, তা AWS-এর বর্তমান ডকুমেন্টেশনে দেখুন। ফিরিয়ে আনার পথটি পর্ব ৩৫-এর demand paging আর copy-on-write-এর আত্মীয়: পুরো মেমরি একবারে না এনে যে পাতা ছোঁয়া হয়, সেটি আনা।
সিমুলেটর ১ — ফলাফল-দিবসে serverless
ধরুন একটি বিশ্ববিদ্যালয়ের ফলাফল-পোর্টাল। মাসের বেশিরভাগ সময় অল্প অনুরোধ — দিনে প্রতি ৪ মিনিটে গড়ে একটি, রাতে প্রতি ৩০ মিনিটে একটি। ৩০তম দিনে সকাল ১০টায় ফলাফল প্রকাশ, আর এক ঘণ্টা ধরে সেকেন্ডে গড়ে ২০০ অনুরোধ। অনুরোধ আসে এলোমেলোভাবে (Poisson প্রক্রিয়া, নির্দিষ্ট seed-এ, তাই প্রতিবার একই ফল)। নিচের সব সংখ্যা — cold start ৮০০ ms, warm কাজ ৫০ ms, ১০ মিনিট অলস থাকলে instance সরানো, আর দাম — উদাহরণের অনুমান, কোনো প্রদানকারীর বর্তমান দাম নয়:
// Serverless result portal for one month: cold vs warm starts. ALL numbers are illustrative assumptions.
const WORK = 0.050, COLD = 0.800, KEEP_WARM = 600; // seconds: handler work, cold-start penalty, idle timeout
const MEM_GB = 0.5, PER_REQ = 0.20 / 1e6, PER_GBS = 0.0000166667; // example prices (USD), not a real quote
let seed = 52; const rnd = () => (seed = (seed * 16807) % 2147483647) / 2147483647;
const rate = (d, h) => d === 29 && h >= 10 && h < 11 ? 200 : // day 30, 10-11 am: results published
(h >= 9 && h < 21 ? 1 / 240 : 1 / 1800); // otherwise: 1 per 4 min by day, 1 per 30 min at night
const idle = [], busyWarm = [], busyCold = [], lat = { quiet: [], spike: [] };
let n = 0, colds = 0, gbs = 0;
for (let d = 0; d < 30; d++) for (let t = 0; t < 86400; ) {
const r = rate(d, t / 3600), hourEnd = (Math.floor(t / 3600) + 1) * 3600, gap = -Math.log(1 - rnd()) / r;
if (t + gap >= hourEnd) { t = hourEnd; continue; } // Poisson arrivals, rate fixed within each hour
t += gap;
const now = d * 86400 + t, kind = r > 1 ? 'spike' : 'quiet';
for (;;) { // finished instances become idle (oldest first)
const q = (busyWarm[0] ?? Infinity) <= (busyCold[0] ?? Infinity) ? busyWarm : busyCold;
if (!q.length || q[0] > now) break;
idle.push(q.shift());
}
while (idle.length && now - idle[0] > KEEP_WARM) idle.shift(); // idle too long: reclaimed (scale to zero)
const cold = idle.length === 0;
if (cold) colds++; else idle.pop(); // reuse the most recently used warm instance
const busy = WORK + (cold ? COLD : 0);
(cold ? busyCold : busyWarm).push(now + busy);
lat[kind].push(Math.round(busy * 1000)); gbs += MEM_GB * busy; n++;
}
const pct = (a, p) => a[Math.min(a.length - 1, Math.floor(p / 100 * a.length))];
for (const k of ['quiet', 'spike']) {
const a = lat[k].sort((x, y) => x - y), c = a.filter(v => v > 100).length;
console.log(`${k.padEnd(5)}: ${String(a.length).padStart(7)} requests, ${String(c).padStart(5)} cold (${(100 * c / a.length).toFixed(2)}%)` +
` p50 = ${pct(a, 50)} ms p95 = ${pct(a, 95)} ms p99 = ${pct(a, 99)} ms`);
}
const cost = n * PER_REQ + gbs * PER_GBS;
console.log(`month: ${n} requests, ${colds} cold starts, ${gbs.toFixed(0)} GB-s billed -> about $${cost.toFixed(2)}`);একটি আসল রানের আউটপুট:
quiet: 6088 requests, 961 cold (15.79%) p50 = 50 ms p95 = 850 ms p99 = 850 ms
spike: 721190 requests, 134 cold (0.02%) p50 = 50 ms p95 = 50 ms p99 = 50 ms
month: 727278 requests, 1095 cold starts, 18620 GB-s billed -> about $0.46
উল্টো মনে হলেও সত্যি: শান্ত দিনগুলোতেই cold start বেশি। শান্ত সময়ের ৬,০৮৮টি অনুরোধের প্রায় ১৬% cold, কারণ রাতে দুই অনুরোধের মাঝে প্রায়ই ১০ মিনিটের বেশি ফাঁক — তাই p50 ৫০ ms হলেও p95 আর p99 পুরো ৮৫০ ms. Spike-এর এক ঘণ্টায় ৭ লাখের বেশি অনুরোধ, কিন্তু cold মাত্র ১৩৪টি (০.০২%): প্রথম মুহূর্তে যতগুলো সমান্তরাল instance লাগে, শুধু সেগুলো cold, তারপর তারা warm থেকে বাকিদের সেবা দেয়। পরীক্ষার্থীর দিক থেকে spike-এ p50, p95, p99 — সবই ৫০ ms. খরচ: প্রায় ৭.৩ লাখ অনুরোধ আর ১৮,৬২০ GB-second-এ পুরো মাসে আধা ডলারেরও কম। মনে রাখবেন, বাস্তব বিলে API gateway, ডেটা পাঠানো (egress), লগ আর ডেটাবেসের খরচ যোগ হয় — প্রায়ই ফাংশনের খরচের চেয়ে বেশি — আর অনেক প্রদানকারীর ফ্রি-কোটা থাকে। Cold start-এর init সময়ও এখানে বিলে ধরা হয়েছে; প্রদানকারীভেদে নিয়ম আলাদা।
সিমুলেটর ২ — একই ট্রাফিক, সবসময় চালু VM-এ
এবার একই অনুরোধগুলো (একই seed) চালানো যাক কয়েকটি সবসময়-চালু VM-এ, প্রতিটিতে ৪টি worker (container বা প্রসেস)। এখানে cold start নেই, কিন্তু worker সংখ্যা স্থির: সবাই ব্যস্ত থাকলে অনুরোধ লাইনে দাঁড়ায় (পর্ব ১৬-এর FCFS queue-র মতো)। VM-এর দাম ঘণ্টায় ০.০৫ ডলার ধরা — এটিও উদাহরণ:
// Same month of traffic, served by an always-on fleet of VMs/containers. Illustrative assumptions only.
const WORK = 0.050, WORKERS_PER_VM = 4, VM_PER_HOUR = 0.05; // 50 ms per request; example price in USD
let seed = 52; const rnd = () => (seed = (seed * 16807) % 2147483647) / 2147483647;
const rate = (d, h) => d === 29 && h >= 10 && h < 11 ? 200 : (h >= 9 && h < 21 ? 1 / 240 : 1 / 1800);
const arrivals = []; // identical traffic generator (same seed)
for (let d = 0; d < 30; d++) for (let t = 0; t < 86400; ) {
const r = rate(d, t / 3600), hourEnd = (Math.floor(t / 3600) + 1) * 3600, gap = -Math.log(1 - rnd()) / r;
if (t + gap >= hourEnd) { t = hourEnd; continue; }
t += gap; arrivals.push(d * 86400 + t);
}
const pct = (a, p) => a[Math.min(a.length - 1, Math.floor(p / 100 * a.length))];
for (const vms of [2, 3, 4]) {
const free = new Array(vms * WORKERS_PER_VM).fill(0), lat = [];
for (const now of arrivals) { // FIFO: next request goes to the earliest free worker
let w = 0;
for (let i = 1; i < free.length; i++) if (free[i] < free[w]) w = i;
const start = Math.max(now, free[w]);
free[w] = start + WORK;
lat.push(Math.round((free[w] - now) * 1000));
}
lat.sort((x, y) => x - y);
const cost = vms * VM_PER_HOUR * 24 * 30;
console.log(`${vms} VMs x ${WORKERS_PER_VM} workers: p50 = ${pct(lat, 50)} ms p95 = ${pct(lat, 95)} ms` +
` p99.9 = ${pct(lat, 99.9)} ms worst = ${lat[lat.length - 1]} ms cost = $${cost.toFixed(2)}/month`);
}
const perReq = 0.20 / 1e6 + 0.5 * WORK * 0.0000166667; // serverless price of one warm 50 ms request
const vm3 = 3 * VM_PER_HOUR * 24 * 30;
console.log(`break-even: ${(vm3 / perReq / 1e6).toFixed(0)} million requests/month = ` +
`${(vm3 / perReq / (30 * 86400)).toFixed(0)} requests/second around the clock`);একটি আসল রানের আউটপুট (latency পুরো মাসের সব অনুরোধের ওপর, যার বেশিরভাগ spike-এর):
2 VMs x 4 workers: p50 = 447871 ms p95 = 861582 ms p99.9 = 906414 ms worst = 907516 ms cost = $72.00/month
3 VMs x 4 workers: p50 = 50 ms p95 = 79 ms p99.9 = 123 ms worst = 154 ms cost = $108.00/month
4 VMs x 4 workers: p50 = 50 ms p95 = 50 ms p99.9 = 69 ms worst = 86 ms cost = $144.00/month
break-even: 175 million requests/month = 68 requests/second around the clock
২টি VM (৮ worker): ৮ worker × (১ / ০.০৫ s) = সেকেন্ডে ১৬০টি অনুরোধের ক্ষমতা, কিন্তু আসছে ২০০টি। লাইন এক ঘণ্টা ধরে বাড়তেই থাকে — p50 প্রায় ৭.৫ মিনিট, সবচেয়ে খারাপটি ১৫ মিনিটের বেশি। বাস্তবে এর অনেক আগেই ব্রাউজার timeout দেখাবে আর মানুষ বারবার refresh করে চাপ আরও বাড়াবেন — ফলাফল-দিবসে সাইট “ডাউন” হওয়ার পরিচিত ছবি। ৩টি VM (১২ worker, ক্ষমতা ২৪০/s): ব্যবহার প্রায় ৮৩%, তাই এলোমেলো আগমনে মাঝে মাঝে ছোট লাইন — p95 ৭৯ ms, সবচেয়ে খারাপ ১৫৪ ms. ৪টি VM: আরও আরাম। কিন্তু মাসের ২৯ দিন এই VM-গুলো প্রায় অলস বসে থাকে, আর খরচ ১০৮ থেকে ১৪৪ ডলার — serverless-এর কয়েকশো গুণ। উল্টো দিকে শান্ত সময়ে এখানে প্রতিটি অনুরোধ ৫০ ms, কোনো cold start নেই। শেষ লাইনটি break-even: এই দামে ৩টি VM-এর মাসিক খরচে serverless-এ প্রায় ১৭.৫ কোটি warm অনুরোধ চলে, অর্থাৎ দিন-রাত সেকেন্ডে গড়ে প্রায় ৬৮টি। ট্রাফিক এর চেয়ে বেশি আর স্থির হলে সবসময়-চালু যন্ত্র সস্তা; কম বা হঠাৎ-হঠাৎ হলে serverless.
Percentile কেন, গড় কেন নয়: p95 মানে যে সময়ের মধ্যে ৯৫% অনুরোধ শেষ হয়। ৮৪% অনুরোধ ৫০ ms আর ১৬% অনুরোধ ৮৫০ ms হলে গড় প্রায় ১৭৮ ms — এমন একটি সংখ্যা, যা কোনো একজন ব্যবহারকারীও আসলে পাননি। Percentile বলে কতজন কতক্ষণ অপেক্ষা করলেন। বড় সিস্টেমে একটি পাতা বানাতে অনেক সার্ভিস ডাকলে প্রায় প্রতিটি পাতায় কোনো না কোনো ধীর অংশ পড়ে, তাই tail latency (p99 বা আরও ওপরে) গুরুত্বপূর্ণ।
৭ · WebAssembly আর V8 isolate — আরও হালকা sandbox, edge-এ
বর্ণালিতে আরেকটি পথ আছে, যা kernel-এর সীমানা নয়, ভাষা-runtime-এর সীমানা ব্যবহার করে। একটি JavaScript ইঞ্জিন (যেমন Chrome-এর V8) একই প্রসেসের ভেতরে অনেকগুলো isolate চালাতে পারে — প্রতিটির নিজের heap আর global অবস্থা, একে অপরের অবজেক্ট ছুঁতে পারে না। Cloudflare Workers এই নকশায় তৈরি: প্রতিটি গ্রাহকের কোড একটি isolate-এ, আর নতুন isolate চালু হতে নতুন প্রসেস বা VM-এর মতো সময় লাগে না — কোম্পানিটি মিলিসেকেন্ডের ঘরের startup-এর কথা বলে। এতে কোড চালানো যায় ব্যবহারকারীর কাছের অনেক ছোট ডেটা সেন্টারে — একে বলে edge computing.
WebAssembly (Wasm) একই চিন্তার একটি বহনযোগ্য রূপ (পর্ব ৪৯-এ এর sandbox-এর পরিচয় পেয়েছেন)। C, Rust, Go-র মতো ভাষা থেকে Wasm-এ compile করা কোড শুধু নিজের linear memory দেখে, আর বাইরের জগতে যেতে পারে শুধু host যে ফাংশন দেয় সেগুলো দিয়ে। ব্রাউজারের বাইরে এই “বাইরের জগৎ”-এর মানদণ্ড WASI — ফাইল, ঘড়ি, এলোমেলো সংখ্যা, socket-এর মতো সেবা, capability-ধাঁচে: runtime যে ফোল্ডার খুলে দেয়, কেবল সেটিই দেখা যায়। WASI-র কয়েকটি সংস্করণ এসেছে (২০১৯ সালের preview 1, ২০২৪ সালে component model-ভিত্তিক 0.2), আর Wasmtime, WasmEdge-এর মতো runtime এগুলো বাস্তবায়ন করে।
| বিষয় | V8 isolate / Wasm | Container / microVM |
|---|---|---|
| সীমানা | ভাষা-runtime (memory-safe bytecode, সীমা-যাচাই) | kernel বা hypervisor |
| চালু হতে | মিলিসেকেন্ড বা তার কম, বলে দাবি করা হয় | শত মিলিসেকেন্ড থেকে সেকেন্ড |
| কী চালানো যায় | JS বা Wasm-এ compile হয় এমন কোড; সীমিত API | যেকোনো Linux প্রোগ্রাম |
| প্রধান ঝুঁকি | runtime বা JIT-এর বাগ, Spectre-ধরনের side channel | kernel (container) বা VMM-এর বাগ |
একই প্রসেসে অনেক গ্রাহক মানে runtime-এর একটি বাগ বা CPU-র side channel সরাসরি প্রতিবেশীর দিকে খোলে। তাই এই প্ল্যাটফর্মগুলো প্রসেস-স্তরের sandbox আর ঘড়ির নির্ভুলতা কমানোর মতো বাড়তি স্তর ব্যবহার করে বলে জানায় — আবার defense in depth.
৮ · Cloud-এর OS-চিন্তা — নতুন kernel, পাল, অপরিবর্তনীয় সার্ভার আর Kubernetes
Hypervisor নতুন kernel. একটি সাধারণ কম্পিউটারে kernel প্রসেসদের মধ্যে CPU, মেমরি আর ডিভাইস ভাগ করে আর তাদের আলাদা রাখে। Cloud-এর ডেটা সেন্টারে এই কাজটিই করে hypervisor — শুধু “প্রসেস” এখানে পুরো VM. Cloud প্রদানকারীরা তাই hypervisor-কে যতটা সম্ভব ছোট আর বিশেষায়িত করছে: যেমন AWS-এর Nitro ব্যবস্থায় নেটওয়ার্ক, স্টোরেজ আর ব্যবস্থাপনার কাজ আলাদা হার্ডওয়্যার কার্ডে সরানো, আর মূল যন্ত্রে KVM-ভিত্তিক একটি পাতলা hypervisor — AWS-এর ভাষ্য অনুযায়ী। পর্ব ৬-এর microkernel-চিন্তা এখানে ডেটা সেন্টারের মাপে ফিরে এসেছে।
Cattle, not pets. পুরোনো দিনে প্রতিটি সার্ভার ছিল পোষা প্রাণী: নাম ছিল, প্রশাসক হাতে যত্ন নিতেন, অসুস্থ হলে সারাতেন। ক্লাউডে সার্ভার হলো পালের গরু: নম্বর আছে, নাম নেই; একটি অসুস্থ হলে সরিয়ে নতুন একটি আনা হয়। তুলনাটির উৎস হিসেবে সাধারণত ২০০০-এর দশকে Bill Baker-এর একটি বক্তৃতার কথা বলা হয়; ২০১০-এর দশকের শুরুতে Randy Bias-এর মতো cloud-প্রকৌশলীদের হাত ঘুরে এটি জনপ্রিয় হয়। এর সাথে আসে immutable infrastructure: চলমান সার্ভারে ssh করে প্যাকেজ আপডেট করা হয় না; নতুন image বানিয়ে পুরোনো সার্ভার বদলে দেওয়া হয়। লাভ: প্রতিটি সার্ভার ঠিক জানা অবস্থায়, “আমার মেশিনে চলে” সমস্যা কম, আর ফেরত যাওয়া মানে পুরোনো image আবার চালানো।
Container-optimised OS. সার্ভার যদি শুধু container চালায়, তবে তার OS-এ কী লাগে? খুব কম। Bottlerocket (AWS, ২০২০), Flatcar Container Linux (CoreOS Container Linux-এর উত্তরসূরি) আর Talos Linux (shell বা SSH ছাড়া, শুধু API দিয়ে পরিচালিত) এর উদাহরণ। সাধারণত: শুধু-পড়ার root file system, প্যাকেজ ম্যানেজার নেই, আর পুরো OS একসাথে A/B partition-এ হালনাগাদ — ব্যর্থ হলে আগের partition থেকে বুট। খুঁটিনাটি প্রকল্পভেদে আলাদা।
Kubernetes — একটি “cluster-এর OS”-এর উপমা
Google ২০১৪ সালে Kubernetes প্রকাশ করে, ভেতরের Borg ব্যবস্থার অভিজ্ঞতা থেকে। একে অনেকে “cluster-এর OS” বলেন। উপমাটি শিক্ষণীয়, তবে এটি একক kernel নয় — অনেক যন্ত্রে ছড়ানো প্রোগ্রামের একটি দল, যারা একটি ডেটাবেসে (etcd) লেখা “কাঙ্ক্ষিত অবস্থা”-র দিকে বাস্তবকে বারবার টেনে আনে।
| এক কম্পিউটারের OS | Kubernetes-এ কাছাকাছি ধারণা | মিল ও অমিল |
|---|---|---|
| প্রসেস | Pod (এক বা একাধিক container) | সবচেয়ে ছোট চালানোর একক |
| CPU scheduler (পর্ব ১৬–২১) | kube-scheduler | কোন pod কোন node-এ — একবার বসায়, প্রতি মিলিসেকেন্ডে নয় |
| Kernel-এর প্রতি-CPU কাজ | kubelet (প্রতি node-এর agent) | container runtime-কে দিয়ে pod চালায়, স্বাস্থ্য জানায় |
| cgroup-এর সীমা | requests আর limits | limits শেষে cgroup-এর cpu.max, memory.max হয় |
| init / systemd নষ্ট সার্ভিস আবার চালায় | controller (যেমন Deployment) | কাঙ্ক্ষিত সংখ্যার pod বজায় রাখে |
| OOM killer (পর্ব ৩৮) | memory limit ছাড়ালে container kill; node-এ চাপ হলে pod সরানো (eviction) | কাকে আগে সরানো হবে, তা QoS শ্রেণি দিয়ে |
requests আর limits বোঝা জরুরি, কারণ ভুল সেটিং বহু রহস্যময় সমস্যার উৎস। requests হলো প্রতিশ্রুতি: scheduler শুধু সেই node-এ pod বসায়, যেখানে অন্তত এতটা CPU আর মেমরি এখনো কাউকে প্রতিশ্রুত নয়। limits হলো ছাদ, যা kubelet cgroup দিয়ে প্রয়োগ করে: CPU limit পেরোলে প্রসেস মারা যায় না, শুধু থামিয়ে রাখা হয় (throttling, পর্ব ২১-এর CFS bandwidth), তাই latency হঠাৎ লাফায়; মেমরি limit পেরোলে OOM kill. দুটো সমান দিলে pod পায় “Guaranteed” শ্রেণি, শুধু requests দিলে “Burstable”, কিছুই না দিলে “BestEffort” — চাপের সময় আগে সরানো হয়।
৯ · খরচ আর সিদ্ধান্ত — বাংলাদেশের তিনটি দৃশ্যপট
প্ল্যাটফর্ম বাছাইয়ে প্রযুক্তির চেয়ে প্রশ্নগুলো বেশি গুরুত্বপূর্ণ। নিচের টেবিলটি একটি সিদ্ধান্ত-flowchart, ওপর থেকে নিচে পড়ুন: প্রথম যে প্রশ্নের উত্তর “হ্যাঁ”, সেখানে থামুন — তারপর বাকি প্রশ্নগুলো দিয়ে সিদ্ধান্তটি আরেকবার যাচাই করুন।
সিদ্ধান্ত-flowchart — কোথায় চালাবেন
ওপর থেকে নিচে; “না” হলে পরের প্রশ্নে যান
- বিশেষ kernel / OS?
Windows, driverহ্যাঁ → পূর্ণ VM - অচেনা লোকের কোড?
multi-tenantহ্যাঁ → microVM / gVisor - ট্রাফিক কম বা হঠাৎ?
event, spikeহ্যাঁ → serverless - স্থির, latency-সংবেদনশীল?
always-onহ্যাঁ → VM-এ container - সর্বোচ্চ পারফরম্যান্স বা নিয়মের শর্ত?
dedicatedহ্যাঁ → bare metal
| প্রশ্ন | “হ্যাঁ” হলে | কেন |
|---|---|---|
| ❶ নিজের kernel module, ভিন্ন OS বা পুরোনো সফটওয়্যার লাগবে? | পূর্ণ VM | container host-এর kernel-ই ব্যবহার করে |
| ❷ অন্যদের (গ্রাহক, শিক্ষার্থী) পাঠানো কোড চালাবেন? | microVM, gVisor বা Kata — কখনো সাধারণ container একা নয় | শত্রুভাবাপন্ন প্রতিবেশী, kernel-এর বাগ |
| ❸ বেশিরভাগ সময় অলস, মাঝে মাঝে spike বা ঘটনাভিত্তিক কাজ? | serverless (state বাইরে) | অলস সময়ের টাকা নেই, নিজে থেকে বাড়ে |
| ❹ সারাদিন স্থির ট্রাফিক, প্রতিটি মিলিসেকেন্ড গুরুত্বপূর্ণ? | VM-এর ওপর container (প্রয়োজনে Kubernetes) | cold start নেই, স্থির খরচ, পূর্ণ নিয়ন্ত্রণ |
| ❺ CPU-র প্রতিটি অংশ লাগবে, বা নিয়মে আলাদা হার্ডওয়্যার চাই? | bare metal বা dedicated host | প্রতিবেশী নেই, পূর্ণ পারফরম্যান্স |
এবার তিনটি কাল্পনিক কিন্তু পরিচিত দৃশ্যপটে প্রশ্নগুলো প্রয়োগ করা যাক। এগুলো কোনো নির্দিষ্ট প্রতিষ্ঠানের বর্ণনা নয়, আর বাস্তব সিদ্ধান্তে আপনার নিজের সংখ্যা, বাজেট আর আইনি শর্ত লাগবে।
| দৃশ্যপট | |
| ট্রাফিক | |
| State আর ডেটা | |
| ঝুঁকি আর শর্ত | |
| সিদ্ধান্ত |
- দোকান — প্রশ্নগুলো। ❶ বিশেষ kernel? না। ❷ অচেনা কোড? না, নিজের কোড। ❸ ট্রাফিক কম? হ্যাঁ, কিন্তু একেবারে শূন্য নয়, আর প্রতিটি অনুরোধে ক্রেতা অপেক্ষা করেন — শান্ত সময়ে cold start মানে প্রথম ক্রেতার জন্য ধীর পাতা। সবচেয়ে বড় শর্ত দল: এক-দুজন মানুষ, যাঁরা ব্যবসাও চালান।
- দোকান — সিদ্ধান্ত। সরল আর পূর্বানুমেয়: একটি ছোট VM-এ container (অথবা যে PaaS আপনার জন্য container চালায়), আর managed ডেটাবেস, যাতে ব্যাকআপ নিজে সামলাতে না হয়। ঈদের আগে VM-এর আকার বাড়ানো বা দ্বিতীয় একটি যোগ করা যথেষ্ট। ছবি রাখুন object storage আর CDN-এ। Serverless-এ ভুল কিছু নেই, কিন্তু এই আকারে জটিলতার লাভ কম। মাসিক বিল স্থির থাকা ছোট ব্যবসার জন্য নিজেই একটি সুবিধা।
- ফলাফল-পোর্টাল — প্রশ্নগুলো। ❸ এখানে স্পষ্ট “হ্যাঁ”: সিমুলেটরের মতোই মাসের বেশিরভাগ সময় প্রায় শূন্য, তারপর এক ঘণ্টায় লাখো অনুরোধ। আর সবচেয়ে গুরুত্বপূর্ণ পর্যবেক্ষণ: প্রকাশের পরে ফলাফল বদলায় না। যে ডেটা বদলায় না, তার জন্য প্রতিটি অনুরোধে কোড চালানোরই কি দরকার?
- ফলাফল-পোর্টাল — সিদ্ধান্ত। সবচেয়ে সস্তা আর শক্ত উত্তর হতে পারে কোনো “compute” নয়: প্রকাশের আগেই প্রতিটি রোল নম্বরের ফল একটি ছোট static ফাইল (বা কয়েকটি ভাগে সাজানো ফাইল) বানিয়ে CDN-এ রাখা — CDN ঠিক এই ধরনের spike-এর জন্য তৈরি। খোঁজা বা অন্য গতিশীল কাজের জন্য serverless ফাংশন, সামনে cache. প্রকাশের কয়েক মিনিট আগে ফাংশনগুলো “গরম” করে রাখা বা কিছু provisioned concurrency নেওয়া যায়, আর ডেটাবেসকে সরাসরি spike-এর মুখে না রাখা — নইলে ফাংশন নয়, ডেটাবেসই বসে যাবে।
- পেমেন্ট — প্রশ্নগুলো। ❸ না: ট্রাফিক সবসময় বেশি আর স্থির, তাই সিমুলেটরের break-even-এর ওপারে। ❹ হ্যাঁ: প্রতিটি লেনদেনে latency আর নির্ভরযোগ্যতা গুরুত্বপূর্ণ, আর cold start-এর অনিশ্চয়তা মূল পথে গ্রহণযোগ্য নয়। ❺ আংশিক: আর্থিক প্রতিষ্ঠানের ক্ষেত্রে ডেটা কোথায় রাখা যাবে, কোন নিরাপত্তা-মান মানতে হবে — এমন নিয়ন্ত্রক শর্ত থাকতে পারে, যা বাংলাদেশ ব্যাংকের মতো সংস্থার বর্তমান নির্দেশনা দেখে নিশ্চিত হতে হয়।
- পেমেন্ট — সিদ্ধান্ত। মূল লেনদেন-পথ: আগে থেকে যথেষ্ট ক্ষমতা রাখা VM বা dedicated host-এ container, সম্ভবত Kubernetes দিয়ে, কয়েকটি ডেটা সেন্টার বা zone জুড়ে; লেনদেনের ডেটা একটি শক্ত ACID ডেটাবেসে (দেখুন DBMS কোর্সের transaction পর্ব)। পাশের কাজ — SMS পাঠানো, রাতের রিপোর্ট, ছবি যাচাই — ঘটনাভিত্তিক, তাই সেখানে serverless বা queue-ভিত্তিক worker ভালো মানায়। একটি সংস্থায় একাধিক প্ল্যাটফর্ম থাকা স্বাভাবিক।
আরও গভীরে — snapshot থেকে ফেরার লুকানো ফাঁদ। একটি microVM-এর snapshot থেকে বহু instance চালালে সবাই একই মেমরি-অবস্থা নিয়ে জাগে — একই এলোমেলো-সংখ্যার seed, একই অস্থায়ী key, একই “অনন্য” ID. তাই snapshot-ভিত্তিক cold start-এর ডকুমেন্টেশন সতর্ক করে যে অনন্যতা-নির্ভর জিনিস init-এ না বানিয়ে প্রতিটি অনুরোধে বানাতে হবে, আর Linux kernel-এ VM-এর প্রজন্ম-পরিবর্তন জানানোর ব্যবস্থাও (vmgenid) যোগ হয়েছে। ফিরিয়ে আনা নিজেই সাধারণত মেমরি-ফাইলকে mmap করে lazy page fault দিয়ে হয় — পর্ব ৩৫-এর demand paging, শুধু ডিস্কের বদলে snapshot ফাইল থেকে।
পরীক্ষা ও ইন্টারভিউতে এই বিষয়ে প্রায়ই যা জিজ্ঞেস করা হয়:
- Container আর VM-এর মূল পার্থক্য? — container host kernel ভাগ করে (namespace + cgroup), VM নিজের guest kernel চালায় hypervisor-এর ওপর; container হালকা ও দ্রুত, VM-এর সীমানা শক্ত।
- Cold start কী, আর কীভাবে কমানো যায়? — নতুন instance তৈরির দেরি (sandbox, runtime, init); ছোট প্যাকেজ, হালকা init, provisioned concurrency, snapshot.
- microVM কী, উদাহরণ? — ন্যূনতম virtual ডিভাইসের VM, দ্রুত বুট ও কম মেমরি; Firecracker (Lambda, Fargate).
- Kubernetes-এ requests আর limits? — requests দিয়ে scheduler node বাছে; limits cgroup-এ প্রয়োগ হয় — CPU-তে throttling, মেমরিতে OOM kill.
- Serverless কখন খারাপ পছন্দ? — স্থির উচ্চ ট্রাফিক, লম্বা কাজ (সময়-সীমা), কঠোর latency, বা অনেক state লাগলে।
১০ · AI-এর যুগে প্ল্যাটফর্ম-পরামর্শ — দামি ভুল কম করুন
“আমার অ্যাপের জন্য Kubernetes নাকি serverless?” — এই প্রশ্নে AI সাধারণত আত্মবিশ্বাসী, গোছানো উত্তর দেয়। সমস্যা হলো, উত্তরটি প্রায়ই আপনার ট্রাফিক, বাজেট আর দলের আকার না জেনেই দেওয়া, আর দাম বা সীমার সংখ্যাগুলো পুরোনো প্রশিক্ষণ-ডেটা থেকে আসে। প্ল্যাটফর্মের ভুল সিদ্ধান্ত মাসের পর মাস বিল আর রক্ষণাবেক্ষণে দাম চায়।
| AI-এর সাধারণ ভুল | সমস্যা | যাচাইয়ের পথ |
|---|---|---|
| “Container VM-এর মতোই নিরাপদ” | kernel ভাগের ঝুঁকি উপেক্ষা | কাকে কার থেকে আলাদা করছেন, তা লিখে ফেলুন |
| পুরোনো সীমা বা দাম (timeout, মেমরি, প্রতি GB-second) | সংখ্যা প্রায়ই বদলায় | প্রদানকারীর বর্তমান pricing ও quota পাতা |
| শুধু ফাংশনের খরচ হিসাব | gateway, egress, লগ, ডেটাবেস বাদ | প্রদানকারীর calculator-এ পুরো স্থাপত্য |
| ছোট দলকে বড় Kubernetes cluster | রক্ষণাবেক্ষণের বোঝা | “রাত ২টায় কে সামলাবেন?” প্রশ্ন |
একটি cloud-এর CLI flag অন্যটিতে, বা --privileged | ভুল কমান্ড, দুর্বল নিরাপত্তা | অফিসিয়াল ডকুমেন্টেশন, ন্যূনতম অনুমতি |
| CPU limit কপি-পেস্ট, না বুঝে | throttling-এ রহস্যময় latency | load test, cgroup-এর cpu.stat দেখা |
| Cold start বা p95-এর হিসাবে ভুল সংখ্যা | ভুল সিদ্ধান্ত | ছোট সিমুলেশন বা হাতে হিসাব, আর আসল মাপ |
AI-কে প্রশ্ন করার আগে নিজের সংখ্যাগুলো জোগাড় করুন, আর অনুমান আলাদা করে দেখাতে বলুন। নিচের কাঠামোটি কাজে লাগাতে পারেন:
AI-এর প্ল্যাটফর্ম-পরামর্শ মানার আগে — চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।
প্র ০১Container তো আগেই ছিল — AWS কেন Lambda-র জন্য নতুন একটি VMM (Firecracker) বানাল?
২০১৪ সালে Lambda চালু হওয়ার সময় AWS-এর সামনে একটি পরিচিত দ্বিধা ছিল। একটি ফাংশন ছোট, প্রায়ই কয়েকশো মিলিসেকেন্ড চলে, আর লাখো গ্রাহকের লাখো ফাংশন পাশাপাশি চালাতে হয়। Container দ্রুত আর ঘন — কিন্তু অচেনা গ্রাহকদের কোড একই kernel-এ চালানোর ঝুঁকি AWS নিতে চায়নি। ২০২০ সালে NSDI সম্মেলনে প্রকাশিত Firecracker-এর গবেষণাপত্রে AWS-এর প্রকৌশলীরা লেখেন, প্রথম দিকে প্রতিটি গ্রাহকের ফাংশন সেই গ্রাহকের জন্য আলাদা করা EC2 VM-এ চলত। সীমানা শক্ত, কিন্তু অপচয় বড়: একটি পূর্ণ VM-এর বাড়তি মেমরি আর বুট-সময় ছোট ফাংশনের তুলনায় বিশাল, আর একটি VM-এর ভেতরে এক গ্রাহকের ফাংশনই রাখা যেত।
বিকল্পগুলো তাঁরা যাচাই করেন। সাধারণ container আর seccomp-এ kernel ভাগের প্রশ্ন থেকে যায়। QEMU-ভিত্তিক VM-এ সীমানা ভালো, কিন্তু QEMU-র বিশাল device-অনুকরণ — যার কোডে বছরের পর বছর নিরাপত্তা-বাগ পাওয়া গেছে — আর ধীর বুট। gVisor-এর মতো user-space kernel-এ Linux-এর সব আচরণ মেলানো কঠিন। তাই সিদ্ধান্ত: KVM-এর hardware সীমানা রাখা, কিন্তু VMM-কে নতুন করে লেখা — Rust-এ, ChromeOS-এর crosvm থেকে শুরু করে, শুধু কয়েকটি virtio ডিভাইস নিয়ে, আর VMM-কেও jailer আর seccomp-এ বন্দি রেখে।
ফল: VM-এর সীমানা, container-এর কাছাকাছি গতি আর ঘনত্ব। একটি host-এ হাজারো microVM, প্রতিটি আলাদা গ্রাহকের — যা আগে অর্থনৈতিকভাবে অসম্ভব ছিল। দামও আছে: GPU বা বিশেষ হার্ডওয়্যার সাধারণত নেই, শুধু Linux guest, আর পুরোনো সফটওয়্যারের জন্য দরকারি ডিভাইসের সমর্থন নেই। AWS এটি উন্মুক্ত করায় Kata Containers-এর মতো প্রকল্পও এটি ব্যবহার করতে পারে।
মূল উপলব্ধি: নিরাপত্তা আর দক্ষতার দ্বন্দ্বে নতুন সমাধান প্রায়ই আসে “কী বাদ দেওয়া যায়” প্রশ্ন থেকে। Firecracker নতুন কিছু যোগ করেনি; পুরোনো VMM থেকে অপ্রয়োজনীয় প্রায় সব সরিয়েছে — কম কোড, কম বাগ, কম মেমরি, দ্রুত বুট।
প্র ০২“WASM আর WASI ২০০৮ সালে থাকলে Docker বানানোর দরকার হতো না” — এই মন্তব্য কতটা সত্যি?
২০১৯ সালের মার্চে Docker-এর প্রতিষ্ঠাতা Solomon Hykes একটি টুইটে প্রায় এই কথাটিই লেখেন, আর যোগ করেন যে server-এ WebAssembly-ই কম্পিউটিংয়ের ভবিষ্যৎ, যদি একটি মানসম্মত সিস্টেম-ইন্টারফেস থাকে। মন্তব্যটি অনেক আলোচনা জন্ম দেয়, কারণ একজন container-অগ্রদূত নিজেই অন্য প্রযুক্তির কথা বলছিলেন।
যুক্তির শক্তি: Docker মূলত দুটি সমস্যা সমাধান করেছিল — “আমার মেশিনে চলে” (একটি প্রোগ্রাম আর তার সব নির্ভরতা একসাথে প্যাক করা) আর হালকা isolation. Wasm দুটোই অন্যভাবে দেয়: একবার compile করা bytecode যেকোনো CPU আর OS-এ চলে (container image কিন্তু CPU-আর্কিটেকচারে বাঁধা — x86 image ARM-এ সরাসরি চলে না), আর sandbox ভাষা-স্তরে, শুরু থেকেই default-deny, WASI-র capability দিয়ে। Startup মিলিসেকেন্ডের ঘরে, image প্রায়ই খুব ছোট। Edge আর plugin-ব্যবস্থায় (যেমন একটি প্রোগ্রামের ভেতরে অন্যের কোড নিরাপদে চালানো) এগুলো বড় সুবিধা।
সীমাবদ্ধতা: বিদ্যমান সফটওয়্যারের বিশাল জগৎ — Python-এর C extension, ডেটাবেস, পুরোনো Java অ্যাপ — Linux-এর পূর্ণ syscall আচরণের ওপর দাঁড়িয়ে, যা WASI এখনো পুরোপুরি দেয় না (থ্রেড, নেটওয়ার্কিং-এর মতো ক্ষেত্রে মান ধীরে ধীরে আসছে)। Container আজকের প্রোগ্রাম প্রায় না বদলে চালায়; Wasm-এ প্রায়ই নতুন করে compile বা লিখতে হয়। তাছাড়া একই প্রসেসে বহু sandbox মানে runtime-এর বাগ আর CPU side channel-এর ঝুঁকি।
বাস্তবে দুটি একসাথে চলছে: কিছু container runtime Wasm module-কে container-এর মতো চালাতে পারে, আর Kubernetes-এর পাশেই Wasm কাজ বসানো যায়।
মূল উপলব্ধি: নতুন isolation প্রযুক্তি পুরোনোটিকে সরায় না, বর্ণালিতে নতুন একটি বিন্দু যোগ করে। কোনটি জিতবে তা নির্ভর করে কোন কাজে, আর বিদ্যমান সফটওয়্যার না বদলে কতটা চালানো যায় — প্রায়ই সেটিই প্রযুক্তি-গ্রহণের সবচেয়ে বড় শক্তি।
প্র ০৩“Cattle, not pets” আর immutable infrastructure একজন Linux প্রশাসকের কাজ কীভাবে বদলে দিয়েছে?
২০০০-এর দশকে একজন প্রশাসকের গর্ব ছিল সার্ভারের uptime — কয়েক বছর রিবুট ছাড়া চলা যন্ত্র। প্রতিটি সার্ভারের নাম (প্রায়ই গ্রহ বা পৌরাণিক চরিত্রের নামে), হাতে বসানো প্যাকেজ, হাতে বদলানো config. সমস্যা ধীরে ধীরে দেখা দিত: দুটি “একই রকম” সার্ভার আসলে কয়েক বছরের ছোট ছোট পরিবর্তনে আলাদা হয়ে যেত — একে বলে configuration drift. একটি নষ্ট হলে ঠিক একই রকম আরেকটি বানানো প্রায় অসম্ভব।
২০১০-এর দশকের শুরুতে “pets vs cattle” তুলনাটি ছড়ায় — Bill Baker-এর scale-up বনাম scale-out আলোচনা থেকে, Randy Bias-এর মতো cloud-প্রকৌশলীদের হাত ধরে। প্রায় একই সময়ে Chad Fowler-এর একটি লেখা (২০১৩) “immutable infrastructure” ধারণাকে জনপ্রিয় করে: সার্ভার কখনো হাতে বদলাবেন না, পুরোটা নতুন করে বানান। Netflix-এর Chaos Monkey (২০১১ সালের দিকে প্রকাশ্যে আলোচিত) ইচ্ছা করে চলমান সার্ভার বন্ধ করে দেখাত সিস্টেম একটি যন্ত্র হারিয়েও চলে কি না — পোষা প্রাণীর জন্য অকল্পনীয়, পালের জন্য স্বাভাবিক।
প্রশাসকের কাজ সরে গেছে যন্ত্র থেকে বর্ণনায়: হাতে কমান্ড নয়, কোডে লেখা infrastructure (Terraform, Ansible-এর মতো টুল), image বানানোর pipeline, পর্যবেক্ষণ, আর স্বয়ংক্রিয় পুনরুদ্ধার। OS-এর জ্ঞান কিন্তু হারায়নি — cgroup, namespace, kernel parameter, বুট, file system বোঝা ছাড়া ভালো image বা নির্ভরযোগ্য cluster বানানো যায় না। বদলেছে প্রয়োগের জায়গা। ঝুঁকিও আছে: সব স্বয়ংক্রিয় হলে একটি ভুল image মুহূর্তে শত সার্ভারে ছড়ায়, তাই ধাপে ধাপে ছড়ানো (canary) আর দ্রুত ফেরতের ব্যবস্থা জরুরি।
মূল উপলব্ধি: নির্ভরযোগ্যতা আর একটি যন্ত্রকে অমর রাখা থেকে আসে না, যেকোনো যন্ত্র হারানো সহ্য করার নকশা থেকে আসে। এজন্য OS-স্তরের পুনরুৎপাদনযোগ্যতা — একই image, একই আচরণ — নতুন যুগের প্রশাসনের ভিত্তি।
প্র ০৪Kubernetes-কে “cluster-এর OS” বলা কতটা ঠিক, আর উপমাটি কোথায় ভেঙে পড়ে?
Google ২০০০-এর দশকের মাঝামাঝি থেকে Borg নামের একটি ভেতরের ব্যবস্থায় হাজারো যন্ত্রে কাজ বসাত; ২০১৫ সালে EuroSys সম্মেলনে Borg-এর গবেষণাপত্র প্রকাশিত হয়। Borg-এর অভিজ্ঞতা, আর পরের পরীক্ষামূলক Omega থেকে শিক্ষা নিয়ে ২০১৪ সালে Kubernetes উন্মুক্ত প্রকল্প হিসেবে আসে। নকশাকারীরা নিজেরাই একে ডেটা সেন্টারের জন্য OS-এর মতো একটি স্তর হিসেবে বর্ণনা করেছেন।
উপমা যেখানে খাটে: OS যেমন প্রসেসের কাছ থেকে হার্ডওয়্যারের খুঁটিনাটি লুকায়, Kubernetes তেমনি অ্যাপের কাছ থেকে কোন যন্ত্রে চলছে তা লুকায়। Scheduler সম্পদ দেখে কাজ বসায়; requests আর limits শেষ পর্যন্ত প্রতিটি node-এর cgroup হয়; নষ্ট কাজ আবার চালু হয়, যেমন init নষ্ট সার্ভিস আবার চালায়; Service আর DNS মিলে একধরনের নাম-ব্যবস্থা, Volume একধরনের mount. API server যেন system call-এর দরজা — সবকিছু তার মাধ্যমে।
উপমা যেখানে ভাঙে: একটি kernel এক যন্ত্রের মালিক, তার কাছে পুরো অবস্থা এক মেমরিতে, সিদ্ধান্ত মাইক্রোসেকেন্ডে, আর একটি context switch নিশ্চিত। Kubernetes-এ অবস্থা থাকে etcd নামের একটি বিতরণকৃত ডেটাবেসে (consensus-ভিত্তিক — পর্ব ৫৩-এর বিষয়ের আত্মীয়), সিদ্ধান্ত সেকেন্ডে, আর প্রতিটি controller বারবার “কাঙ্ক্ষিত অবস্থা আর বাস্তব অবস্থার পার্থক্য” মেটানোর চেষ্টা করে — reconciliation loop. নেটওয়ার্ক ভাগ হয়ে গেলে, একটি node মরে গেছে নাকি শুধু যোগাযোগ বিচ্ছিন্ন, তা নিশ্চিত জানা যায় না — এক যন্ত্রের kernel-এ এমন অনিশ্চয়তা নেই। তাছাড়া Kubernetes নিজে isolation দেয় না; সেটি আসে প্রতিটি node-এর Linux kernel, container runtime আর প্রয়োজনে gVisor বা Kata থেকে।
মূল উপলব্ধি: উপমা ধারণা শেখার সিঁড়ি, প্রমাণ নয়। Kubernetes OS-এর ধারণাগুলো (scheduling, resource limit, supervision, naming) একটি বিতরণকৃত পরিবেশে নিয়ে গেছে, কিন্তু বিতরণকৃত ব্যবস্থার নতুন সমস্যা — আংশিক ব্যর্থতা, সময়ের অনিশ্চয়তা — সঙ্গে এনেছে।
প্র ০৫Serverless কি সবকিছুর ভবিষ্যৎ? কেউ কেউ serverless ছেড়ে আবার “সাধারণ” সার্ভারে ফিরছেন কেন?
২০১৯ সালে UC Berkeley-র গবেষকদের একটি আলোচিত প্রতিবেদন (“Cloud Programming Simplified: A Berkeley View on Serverless Computing”) বলেছিল, serverless cloud প্রোগ্রামিংয়ের প্রধান ধরন হয়ে উঠতে পারে — যেমন একসময় উচ্চস্তরের ভাষা assembly-কে সরিয়েছিল। একই প্রতিবেদনে সীমাবদ্ধতাও স্পষ্ট লেখা: ফাংশনগুলোর মধ্যে দ্রুত state ভাগ করার উপায় নেই, নেটওয়ার্ক-নির্ভর যোগাযোগ ধীর, পূর্বানুমেয় পারফরম্যান্স কঠিন, আর cold start.
বাস্তব অভিজ্ঞতা মিশ্র। ঘটনাভিত্তিক, অনিয়মিত কাজে — ছবি ছোট করা, webhook, রাতের রিপোর্ট, অল্প-ব্যবহৃত API — serverless অনেক দলের খরচ আর রক্ষণাবেক্ষণ দুটোই কমিয়েছে। কিন্তু ২০২৩ সালে Amazon Prime Video-র একটি প্রকৌশল-ব্লগ আলোচনার ঝড় তোলে: তাদের একটি ভিডিও-পর্যবেক্ষণ ব্যবস্থা প্রথমে বহু serverless অংশ দিয়ে বানানো ছিল, যেখানে অংশগুলোর মধ্যে ডেটা পাঠানো আর সমন্বয়ের খরচ বেড়ে যাচ্ছিল; তারা সেটি container-এ চলা একটি একক প্রোগ্রামে সাজিয়ে খরচ অনেক কমানোর কথা জানায়। মনে রাখার মতো বিষয়, কোম্পানিটি নিজেই প্রধান serverless প্রদানকারীদের একটি।
শিক্ষাটি “serverless খারাপ” নয়। সিমুলেটর ২-এর break-even মনে করুন: স্থির উচ্চ ট্রাফিকে প্রতি-অনুরোধের দাম যোগ হতে হতে সবসময়-চালু যন্ত্রকে ছাড়িয়ে যায়। কাজগুলো খুব ঘনভাবে কথা বললে প্রতিটি সীমানা-পারাপার নেটওয়ার্ক কল হয়ে যায়। সাথে আছে vendor lock-in — একটি প্রদানকারীর ঘটনা, অনুমতি আর workflow-ব্যবস্থায় বাঁধা কোড অন্যত্র সরানো কঠিন — আর সময়-সীমার কারণে লম্বা কাজ ভাঙার জটিলতা।
মূল উপলব্ধি: প্ল্যাটফর্ম কোনো দল বা মতাদর্শ নয়, কাজের আকৃতির সাথে মেলানোর বিষয়। ট্রাফিকের ধরন, state কোথায়, অংশগুলো কত ঘনভাবে কথা বলে — এগুলো মাপুন; প্রয়োজনে এক সিস্টেমে কয়েকটি প্ল্যাটফর্ম রাখুন, আর কাজ বদলালে সিদ্ধান্তও বদলান।
অনুশীলন
খাতায় হিসাব করুন, তারপর উত্তর মেলান।
-
হিসাব — serverless বনাম VM-এর মাসিক খরচ: একটি ফাংশন ২৫৬ MB মেমরিতে চলে, প্রতিটি warm কল ১০০ ms. মাসে ৩০ লাখ অনুরোধ, যার ২% cold, আর প্রতিটি cold start-এ বাড়তি ১ সেকেন্ড বিল হয়। উদাহরণ-দাম: প্রতি ১০ লাখ অনুরোধে ০.২০ ডলার, প্রতি GB-second-এ ০.০০০০১৬৬৬৬৭ ডলার। ফ্রি-কোটা ধরবেন না। (ক) মাসিক খরচ কত? (খ) একই কাজে ঘণ্টায় ০.০৫ ডলারের ২টি VM সারা মাস (৭২০ ঘণ্টা) চালালে খরচ কত?
(ক) অনুরোধের দাম: ৩০ লাখ = ৩ × ১০ লাখ ⇒ ৩ × ০.২০ = ০.৬০ ডলার। কাজের সময়: ২৫৬ MB = ০.২৫ GB. Warm অংশ: ৩০,০০,০০০ × ০.১ s × ০.২৫ GB = ৭৫,০০০ GB-s. Cold start-এর বাড়তি অংশ: ২% = ৬০,০০০টি cold × ১ s × ০.২৫ GB = ১৫,০০০ GB-s. মোট ৯০,০০০ GB-s × ০.০০০০১৬৬৬৬৭ ≈ ১.৫০ ডলার (৭৫,০০০-এ ১.২৫ আর ১৫,০০০-এ ০.২৫)। সব মিলিয়ে প্রায় ২.১০ ডলার।
(খ) ২ × ০.০৫ × ৭২০ = ৭২ ডলার। এই ট্রাফিক (৩০ লাখ ÷ ২৫,৯২,০০০ সেকেন্ড ≈ গড়ে সেকেন্ডে মাত্র ১.২টি অনুরোধ) serverless-এর জন্য আদর্শ। কিন্তু gateway, ডেটাবেস আর egress-এর খরচ দুই ক্ষেত্রেই আলাদা করে যোগ করতে হবে, আর cold start-এর ২% অনুরোধে ব্যবহারকারী বাড়তি ১ সেকেন্ড অপেক্ষা করবেন — p95 হয়তো ভালো, কিন্তু p99 খারাপ।
-
হিসাব — density: একটি host-এ ৩২ GiB RAM, যার ২ GiB host OS আর hypervisor রাখে। প্রতিটি অ্যাপের নিজের দরকার ২৫৬ MiB. প্রতি instance-এর বাড়তি অংশ ধরুন: পূর্ণ VM ৪০০ MiB, microVM ৪০ MiB, container ৮ MiB. প্রতিটি ধরনে কয়টি instance ধরবে? microVM বেছে নিলে VM-এর চেয়ে কতগুলো বেশি অ্যাপ চলবে?
ব্যবহারযোগ্য = ৩০ GiB = ৩০ × ১০২৪ = ৩০,৭২০ MiB. VM: ২৫৬ + ৪০০ = ৬৫৬ MiB ⇒ ৩০,৭২০ ÷ ৬৫৬ = ৪৬.৮ ⇒ ৪৬টি। microVM: ২৯৬ MiB ⇒ ১০৩.৮ ⇒ ১০৩টি। Container: ২৬৪ MiB ⇒ ১১৬.৪ ⇒ ১১৬টি। (ভগ্নাংশ নিচে নামাতে হয় — অর্ধেক instance চালানো যায় না।)
microVM-এ VM-এর চেয়ে ১০৩ − ৪৬ = ৫৭টি বেশি অ্যাপ — দ্বিগুণের বেশি, অথচ প্রতিটির নিজের guest kernel আর hardware-সীমানা থাকছে। Container আরও ১৩টি বেশি দেয়, কিন্তু kernel ভাগ করে। বাস্তবে মেমরির পাশাপাশি CPU আর নেটওয়ার্কও সীমা হতে পারে, আর সব অ্যাপ একসাথে পুরো মেমরি ব্যবহার না করলে overcommit (পর্ব ৩৫, ৩৮) দিয়ে আরও বেশি বসানো যায় — ঝুঁকিসহ।
-
আউটপুট অনুমান করুন: তিনটি জায়গায় একই কমান্ড চালানো হলো:
grep -c -w hypervisor /proc/cpuinfo; cat /proc/1/comm. জায়গাগুলো: (ক) নিজের ল্যাপটপে সরাসরি ইনস্টল করা Ubuntu, যার CPU-তে ৮টি logical core; (খ) একটি cloud VM-এ সরাসরি SSH করে, ২টি vCPU; (গ) সেই VM-এর ভেতরে একটি Docker container (docker run --init). প্রতিটিতে কী আশা করবেন?grep -cমিলে যাওয়া লাইন গোনে, আর/proc/cpuinfo-এ প্রতিটি logical CPU-র নিজেরflagsলাইন আছে।(ক) bare metal:
0(কোনো hypervisor flag নেই — যদি না Windows-এর Hyper-V বা VBS-এর মতো কিছু Linux-কে নিজেই VM-এ চালায়) আরsystemd. খেয়াল করুন,grep -cকিছু না মিললে exit status ১ দেয়, কিন্তু সংখ্যা0ছাপে। (খ) VM:2(প্রতিটি vCPU-র লাইনে flag) আর সাধারণতsystemd— VM-এর ভেতরে পূর্ণ OS. (গ) VM-এর ভেতরে container:2—/proc/cpuinfocontainer-কে VM-এরই CPU দেখায়, কারণ kernel একই — আর PID ১ হলো Docker-এর ছোট init, সাধারণতdocker-init(tini). এই পাতার রানারে একই ধরনের ছবি দেখেছেন, শুধু সেখানে Podman বলেpodman-init. -
লিখুন — requests আর limits থেকে cgroup: একটি Kubernetes pod-এর container-এর জন্য YAML-এর
resourcesঅংশ লিখুন: request ২৫০ millicore CPU আর ১২৮ MiB মেমরি, limit ৫০০ millicore আর ২৫৬ MiB. cgroup v2-এcpu.maxআরmemory.max-এ কী মান হবে (CFS period ১০০,০০০ µs ধরে)? Pod-টি কোন QoS শ্রেণি পাবে?resources: requests: cpu: "250m" memory: "128Mi" limits: cpu: "500m" memory: "256Mi"cpu.max: ৫০০m = অর্ধেক CPU ⇒ প্রতি ১০০,০০০ µs-এ ৫০,০০০ µs ⇒
50000 100000. memory.max: ২৫৬ MiB = ২৫৬ × ১,০৪৮,৫৭৬ =268435456বাইট। Request-গুলো scheduler-এর হিসাবে লাগে (node-এ অন্তত ০.২৫ CPU আর ১২৮ MiB খালি থাকতে হবে) আর CPU-র ভাগ হিসেবে cgroup-এরcpu.weight-এ প্রভাব ফেলে। Request আর limit সমান নয়, তাই QoS শ্রেণি Burstable. নিজের cluster-এ container-এর ভেতরেcat /sys/fs/cgroup/cpu.max /sys/fs/cgroup/memory.maxদিয়ে মিলিয়ে দেখতে পারেন (cgroup v2 আর runtime-এর ওপর নির্ভর করে)।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমপ্রতিবেশীর সাথে সবচেয়ে বেশি ভাগ করা থেকে সবচেয়ে কম ভাগ করা (দুর্বলতম থেকে শক্ততম সীমানা) — সাজান (▲▼ চেপে সরান)
- ⚙️ একই OS-এ সাধারণ process
- 📦 container (namespace + cgroup, একই kernel)
- 🛡️ gVisor-এর মতো user-space kernel-এ container
- 🔥 microVM (নিজস্ব guest kernel, ন্যূনতম VMM)
- 🏠 bare metal — নিজের ভৌত যন্ত্র
নিচের কোনটি host-এর kernel সরাসরি অন্য ভাড়াটেদের সাথে ভাগ করে?
- Firecracker microVM
- সাধারণ Docker/Podman container
- Kata Containers-এর pod
- KVM-এর ওপর পূর্ণ VM
gVisor কীভাবে container-এর isolation বাড়ায়?
- প্রতিটি container-কে একটি পূর্ণ VM-এ চালিয়ে
- host kernel-এ একটি বিশেষ module লোড করে
- অ্যাপের syscall একটি user-space kernel (Sentry) ধরে সামলায়
- container-এর সব ফাইল শুধু-পড়ার অনুমতিতে রেখে
একটি serverless ফাংশনে রাতে প্রতি ৩০ মিনিটে গড়ে একটি অনুরোধ আসে, আর প্ল্যাটফর্ম ১০ মিনিট অলস instance সরিয়ে দেয়। cold start ৮০০ ms, কাজ ৫০ ms. রাতের অনুরোধগুলোর জন্য কোনটি সবচেয়ে সম্ভাব্য?
- বেশিরভাগ অনুরোধ cold — প্রায় ৮৫০ ms
- প্রায় সব অনুরোধ warm — প্রায় ৫০ ms
- অনুরোধগুলো বাতিল হয়, কারণ instance নেই
- অনুরোধগুলো লাইনে দাঁড়িয়ে কয়েক মিনিট অপেক্ষা করে
Public cloud-গুলো সাধারণত ভিন্ন গ্রাহকের container-কে আলাদা VM বা microVM-এ রাখে। প্রধান কারণ কী?
- VM-এ container দ্রুত চালু হয়
- container image VM-এ ছোট হয়ে যায়
- container-এ মেমরির সীমা দেওয়া যায় না
- একই kernel ভাগ করায় kernel-এর বাগ ভাড়াটেদের মধ্যে সীমানা ভাঙতে পারে
Kubernetes-এ একটি container-এর resources.limits.cpu: "500m" দেওয়া। Container-টি এর বেশি CPU চাইলে কী ঘটে?
- Container সাথে সাথে OOM kill হয়
- cgroup কোটা শেষে container-কে থামিয়ে রাখা হয় (throttling)
- Scheduler pod-টি অন্য node-এ সরিয়ে দেয়
- Container বাড়তি CPU পায়, শুধু লগে সতর্কবার্তা আসে
একটি Linux shell-এ দেখা গেল: /proc/cpuinfo-তে hypervisor flag আছে, root file system overlay, আর PID ১ হলো podman-init. সবচেয়ে যুক্তিসঙ্গত সিদ্ধান্ত কী?
- shell সরাসরি bare metal-এ, কোনো container ছাড়া
- shell একটি VM-এ, কিন্তু container-এ নয়
- shell একটি container-এ, যা নিজে একটি VM-এর ভেতরে
- তথ্যগুলো পরস্পরবিরোধী, এমন অবস্থা সম্ভব নয়
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- Isolation-এর বর্ণালি: process → container → gVisor/Kata → microVM → পূর্ণ VM → bare metal; ডানে গেলে কম ভাগ, শক্ত সীমানা, কিন্তু বেশি startup সময় আর মেমরি।
- Container kernel ভাগ করে, তাই multi-tenant cloud container-কে VM বা microVM-এর ভেতরে রাখে; Firecracker ন্যূনতম ডিভাইসের microVM (AWS-এর দাবি: প্রায় ১২৫ ms বুট, ৫ MiB-এর কম বাড়তি মেমরি)।
- Serverless: event-driven, scale to zero, pay-per-use, সময়-সীমা, state বাইরে; cold start শান্ত সময়েই বেশি ঘটে; provisioned concurrency আর snapshot এর প্রতিকার।
- Percentile দেখুন, গড় নয়; স্থির উচ্চ ট্রাফিকে সবসময়-চালু যন্ত্র সস্তা, কম বা হঠাৎ ট্রাফিকে serverless — break-even হিসাব করুন।
- Cloud-OS: hypervisor নতুন kernel, cattle not pets, immutable infrastructure, container-optimised OS; Kubernetes-এ requests = scheduler-এর হিসাব, limits = cgroup-এর ছাদ।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৫৩ · Distributed system-এর OS-দিক — RPC, clock আর distributed file systemপরবর্তী পর্বঅনেক যন্ত্র মিলে একটি সিস্টেম।
- অপারেটিং সিস্টেম · কোর্স হোম৬৪টি পর্বপুরো কোর্সের রোডম্যাপ — ১০টি মডিউল।
- পর্ব ৫০ · Virtual machine আর hypervisorপেছনে ফিরেVM-এর ভেতরের যন্ত্র — trap, VT-x, nested paging.
- পর্ব ৫১ · Container-এর ভেতর — namespace, cgroup আর Dockerপেছনে ফিরেContainer কীভাবে তৈরি হয়, ধাপে ধাপে।
- DBMS · Cloud databaseপাশের ট্র্যাকServerless-এর state কোথায় রাখবেন।
- Computer Fundamentals · Cloud computingসহজ পরিচিতিIaaS, PaaS, SaaS — শুরুর স্তর থেকে।