এই পর্বে যা শিখবেন
- Compose কোন তিনটি কাজ পারে না, আর সেখান থেকে Kubernetes-এর দরকার কেন আসে
- Control plane আর node-এর প্রতিটি অংশের কাজ, এবং কে কার সাথে কথা বলে
- Declarative আর imperative-এর পার্থক্য,
specআরstatus-এর অর্থ kubectl applyথেকে চলন্ত container পর্যন্ত পুরো পথ, watch আর scheduler-সহget,describe,explain, event, namespace, label আর selector ব্যবহার করে cluster পড়া- etcd quorum-এর হিসাব, managed Kubernetes-এ কে কী চালান, আর কখন Kubernetes না নেওয়াই ভালো
১ · Compose যেখানে থেমে যায়
মডিউল ৭-এ আপনি image বানিয়েছেন, container চালিয়েছেন, আর পর্ব ৪০-এ একটি file দিয়ে পুরো stack তুলেছেন। একটি VM-এ একটি কাল্পনিক অনলাইন বাজার অ্যাপ এভাবে ভালোই চলে। তারপর ঈদের আগে ভিড় বাড়ল, আর VM-টি রাত ২টায় বন্ধ হয়ে গেল। Compose এখানে কী করবে? কিছুই না, কারণ Compose নিজেই সেই VM-এর ওপর নির্ভর করে।
| দরকার | Docker Compose | Kubernetes-এর মতো orchestrator |
|---|---|---|
| অনেক machine | একটি Docker Engine-এর ভেতরেই কাজ করে | অনেক machine মিলে একটি cluster; কোন container কোথায় বসবে, সে নিজে ঠিক করে |
| নিজে থেকে সেরে ওঠা | restart: always একই machine-এ container আবার চালায়; machine গেলে সব গেল | Self-healing: Pod হারালে অন্য node-এ নতুন Pod বসে |
| ধাপে ধাপে আপডেট | up -d container বদলায়, মাঝে সাধারণত অল্প ফাঁক থাকে | কয়েকটি করে Pod বদলায়, সমস্যা হলে থামে আর ফিরিয়ে আনা যায় (পর্ব ৪৪) |
| নজরদারি | docker compose up চললে কাজ শেষ; পরে কেউ মিলিয়ে দেখে না | Controller সারাক্ষণ মিলিয়ে দেখে |
একটি বাস কোম্পানির ডিপো ভাবুন। দেয়ালের চার্টে লেখা: “সকাল ৮টায় এই রুটে ৩০টি বাস থাকবে।” একটি বাস নষ্ট হলে ম্যানেজার কোনো মালিককে ফোন করে অনুমতি চান না। তিনি চার্ট দেখেন, গুনে দেখেন ২৯টি আছে, আর অন্য একটি বাস পাঠান। চার্ট হলো desired state, ম্যানেজার হলো control plane, আর প্রতিটি বাসের চালক হলেন node-এ থাকা kubelet. Compose সেখানে একজন মালিক, যাঁর একটিই গাড়ি: গাড়ি নষ্ট হলে তিনি নিজেই সারান, অন্য গাড়ি পাঠানোর উপায় তাঁর নেই।
Google বহু বছর ধরে নিজের সব কাজ Borg নামের একটি অভ্যন্তরীণ cluster manager-এ চালাত। ২০১৪ সালে Google Kubernetes open source হিসেবে প্রকাশ করে; নামটি গ্রিক, অর্থ জাহাজের চালক বা মাঝি। ছোট করে লেখা হয় k8s, কারণ k আর s-এর মাঝে ৮টি অক্ষর। Borg-এর বিস্তারিত নিবন্ধ আসে পরে: Verma ও সহকর্মীদের “Large-scale cluster management at Google with Borg” (EuroSys, ২০১৫). একই বছর Kubernetes ১.০ প্রকাশিত হয়, আর Linux Foundation-এর অধীনে গঠিত CNCF (Cloud Native Computing Foundation) প্রকল্পটির দায়িত্ব নেয়। Burns ও সহকর্মীদের “Borg, Omega, and Kubernetes” (২০১৬) লেখায় এই তিন প্রজন্মের শিক্ষা পাবেন।
একই সময়ে Docker Swarm আর Apache Mesos-ও এই কাজ করত, আর আজও HashiCorp Nomad-এর মতো বিকল্প আছে। Kubernetes শুধু বেশি ব্যবহৃত, একমাত্র নয়। তবে cloud provider-গুলোর managed সেবা আর tool-এর বড় বাস্তুতন্ত্র একে আজকের চাকরির বাজারে প্রায় আবশ্যিক দক্ষতা করে তুলেছে।
২ · Cluster-এর মানচিত্র: control plane আর node
একটি Kubernetes cluster-এর দুই ভাগ। Control plane হলো ডিপোর অফিস: সিদ্ধান্ত নেয়, হিসাব রাখে। Node হলো সেই VM বা physical machine, যেখানে আপনার container সত্যিই চলে। পর্ব ৬-এ আপনি control plane আর data plane শব্দ দুটি cloud-এর প্রসঙ্গে দেখেছেন; এখানেও একই ভাগ: node-গুলো মিলে data plane.
Kubernetes cluster-এর অংশগুলো
সব তির API server-এ এসে মেলে। etcd-র সাথে কথা বলে শুধু API server.
| অংশ | কোথায় | কাজ | ডিপোর ভাষায় |
|---|---|---|---|
| API server | Control plane | একমাত্র দরজা: পরিচয় যাচাই, অনুমতি, নিয়ম যাচাই, তারপর সংরক্ষণ | অফিসের কাউন্টার |
| etcd | Control plane | সব object রাখার key-value database; Raft দিয়ে কয়েকটি কপি মেলায় | হিসাবের মূল খাতা |
| Scheduler | Control plane | যে Pod-এর node ঠিক হয়নি, তার জন্য node বেছে দেয় | কোন বাস কোন রুটে, যিনি ঠিক করেন |
| Controller manager | Control plane | অনেকগুলো controller একসাথে চালায়: Deployment, ReplicaSet, Node, Job প্রভৃতি | চার্ট মিলিয়ে দেখা ম্যানেজার |
| kubelet | প্রতিটি node | এই node-এর জন্য ঠিক হওয়া Pod চালায়, অবস্থা জানায় | বাসের চালক |
| kube-proxy | প্রতিটি node | Service-এর ঠিকানা থেকে Pod-এ traffic পাঠানোর নিয়ম বসায় (পর্ব ৪৫); কিছু network plugin এই কাজ নিজেই করে | স্টেশনের দিকনির্দেশক |
| Container runtime | প্রতিটি node | আসলে container চালায়; kubelet তার সাথে CRI দিয়ে কথা বলে। যেমন containerd, CRI-O | বাসের ইঞ্জিন |
CRI (Container Runtime Interface) হলো kubelet আর runtime-এর মাঝের একটি gRPC চুক্তি। এর ফলে Kubernetes কোনো একটি runtime-এ বাঁধা নয়। ১.২৪ version (২০২২) থেকে Kubernetes-এর ভেতরের Docker সংযোগ (dockershim) সরানো হয়েছে, তবে Docker দিয়ে বানানো OCI image আগের মতোই চলে। পর্ব ৩৬-এর image এখানেও একই image.
এই পর্বের আউটপুট কোথা থেকে: নিচের সব kubectl আউটপুট একটি local single-node cluster-এ (Kubernetes ১.৩৪) আসল run থেকে নেওয়া। নাম, age, IP আর সময় আপনার cluster-এ আলাদা হবে। অপ্রাসঙ্গিক লাইন … দিয়ে ছাঁটা হয়েছে। Production থেকে এর বড় পার্থক্য: এখানে একটি machine-ই control plane আর node, আর etcd-র সদস্য মাত্র একজন। একাধিক node লাগে এমন অংশ আমরা model হিসেবে চিহ্নিত করেছি।
এই cluster-এ control plane-এর অংশগুলোও Pod হিসেবে চলে। তাই সেগুলো চোখে দেখা যায়:
$ kubectl get pods -n kube-system --no-headers
etcd-docker-desktop 1/1 Running 0 4m52s
kube-apiserver-docker-desktop 1/1 Running 0 4m52s
kube-controller-manager-docker-desktop 1/1 Running 0 4m16s
kube-proxy-j6w94 1/1 Running 0 4m16s
kube-scheduler-docker-desktop 1/1 Running 0 4m52s
…৩ · আদেশ নয়, ফল বলুন: declarative desired state
Docker শেখার সময় আপনি ধাপগুলো বলে দিতেন: “এই container চালান, তারপর ওইটি মুছুন।” একে বলে imperative ধরন। Kubernetes-এর মূল ধরন declarative: আপনি শেষ ফলটি লিখে জমা দেন, আর পথ খোঁজে সিস্টেম। পার্থক্যটি রিকশাচালককে “ডানে যান, তারপর বামে” বলা আর রাইড-শেয়ারিং অ্যাপে শুধু গন্তব্য বসিয়ে দেওয়ার মতো।
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: abcl-ka
labels:
app: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: 10m
memory: 16Mi
limits:
cpu: 100m
memory: 32Miএই file-কে বলে manifest. প্রতিটি API object-এর একই গড়ন:
| ক্ষেত্র | অর্থ | কে লেখে |
|---|---|---|
apiVersion | API-র কোন দল আর version. apps/v1 মানে apps দল, v1 | আপনি |
kind | Object-এর ধরন: Pod, Deployment, Service… | আপনি |
metadata | নাম, namespace, label, আর সিস্টেমের লেখা uid, resourceVersion | দুজনেই |
spec | যা চান: desired state | আপনি |
status | যা আছে: দেখা অবস্থা | Controller, kubelet |
এই একটি ভাগ, spec বনাম status, পুরো Kubernetes-কে ব্যাখ্যা করে। Server-এ জমা থাকা object পড়লে দুটিই পাশাপাশি দেখা যায়:
$ kubectl get deploy web -n abcl-ka -o yaml
apiVersion: apps/v1
kind: Deployment
metadata:
…
generation: 3
name: web
namespace: abcl-ka
resourceVersion: "1303"
uid: 152895a9-4ff0-43cd-88c8-9e8e2228b19c
spec:
progressDeadlineSeconds: 600
replicas: 3
…
status:
availableReplicas: 3
…
observedGeneration: 3
readyReplicas: 3
replicas: 3
updatedReplicas: 3spec-এ আপনার না লেখা progressDeadlineSeconds-ও আছে: API server ডিফল্ট মান বসিয়েছে। generation বাড়ে যখন spec বদলায়। observedGeneration জানায় controller কোন generation পর্যন্ত দেখেছে। দুটি সমান, অর্থাৎ controller সর্বশেষ চাওয়াটি দেখেছে।
একই file বারবার জমা দিলে কী হয়
create একটি আদেশ, তাই দ্বিতীয়বার ব্যর্থ। apply বলে “এটাই হোক”, তাই সে idempotent. নিচের run-এ আরেকটি পরীক্ষাও আছে: হাতে scale করে file থেকে সরে যাওয়া, যাকে drift বলে, তারপর apply-এ ফিরে আসা।
$ kubectl create -f web.yaml
Error from server (AlreadyExists): error when creating "web.yaml":
deployments.apps "web" already exists
$ kubectl apply -f web.yaml
deployment.apps/web unchanged
$ kubectl scale deploy/web --replicas=5 -n abcl-ka
deployment.apps/web scaled
$ kubectl get deploy web -n abcl-ka
NAME READY UP-TO-DATE AVAILABLE AGE
web 5/5 5 5 109s
$ kubectl apply -f web.yaml
deployment.apps/web configured
$ kubectl get deploy web -n abcl-ka
NAME READY UP-TO-DATE AVAILABLE AGE
web 3/3 3 3 111sপ্রথম error লম্বা বলে দুই লাইনে ভাঙা হয়েছে। scale-এর পরে cluster আর file মিলছিল না। Kubernetes এই drift নিজে থেকে ফেরায় না, কারণ scale-ও একটি বৈধ নতুন চাওয়া: সে spec-ই বদলেছে। apply আবার file-এর চাওয়াটি বসাল। তাই নিয়ম হলো, মূল সত্য একটি জায়গায় রাখুন, সাধারণত Git-এ। পর্ব ৬০-এর GitOps এই ধারণার ওপরেই দাঁড়ানো।
৪ · Reconciliation loop: পাম্প-অপারেটরের নিয়ম
একটি ফ্ল্যাটবাড়ির পাম্প-অপারেটর ভাবুন। তাঁর একটিই নিয়ম: ছাদের ট্যাংক তিন-চতুর্থাংশ ভরা থাকবে। তিনি কিছুক্ষণ পরপর ট্যাংক দেখেন। কম থাকলে পাম্প চালান, বেশি থাকলে বন্ধ করেন, ঠিক থাকলে কিছুই করেন না। কে কতটা পানি খরচ করল, তা তাঁর মনে রাখার দরকার নেই। এক ঘণ্টা ঘুমিয়ে উঠলেও তিনি শুধু এখনকার পানির উচ্চতা দেখে কাজ করেন। AC-র thermostat-ও একই নিয়মে চলে।
Kubernetes-এর প্রতিটি controller ঠিক এই পাম্প-অপারেটর। তার কাজ তিনটি ধাপের একটি অশেষ লুপ:
status দেখা → spec-এর সাথে পার্থক্য → পার্থক্যটুকু ঠিক করালক্ষ করুন, কোনো controller পুরো কাজ একবারে শেষ করে না। ReplicaSet controller Pod গোনে আর কম-বেশি মেটায়। Pod কোথায় বসবে, সেটি scheduler-এর কাজ। Container চালানো kubelet-এর কাজ। প্রত্যেকে নিজের ছোট লুপ চালায়, আর ফলটি API server-এ লিখে রাখে। একজনের লেখা অন্যজনের কাজের সূত্র হয়।
নিচের simulator-এ ১ tick মানে এক দফা লুপ। EVENTS বদলে দেখুন: Pod মরুক, আপনি চাওয়া বদলান, node হারান। Controller প্রতিবার শুধু পার্থক্যটুকু ঠিক করে।
const START_TICKS = 2; // a new Pod needs 2 ticks to become Running
const EVENTS = {
4: { kill: 1, note: "1 Pod crashed" },
7: { desired: 5, note: "user: replicas=5" },
11: { kill: 2, note: "2 Pods lost with a node" },
15: { desired: 2, note: "user: replicas=2" },
};
let desired = 3, pods = [], nextId = 1;
console.log("tick desired running starting action");
for (let t = 0; t <= 17; t++) {
const ev = EVENTS[t];
if (ev && ev.desired) desired = ev.desired; // spec changed
if (ev && ev.kill) pods.splice(0, ev.kill); // world changed
// OBSERVE: count what really exists now
const running = pods.filter(p => p.readyAt <= t).length;
const starting = pods.length - running;
// DIFF and ACT: only the difference, then stop
const diff = desired - pods.length;
let action = "nothing to do";
if (diff > 0) {
for (let i = 0; i < diff; i++)
pods.push({ name: "pod-" + nextId++, readyAt: t + START_TICKS });
action = "create " + diff;
} else if (diff < 0) {
pods.splice(0, -diff);
action = "delete " + (-diff);
}
const line = String(t).padStart(4) + String(desired).padStart(8) +
String(running).padStart(8) + String(starting).padStart(9) + " " + action;
console.log(line + (ev ? " <- " + ev.note : ""));
}
console.log("final pods:", pods.map(p => p.name).join(" "));আউটপুটের অংশ:
tick desired running starting action
0 3 0 0 create 3
1 3 0 3 nothing to do
2 3 3 0 nothing to do
3 3 3 0 nothing to do
4 3 2 0 create 1 <- 1 Pod crashed
…
11 5 3 0 create 2 <- 2 Pods lost with a node
…
15 2 5 0 delete 3 <- user: replicas=2
…
final pods: pod-7 pod-8
Tick ১-এ running ০, তবু controller নতুন Pod বানায়নি। কারণ starting-এর ৩টি সে গুনেছে। না গুনলে প্রতি tick-এ ৩টি করে Pod বাড়ত: এটি নিয়ন্ত্রণ-লুপের ক্লাসিক ভুল। Tick ৪ আর ১১-তে ঘটনার কারণ তার জানার দরকার নেই, সে শুধু সংখ্যা মেলায়। আসল ReplicaSet controller কোন Pod আগে মুছবে, তার নিয়ম এখানকার চেয়ে জটিল; এই simulator শুধু লুপটি দেখায়।
Level-triggered: ঘটনা নয়, অবস্থা
এই ধরনটিকে বলে level-triggered. Edge-triggered ব্যবস্থা ঘটনার ওপর চলে: “একটি Pod মরেছে” বার্তা হারালে সমস্যা চিরকাল থেকে যায়। Level-triggered ব্যবস্থা চলে এখনকার অবস্থার ওপর: controller crash করে ফিরলেও সে আবার গুনবে, আর ঠিক করে ফেলবে। তাই controller-কে যেকোনো মুহূর্তে বন্ধ করে আবার চালানো নিরাপদ। পর্ব ১৭-এর autoscaling-ও একই ধরনের লুপ।
৫ · kubectl apply-এর যাত্রা আর scheduler
এবার সব অংশকে একসাথে কাজ করতে দেখি। আসল run-এর web.yaml জমা দেওয়ার পর কী কী ঘটে, তা “পরে” চেপে দেখুন। লক্ষ করুন, kubectl কোনো node-এর সাথে কথাই বলে না।
- kubectlঅনুরোধ
- API serverযাচাই
- etcdসংরক্ষণ
- ControllerPod object
- Schedulernode বাছাই
- kubeletcontainer
- status৩/৩ Ready
$ kubectl apply -f web.yaml # YAML থেকে JSON, তারপর HTTPS-এ API server-এ
Authentication # আপনি কে? Authorization # RBAC: এই কাজের অনুমতি আছে? Admission # নীতি, ডিফল্ট মান Validation # schema ঠিক আছে?
deployment.apps/web created # etcd-তে Deployment object জমা হয়েছে
deployment/web Scaled up replica set
web-69f4d599d6 from 0 to 3
replicaset/web-69f4d599d6 Created pod: web-69f4d599d6-jszbc
# Pod-এর spec.nodeName এখনো ফাঁকা
pod/web-69f4d599d6-jszbc Scheduled
Successfully assigned abcl-ka/web-69f4d599d6-jszbc
to docker-desktop
Pulled Container image "nginx:1.27-alpine"
already present on machine
Created Created container: nginx
Started Started container nginx
$ kubectl rollout status deploy/web -n abcl-ka
…
deployment "web" successfully rolled out
- জমা দেওয়া।
kubectlmanifest পড়ে আপনার পরিচয়সহ API server-এ অনুরোধ পাঠায়।kubectlকোনো machine-এ ঢুকে কিছু চালায় না। - দরজার যাচাই। API server চারটি প্রশ্নের উত্তর খোঁজে: আপনি কে, অনুমতি আছে কি না, কোনো নীতি আটকাচ্ছে কি না, object-এর গড়ন ঠিক কি না। এখানে ব্যর্থ হলে কিছুই জমা হয় না।
- খাতায় লেখা। Object etcd-তে জমা হলো, আর আপনি পেলেন
created. খেয়াল করুন: এই শব্দের অর্থ “চাওয়াটি লেখা হয়েছে”, Pod চলছে এমন নয়। এই মুহূর্তে একটি Pod-ও নেই। - Controller-দের পালা। Deployment controller নতুন object দেখে একটি ReplicaSet বানাল। ReplicaSet controller গুনে পেল ০, চাই ৩, তাই ৩টি Pod object বানাল। এগুলো এখনো শুধু API-র লেখা, কোনো container নয়। পর্ব ৪৪-এ এই স্তর দুটি বিস্তারে দেখবেন।
- Scheduler-এর পালা। Scheduler নজর রাখে এমন Pod-এ, যার
spec.nodeNameফাঁকা। সে node বেছে একটি binding লেখে। Scheduler নিজে কোনো container চালায় না; সে শুধু একটি ক্ষেত্র ভরে। - kubelet-এর পালা। প্রতিটি kubelet নিজের node-এর Pod-এ নজর রাখে। সে নিজের নাম দেখে CRI দিয়ে runtime-কে ডাকল। Image আগেই ছিল, তাই pull লাগেনি।
- Status ফেরা। kubelet Pod-এর অবস্থা লেখে, ReplicaSet আর Deployment controller নিজের
statusমেলায়। তিনটি Pod Ready হওয়ায়rollout statusশেষ হলো।
$ kubectl get deploy,rs,pods -n abcl-ka
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/web 3/3 3 3 2s
NAME DESIRED CURRENT READY AGE
replicaset.apps/web-69f4d599d6 3 3 3 2s
NAME READY STATUS RESTARTS AGE
pod/web-69f4d599d6-92562 1/1 Running 0 2s
pod/web-69f4d599d6-jszbc 1/1 Running 0 2s
pod/web-69f4d599d6-wbths 1/1 Running 0 2sআসল আউটপুটে -o wide দেওয়া ছিল; এখানে সেই বাড়তি কলাম ছাঁটা। তিন স্তর একসাথে দেখা যাচ্ছে: Deployment, তার ReplicaSet, আর তার Pod. Pod-এর নামে ReplicaSet-এর নাম ঢুকে আছে। deployment.apps/web-এ ২ সেকেন্ডে ৩/৩ সম্ভব হয়েছে, কারণ image আগেই node-এ ছিল। নতুন image নামাতে হলে বেশি সময় লাগবে।
Scheduler: আগে বাদ, তারপর নম্বর
Scheduler দুই ধাপে কাজ করে। Filter: যে node-এ Pod বসতেই পারবে না, সেগুলো বাদ। Score: বাকিদের নম্বর দিয়ে সেরাটি বাছাই। নিচের simulator-এ POD আর NODES বদলে দেখুন। সব node একটি নমুনা। Capacity-র হিসাব হয় Pod-এর ঘোষিত request দিয়ে, node-এর এখনকার আসল ব্যবহার দিয়ে নয়। পুরো গল্প পর্ব ৪৮-এ।
const POD = { cpu: 500, mem: 512, nodeSelector: { disk: "ssd" }, image: "shop:1.4" };
const NODES = [ // cpu in millicores, mem in MiB, free = capacity - requested
{ name: "node-a", ready: true, cap: [2000, 4096], used: [1800, 1024], disk: "ssd", images: [] },
{ name: "node-b", ready: true, cap: [2000, 4096], used: [ 500, 1024], disk: "ssd", images: [] },
{ name: "node-c", ready: true, cap: [4000, 8192], used: [2000, 6000], disk: "ssd", images: ["shop:1.4"] },
{ name: "node-d", ready: true, cap: [4000, 8192], used: [ 200, 512], disk: "hdd", images: [] },
{ name: "node-e", ready: false, cap: [4000, 8192], used: [ 0, 0], disk: "ssd", images: [] },
];
function why(n) { // FILTER: any "no" removes the node
if (!n.ready) return "node not Ready";
if (POD.nodeSelector.disk !== n.disk) return "selector disk=ssd";
if (n.cap[0] - n.used[0] < POD.cpu) return "not enough cpu";
if (n.cap[1] - n.used[1] < POD.mem) return "not enough memory";
return "";
}
function score(n) { // SCORE: most free share after placing
const cpu = (n.cap[0] - n.used[0] - POD.cpu) / n.cap[0];
const mem = (n.cap[1] - n.used[1] - POD.mem) / n.cap[1];
const free = Math.round(((cpu + mem) / 2) * 100);
const bonus = n.images.includes(POD.image) ? 10 : 0; // image is cached
return { free, bonus, total: free + bonus };
}
const left = NODES.filter(n => {
console.log("filter", n.name, why(n) ? "OUT: " + why(n) : "ok");
return !why(n);
});
const ranked = left.map(n => ({ n: n.name, ...score(n) }))
.sort((a, b) => b.total - a.total || a.n.localeCompare(b.n));
for (const r of ranked)
console.log("score ", r.n, "free", r.free, "+ image", r.bonus, "=", r.total);
console.log(ranked.length ? "result: bind Pod to " + ranked[0].n
: "result: Pending (no node fits)");আউটপুট:
filter node-a OUT: not enough cpu
filter node-b ok
filter node-c ok
filter node-d OUT: selector disk=ssd
filter node-e OUT: node not Ready
score node-b free 56 + image 0 = 56
score node-c free 29 + image 10 = 39
result: bind Pod to node-b
হাতে মিলিয়ে দেখুন। node-b-তে Pod বসানোর পরে CPU-র ৫০% আর memory-র (4096 − 1024 − 512) ÷ 4096 = ৬২.৫% ফাঁকা থাকবে, গড় ৫৬%. node-c-তে image থাকার বোনাস পেয়েও ৩৯. আসল scheduler-এ নম্বর আসে অনেকগুলো plugin থেকে, ওজনও আলাদা; এখানকার ১০ নম্বরের বোনাস বোঝানোর জন্য ধরে নেওয়া। সব node বাদ গেলে Pod ব্যর্থ হয় না, Pending থেকে অপেক্ষা করে।
এই Pending অবস্থা আমরা আসল cluster-এও তৈরি করেছি। একটি Pod-এ nodeSelector: {disktype: ssd} দিলাম, অথচ node-এ এমন label নেই:
$ kubectl get pod needs-ssd -n abcl-ka
NAME READY STATUS RESTARTS AGE
needs-ssd 0/1 Pending 0 4s
$ kubectl get events -n abcl-ka \
--field-selector involvedObject.name=needs-ssd
LAST SEEN TYPE REASON OBJECT MESSAGE
4s Warning FailedScheduling pod/needs-ssd 0/1 nodes are available:
1 node(s) didn't match Pod's node affinity/selector. no new claims to deallocate,
preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling.Event-এর একটি লম্বা বার্তা পড়ার সুবিধায় কয়েক লাইনে ভাঙা হয়েছে। 0/1 nodes are available মানে ১টি node-এর ০টি ফিল্টার পার হয়েছে। Pending দেখলে প্রথম কাজ এই event পড়া: scheduler নিজেই কারণ লিখে রাখে।
৬ · আসল পরীক্ষা: একটি Pod মুছে দেখি
এতক্ষণের দাবি কি সত্য? ৩টি Pod চলছে। আমরা একটি মুছে দিলাম, আর পাশে একটি watch চালিয়ে রাখলাম। বাঁয়ের সময়ের কলামটি kubectl-এর নয়: আমাদের ছোট একটি script প্রতিটি লাইনের সাথে যোগ করেছে, delete কমান্ড শুরুর কত সেকেন্ড পরে লাইনটি এলো।
$ kubectl get pods -l app=web -n abcl-ka --watch-only --output-watch-events &
$ kubectl delete pod web-69f4d599d6-92562 -n abcl-ka
সময় EVENT NAME READY STATUS
+0.07s MODIFIED web-69f4d599d6-92562 1/1 Terminating
+0.08s ADDED web-69f4d599d6-ns5z4 0/1 Pending
+0.09s MODIFIED web-69f4d599d6-ns5z4 0/1 Pending
+0.09s MODIFIED web-69f4d599d6-92562 1/1 Terminating
+0.11s MODIFIED web-69f4d599d6-ns5z4 0/1 ContainerCreating
+0.92s MODIFIED web-69f4d599d6-92562 0/1 Completed
+1.94s MODIFIED web-69f4d599d6-92562 0/1 Completed
+1.95s MODIFIED web-69f4d599d6-92562 0/1 Completed
+1.95s DELETED web-69f4d599d6-92562 0/1 Completed
+1.99s MODIFIED web-69f4d599d6-ns5z4 1/1 Running
$ kubectl get pods -n abcl-ka
NAME READY STATUS RESTARTS AGE
web-69f4d599d6-jszbc 1/1 Running 0 36s
web-69f4d599d6-ns5z4 1/1 Running 0 10s
web-69f4d599d6-wbths 1/1 Running 0 36sWatch-এর অংশ থেকে RESTARTS আর AGE কলাম ছাঁটা হয়েছে। লক্ষণীয় চারটি বিষয়:
- ০.০১ সেকেন্ডের ব্যবধান: Watch-এ পুরোনো Pod Terminating দেখানোর ১০ মিলিসেকেন্ডের মধ্যে নতুন Pod
ADDED. ReplicaSet controller মুছে যাওয়ার অপেক্ষা করেনি, কারণ Terminating Pod-কে সে আর গোনে না। - নতুন নাম, নতুন Pod:
92562ফিরে আসেনি, এসেছেns5z4. Pod বদলযোগ্য, পোষা প্রাণী নয়। তার IP-ও বদলাতে পারে, তাই Service লাগে (পর্ব ৪৫). - প্রায় ২ সেকেন্ড: Pending থেকে Running. এই গতি শুধু এখানকার: image আগেই ছিল, এবং app-টি ছোট।
- এটি জাদু নয়: মাত্র একটি controller গুনে দেখেছে ২ আছে, ৩ চাই।
Watch: বারবার জিজ্ঞেস না করে খবর পাওয়া
Controller যদি প্রতি সেকেন্ডে পুরো তালিকা চাইত, ৫০০০ Pod-এর cluster-এ API server আর etcd ভেঙে পড়ত। সমাধান watch: client একটি HTTP সংযোগ খোলা রাখে, আর API server বদল হলেই একটি করে event ঠেলে পাঠায়। প্রতিটি event-এ থাকে পুরো object আর একটি resourceVersion. আসল stream দেখুন। এখানে Deployment-টি watch করে আমরা --replicas=4 দিলাম:
$ kubectl get --raw '/apis/apps/v1/namespaces/abcl-ka/deployments?watch=true&timeoutSeconds=12&fieldSelector=metadata.name%3Dweb'
{"type":"ADDED","object":{"kind":"Deployment","apiVersion":"apps/v1","metadata":{"name":"web","namespace":"abcl-ka",…,"resourceVersion":"1062","generation":1,…
…
# script-এ প্রতিটি লাইনের কয়েকটি ক্ষেত্র ছেঁকে নিলে (scale কমান্ড থেকে সময়):
সময় type rv gen spec replicas ready observedGen
-1.44s ADDED 1062 1 3 3 3 1
+0.08s MODIFIED 1190 2 4 3 3 1
+0.09s MODIFIED 1193 2 4 3 3 2
+0.10s MODIFIED 1195 2 4 3 3 2
+0.12s MODIFIED 1201 2 4 4 3 2
+2.38s MODIFIED 1238 2 4 4 4 2এই একই stream ধাপে ধাপে দেখা যাক। Table-এর মানগুলো আসল run-এর, শুধু শেষ ধাপটি পুনঃসংযোগের ধারণা দেখানো একটি model.
| সময় | |
| event type | |
| resourceVersion | |
| spec.replicas | |
| status.replicas | |
| status.readyReplicas | |
| observedGeneration |
- প্রথম ছবি। Watch শুরু হতেই API server বর্তমান অবস্থা
ADDEDহিসেবে পাঠাল।specআরstatusমিলছে, ৩ আর ৩, করার কিছু নেই। - চাওয়া বদল।
scalespec.replicas-কে ৪ করল। ৮০ মিলিসেকেন্ডে event এলো। এখনspec৪,status৩: এই ফাঁকই controller-এর কাজের সংকেত।generationবেড়ে ২, কিন্তুobservedGenerationএখনো ১। - Controller দেখেছে। ১০ মিলিসেকেন্ড পরে
observedGeneration২. Deployment controller জানিয়ে দিল, নতুন চাওয়াটি সে পড়েছে। Replica সংখ্যা এখনো ৩। - নতুন Pod object.
status.replicas৪, কিন্তুreadyReplicas৩: চতুর্থ Pod আছে, প্রস্তুত নয়। এর আগের1195event-টি জায়গা বাঁচাতে এখানে বাদ। - মিলে গেছে। প্রায় ২.৩৮ সেকেন্ডে
readyReplicas৪. চাওয়া আর পাওয়া সমান, তাই controller আবার চুপ।rvধারাবাহিক নয়, কারণ এই সংখ্যাটি পুরো etcd জুড়ে একটিই ক্রম, সব object-এর লেখা তা বাড়ায়। - সংযোগ কাটলে (model). Watch চিরকাল চলে না। Client শেষ দেখা
resourceVersionদিয়ে আবার watch শুরু করে, তাই মাঝের event হারায় না। অনেক পুরোনো নম্বর দিলে server410 Goneবলে, তখন client পুরো তালিকা আবার পড়ে। Controller level-triggered বলে এতে কোনো ক্ষতি নেই।
৭ · Cluster পড়া: kubectl, event, namespace আর label
kubectl-এর শত কমান্ডের মধ্যে প্রথম দিনের জন্য এই পাঁচটিই যথেষ্ট। তিনটি শুধু পড়ে, তাই নিরাপদ।
| কমান্ড | প্রশ্ন |
|---|---|
kubectl get pods | কী কী আছে, কী অবস্থায়? |
kubectl describe pod X | এটির বিস্তারিত আর সাম্প্রতিক event কী? |
kubectl explain deployment.spec.replicas | এই ক্ষেত্রটির অর্থ কী? (cluster-এর নিজের schema থেকে) |
kubectl get events --sort-by=.lastTimestamp | সম্প্রতি কী ঘটেছে? |
kubectl logs X | Container নিজে কী বলছে? (পর্ব ৪৩) |
$ kubectl explain deployment.spec.replicas
GROUP: apps
KIND: Deployment
VERSION: v1
FIELD: replicas <integer>
DESCRIPTION:
Number of desired pods. This is a pointer to distinguish between explicit
zero and not specified. Defaults to 1.
$ kubectl describe pod web-69f4d599d6-jszbc -n abcl-ka
Name: web-69f4d599d6-jszbc
Namespace: abcl-ka
…
Labels: app=web
pod-template-hash=69f4d599d6
Status: Running
IP: 10.1.0.9
Controlled By: ReplicaSet/web-69f4d599d6
Containers:
nginx:
Image: nginx:1.27-alpine
…
Ready: True
Restart Count: 0
Limits:
cpu: 100m
memory: 32Mi
Requests:
cpu: 10m
memory: 16Mi
…
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 24s default-scheduler Successfully assigned abcl-ka/web-69f4d599d6-jszbc to docker-desktop
Normal Pulled 23s kubelet Container image "nginx:1.27-alpine" already present on machine
Normal Created 23s kubelet Created container: nginx
Normal Started 23s kubelet Started container nginxDebug-এর সময় describe-এর সবচেয়ে নিচের Events অংশই প্রথম পড়ুন। From কলামে দেখুন কে কাজটি করেছে: scheduler, তারপর kubelet, ঠিক ধাপে ধাপে দেখা ক্রমে। Controlled By জানায় এই Pod-এর মালিক কে। AI-এর দেওয়া কোনো ক্ষেত্রের নামে সন্দেহ হলে explain চালান: সে আপনার cluster-এর সত্যিকারের API থেকে উত্তর দেয়। Event চিরকাল থাকে না, ডিফল্টে প্রায় এক ঘণ্টা পরে মুছে যায়, তাই ঘটনার পরপরই পড়ুন।
Namespace: একই cluster-এ আলাদা ঘর
Namespace হলো একই cluster-এর ভেতরে নামের আলাদা এলাকা, বহুতল ভবনের আলাদা ফ্ল্যাটের মতো। দুই namespace-এ একই নামের Deployment থাকতে পারে। -n না দিলে kubectl default namespace দেখে, আর তাই “আমার Pod গেল কোথায়” প্রশ্নের অর্ধেক উত্তর এটাই:
$ kubectl get pods -n default
No resources found in default namespace.Pod চলছিল, শুধু অন্য namespace-এ। মনে রাখবেন, namespace নামের সীমানা আর অনুমতির একক, তবে নিজে থেকে কোনো network দেয়াল নয়; সেজন্য NetworkPolicy লাগে (পর্ব ৪৯). Node আর Namespace-এর মতো কিছু object কোনো namespace-এর ভেতরে থাকে না।
Label আর selector: Kubernetes-এর আঠা
Label হলো object-এ লাগানো ছোট key-value ট্যাগ, যেমন app=web. Selector হলো সেই ট্যাগ দিয়ে খোঁজা। ReplicaSet কোনো নামের তালিকা রাখে না। সে প্রতিবার জিজ্ঞেস করে: “app=web label-ওয়ালা কয়টি Pod আছে?” তাই label বদলালে কী হয়, সেটি আসল cluster-এ দেখা যাক:
$ kubectl get pods -n abcl-ka -L app
NAME READY STATUS RESTARTS AGE APP
web-69f4d599d6-jszbc 1/1 Running 0 107s web
web-69f4d599d6-ns5z4 1/1 Running 0 81s web
web-69f4d599d6-wbths 1/1 Running 0 107s web
$ kubectl label pod web-69f4d599d6-wbths app=debug --overwrite -n abcl-ka
pod/web-69f4d599d6-wbths labeled
$ kubectl get pods -n abcl-ka -L app # 4 সেকেন্ড পরে
NAME READY STATUS RESTARTS AGE APP
web-69f4d599d6-gk8vl 1/1 Running 0 4s web
web-69f4d599d6-jszbc 1/1 Running 0 111s web
web-69f4d599d6-ns5z4 1/1 Running 0 85s web
web-69f4d599d6-wbths 1/1 Running 0 111s debug
$ kubectl get pods -n abcl-ka -l app=web
NAME READY STATUS RESTARTS AGE
web-69f4d599d6-gk8vl 1/1 Running 0 4s
web-69f4d599d6-jszbc 1/1 Running 0 111s
web-69f4d599d6-ns5z4 1/1 Running 0 85s
$ kubectl get pods -n abcl-ka -l 'app in (web,debug)' --no-headers | wc -l
4কোনো Pod মারা যায়নি, অথচ এখন ৪টি Pod. ReplicaSet গুনে পেল app=web মাত্র ২টি, তাই নতুন gk8vl বানাল। wbths এখনো চলছে, কিন্তু তার মালিক নেই: আগে ownerReferences-এ ReplicaSet ছিল, label বদলের পরে তালিকাটি ফাঁকা। এই কৌশলে কোনো সমস্যাগ্রস্ত Pod-কে সরিয়ে রেখে তদন্ত করা যায়। কাজ শেষে এটি হাতে মুছতে হবে, কারণ কোনো controller আর এর দায়িত্বে নেই।
একটি Deployment-এর selector যেন অন্য কারও Pod ধরে না ফেলে। দুটি workload একই label ব্যবহার করলে একে অন্যের Pod-কে নিজের ভেবে গুনতে পারে, আর Service ভুল Pod-এ traffic পাঠাতে পারে। apps/v1-এ Deployment-এর selector তৈরির পরে বদলানো যায় না, তাই শুরুতেই ভেবে লিখুন।
৮ · কিছু নষ্ট হলে: etcd quorum আর node failure
etcd হলো cluster-এর একমাত্র স্মৃতি। তাই production-এ এর সাধারণত ৩ বা ৫টি সদস্য থাকে, আর প্রতিটি লেখা quorum, অর্থাৎ সদস্যদের সংখ্যাগরিষ্ঠ অংশ, নিশ্চিত করলে তবেই পাকা হয়। সদস্য সংখ্যা n হলে quorum = ⌊n/2⌋ + ১, আর সহ্য করা যায় n − quorum সদস্যের বিদায়। Raft কীভাবে এটি করে, তার পুরো গল্প System Design কোর্সের Consensus আর Raft পর্বে পাবেন। এখানে শুধু operations-এর হিসাব:
// etcd quorum math for 1 to 7 members. Change DOWN and P_DOWN.
const DOWN = 2; // members that are down right now
const P_DOWN = 0.01; // assumed: each member down 1% of the time, independently
function choose(n, k) {
let c = 1;
for (let i = 1; i <= k; i++) c = c * (n - i + 1) / i;
return c;
}
function pNoQuorum(n, q) { // lost if more than n - q members are down
let sum = 0;
for (let k = n - q + 1; k <= n; k++)
sum += choose(n, k) * P_DOWN ** k * (1 - P_DOWN) ** (n - k);
return sum;
}
console.log("members quorum tolerate down=" + DOWN + " P(no quorum)");
for (let n = 1; n <= 7; n++) {
const q = Math.floor(n / 2) + 1;
const alive = Math.max(n - DOWN, 0);
const state = alive >= q ? "writes ok" : "BLOCKED";
console.log(String(n).padStart(7) + String(q).padStart(7) +
String(n - q).padStart(9) + " " + state.padEnd(10) +
pNoQuorum(n, q).toExponential(2).padStart(13));
}আউটপুট:
members quorum tolerate down=2 P(no quorum)
1 1 0 BLOCKED 1.00e-2
2 2 0 BLOCKED 1.99e-2
3 2 1 BLOCKED 2.98e-4
4 3 1 BLOCKED 5.92e-4
5 3 2 writes ok 9.85e-6
6 4 2 writes ok 1.96e-5
7 4 3 writes ok 3.42e-7
৪ সদস্য ৩-এর চেয়ে বেশি সহ্য করে না, উল্টো ঝুঁকি প্রায় দ্বিগুণ (2.98e-4 বনাম 5.92e-4), কারণ নষ্ট হতে পারে এমন যন্ত্র বেড়েছে কিন্তু সহনশীলতা বাড়েনি। তাই etcd-র সদস্য সংখ্যা বিজোড় রাখা হয়। ১% আর স্বাধীন ব্যর্থতা এখানে ধরে নেওয়া সংখ্যা; এক rack-এ সব রাখলে স্বাধীনতার ধারণা ভেঙে যায়, তাই সদস্যরা আলাদা availability zone-এ থাকে। ৩-এর উদাহরণ হাতে: 3 × (0.01)² × 0.99 + (0.01)³ ≈ 2.98 × 10⁻⁴.
Quorum গেলে চলমান app সঙ্গে সঙ্গে বন্ধ হয় না। লেখা থেমে যায়: নতুন deploy, scale বা সেরে ওঠা হবে না। কিন্তু node-এ চলা container চলতেই থাকে, কারণ kubelet আর runtime-এর প্রতিটি মুহূর্তে control plane লাগে না। অফিস বন্ধ হলেও রাস্তার বাস চলতে থাকে, শুধু নষ্ট বাসের জায়গায় নতুন বাস আসে না।
একটি node চলে গেলে
আমাদের cluster-এ node একটিই, তাই এই পরীক্ষা সেখানে করা সম্ভব নয়। নিচের stepper একটি model: তিন node, ৪ replica. সময়গুলো ডিফল্ট আচরণের মোটামুটি হিসাব, version আর provider-ভেদে আলাদা হতে পারে। তবে ৩০০ সেকেন্ডের সংখ্যাটি আমাদের আসল describe-এর Tolerations লাইনেই দেখেছেন।
| সময় | |
| node-1 | |
| node-1-এর Pod | |
| node-2-এর Pod | |
| node-3-এর Pod | |
| API-র Ready হিসাব |
- স্বাভাবিক অবস্থা। ৪টি Pod ৩টি node-এ ছড়ানো। প্রতিটি kubelet নিয়মিত হৃৎস্পন্দন পাঠায়, যা একটি Lease object নবায়ন করে।
- node-1-এর বিদ্যুৎ গেল। ঠিক এই মুহূর্তে API server এখনো ৪/৪ দেখাচ্ছে, অথচ ২টি Pod আসলে নেই। Control plane-এর জানা সবসময় একটু পুরোনো।
- Node controller টের পেল। কিছুক্ষণ হৃৎস্পন্দন না পেয়ে (ডিফল্টে প্রায় ৪০–৫০ সেকেন্ড, version-ভেদে) সে node-কে
Unknownকরল, একটি taint বসাল, আর Pod-গুলোকে NotReady করল। ফলে Service আর এগুলোতে traffic পাঠায় না। - ৩০০ সেকেন্ডের সহনশীলতা শেষ। Pod-এর toleration ফুরোলে এগুলো মোছার জন্য চিহ্নিত হলো: Terminating. কেন অপেক্ষা? ছোট network সমস্যায় সব Pod সরিয়ে ফেললে ক্ষতিই বেশি।
- ReplicaSet লুপ চলল। Terminating Pod গোনা হয় না, তাই ২ আছে, ৪ চাই। ২টি নতুন Pod object তৈরি হলো। Scheduler taint-এর কারণে node-1-কে বাদ দিয়ে node-2 আর node-3 বেছে নিল।
- আবার ৪/৪. নতুন দুটি Pod চলছে। পুরোনো দুটি API-তে Terminating হয়ে আটকে আছে, কারণ তাদের kubelet সাড়া দিতে পারছে না। এতে নতুন Pod চলতে বাধা নেই।
- Node ফিরল। kubelet ফিরে দেখল a আর b মোছার চিহ্ন পেয়েছে, তাই সেগুলো পরিষ্কার করল। node-1 আবার খালি ও প্রস্তুত, তবে Pod নিজে থেকে এখানে ফেরে না।
এই মডেল একটি অপ্রিয় সত্য জানায়: ডিফল্টে node-এর মৃত্যুর পর Pod সরতে পাঁচ মিনিটের বেশি লাগতে পারে। তাই replica আগে থেকেই একাধিক node আর zone-এ ছড়িয়ে রাখা দরকার, যাতে বাকিরা ততক্ষণ traffic সামলাতে পারে।
আরও গভীরে: Controller সরাসরি API server-এ বারবার প্রশ্ন করে না। Client library-র informer একবার list করে, তারপর watch চালিয়ে একটি local cache তাজা রাখে, আর পরিবর্তিত object-এর key একটি work queue-তে তোলে। দুই লেখকের সংঘাত ঠেকায় resourceVersion-ভিত্তিক optimistic concurrency: পুরোনো নম্বর নিয়ে লিখতে গেলে 409 Conflict আসে। HA control plane-এ API server একসাথে অনেকগুলো চলে (load balancer-এর পেছনে), কিন্তু scheduler আর controller manager-এর একটিই সক্রিয় থাকে, Lease দিয়ে leader election করে।
৯ · Managed Kubernetes, আর কখন Kubernetes নয়
etcd-র backup, control plane-এর আপগ্রেড আর certificate নবায়ন নিজের হাতে করা ভারী কাজ। তাই বেশিরভাগ দল managed Kubernetes নেয়: provider control plane চালায়, আপনি সাধারণত node আর workload সামলান। পর্ব ২-এর shared responsibility এখানেও খাটে।
| কাজ | নিজে চালানো (kubeadm) | Managed (EKS, AKS, GKE) |
|---|---|---|
| API server, etcd, scheduler | আপনি | Provider; etcd আপনি সরাসরি দেখতেই পান না |
| Control plane আপগ্রেড | আপনি | Provider করে, তবে কখন হবে, সেই সিদ্ধান্ত অনেকটা আপনার |
| Node VM আর তার আপগ্রেড | আপনি | সাধারণত ভাগাভাগি; GKE Autopilot বা AWS Fargate-এর মতো ধরনে provider বেশি নেয় |
| Workload, YAML, RBAC, Secret | আপনি | সবসময় আপনি |
| খরচ | নিজের machine আর শ্রম | Node-এর খরচ, আর অনেক ক্ষেত্রে cluster-প্রতি ফি; provider-এর pricing পাতা দেখুন |
তিন provider-এর নাম: Amazon EKS, Azure Kubernetes Service (AKS) আর Google Kubernetes Engine (GKE). এদের কোনোটি সেরা বলা হচ্ছে না; আপনার দল যে cloud-এ আছে, সাধারণত সেটিই স্বাভাবিক পছন্দ। শেখার জন্য নিজের কম্পিউটারে kind, minikube বা Docker Desktop-এর Kubernetes যথেষ্ট, তাতে cloud-এর খরচ নেই।
কখন Kubernetes নয়
| পরিস্থিতি | ভালো পথ |
|---|---|
| ১–২টি service, একটি VM-ই যথেষ্ট | VM + Compose + systemd (পর্ব ১১, ৪০) |
| Stateless API, ops জানা মানুষ নেই | Managed container সেবা বা PaaS (পর্ব ১৯) |
| মাঝে মাঝে আসা ছোট কাজ | FaaS (পর্ব ৬৯) |
| অনেক service, অনেক দল, ঘন ঘন deploy | Kubernetes যুক্তিসঙ্গত |
| Cloud-এর বাইরেও একই platform দরকার | Kubernetes যুক্তিসঙ্গত |
Kubernetes আপনার অ্যাপকে নিজে থেকে নির্ভরযোগ্য করে না। ভুল image, ভুল config আর ভুল probe সে নিখুঁত দক্ষতায় সব node-এ ছড়িয়ে দেবে। তার ওপর network, নিরাপত্তা, আপগ্রেড আর observability শেখার বড় খরচ আছে। প্রশ্নটি “Kubernetes কি ভালো?” নয়; প্রশ্নটি “আমাদের সমস্যা কি এই খরচের যোগ্য?”
পরীক্ষা ও ইন্টারভিউতে:
- Control plane-এর চারটি অংশ কী? API server, etcd, scheduler, controller manager.
- etcd-র সাথে সরাসরি কে কথা বলে? শুধু API server.
- ৫ সদস্যের etcd কয়টি বিদায় সহ্য করে? Quorum ৩, তাই ২টি।
- Reconciliation loop কী? Desired আর observed অবস্থা মিলিয়ে পার্থক্যটুকু ঠিক করার অশেষ লুপ।
- Pod মুছলে আবার আসে কেন? কারণ Deployment-এর ReplicaSet সংখ্যা মেলায়; স্থায়ীভাবে কমাতে
replicasবদলাতে হবে।
১০ · AI-এর যুগে Kubernetes
AI YAML খুব দ্রুত লেখে, আর YAML দেখতে নিখুঁত হলেও ভুল হতে পারে। বিশেষ করে পুরোনো টিউটোরিয়ালের API version তার স্মৃতিতে রয়ে গেছে। Cluster বদলানোর আগে যাচাই করা আপনার কাজ।
| AI-এর ভুল | ঝুঁকি | যাচাই |
|---|---|---|
সরানো API version, যেমন extensions/v1beta1 Deployment (১.১৬-তে সরানো) | Apply ব্যর্থ | kubectl api-resources |
| বানানো ক্ষেত্র বা flag | Error, বা ক্ষেত্র নিষ্ক্রিয় | kubectl explain, --dry-run=server |
image: nginx:latest | কোন version চলছে জানা যায় না | নির্দিষ্ট tag বা digest (পর্ব ৪১) |
resources বাদ | Scheduler অন্ধ, node-এ চাপ | Request আর limit (পর্ব ৪৮) |
cluster-admin অনুমতি | এক ভুলে পুরো cluster ঝুঁকিতে | Least privilege (পর্ব ৪৯) |
সতর্কতা ছাড়া kubectl delete বা --force | ভুল namespace-এ কাজ মুছে যাওয়া | আগে get, context আর namespace দেখা |
AI-কে YAML লিখতে বলার সময় cluster-এর version আর যাচাইয়ের ধাপ জুড়ে দিন:
AI-এর দেওয়া YAML apply করার আগে
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন।
প্র ০১Borg তো Google-এ চলছিলই। তাহলে Kubernetes আলাদা করে বানানো হলো কেন, আর Borg থেকে কী শেখা হলো?
২০১৫ সালের Borg নিবন্ধে দেখা যায়, Google তার প্রায় সব কাজ বড় বড় cluster-এ এই একটি ব্যবস্থায় চালাত। কিন্তু Borg ছিল Google-এর নিজের পরিবেশে বাঁধা, ভেতরের নানা সিস্টেমের সাথে জড়ানো। সেটি হুবহু বাইরে দেওয়ার মতো ছিল না। তাই নতুন করে লেখা হলো, আর শেখা বিষয়গুলো নকশায় ঢোকানো হলো। “Borg, Omega, and Kubernetes” (২০১৬) লেখায় লেখকেরা নিজেই কয়েকটি শিক্ষা তুলে ধরেছেন:
- Label, নির্দিষ্ট দল নয়: Borg-এ কাজ দলবদ্ধ হতো “job”-এর ভেতরে সংখ্যা দিয়ে। Kubernetes-এ কোনো দল আগে থেকে বাঁধা নয়; label আর selector দিয়ে যেভাবে খুশি ভাগ করা যায়। একই Pod একসাথে Deployment আর Service-এর ভেতরে পড়ে।
- প্রতি Pod-এ একটি IP: Borg-এ এক machine-এর সব কাজ একই IP ভাগ করত, তাই port বরাদ্দ একটি বড় ঝামেলা ছিল। Kubernetes প্রতিটি Pod-কে নিজের IP দিল।
- একটি কেন্দ্রীয় API: সব অংশ API server-এর মাধ্যমে কথা বলে, তাই নতুন controller লেখা সহজ।
ব্যবসার দিকটিও ভাবার মতো। Open source করলে একটি মানদণ্ড তৈরি হয়, যা কোনো এক cloud-এ বাঁধা নয়; তখনকার প্রতিযোগিতায় এটি Google Cloud-এর পক্ষে সাহায্য করতে পারে, এমন ব্যাখ্যা প্রায়ই শোনা যায়। এটি বিশ্লেষকদের ব্যাখ্যা, কোনো ঘোষিত তথ্য নয়। CNCF-এর হাতে দেওয়ায় অন্য কোম্পানিগুলোও নিশ্চিন্তে যোগ দিতে পেরেছে।
মূল উপলব্ধি: একটি সিস্টেমের দ্বিতীয় প্রজন্ম প্রথমটির ভুল থেকে শেখে। Kubernetes কোনো হঠাৎ আবিষ্কার নয়; এটি এক দশকের বেশি সময়ের cluster চালানোর অভিজ্ঞতার পরিষ্কার একটি রূপ।
প্র ০২Controller কেন ঘটনার ওপর নির্ভর না করে বর্তমান অবস্থার ওপর চলে? ঘটনা-ভিত্তিক নকশা কি বেশি দক্ষ নয়?
ঘটনা-ভিত্তিক নকশায় নিয়ম হয় এমন: “Pod মরলে একটি নতুন বানান।” শুনতে সরল আর দক্ষ। সমস্যা শুরু হয় বিতরণ করা সিস্টেমের বাস্তবতায়: বার্তা হারায়, দুবার আসে, ভুল ক্রমে আসে, আর শোনার সময় প্রোগ্রাম নিজেই বন্ধ থাকতে পারে।
- বার্তা হারালে: controller আপগ্রেডের সময় একটি Pod মরল। ঘটনা-ভিত্তিক ব্যবস্থায় Pod চিরকালের জন্য কম থাকল।
- বার্তা দুবার এলে: ২টি নতুন Pod বানানো হলো, চাওয়ার চেয়ে বেশি।
- বার্তা জমে গেলে: ১০০টি পুরোনো ঘটনা একে একে সামলাতে হয়, যদিও এখনকার অবস্থা দেখলে একটিই কাজ বাকি।
Level-triggered নকশায় controller প্রশ্ন করে: “এখন কী আছে, আর কী চাই?” বার্তা হারালে পরের দফায় ঠিক হয়ে যায়। দুবার চালালেও ক্ষতি নেই, কারণ দ্বিতীয়বার পার্থক্য শূন্য। অর্থাৎ কাজটি নিজে থেকেই idempotent. আপনি simulator-এ দেখেছেন, controller কারণ জানে না, শুধু সংখ্যা জানে।
দক্ষতার প্রশ্নের উত্তর হলো, Kubernetes দুটিকে মিলিয়েছে। Watch event দ্রুত খবর দেয়, তাই controller সাথে সাথে জাগে। কিন্তু event শুধু “দেখার সংকেত”, সিদ্ধান্তের ভিত্তি নয়। সিদ্ধান্ত নেওয়া হয় পুরো অবস্থা মিলিয়ে। Informer আবার নির্দিষ্ট সময় পরপর cache-এর সব object নতুন করে দেখতেও পারে, যাকে resync বলে। এটি নিরাপত্তার জাল।
দাম আছে: controller-কে অবস্থা মিলিয়ে দেখার মতো করে লিখতে হয়, আর “এখনো চলছে” বুঝতে হয় (যেমন starting Pod গোনা)। না হলে simulator-এ দেখা অতিরিক্ত Pod তৈরির ভুল হবে। Terraform-এর plan (পর্ব ৫২) আর GitOps (পর্ব ৬০) একই ধারণার ভিন্ন রূপ।
মূল উপলব্ধি: নির্ভরযোগ্য সিস্টেম ধরে নেয়, বার্তা হারাবে আর প্রোগ্রাম মরবে। বর্তমান অবস্থা থেকে বারবার হিসাব করলে অতীতের ভুল নিজে থেকেই মুছে যায়।
প্র ০৩পুরো control plane বন্ধ হয়ে গেলে কি আপনার অ্যাপও বন্ধ হবে? তাহলে control plane-কে এত সুরক্ষিত রাখার দরকার কী?
সাধারণ উত্তর: সঙ্গে সঙ্গে নয়। kubelet যে Pod চালাচ্ছে, সেগুলো চলতে থাকে। Container crash করলে kubelet নিজেই সেই node-এ আবার চালাতে পারে, যদি Pod-টি তার জানা থাকে। kube-proxy-র বসানো নিয়মও থেকে যায়। ফলে ব্যবহারকারী হয়তো কিছুই টের পান না। এই ধর্মকে অনেকে static stability বলেন: পরিচালনার অংশ পড়ে গেলেও চলমান কাজ টিকে থাকে।
তবে সময়ের সাথে ক্ষতি জমে:
- Self-healing বন্ধ: কোনো node মরলে Pod আর সরে না।
- Deploy বন্ধ: জরুরি bug fix-ও পাঠানো যায় না।
- Scale বন্ধ: ভিড় বাড়লে নতুন Pod আসে না।
- Service-এর তালিকা জমে যায়: Pod বদলালে নতুন ঠিকানা ছড়ায় না।
আবার কিছু জিনিস control plane-এর ওপর গোপনে নির্ভর করে। ডিসেম্বর ২০২৪-এ OpenAI তাদের প্রকাশিত postmortem-এ জানায়, একটি নতুন telemetry সেবা বড় cluster-গুলোর API server-এ অতিরিক্ত চাপ দিয়েছিল; control plane ভেঙে পড়লে cluster-এর ভেতরের DNS-ও কিছু সময় পরে কাজ বন্ধ করে, আর data plane-এর সেবাগুলো আক্রান্ত হয়। প্রকাশিত বিবরণ অনুযায়ী DNS cache কিছুক্ষণ সমস্যাটি আড়াল করেছিল। আর, control plane-এ ঢোকার পথটিই বন্ধ থাকায় ঠিক করতেও বেশি সময় লাগে। এটি একটি জনসমক্ষে প্রকাশিত ঘটনা, বিস্তারিত তাদের লেখাতেই দেখা ভালো।
etcd-র ক্ষেত্রে আরও একটি কথা: হারানো মানে cluster-এর স্মৃতি হারানো। তাই নিজে চালালে নিয়মিত snapshot নেওয়া আর restore অনুশীলন করা বাধ্যতামূলক।
মূল উপলব্ধি: Control plane না থাকলে অ্যাপ চলে, কিন্তু সেরে ওঠা, বদল আর বৃদ্ধি থেমে যায়। আর যে control plane দিয়ে সমস্যা ঠিক করবেন, সেটিই অচল হলে উদ্ধারের পথও বন্ধ, তাই আগে থেকে ভাবুন।
প্র ০৪Managed Kubernetes নিলে কি অপারেশনের কাজ শেষ? Provider কী নেয়, আর কী আপনার ওপরই থেকে যায়?
Managed সেবা শুধু সবচেয়ে কঠিন কিন্তু সবচেয়ে একঘেয়ে অংশটি নেয়। etcd-র quorum, API server-এর স্কেল, control plane-এর certificate আর আপগ্রেডের ভারী কাজ provider-এর। সাধারণত control plane একাধিক availability zone-এ চলে, আর provider একটি SLA দেয়, যার শর্ত provider-ভেদে আলাদা। শুধু এই অংশ নিজে চালানোর চেয়ে কম মানুষে কাজ হয়, এটি বড় লাভ। রাত ২টায় etcd-র disk ভরে গেলে আপনাকে ঘুম থেকে উঠতে হয় না, সেই দায়িত্ব provider-এর দলের।
তবু আপনার ওপরে অনেক কিছু থাকে:
- আপগ্রেডের দায়: Kubernetes-এ প্রতি বছর কয়েকটি minor version আসে, পুরোনোগুলোর সহায়তা শেষ হয়। Provider-ভেদে নীতি আলাদা, তবে সরানো API আপনার YAML-এ থাকলে ঠিক করবেন আপনিই।
- Add-on: ingress, DNS-এর স্কেল, CNI, monitoring আর certificate-এর অনেকটা আপনার পছন্দ ও দায়।
- নিরাপত্তা: RBAC, cloud IAM-এর সাথে পরিচয় জোড়া, Secret, Pod-এর অনুমতি।
- খরচ: ভুলে যাওয়া load balancer, বড় আকারের node আর ফাঁকা request-এর বিল আসবে আপনার নামে (পর্ব ৭৩).
আরও একটি ভুল ধারণা হলো, Kubernetes মানে lock-in থেকে মুক্তি। YAML অনেকটাই বহনযোগ্য, কিন্তু load balancer-এর annotation, storage class, IAM-এর সংযোগ আর network প্রতিটি cloud-এ আলাদা। একই সাথে, সবকিছু provider-এর ভরসায় ছেড়ে দিলে provider-এর নিজের outage-এ আপনার করার কিছু থাকে না; multi-zone নকশা তাই আপনার দায়। পর্ব ২-এর বাড়ির মালিক আর ভাড়াটের উদাহরণ এখানেও খাটে: দালান provider-এর, ঘরের ভেতর আপনার।
মূল উপলব্ধি: Managed সেবা দায়িত্ব সরায়, শেষ করে না। চুক্তিতে কে কী করবে তা পড়ুন, আর যা আপনার ওপর আছে, তার জন্য মানুষ ও সময় রাখুন।
প্র ০৫“সবাই Kubernetes ব্যবহার করে” কি একটি ভালো যুক্তি? একটি ছোট দল এই সিদ্ধান্ত কীভাবে নেবে?
না। Kubernetes ডিজাইন করা হয়েছে অনেক machine, অনেক দল আর অনেক service-এর জটিলতা সামলাতে। এই জটিলতা আপনার না থাকলে আপনি সমস্যা কিনছেন, সমাধান নয়। একে অনেকে resume-driven development বলেন: সিদ্ধান্ত নেওয়া হয় চাকরির বাজার দেখে, প্রয়োজন দেখে নয়। শেখার জন্য Kubernetes চমৎকার, কিন্তু প্রতিটি অ্যাপের জন্য নয়।
একটি কাল্পনিক অনলাইন বাজার অ্যাপের ৩ জনের দল ভাবুন। একটি API, একটি worker, একটি managed database. দুটি VM আর একটি load balancer দিয়ে মাসে কয়েকবার deploy হয়। এখানে Kubernetes আনলে কী যোগ হবে?
- নতুন ধারণা: Pod, Service, Ingress, RBAC, probe, CNI, Helm: প্রতিটি শেখার ও ভুলের ঝুঁকি।
- নতুন কাজ: নিয়মিত cluster আপগ্রেড, add-on আপগ্রেড, node patching.
- নতুন ব্যর্থতার ধরন: DNS, network plugin, control plane-এর সীমা। আগে যে সমস্যা একটি VM-এর log দেখে বোঝা যেত, এখন তা কয়েকটি স্তর ঘুরে খুঁজতে হবে।
সিদ্ধান্তের জন্য কয়েকটি প্রশ্ন কাজে লাগে:
- কয়টি service আছে, আর কয়টি দল একসাথে deploy করে?
- Traffic-এর ওঠানামা কতটা? সত্যিই কি স্বয়ংক্রিয় scaling দরকার?
- দলে কে রাত ২টায় cluster ঠিক করবেন?
- আজকের সবচেয়ে বড় কষ্টটি কি সত্যিই orchestration, নাকি পরীক্ষা, অবজারভেবিলিটি বা deploy প্রক্রিয়া?
পথটি একমুখী নয়। Image আর Twelve-Factor অভ্যাস ঠিক থাকলে পরে স্থানান্তর সহজ। আজ managed container সেবায় থেকে, দল বড় হলে Kubernetes-এ যাওয়া একটি স্বাভাবিক পথ। উল্টো, একটি বড় দলের জন্য Kubernetes-ই প্রায়ই সবচেয়ে সরল পথ, কারণ সবাই একই ভাষায় কথা বলে, আর পর্ব ৬২-এর platform engineering সেটি আরও সহজ করে।
মূল উপলব্ধি: প্রযুক্তি বাছাই হোক আপনার সমস্যা দেখে। সবচেয়ে ছোট যে ব্যবস্থায় কাজ নিরাপদে চলে, সেটিই ভালো প্রকৌশল।
অনুশীলন
হিসাব, অনুমান আর নকশার সমালোচনা। শেষটি ঐচ্ছিক, নিজের কম্পিউটারে করার মতো।
-
Quorum-এর হিসাব: ৫ সদস্যের একটি etcd cluster-এ ২ সদস্য নষ্ট। (ক) এখনো লেখা যাবে কি? (খ) আরও একজন গেলে কী হবে? (গ) খরচ বাঁচাতে কেউ ৫ থেকে ৪-এ নামাতে চাইছেন: সহনশীলতা কত হবে?
(ক) Quorum = ⌊5/2⌋ + 1 = ৩. বাকি ৩ জন, তাই লেখা যাবে, তবে আর একটিও সহ্য করার ক্ষমতা নেই।
(খ) বাকি ২ জন, যা quorum-এর কম। লেখা থেমে যাবে, তাই API server-এ নতুন বদল হবে না। চলমান Pod চলতে থাকবে।
(গ) ৪-এর quorum ৩, সহনশীলতা ১: ৫-এর ২ থেকে কমে গেল। ৩ সদস্যেও সহনশীলতা ১, তাই ৪ রাখার কোনো লাভ নেই, নামাতে হলে ৩-এ নামান।
-
ফল অনুমান করুন:
web.yaml-এreplicas: 3. এই ক্রমে কমান্ড চলল:kubectl apply -f web.yaml, তারপরkubectl scale deploy/web --replicas=5, তারপর একটি Pod-এরkubectl delete pod, শেষে আবারkubectl apply -f web.yaml. প্রতিটি কমান্ডের পরে কয়টি Pod থাকবে, আর শেষ কমান্ড কী ছাপবে? এবার একটি Pod-এর label বদলেapp=debugকরলে মোট কয়টি Pod হবে?প্রথমে ৩,
scale-এর পরে ৫.delete pod-এর পরেও ৫, কারণ ReplicaSet নতুন একটি বানাবে; মোছা Pod শুধু বদলে যাবে। শেষapply-এ চাওয়া আবার ৩ হবে, তাই ২টি Pod মুছবে, আর ছাপবেdeployment.apps/web configured. আমাদের আসল run-এ এই শব্দটিই দেখেছেন।Label বদলালে মোট Pod হবে ৪: ReplicaSet-এর গোনা ৩টি আর মালিকহীন ১টি, যেমন আসল run-এ
wbths. -
নকশার সমালোচনা: একজন সহকর্মী প্রস্তাব দিলেন, “একটি VM-এ kubeadm দিয়ে Kubernetes বসাই, etcd-ও সেখানে। Backup লাগবে না, কারণ Kubernetes self-healing. আমাদের দুটি service, আর ops জানেন শুধু একজন।” এই প্রস্তাবের সমস্যাগুলো ধরুন, আর একটি ভালো বিকল্প দিন।
- একক ব্যর্থতার বিন্দু: VM গেলে control plane, etcd আর সব Pod একসাথে গেল। এক node-এ self-healing যাওয়ার কোনো জায়গা নেই।
- etcd সদস্য ১: সহনশীলতা ০; disk নষ্ট হলে cluster-এর সব তথ্য হারাবে।
- Self-healing মানে backup নয়: ভুল করে মোছা object “চাওয়া” হিসেবেই গৃহীত হয়, ফেরত আসে না।
- মানুষের ঝুঁকি: একজনই জানেন, তিনি ছুটিতে থাকলে কী হবে?
বিকল্প: দুটি service-এর জন্য Compose আর systemd সহ VM, নিয়মিত backup আর health check, বা managed container সেবা। Kubernetes সত্যিই দরকার হলে managed cluster নিন, আর node একাধিক zone-এ রাখুন।
-
হাতে-কলমে (ঐচ্ছিক, কমান্ড): নিজের কম্পিউটারে kind বা minikube-এর মতো local cluster চালিয়ে পরীক্ষাটি করুন। এতে cloud-এর খরচ নেই, তবে কিছু disk আর memory লাগবে। একটি Deployment বানান, একটি Pod মুছুন, event পড়ুন। তিনটি কমান্ড লিখুন: নতুন namespace তৈরি, Deployment-এর সব Pod label দিয়ে দেখা, আর কাজ শেষে পরিষ্কার করা।
kubectl config current-context # local cluster তো? kubectl create namespace demo kubectl create deployment web --image=nginx:1.27-alpine \ --replicas=3 -n demo kubectl get pods -n demo -l app=web -w # আরেক terminal-এ kubectl delete pod <pod-name> -n demo kubectl get events -n demo --sort-by=.lastTimestamp kubectl delete namespace demo # শুধু demo মুছবেkubectl create deploymentডিফল্টেapp=weblabel দেয়।delete namespaceভেতরের সবকিছু মুছে দেয়, তাই নাম দুবার পড়ুন। আসল কোনো cluster-এ এই পরীক্ষা করবেন না।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমkubectl apply থেকে চলন্ত container পর্যন্ত ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- API server পরিচয়, অনুমতি আর schema যাচাই করে
- Object etcd-তে জমা হয়
- Controller Pod object তৈরি করে
- Scheduler Pod-এর জন্য node বেছে দেয়
- kubelet CRI দিয়ে runtime-কে container চালাতে বলে
Kubernetes cluster-এ etcd-র সাথে সরাসরি কোন অংশ কথা বলে?
- প্রতিটি node-এর kubelet
- শুধু API server
- Scheduler আর controller manager
- kubectl
kubectl apply ছাপল deployment.apps/web created. এই মুহূর্তে নিশ্চিতভাবে কী সত্য?
- সব Pod চলছে
- Scheduler সব Pod-এর node ঠিক করেছে
- Deployment object API server-এ জমা হয়েছে
- Image pull শেষ হয়েছে
৩ replica-র Deployment-এর একটি Pod kubectl delete pod দিয়ে মোছা হলো। Pod-টি কেন আবার তৈরি হলো?
- kubelet মোছা Pod ফিরিয়ে আনে
- Scheduler নতুন Pod বানায়
- API server মুছে যাওয়া রেকর্ড ফিরিয়ে আনে
- ReplicaSet controller চাওয়া আর পাওয়ার পার্থক্য মেটায়
৪ সদস্যের etcd cluster-এ কয়টি সদস্য নষ্ট হলেও লেখা চলবে?
- ১টি
- ২টি
- ৩টি
- একটিও নয়
একটি Pod Pending অবস্থায় আছে, আর event-এ লেখা 0/3 nodes are available. প্রথমে কী করা উচিত?
- Pod মুছে আবার চেষ্টা করা
- kubectl describe pod দিয়ে পুরো event পড়া
- kubectl logs দেখা
- Cluster আবার চালু করা
Managed Kubernetes (যেমন EKS, AKS বা GKE) নিলেও সাধারণত কোন কাজটি ব্যবহারকারীর দায়িত্বে থাকে?
- etcd-র quorum সামলানো
- API server-এর availability
- Workload-এর manifest আর RBAC অনুমতি
- Control plane-এর certificate নবায়ন
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন, অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- Compose এক machine-এ থামে; Kubernetes অনেক node জুড়ে বসানো, সেরে ওঠা আর ধাপে ধাপে আপডেট দেয়।
- Control plane: API server, etcd, scheduler, controller manager. Node: kubelet, kube-proxy, container runtime.
specহলো চাওয়া,statusহলো পাওয়া; controller পার্থক্যটুকু লুপে ঠিক করে।- Pending, event, label:
describe-এর Events আগে পড়ুন; label আর selector দিয়েই object-গুলো একে অন্যকে চেনে। - etcd quorum = ⌊n/2⌋ + ১, সদস্য বিজোড় রাখুন; quorum গেলে লেখা থামে, চলমান Pod চলে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৪৩ · Pod — container-এর দল, lifecycle আর probeপরবর্তী পর্বKubernetes-এর সবচেয়ে ছোট চালানোর একক।
- ক্লাউড কম্পিউটিং ও DevOps · কোর্স হোম৮১টি পর্বপুরো কোর্সের রোডম্যাপ।
- পর্ব ৪০ · Docker Composeআগের ভিত্তিযেখান থেকে এই পর্বের প্রশ্ন শুরু।
- System Design · Consensus আর Raftগভীরে যানetcd-র quorum আর leader election কীভাবে কাজ করে।
- Operating Systems · Containersগভীরে যানRuntime আসলে kernel-এ কী করে।
- Computer Networks · Cloud ও container networkingপাশের ট্র্যাকPod-এর network-এর ভিত্তি।