Kubernetes Fundamentals
Pods, deployments, services, আর control loop — Kubernetes আসলে কী করে এবং যে primitive-গুলোর উপর বাকি সবকিছু দাঁড়িয়ে।
গল্পে বুঝি
ফাতিমা আল-ফিহরি একটা নাচের প্রোডাকশনের ডিরেক্টর। তার হাতে একটা বাঁধা স্ক্রিপ্ট, আর সেই স্ক্রিপ্টে লেখা একটাই কড়া নিয়ম — “মঞ্চে সবসময় ঠিক তিনজন নাচিয়ে থাকবে, একটাও কম নয়।” ফাতিমা কখনো নিজে দৌড়ে গিয়ে কাকে মঞ্চে পাঠাবে সেটা ঠিক করে না; সে শুধু স্ক্রিপ্টটা মঞ্চের সামনে ধরে রাখে আর ক্রমাগত মিলিয়ে দেখে — মঞ্চের বাস্তব অবস্থা স্ক্রিপ্টের সাথে মিলছে তো? এক শো-তে এক নাচিয়ে হঠাৎ অজ্ঞান হয়ে পড়ে গেল, মঞ্চে রইল দুজন। মুহূর্তের মধ্যে উইংসে দাঁড়িয়ে থাকা একজন আন্ডারস্টাডি ছুটে এসে জায়গা নিয়ে নিল, আবার তিনজন। ফাতিমাকে কিছুই আলাদা করে বলতে হয়নি — স্ক্রিপ্ট আর বাস্তবের ফাঁকটা সে নিজেই মিটিয়ে দিয়েছে।
এই প্রোডাকশনে পারফর্মাররা একা নড়াচড়া করে না। প্রতিটা নাচিয়ের সাথে বাঁধা থাকে তার নিজের একজন আলো-ধরা সহকারী আর একজন মেকআপ আর্টিস্ট — এই ছোট দলটা সবসময় একসাথে মঞ্চে ঢোকে, একসাথে বেরোয়। আর দর্শকদের কথা ভাবুন: হলের সামনে একজন আশার (usher) দাঁড়িয়ে আছে। কোন নাচিয়ে আজ রাতে পারফর্ম করছে সেটা কোনো দর্শককে জানতে হয় না — তারা শুধু আশারকে বলে “নাচিয়েদের কাছে যাব”, আর আশার তাদের ঠিক জায়গায় পাঠিয়ে দেয়। কাল যদি পুরো তিনজন বদলে যায়, দর্শকের কিছুই বদলায় না; আশার একই ঠিকানা।
গল্পটা হুবহু Kubernetes। স্ক্রিপ্টের “তিনজন নাচিয়ে” হলো আপনার desired state, আর ফাতিমার বারবার মিলিয়ে দেখে ফাঁক মেটানোটাই controller-এর reconciliation (control loop)। নাচিয়ে + তার সহকারী + মেকআপ আর্টিস্ট — একসাথে ঢোকা এই ছোট দলটা একটা pod (একসাথে চলা container-দের গুচ্ছ)। “সবসময় ঠিক তিনজন” নিয়মটা ধরে রাখা, আর অজ্ঞান হওয়া নাচিয়ের জায়গায় আন্ডারস্টাডি বসানো — এটাই একটা deployment, যা নির্দিষ্ট সংখ্যক replica বজায় রাখে আর নিজে থেকে self-healing করে। আর আশার হলো service — বদলাতে থাকা pod-দের সামনে একটা স্থিতিশীল ঠিকানা, যাতে ক্লায়েন্টকে কখনো জানতে না হয় ঠিক কোন pod এখন সাড়া দিচ্ছে। বাস্তবে ঠিক এভাবেই একটা pod ক্র্যাশ করলে deployment নতুন pod তুলে দেয় আর service-এর IP অপরিবর্তিত থাকে — তাই আপনার অ্যাপ চলতেই থাকে, কেউ টেরও পায় না।
বাস্তব জীবনের উদাহরণ
একটা সেল্ফ-কারেক্টিং ফ্যাক্টরি ফ্লোর: আপনি ফ্যাক্টরি ম্যানেজারকে বলেন “সবসময় ৩টা welding robot চালু থাকা চাই।” কোন রোবটগুলো বা ভাঙলে কীভাবে রিস্টার্ট করতে হবে সেটা আপনি বলে দেন না — ম্যানেজার সেটা সামলায়। কোনো রোবট ফেইল করলে সেটা অটোমেটিক রিপ্লেস হয়ে যায়। ৫টা লাগলে আপনি সংখ্যাটা আপডেট করেন, বাকিটা ম্যানেজার বুঝে নেয়। container-এর জন্য Kubernetes হলো সেই ফ্যাক্টরি ম্যানেজার।
The Control Loop
Kubernetes-এর সবকিছু একই প্যাটার্ন অনুসরণ করে:
Desired state (what you declared) → Controller watches → Actual state
↑ |
└── Controller reconciles ←────────────────┘ আপনি যা চান সেটা declare করেন (order-service-এর ৩টা replica)। Controller-রা ক্রমাগত desired state আর actual state তুলনা করে এবং ফাঁকটা মিটিয়ে দিতে পরিবর্তন আনে। একটা pod ক্র্যাশ করলো → actual state নেমে ২ হলো → controller নতুন একটা pod তৈরি করলো → actual state আবার ৩-এ ফিরে এলো।
এটাই declarative: আপনি ফলাফল বর্ণনা করেন, ধাপগুলো নয়।
Core Objects
Pod: সবচেয়ে ছোট deployable unit। এক বা একাধিক container যারা network আর storage শেয়ার করে। একটা pod-এর ভেতরের container-রা localhost দিয়ে যোগাযোগ করে।
# Pods are rarely created directly — use Deployments
apiVersion: v1
kind: Pod
metadata:
name: order-service
labels:
app: order-service
spec:
containers:
- name: order-service
image: myorg/order-service:1.2.0
ports:
- containerPort: 3000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: order-service-secrets
key: database_url
resources:
requests:
memory: '128Mi'
cpu: '100m'
limits:
memory: '256Mi'
cpu: '500m' Deployment: একটা ReplicaSet ম্যানেজ করে যা আবার Pod-গুলো ম্যানেজ করে। rolling update আর rollback সামলায়।
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: order-service
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # create 1 extra pod before killing old
maxUnavailable: 0 # never go below desired replicas during update
template:
metadata:
labels:
app: order-service
version: '1.2.0'
spec:
containers:
- name: order-service
image: myorg/order-service:1.2.0
ports:
- containerPort: 3000
readinessProbe:
httpGet:
path: /health/ready
port: 3000
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /health/live
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
resources:
requests:
memory: '256Mi'
cpu: '250m'
limits:
memory: '512Mi'
cpu: '1000m'
lifecycle:
preStop:
exec:
command: ['sleep', '5']
terminationGracePeriodSeconds: 35 Service: একদল pod-এর জন্য একটা স্থিতিশীল network endpoint। Pod আসে-যায় নতুন IP নিয়ে; কিন্তু Service IP অপরিবর্তিত থাকে।
apiVersion: v1
kind: Service
metadata:
name: order-service
namespace: production
spec:
selector:
app: order-service # routes to pods with this label
ports:
- port: 80
targetPort: 3000
type: ClusterIP # internal only Service type-গুলো:
ClusterIP— শুধু cluster-এর ভেতরের IP (default)NodePort— প্রতিটা node-এ একটা static port-এ expose করেLoadBalancer— cloud load balancer provision করে (AWS ALB, GCP LB)
Namespaces
Namespace ক্লাস্টারকে ভার্চুয়াল সাব-ক্লাস্টারে ভাগ করে। ভিন্ন namespace-এর resource-গুলো একে অপর থেকে isolated (আলাদা RBAC, resource quota, network policy)।
kubectl create namespace production
kubectl create namespace staging
# Deploy to a specific namespace
kubectl apply -f deployment.yml -n production Essential kubectl
# Context and cluster
kubectl config get-contexts
kubectl config use-context k3s-production
# Get resources
kubectl get pods -n production
kubectl get deployments -n production
kubectl get services -n production
kubectl get all -n production # everything
# Inspect
kubectl describe pod order-service-7d4b5-xyz -n production
kubectl logs order-service-7d4b5-xyz -n production --tail=100 -f
kubectl exec -it order-service-7d4b5-xyz -n production -- sh
# Apply and delete
kubectl apply -f deployment.yml
kubectl delete -f deployment.yml
# Rollouts
kubectl rollout status deployment/order-service -n production
kubectl rollout history deployment/order-service -n production
kubectl rollout undo deployment/order-service -n production # rollback
kubectl rollout undo deployment/order-service --to-revision=3 -n production
# Scale
kubectl scale deployment order-service --replicas=5 -n production
# Port-forward for debugging
kubectl port-forward pod/order-service-7d4b5-xyz 3000:3000 -n production ConfigMaps and Secrets
apiVersion: v1
kind: ConfigMap
metadata:
name: order-service-config
namespace: production
data:
LOG_LEVEL: 'info'
QUEUE_CONCURRENCY: '10'
FEATURE_NEW_CHECKOUT: 'true' apiVersion: v1
kind: Secret
metadata:
name: order-service-secrets
namespace: production
type: Opaque
data:
# base64-encoded values (echo -n "value" | base64)
database_url: cG9zdGdyZXM6Ly8...
jwt_secret: c2VjcmV0... pod-এ reference করা:
spec:
containers:
- name: order-service
envFrom:
- configMapRef:
name: order-service-config
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: order-service-secrets
key: database_url Secret কখনো git-এ commit করবেন না। এগুলো ম্যানেজ করতে Sealed Secrets, External Secrets Operator, বা Vault ব্যবহার করুন। Base64 কোনো encryption নয়।
Resource Requests and Limits
requests — pod-কে যা guaranteed দেওয়া হয়। Scheduler এটা দিয়ে যথেষ্ট capacity আছে এমন একটা node খুঁজে নেয়। limits — একটা pod সর্বোচ্চ যতটা ব্যবহার করতে পারবে। memory limit ছাড়িয়ে গেলে container OOMKilled হয়।
resources:
requests:
memory: '256Mi'
cpu: '250m' # 250 millicores = 0.25 CPU cores
limits:
memory: '512Mi'
cpu: '1000m' # 1 full core Memory: সবসময় limit সেট করুন। OOM-killed pod রিস্টার্ট হয়; কিন্তু OOM node সবকিছু evict করে দেয়।
CPU: limit ছাড়িয়ে গেলে container throttle হয় (kill হয় না)। CPU limit বেশি নিচে রাখলে latency বাড়ে। অনেক টিম CPU request সেট করে কিন্তু limit করে না — এতে pod সঠিকভাবে schedule হওয়ার পরও burst করতে পারে।
Ingress
external traffic-কে service-এর দিকে route করে:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
namespace: production
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: '10m'
cert-manager.io/cluster-issuer: 'letsencrypt-prod'
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: api-tls-cert
rules:
- host: api.example.com
http:
paths:
- path: /api/orders
pathType: Prefix
backend:
service:
name: order-service
port:
number: 80
- path: /api/products
pathType: Prefix
backend:
service:
name: product-service
port:
number: 80 ক্লাস্টারে একটা ingress controller (nginx-ingress) ইনস্টল থাকা লাগে। cert-manager অটোমেটিক Let’s Encrypt সার্টিফিকেট provision আর renew করে।
Labels and Selectors
Label হলো যেকোনো resource-এর উপর key-value pair। Selector label দিয়ে resource ফিল্টার করে। Kubernetes-এর পুরো scheduling আর routing মডেল label-এর উপর নির্ভরশীল।
# Find pods by label
kubectl get pods -l app=order-service -n production
kubectl get pods -l app=order-service,version=1.2.0 -n production
# Add a label to a running pod (for debugging)
kubectl label pod order-service-7d4b5-xyz debug=true -n production
# Remove a pod from a Service (stop routing to it without killing)
kubectl label pod order-service-7d4b5-xyz app=order-service-debug --overwrite -n production
# Service selector no longer matches — this pod gets no traffic
# Use this to debug a single instance under real conditions