সরাসরি পর্বে যান
পর্ব ৪৭ · ৮১-এর মধ্যে · মডিউল ৮

ConfigMap, Secret আর storage — PV, PVC আর StatefulSet

ConfigMap, Secret and Storage — PV, PVC and StatefulSet

Taught in Bangla, this lesson separates config from images with ConfigMaps as env and files, shows which changes reach a running Pod, why base64 is not encryption, RBAC, etcd encryption and external secrets, then PV, PVC, StorageClass, access modes, reclaim policy, StatefulSet ordering and stable identity, and databases on Kubernetes versus managed services, with real output and simulators.

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

  • ConfigMap-কে env আর ফাইল হিসেবে দেওয়া, আর কোন পথে পরিবর্তন পৌঁছায়
  • env, envFrom আর mounted ফাইলের মধ্যে কোন মান জেতে
  • Secret-এর আসল ঝুঁকি, etcd encryption, RBAC আর external secret-এর পথ
  • emptyDir, PV, PVC, StorageClass, dynamic provisioning, access mode আর reclaim policy
  • StatefulSet-এর ক্রম, স্থায়ী নাম, headless Service আর replica-প্রতি PVC
  • Kubernetes-এ database চালানো বনাম managed service, snapshot, Velero আর zone-বাঁধা disk

এই পর্বের আউটপুট কোথা থেকে: সব transcript একটি local single-node cluster-এ (Kubernetes ১.৩৪) আসল run থেকে নেওয়া। নাম, age, IP আর সময় আপনার ক্ষেত্রে আলাদা হবে। জায়গা বাঁচাতে লম্বা লাইন ছেঁটে … দেওয়া হয়েছে। এই cluster-এর default storage একটি local hostpath provisioner, তাই আসল cloud disk নেই। Cloud-এর আচরণ আলাদা হলে তা স্পষ্ট করে বলা আছে।

১ · Config কেন image-এর বাইরে

ধরুন একটি কাল্পনিক অনলাইন বাজার অ্যাপ। Staging-এ log হবে বিস্তারিত, production-এ সংক্ষিপ্ত। Database-এর ঠিকানাও দুই জায়গায় আলাদা। এই মানগুলো image-এর ভেতরে লিখলে প্রতিটি পরিবেশের জন্য আলাদা image বানাতে হবে। তখন যে image পরীক্ষা হলো, production-এ সেটি আর যাচ্ছে না।

The Twelve-Factor App-এর পরামর্শ তাই স্পষ্ট: যা পরিবেশভেদে বদলায়, তা কোডের বাইরে থাকবে। Kubernetes-এ এই কাজের জন্য আছে দুটি object: ConfigMap আর Secret. দুটিই namespace-এর ভেতরে থাকে, আর একটি object-এর আকার সর্বোচ্চ প্রায় ১ MiB. ফলে এগুলো config-এর জন্য, বড় ফাইল বা ডেটার জন্য নয়।

রেস্তোরাঁর কথা ভাবুন। রান্নাঘর আর রেসিপি হলো image, প্রতিটি শাখায় একই। আজকের দাম আর “আজ ইলিশ নেই” লেখা থাকে মেনুবোর্ডে। মেনুবোর্ড বদলাতে রান্নাঘর ভাঙতে হয় না। ConfigMap হলো সেই মেনুবোর্ড। আর ক্যাশবাক্সের চাবি বোর্ডে ঝোলানো হয় না, সেটি আলাদা জায়গায় থাকে। সেটিই Secret.

নিচের manifest-এ একটি ConfigMap আর একটি Pod আছে। Pod একই ConfigMap তিন পথে পাচ্ছে: environment variable হিসেবে, পুরো volume হিসেবে, আর subPath দিয়ে একটি ফাইল হিসেবে।

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  LOG_LEVEL: info
  app.conf: |
    greeting=salam
    max_items=20
---
apiVersion: v1
kind: Pod
metadata:
  name: cfg-demo
spec:
  containers:
    - name: app
      image: alpine:3.20
      command: ["sleep", "3600"]
      env:
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: LOG_LEVEL
      volumeMounts:
        - name: conf
          mountPath: /etc/app
        - name: conf
          mountPath: /etc/one/app.conf
          subPath: app.conf
      resources:
        requests: { cpu: 10m, memory: 16Mi }
        limits: { cpu: 100m, memory: 32Mi }
  volumes:
    - name: conf
      configMap:
        name: app-config
$ kubectl exec cfg-demo -- printenv LOG_LEVEL
info
$ kubectl exec cfg-demo -- cat /etc/app/LOG_LEVEL
info
$ kubectl exec cfg-demo -- ls -la /etc/app
…
drwxr-xr-x  2 root root 4096 Oct 30 19:15 ..2026_10_30_19_15_08.3481835902
lrwxrwxrwx  1 root root   32 Oct 30 19:15 ..data -> ..2026_10_30_19_15_08.3481835902
lrwxrwxrwx  1 root root   16 Oct 30 19:15 LOG_LEVEL -> ..data/LOG_LEVEL
lrwxrwxrwx  1 root root   15 Oct 30 19:15 app.conf -> ..data/app.conf

ConfigMap-এর প্রতিটি key একটি ফাইল হয়েছে। ফাইলগুলো আসলে symlink: LOG_LEVEL যায় ..data-য়, আর ..data যায় একটি সময়-চিহ্নিত folder-এ। এই নকশাই পরে কাজে লাগবে। নতুন মান এলে kubelet নতুন folder বানিয়ে শুধু ..data ঘুরিয়ে দেয়, তাই সব ফাইল একসাথে বদলায়।

ConfigMap না থাকলে (আর optional: true না দিলে) container চালু হয় না। আসল run-এ অনুপস্থিত ConfigMap-এর জন্য status এলো CreateContainerConfigError, আর event-এ configmap "no-such-config" not found. এই শব্দটি দেখলে আগে নাম আর namespace মেলান।

২ · ConfigMap বদলালে কে দেখে — env বনাম ফাইল

এবার মান বদলানো যাক। LOG_LEVEL হবে debug, max_items হবে 50. Pod restart করা হবে না।

$ kubectl patch configmap app-config --type merge -p \
  '{"data":{"LOG_LEVEL":"debug","app.conf":"greeting=salam\nmax_items=50\n"}}'
configmap/app-config patched

# after the kubelet sync, same Pod, no restart
$ kubectl exec cfg-demo -- printenv LOG_LEVEL
info
$ kubectl exec cfg-demo -- cat /etc/app/LOG_LEVEL
debug
$ kubectl exec cfg-demo -- cat /etc/app/app.conf
greeting=salam
max_items=50
$ kubectl exec cfg-demo -- cat /etc/one/app.conf
greeting=salam
max_items=20

একই ConfigMap, একই Pod, তিন রকম ফল। ফাইল নতুন মান পেয়েছে। printenv এখনো info, কারণ environment variable process চালু হওয়ার সময় একবারই বসে। subPath ফাইলে এখনো 20, কারণ subPath mount সেই symlink-বদলের সুবিধা পায় না।

আসল run-এর মান
ConfigMap LOG_LEVEL
চলমান Pod-এর env
/etc/app/LOG_LEVEL
/etc/app/app.conf
subPath ফাইল
Restart-এর পর নতুন Pod-এর env
  1. শুরুর অবস্থা। ConfigMap-এ info আর max_items=20. তিনটি পথেই একই মান দেখা যাচ্ছে।
  2. API-তে বদল। kubectl patch শুধু etcd-তে থাকা object বদলেছে। Node-এ এখনো কিছু বদলায়নি, তাই container আগের মানই দেখছে।
  3. kubelet মিলিয়ে নিল। kubelet নতুন মান নিয়ে নতুন folder বানিয়ে ..data ঘুরিয়ে দিল। দুটি ফাইলই একসাথে বদলাল। আসল run-এ এই দেরি মাপা হয়েছে ৬৩, ১৬ আর ১৮ সেকেন্ড, আর Pod চালুর ঠিক পরের বদলে প্রায় সাথে সাথে। সময়টি নির্দিষ্ট নয়, kubelet-এর sync আর cache-এর ওপর নির্ভর করে।
  4. যা বদলায়নি। env আর subPath ফাইল আগের মানে আছে। কোনো error নেই, কোনো সতর্কবার্তাও নেই। তাই এই ভুল চুপচাপ টিকে থাকে।
  5. নতুন Pod. একটি Deployment-এ kubectl rollout restart চালানো হলো। নতুন Pod শুরুতে ConfigMap পড়ল, তাই তার env-এ debug. পুরোনো Pod-এ ৭৫ সেকেন্ড পরেও info ছিল।

ফাইল বদলালেই app নতুন মান নেবে, এমন নয়। App ফাইলটি আবার পড়লে তবেই নেবে। Nginx-এর মতো অনেক program-কে reload করতে হয়। তাই ফাইলে বদল এলেও app-এর আচরণ যাচাই করুন। উল্টো দিকে, একটি ভুল config সব Pod-এ একসাথে পৌঁছাতে পারে। ফলে অনেক দল নতুন নামের ConfigMap বানিয়ে rolling update চালায়। এতে পুরোনো মানে ফিরে যাওয়াও সহজ হয়।

আরেকটি উপায় immutable: true. আসল run-এ এমন ConfigMap বদলাতে গেলে API server বলল: data: Forbidden: field is immutable when `immutable` is set. এতে ভুল বদল আটকায়, আর kubelet-এর নজরদারির চাপও কমে।

env, envFrom আর ফাইল — কোন মান জেতে

নিচের Pod-এ envFrom-এ দুটি ConfigMap আছে, env-এ নিজের মান আছে, আর base ফাইল হিসেবেও mount করা। Pod-এর log-এ আসল ফল এলো COLOR=green SIZE=large MODE=fast, আর ফাইলে blue.

নিয়মআসল run-এ প্রমাণ
envFrom-এ একই key থাকলে পরের উৎস জেতেbase-এ blue, extra-তে green. ফল green
env-এর মান সব envFrom-কে হারায়base-এ small, env-এ large. ফল large
Mounted ফাইল এই হিসাবের বাইরেenv-এ green থাকলেও ফাইলে blue
ফাইল আর env-এর মধ্যে কে জিতবে, তা Kubernetes ঠিক করে নাএটি app-এর নিজের নিয়ম

নিচের simulator-এ POD আর CHANGE বদলে দেখুন। উৎসের ক্রম উল্টে দিলে বা env থেকে একটি মান সরালে ফল কীভাবে বদলায়, তা মিলিয়ে নিন।

// Which value does the container see? Edit POD and CHANGE, then run.
const cm = {
  base:  { COLOR: "blue", SIZE: "small" },
  extra: { COLOR: "green" },
};
const POD = {
  envFrom: ["base", "extra"],          // later source wins
  env: { MODE: "fast", SIZE: "large" },// env beats every envFrom
  file: "base",                        // mounted at /etc/app
};
const CHANGE = { map: "base", key: "SIZE", value: "medium" };
function envAtStart(p, maps) {
  const out = {};
  for (const name of p.envFrom) Object.assign(out, maps[name]);
  return Object.assign(out, p.env);
}
const show = (o) => Object.keys(o).sort().map(k => k + "=" + o[k]).join(" ");
const running = envAtStart(POD, cm);    // fixed when the container starts
console.log("at start  env : " + show(running));
console.log("at start  file: " + show(cm[POD.file]));
const after = JSON.parse(JSON.stringify(cm));
after[CHANGE.map][CHANGE.key] = CHANGE.value;
console.log(`-- ConfigMap ${CHANGE.map}: ${CHANGE.key}=${CHANGE.value}`);
console.log("same Pod  env : " + show(running) + "   (frozen)");
console.log("same Pod  file: " + show(after[POD.file]) + "   (after kubelet sync)");
console.log("new Pod   env : " + show(envAtStart(POD, after)));
// The app, not Kubernetes, decides between a file and a variable.
const appReads = (key) => after[POD.file][key] ?? running[key] ?? "default";
console.log("app rule file > env > default: SIZE=" + appReads("SIZE") +
            " MODE=" + appReads("MODE") + " PORT=" + appReads("PORT"));

প্রথম লাইনে আসে at start env : COLOR=green MODE=fast SIZE=large, যা আসল Pod-এর সাথে মেলে। ConfigMap-এ SIZE=medium করার পর ফাইল হয় COLOR=blue SIZE=medium, কিন্তু env থাকে আগের মতো। new Pod env-এও SIZE=large, কারণ env-এর মান ConfigMap-কে হারায়। শেষ লাইন SIZE=medium MODE=fast PORT=default দেখায়, app নিজে ফাইলকে আগে পড়লে ফল কেমন হয়।

৩ · Secret — base64 কেন encryption নয়

Secret-এর গঠন ConfigMap-এর মতোই, তবে মান থাকে base64-এ। নামটি শুনে অনেকে ভাবেন মানটি লুকানো আছে। একটি নকল মান দিয়ে দেখা যাক।

$ kubectl create secret generic db-cred \
    --from-literal=username=demo_user \
    --from-literal=password='fake-Passw0rd-123'
secret/db-cred created

$ kubectl get secret db-cred -o yaml
apiVersion: v1
data:
  password: ZmFrZS1QYXNzdzByZC0xMjM=
  username: ZGVtb191c2Vy
kind: Secret
…
type: Opaque

$ kubectl get secret db-cred -o jsonpath='{.data.password}' | base64 -d
fake-Passw0rd-123

$ kubectl describe secret db-cred
…
Data
====
password:  17 bytes
username:  9 bytes

কোনো চাবি ছাড়াই মানটি ফিরে এলো। Encoding মানে লেখার ধরন বদলানো, যেমন বাংলা কথাকে রোমান হরফে লেখা। Encryption মানে চাবি ছাড়া পড়া যায় না। Base64 আছে যেকোনো byte, যেমন certificate, YAML-এ রাখার সুবিধার জন্য। describe মান দেখায় না, শুধু আকার দেখায়। এটি ভুলে চোখে পড়া আটকায়, কিন্তু get-এর অনুমতি থাকলে কোনো বাধা নেই। এই কারণেই manifest-এর Secret git-এ রাখা বিপজ্জনক।

Secret Pod-এ দুই পথে যায়, ConfigMap-এর মতোই। আসল run-এ ফাইল পথে defaultMode: 0400 দিয়ে দেখা গেল:

$ kubectl exec sec-demo -- ls -lL /run/cred
total 8
-r--------  1 root root 17 Oct 30 19:20 password
-r--------  1 root root  9 Oct 30 19:20 username

# which filesystem holds the mount
tmpfs /run/cred tmpfs

$ kubectl auth can-i get secrets \
    --as=system:serviceaccount:abcl-kc:default
no

Secret volume বসেছে tmpfs-এ, মানে node-এর memory-তে, disk-এ নয়। ফাইলের অনুমতি -r--------, শুধু owner পড়তে পারে। আর namespace-এর default service account-এর Secret পড়ার অনুমতি নেই। তবে যিনি namespace-এ Pod বানাতে পারেন, তিনি সেই Pod-এ Secret mount করেও পড়তে পারেন। তাই create pods-এর অনুমতিও আসলে Secret-এর অনুমতির কাছাকাছি।

Env-এ Secret দেওয়া সহজ, কিন্তু ফাইলই সাধারণত নিরাপদ। Env প্রায়ই crash report, debug endpoint আর child process-এ ছড়িয়ে পড়ে। আর ফাইল পথে rotation-এর মান চলমান Pod-এ পৌঁছায়। শর্ত একটাই: app-কে ফাইলটি আবার পড়তে হবে।

৪ · Secret আসলে যেভাবে রক্ষা পায়

পর্ব ৩৩ Secret আর KMS-এ envelope encryption আর secret manager দেখেছেন। Kubernetes-এ সেগুলো তিন স্তরে বসে।

স্তর ১: etcd-তে encryption at rest

Kubernetes-এর default-এ Secret etcd-তে encrypted না-ও থাকতে পারে। Upstream Kubernetes-এ encryption at rest নিজে চালু করতে হয়। API server একটি config ফাইল পড়ে আর etcd-তে লেখার আগে encrypt করে। নিচের ফাইলটি API server-এর flag দিয়ে দেওয়া হয়, kubectl apply দিয়ে নয়।

# Passed to kube-apiserver with --encryption-provider-config
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - kms:
          apiVersion: v2
          name: example-kms
          endpoint: unix:///var/run/kmsplugin/socket.sock
      - identity: {}

প্রথম provider নতুন লেখা encrypt করে। পরের provider-গুলো শুধু পুরোনো ডেটা পড়ার জন্য। identity মানে encryption নেই। এটি চালু করলে আগের Secret নিজে থেকে encrypted হয় না, প্রতিটি আবার লিখতে হয়। KMS provider-এ মূল চাবি cluster-এর বাইরে থাকে। Managed Kubernetes-এ এই অংশ provider সামলায়, তবে সুবিধার নাম আর default provider-ভেদে আলাদা। নিজের cluster-এর docs দেখে নিন। এই ফাইল এই পর্বের cluster-এ চালানো হয়নি, কারণ এতে API server বদলাতে হয়।

স্তর ২: RBAC — কে পড়তে পারে

etcd encryption চুরি যাওয়া disk বা backup থেকে বাঁচায়। কিন্তু API দিয়ে get secret করলে মান খোলাই আসে। তাই RBAC-এ Secret-এর get আর list অল্প কয়েকজনকে দিন। list-ও মান ফেরত দেয়, শুধু নাম নয়। Audit log চালু রাখুন, যাতে কে পড়ল তা জানা যায়। পর্ব ৪৯-এ RBAC আরও বিস্তারে আসবে।

স্তর ৩: মান cluster-এর বাইরে রাখা

পথকীভাবে কাজ করেমান কোথায় থাকেমনে রাখুন
External Secrets OperatorController বাইরের manager থেকে মান এনে Secret বানায়Manager-এ, আর একটি কপি cluster-এর Secret-এApp বদলাতে হয় না, তবে etcd সুরক্ষা লাগবেই
Secrets Store CSI DriverPod চালুর সময় মান ফাইল হিসেবে mount হয়Manager-এ, আর Pod-এর tmpfs-এetcd-তে কপি না-ও থাকতে পারে, env চাইলে অতিরিক্ত ব্যবস্থা লাগে
App নিজে manager-কে ডাকেSDK দিয়ে চলার সময় মান আনেManager-এ, আর process-এর memory-তেসবচেয়ে কম ছড়ায়, তবে কোড জড়িয়ে যায়

Provider-এর উদাহরণ: AWS Secrets Manager, Azure Key Vault, Google Secret Manager, আর নিজে চালানোর জন্য HashiCorp Vault. Pod কীভাবে manager-এর কাছে পরিচয় দেবে, সেটিও প্রশ্ন। স্থায়ী চাবি না রেখে workload identity দিয়ে সাময়িক পরিচয় নেওয়াই ভালো। Secret rotation-এর নিয়ম পর্ব ৩৩-এই আছে।

৫ · Pod মরে, ডেটা কোথায় থাকে

Container-এর নিজের filesystem তার সাথেই মুছে যায়, পর্ব ৩৯-এ Docker-এ যা দেখেছেন। Kubernetes-এ এর সমাধান volume. প্রশ্ন হলো, volume-এর আয়ু কতটা।

কী ঘটলContainer-এর filesystememptyDirPVC
Container crash করে restartমুছে যায়থাকেথাকে
Pod মুছে নতুন Podমুছে যায়মুছে যায়থাকে
PVC মুছে দিলেন——Reclaim policy ঠিক করে

emptyDir হলো Pod-এর সাথে জন্মানো ফাঁকা ফোল্ডার। এটি cache, সাময়িক ফাইল বা দুই container-এর মধ্যে ফাইল দেওয়া-নেওয়ার জন্য ভালো। medium: Memory দিলে এটি tmpfs হয়, আর ব্যবহৃত অংশ container-এর memory limit-এ গোনা হয়।

গুদামঘরের কথা ভাবুন। PersistentVolume হলো গুদামের একটি আসল ঘর। PersistentVolumeClaim হলো দরখাস্ত: “১০০ বর্গফুট শুকনো ঘর চাই।” ব্যবসায়ী ঘরের নম্বর জানেন না, রসিদ দেখালেই মাল রাখতে পারেন। আর StorageClass হলো ঘরের ধরন: সাধারণ ঘর, না হিমঘর। ব্যবসায়ী বদলালেও রসিদ থাকলে মাল ঘরেই থাকে।

Pod থেকে আসল disk পর্যন্ত

Pod → PVC → PV → cloud disk

Pod, PVC, PV আর cloud disk-এর সম্পর্ক namespace · আপনার দল Pod mountPath: /data Pod মুছলে এটিই শুধু যায় PVC · দরখাস্ত 100Gi · RWO · fast-ssd কত জায়গা আর কী ধরন Bound PV · cluster-এর reclaim: Delete বা Retain StorageClass provisioner CSI driver cloud provider · একটি zone আসল block disk এখানেই ডেটা, এখানেই বিল
ছবি ৪৭.১ — Pod শুধু PVC-র নাম জানে। Pod বদলালেও নিচের তিন স্তর একই থাকে। PVC থাকে namespace-এ, PV থাকে cluster স্তরে।

নিচের প্রমাণটি দেখুন। Pod notes-1 দুটি ফাইল লিখল: একটি PVC-তে, একটি emptyDir-এ। তারপর Pod মুছে একই PVC দিয়ে নতুন Pod চালানো হলো।

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: notes-data
spec:
  accessModes: ["ReadWriteOnce"]
  resources:
    requests:
      storage: 16Mi
---
apiVersion: v1
kind: Pod
metadata:
  name: notes-1
spec:
  containers:
    - name: app
      image: alpine:3.20
      command: ["sleep", "3600"]
      volumeMounts:
        - name: data
          mountPath: /data
        - name: scratch
          mountPath: /scratch
      resources:
        requests: { cpu: 10m, memory: 16Mi }
        limits: { cpu: 100m, memory: 64Mi }
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: notes-data
    - name: scratch
      emptyDir: {}
$ kubectl exec notes-1 -- sh -c \
    'echo "order-1001 paid" > /data/orders.txt; echo "temp cache" > /scratch/tmp.txt'

$ kubectl delete pod notes-1
pod "notes-1" deleted from abcl-kc namespace

$ kubectl get pvc notes-data
NAME         STATUS   VOLUME           CAPACITY   ACCESS MODES   STORAGECLASS   …
notes-data   Bound    pvc-75471c50-…   16Mi       RWO            hostpath       …

# a new Pod notes-2 uses the same claim
$ kubectl exec notes-2 -- cat /data/orders.txt
order-1001 paid
$ kubectl exec notes-2 -- sh -c 'ls -la /scratch; cat /scratch/tmp.txt'
total 8
drwxrwxrwx  2 root root 4096 Oct 30 19:19 .
drwxr-xr-x  1 root root 4096 Oct 30 19:19 ..
cat: can't open '/scratch/tmp.txt': No such file or directory
command terminated with exit code 1

PVC-র ফাইল নতুন Pod পেল, emptyDir-এর ফাইল হারাল। Pod মোছার পরও PVC Bound ছিল। আলাদা run-এ container-কে ইচ্ছা করে থামানো হলো: RESTARTS 1 হলো, আর emptyDir-এর ফাইল তখনও ছিল। তাই emptyDir-এর আয়ু container নয়, Pod.

৬ · StorageClass, dynamic provisioning আর access mode

আগে admin হাতে disk আর PV বানিয়ে রাখতেন। একে বলে static provisioning. এখন সাধারণ পথ dynamic provisioning: PVC এলেই provisioner disk আর PV বানায়। উপরের PVC-তে StorageClass লেখা ছিল না, তাই cluster-এর default class hostpath কাজে লেগেছে।

$ kubectl get storageclass
NAME                 PROVISIONER          RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   …
hostpath (default)   docker.io/hostpath   Delete          Immediate           false                  …
  1. PVCnotes-dataদরখাস্ত
  2. StorageClasshostpathধরন
  3. Provisionerdocker.io/hostpathdisk বানায়
  4. PVpvc-75471c50-…আসল জায়গা
আসল run-এর অবস্থা আর event
PVC-র status
PV
Event
Pod notes-1
  1. দরখাস্ত এলো। kubectl apply PVC বানাল: ১৬ MiB, ReadWriteOnce. এখনো কোনো জায়গা নেই।
  2. Controller ধরন খুঁজল। PV controller দেখল মানানসই PV নেই। Default StorageClass hostpath পেল। Event এলো “Waiting for a volume to be created”.
  3. Provisioner কাজ ধরল। Event Provisioning. এখানে এটি node-এ একটি folder বানায়। Cloud-এ এই ধাপে CSI driver provider-এর API ডেকে আসল disk বানায়, আর খরচ শুরু হয়।
  4. PV তৈরি। Event ProvisioningSucceeded. PV-র নাম pvc- দিয়ে শুরু, পরে PVC-র UID. Reclaim policy এসেছে StorageClass থেকে: Delete.
  5. Bind. PVC আর PV একে অপরের সাথে জোড়া লাগল। এই জোড়া এক-এক: একটি PV একই সময়ে একটিই PVC-র।
  6. Pod লাগাল। kubelet volume-টি /data-য় mount করল। Cloud-এ এর আগে disk-কে node-এ attach করতে হয়, তাই Pod শুরুতে কিছুটা দেরি হতে পারে।

volumeBindingMode ঠিক করে disk কখন বানানো হবে। Immediate মানে PVC এলেই। WaitForFirstConsumer মানে Pod কোথায় বসবে তা জানার পরে। নিজের একটি class বানিয়ে আসল run-এ দেখা গেল:

$ kubectl get storageclass abcl-kc-late
NAME           PROVISIONER          RECLAIMPOLICY   VOLUMEBINDINGMODE      …
abcl-kc-late   docker.io/hostpath   Retain          WaitForFirstConsumer   …

$ kubectl get pvc late-data
NAME        STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   …
late-data   Pending                                      abcl-kc-late   …

# event on the claim
WaitForFirstConsumer  waiting for first consumer to be created before binding

# after a Pod that uses the claim is created
$ kubectl get pvc late-data
NAME        STATUS   VOLUME           CAPACITY   ACCESS MODES   STORAGECLASS   …
late-data   Bound    pvc-9065f351-…   16Mi       RWO            abcl-kc-late   …

Pod না আসা পর্যন্ত PVC Pending ছিল, আর এটি কোনো ভুল নয়। Pod আসার পরই disk তৈরি হলো। অনেক cloud class-এ এই মোড ব্যবহার হয়, যার কারণ অংশ ৯-এ দেখবেন। তাই Pending দেখে ঘাবড়ানোর আগে event পড়ুন।

Access mode — কে কীভাবে লাগাবে

Modeমানেসাধারণ উৎস
ReadWriteOnce (RWO)একটি node পড়া-লেখা করতে পারেBlock storage: EBS, Azure Disk, Persistent Disk
ReadWriteOncePod (RWOP)একটিমাত্র PodDocs-এ মূলত CSI volume-এর জন্য বলা; এই run-এ hostpath-এও মানা হয়েছে
ReadOnlyMany (ROX)অনেক node, শুধু পড়াDriver-ভেদে আলাদা
ReadWriteMany (RWX)অনেক node, পড়া-লেখাFile storage: EFS, Azure Files, Filestore, NFS

এটি সবচেয়ে ভুল বোঝা বিষয়গুলোর একটি। RWO-র “Once” মানে এক Pod নয়, এক node. আসল run-এ এক node-এ দুটি Pod একই RWO PVC লাগিয়ে দুজনেই order-1001 paid পড়তে পেরেছে। RWOP-তে দ্বিতীয় Pod Pending থেকে গেছে, event-এ: PersistentVolumeClaim with ReadWriteOncePod access mode already in-use by another pod.

এই cluster-এর একটি সীমা: ১৬ MiB-র PVC-তে ২৪ MiB-র ফাইল লেখা গেছে। Hostpath provisioner আকারের সীমা মানে না। Cloud disk-এ PVC-র আকারই disk-এর আকার। তাই local-এ চলা মানেই production-এ জায়গা কুলোবে, এমন নয়।

৭ · PVC মুছলে disk-এর কী হয় — reclaim policy

Pod মুছলে ডেটা থাকে। কিন্তু PVC মুছলে কী হবে, তা ঠিক করে reclaim policy. দুটি আসল মান আছে। পুরোনো Recycle deprecated, নতুন কাজে ব্যবহার করবেন না। Dynamic PV সাধারণত StorageClass থেকে policy পায়, আর StorageClass-এ না লিখলে default হয় Delete.

# Policy Delete (class hostpath)
$ kubectl delete pvc notes-data
$ kubectl get pv pvc-75471c50-…
Error from server (NotFound): persistentvolumes "pvc-75471c50-…" not found

# Policy Retain (class abcl-kc-late)
$ kubectl delete pvc late-data
$ kubectl get pv
NAME             CAPACITY   …   RECLAIM POLICY   STATUS     CLAIM               …
pvc-8c4331f9-…   16Mi       …   Retain           Released   abcl-kc/late-data   …

# A new claim naming that PV does not bind yet
FailedBinding  volume "pvc-9065f351-…" already bound to a different claim.

# Remove the old claim reference, then bind again
$ kubectl patch pv pvc-8c4331f9-… --type json \
    -p '[{"op":"remove","path":"/spec/claimRef"}]'
$ kubectl get pv
NAME             CAPACITY   …   RECLAIM POLICY   STATUS      …
pvc-8c4331f9-…   16Mi       …   Retain           Available   …
$ kubectl get pvc late-new
NAME       STATUS   VOLUME           CAPACITY   …
late-new   Bound    pvc-8c4331f9-…   16Mi       …
$ kubectl exec late-pod2 -- cat /data/orders.txt
order-1001 paid

Delete-এ PV সাথে সাথে চলে গেছে, আর এই provisioner-এ ডেটাও। Retain-এ PV থেকে গেছে, তবে Released অবস্থায়, পুরোনো PVC-র নাম নিয়ে। Released মানে আপনা থেকে আবার ব্যবহার হবে না। পুরোনো মালিকের তথ্য নতুন কেউ যেন না পায়, এটিই এর উদ্দেশ্য। claimRef সরালে PV হলো Available, নতুন PVC জুড়ল, আর পুরোনো ফাইল ফিরে এলো। তাই PV আবার খোলার আগে ভেবে নিন, এই ডেটা কার হাতে যাচ্ছে। ওপরের FailedBinding লাইনটি আগের একটি run-এর, তাই PV-র নাম আলাদা।

Retain-এ PV-র জীবনচক্র

Phases of a retained PersistentVolume

  1. BoundPVC-র সাথে যুক্ত
  2. ReleasedPVC মোছা, ডেটা আছে
  3. AvailableclaimRef সরানো
  4. Boundনতুন PVC
ছবি ৪৭.২ — Released অবস্থা থেকে ফেরা হাতের কাজ, Kubernetes নিজে করে না।

Retain ডেটা বাঁচায়, কিন্তু বিলও বাঁচিয়ে রাখে। PV মুছলেও আসল cloud disk মোছে না, সেটি আলাদা করে মুছতে হয়। নিচের simulator-এ policy, আকার আর admin-এর পদক্ষেপ বদলে দেখুন। দাম এখানে হিসাবের জন্য ধরে নেওয়া মান, আসল দাম provider-এর pricing পাতায় দেখুন।

// What happens to the disk after a PVC is deleted. Edit CASES and run.
// RATE is an assumed round number for the arithmetic, not a real price.
const RATE = 0.10;                     // assumed $ per GiB per month
const CASES = [
  { name: "dev-cache",  policy: "Delete", gib: 20,  pvcDeleted: true,  admin: "" },
  { name: "orders-db",  policy: "Retain", gib: 100, pvcDeleted: true,  admin: "" },
  { name: "old-logs",   policy: "Retain", gib: 200, pvcDeleted: true,  admin: "pv" },
  { name: "old-report", policy: "Retain", gib: 50,  pvcDeleted: true,  admin: "pv+disk" },
  { name: "reports",    policy: "Delete", gib: 50,  pvcDeleted: false, admin: "" },
];
function outcome(c) {
  if (!c.pvcDeleted) return { pv: "Bound", disk: "in use", data: "safe" };
  if (c.policy === "Delete") return { pv: "gone", disk: "deleted", data: "LOST" };
  if (c.admin === "pv+disk") return { pv: "gone", disk: "deleted", data: "LOST" };
  if (c.admin === "pv") return { pv: "gone", disk: "orphan", data: "on disk, no PV" };
  return { pv: "Released", disk: "kept", data: "on disk, not reusable yet" };
}
let bill = 0, orphan = 0;
console.log("name        policy  PV        disk     data");
for (const c of CASES) {
  const o = outcome(c);
  const cost = o.disk === "deleted" ? 0 : c.gib * RATE;
  bill += cost;
  if (c.pvcDeleted && o.disk !== "deleted") orphan += cost;
  console.log(c.name.padEnd(12) + c.policy.padEnd(8) + o.pv.padEnd(10) +
    o.disk.padEnd(9) + o.data + "  ($" + cost.toFixed(2) + "/month)");
}
console.log("monthly disk bill: $" + bill.toFixed(2));
console.log("paid for disks no PVC uses: $" + orphan.toFixed(2));

orders-db Released অবস্থায় ডেটা রেখেছে, খরচ ধরা হয়েছে $10.00/month. old-logs-এ PV মোছা হলেও disk orphan, তাই ২০০ × ০.১০ = $20.00 চলছে, অথচ cluster থেকে তাকে আর দেখা যায় না। মোট বিল $35.00, যার $30.00 এমন disk-এর, যা কোনো PVC ব্যবহার করে না। মিলিয়ে দেখুন: ০ + ১০ + ২০ + ০ + ৫ যোগ করলে ৩৫ ডলারই হয়।

৮ · StatefulSet — নাম, ক্রম আর নিজের disk

Deployment-এর Pod-গুলো একে অপরের বদলি। নাম এলোমেলো, কাজ একই, আর কোনো Pod-এর আলাদা গুরুত্ব নেই। কিন্তু database replica এমন নয়। প্রতিটির নিজের ডেটা, নিজের ভূমিকা। Primary কে, replica কে, কে কার সাথে কথা বলবে, সব আগে থেকে জানা দরকার। এই stateful কাজের জন্য StatefulSet.

Deployment হলো হোটেল: যে ঘর খালি, সেখানে থাকুন, চলে গেলে ঘর অন্য কারও। StatefulSet হলো মেসের নির্দিষ্ট সিট: সিট ১-এর বাসিন্দা বাড়ি থেকে ফিরলে আবার সিট ১-এই ওঠেন, আর খাটের নিচে তাঁর নিজের ট্রাঙ্কটিই পান।

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  clusterIP: None        # headless: no virtual IP
  selector:
    app: web
  ports:
    - port: 80
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: web       # must name the headless Service
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: nginx
          image: nginx:1.27-alpine
          ports:
            - containerPort: 80
          readinessProbe:
            tcpSocket:
              port: 80
            periodSeconds: 2
          volumeMounts:
            - name: data
              mountPath: /usr/share/nginx/html
          resources:
            requests: { cpu: 20m, memory: 16Mi }
            limits: { cpu: 100m, memory: 48Mi }
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 16Mi
$ kubectl get pods -l app=web -w      # trimmed to one line per change
web-0   0/1   Pending             0     0s
web-0   0/1   ContainerCreating   0     0s
web-0   1/1   Running             0     2s
web-1   0/1   Pending             0     0s
web-1   0/1   ContainerCreating   0     0s
web-1   1/1   Running             0     2s
web-2   0/1   Pending             0     0s
web-2   0/1   ContainerCreating   0     0s
web-2   1/1   Running             0     2s

$ kubectl get pvc
NAME         STATUS   VOLUME           CAPACITY   …
data-web-0   Bound    pvc-045ec649-…   16Mi       …
data-web-1   Bound    pvc-23fc99da-…   16Mi       …
data-web-2   Bound    pvc-2f530807-…   16Mi       …

# from another Pod; each replica wrote its own name to its own disk
$ kubectl exec client -- nslookup -type=a web.abcl-kc.svc.cluster.local
…
Name:	web.abcl-kc.svc.cluster.local
Address: 10.1.0.241
Name:	web.abcl-kc.svc.cluster.local
Address: 10.1.0.242
Name:	web.abcl-kc.svc.cluster.local
Address: 10.1.0.245
$ kubectl exec client -- nslookup -type=a web-1.web.abcl-kc.svc.cluster.local
…
Address: 10.1.0.242
$ kubectl exec client -- wget -qO- http://web-1.web
I am web-1

web-1 শুরু হয়েছে web-0 Ready হওয়ার পরে, web-2 তার পরে। প্রতিটি replica পেয়েছে নিজের PVC: নামের নিয়ম <template>-<pod>. পর্ব ৪৫-এর সাধারণ Service একটি virtual IP দেয়। Headless Service-এ DNS সরাসরি তিনটি Pod-এর IP ফেরত দিল, আর প্রতিটি Pod নিজের নামেও DNS record পেল। StatefulSet এই Service নিজে বানায় না, তাই আগেই বানাতে হয়। volumeClaimTemplates অংশটি replica-প্রতি PVC বানায়।

ক্রম সত্যিই বাধ্যতামূলক। একটি run-এ readiness probe ইচ্ছা করে ভাঙা হয়েছিল। replicas: 2 থাকলেও ১৫ সেকেন্ড পরে শুধু gate-0 ছিল 0/1 Running, আর gate-1 তৈরিই হয়নি। StatefulSet আটকে থাকলে তাই সবচেয়ে ছোট ordinal-এর যে Pod Ready নয়, তাকে আগে দেখুন।

আসল run-এর মান
Pod-এর অবস্থা
Pod-এর নাম
Pod UID
Pod IP
PVC
Disk-এর ফাইল
  1. স্বাভাবিক অবস্থা। web-1 চলছে, IP 10.1.0.242, নিজের PVC data-web-1-এ একটি ফাইল আছে।
  2. Pod মোছা হলো। kubectl delete pod web-1. Pod Terminating. PVC-তে কোনো বদল নেই, disk আগের মতোই আছে।
  3. Controller ফাঁক দেখল। Ordinal ১ অনুপস্থিত। পুরোনো Pod পুরো চলে যাওয়ার পরেই সে একই নামে নতুন Pod বানাল। UID নতুন: নামে একই, আসলে আরেকটি Pod.
  4. নিজের PVC. নতুন Pod যেকোনো PVC নয়, ঠিক data-web-1 পেল। নামের নিয়মই এই জোড়া ঠিক করে দেয়।
  5. আবার Ready. IP বদলে হলো 10.1.0.252. তাই IP মনে রাখা চলে না, নাম মনে রাখতে হয়।
  6. যাচাই। wget http://web-1.web ফেরত দিল I am web-1. পরিচয় আর ডেটা টিকে আছে, Pod আর IP বদলে গেছে।

কমানো, মোছা আর রেখে যাওয়া PVC

$ kubectl scale sts web --replicas=1
# watch: web-2 Terminating first, then web-1
$ kubectl get pvc --no-headers
data-web-0   Bound   pvc-045ec649-…   16Mi   …
data-web-1   Bound   pvc-23fc99da-…   16Mi   …
data-web-2   Bound   pvc-2f530807-…   16Mi   …

$ kubectl scale sts web --replicas=3
$ kubectl exec client -- wget -qO- http://web-2.web
I am web-2

$ kubectl delete sts web
statefulset.apps "web" deleted from abcl-kc namespace
$ kubectl get pods -l app=web
No resources found in abcl-kc namespace.
$ kubectl get pvc --no-headers
data-web-0   Bound   pvc-045ec649-…   16Mi   …
data-web-1   Bound   pvc-23fc99da-…   16Mi   …
data-web-2   Bound   pvc-2f530807-…   16Mi   …

কমানোর সময় ক্রম উল্টো: বড় ordinal আগে যায়। PVC কোনোটিই মোছেনি, তাই বাড়ানোর পর web-2 নিজের ডেটা ফেরত পেল। StatefulSet মুছলেও PVC-গুলো থেকে গেছে। এটি ইচ্ছাকৃত নিরাপত্তা, কিন্তু cloud-এ এই disk-গুলোর বিল চলতেই থাকে। একই StatefulSet আবার বানালে web-1 পুরোনো ফাইলই পড়েছে: I am web-1.

Kubernetes-এর নতুন version-এ persistentVolumeClaimRetentionPolicy দিয়ে এই আচরণ বদলানো যায়। আসল run-এ whenDeleted: Delete বসিয়ে StatefulSet মোছার পর PVC-ও চলে গেছে: No resources found. Default হলো দুটিই Retain. Production database-এ default-ই নিরাপদ, আর পরীক্ষার পরিবেশে Delete খরচ বাঁচায়।

Rolling update — উল্টো ক্রমে

আসল run-এ template বদলানোর পর Pod গেছে web-2 → web-1 → web-0 ক্রমে, প্রতিটি Ready হওয়ার পরে পরেরটি। partition: N দিলে শুধু N বা তার বড় ordinal বদলায়, বাকিরা পুরোনো version-এ থাকে। এটি canary-র মতো সতর্ক পরীক্ষায় কাজে লাগে। podManagementPolicy: Parallel দিলে তৈরি আর মোছা একসাথে হয়, তবে rolling update-এর ক্রম তাতে বদলায় না। নিচের simulator-এ এগুলো বদলে দেখুন।

// StatefulSet create and rolling update on a simulated clock (seconds).
// Edit SET and run. broken = ordinal that never becomes Ready, -1 = none.
const SET = { replicas: 4, ready: 5, stop: 2, policy: "OrderedReady",
              partition: 0, broken: -1 };
function create(s) {
  let t = 0;
  console.log("-- create, policy " + s.policy);
  for (let i = 0; i < s.replicas; i++) {
    const at = s.policy === "Parallel" ? 0 : t;
    if (i === s.broken) {
      console.log(`t=${at}s web-${i} created, never Ready`);
      if (s.policy !== "Parallel") {
        console.log(`STOP: web-${i + 1} and later are not created`); return false;
      }
      continue;
    }
    console.log(`t=${at}s web-${i} created, Ready at t=${at + s.ready}s`);
    t = at + s.ready;
  }
  return s.broken < 0;
}
function update(s) {
  let t = 0;
  console.log(`-- rolling update, partition ${s.partition}`);
  for (let i = s.replicas - 1; i >= 0; i--) {
    if (i < s.partition) { console.log(`        web-${i} keeps the old revision`); continue; }
    const up = t + s.stop, done = up + s.ready;
    console.log(`t=${t}s web-${i} stops, new one at t=${up}s, Ready at t=${done}s`);
    t = done;
  }
  console.log(`rollout time: ${t}s`);
}
if (create(SET)) update(SET); else console.log("rolling update not started");

Default মানে চারটি Pod ক্রমে Ready হয়: t=15s web-3 created, Ready at t=20s. তাই শুরু করতে লাগে ৪ × ৫ = ২০ সেকেন্ড। Update-এ web-3 আগে যায়, আর শেষ লাইন rollout time: 28s, মানে ৪ × (২ + ৫) = ২৮. partition: 2 দিলে শুধু web-3 আর web-2 বদলাবে। broken: 1 দিলে web-2 তৈরিই হবে না। সময়গুলো ধরে নেওয়া মান, আসল সময় নয়।

৯ · Database, snapshot আর zone-বাঁধা disk

Kubernetes-এ database, নাকি managed service

StatefulSet নাম আর disk দেয়। সে replication, failover, backup বা version upgrade জানে না। সেই জ্ঞান আসে database নিজে থেকে, অথবা একটি operator থেকে, যা পর্ব ৫০-এ দেখবেন।

প্রশ্নManaged service (পর্ব ২২)Kubernetes-এ নিজে
Failover, backup, patchProvider সামলায়আপনি বা operator সামলান
নিয়ন্ত্রণ, extension, versionProvider যা দেয়পুরোটা আপনার
খরচের ধরনসেবার দাম বেশি, মানুষের সময় কমসেবার দাম কম, মানুষের সময় বেশি
একই পরিবেশ সব জায়গায়Provider-ভেদে আলাদাOn-premises আর cloud-এ একই নিয়ম
কার জন্য ভালোবেশিরভাগ দলDatabase চালানোর দক্ষতা আছে এমন দল

সাধারণ নিয়ম: দলে অভিজ্ঞ database engineer না থাকলে production-এর মূল database managed service-এ রাখুন। Kubernetes-এ রাখলে বুঝেশুনে operator বাছুন, আর restore নিয়মিত পরীক্ষা করুন।

Snapshot আর backup

PVC ডেটা টিকিয়ে রাখে, backup নয়। ভুল করে DROP TABLE চালালে সেটি disk-এ আর replica-তে ছড়িয়ে যায়। VolumeSnapshot API (snapshot.storage.k8s.io/v1, Kubernetes ১.২০-এ GA) দিয়ে PVC-র snapshot নেওয়া যায়, আর সেখান থেকে নতুন PVC বানানো যায়। এর জন্য snapshot-সমর্থিত CSI driver আর snapshot controller লাগে। এই পর্বের cluster-এ এই API নেই, kubectl api-resources-এ দেখা যায়নি। তাই এখানে চালিয়ে দেখানো হয়নি।

Velero একধাপ ওপরের হাতিয়ার। এটি Kubernetes object-গুলো object storage-এ রাখে, আর volume-এর জন্য snapshot বা ফাইল-স্তরের কপি নেয়। ধারণাটি হলো পুরো namespace-কে অন্য cluster-এ ফিরিয়ে আনা। Disk snapshot সাধারণত crash-consistent, database-এর জন্য তাই নিজের backup হাতিয়ারও রাখুন। RPO, RTO আর restore পরীক্ষার হিসাব পর্ব ২৪-এ আছে।

Cloud CSI driver আর zone-এর বাঁধন

CSI হলো storage জোড়ার মানসম্মত interface. প্রতিটি provider নিজের driver দেয়, যেমন AWS EBS CSI, Azure Disk CSI, Google Compute Engine Persistent Disk CSI. এই পর্বের cluster-এ কোনো CSI driver নেই, kubectl get csidrivers বলেছে No resources found. একটি node-এ zone-এর বাঁধন দেখানোও সম্ভব নয়, তাই নিচের অংশ ধারণা হিসেবে পড়ুন।

Block disk সাধারণত একটি availability zone-এ থাকে। অন্য zone-এর node তাকে লাগাতে পারে না। Immediate মোডে disk আগে বানানো হলে সেটি এমন zone-এ পড়তে পারে, যেখানে Pod-এর জায়গা নেই। তখন Pod Pending থাকে। WaitForFirstConsumer এই সমস্যা কাটায়: scheduler আগে node বাছে, disk সেই zone-এ বানানো হয়।

Disk যে zone-এ, Pod-কেও সেখানেই থাকতে হয়

Zonal block volume and node affinity

Zone-এ বাঁধা block disk Zone A Zone B node-1 Pod web-0 node-2 ফাঁকা data-web-0 ✕ লাগানো যায় না অন্য zone-এর disk
ছবি ৪৭.৩ — Node-1 বিকল হলে web-0 zone B-তে চালু হতে পারবে না, যদি না zone A-তে অন্য node থাকে।

এজন্য StatefulSet-এর replica-গুলো আলাদা zone-এ ছড়িয়ে রাখা হয়, আর প্রতি zone-এ ফাঁকা জায়গা রাখা হয়। Provider-ভেদে zone-redundant disk-এর ব্যবস্থাও আছে, তবে তার সীমা আর দাম আলাদা। Replica দিয়ে ডেটার কপি অন্য zone-এ রাখলে disk বিকল হলেও সেবা চলে। পর্ব ২০-এ block আর file storage-এর পার্থক্য আছে, আর System Design-এর replication পর্বে আছে replica-র তত্ত্ব।

আরও গভীরে: StatefulSet “at most one” আচরণ রক্ষা করতে চায়: একই নামের দুটি Pod একসাথে থাকবে না। Node যোগাযোগহীন হলে Pod Terminating বা Unknown থেকে যেতে পারে, আর controller নতুনটি বানায় না। --force দিয়ে মুছলে পুরোনো process হয়তো তখনও disk-এ লিখছে, ফলে split-brain-এর ঝুঁকি আসে। তাই force delete-এর আগে node সত্যিই বন্ধ কি না নিশ্চিত হোন। Kubernetes এখানে ইচ্ছা করেই availability-র বদলে safety বেছে নিয়েছে।

পরীক্ষা ও ইন্টারভিউতে:

  • ConfigMap বনাম Secret? দুটি প্রায় একই গঠনের; Secret-এ base64, tmpfs mount আর আলাদা RBAC-এর সুবিধা, কিন্তু base64 encryption নয়।
  • PV বনাম PVC? PV হলো cluster-এর আসল storage, PVC হলো namespace-এর দরখাস্ত।
  • RWO মানে কী? এক node-এ পড়া-লেখা। এক Pod চাইলে RWOP.
  • Deployment বনাম StatefulSet? StatefulSet স্থায়ী নাম, ক্রম আর replica-প্রতি PVC দেয়।
  • PVC কেন Pending? মানানসই class নেই, provisioner সমস্যা, অথবা WaitForFirstConsumer-এ Pod-এর অপেক্ষা।

১০ · AI-এর যুগে config আর storage

AI মিনিটে YAML লিখে দেয়। Config আর storage-এর ভুল কিন্তু চালানোর সময় ধরা পড়ে না। ডেটা হারালে বা গোপন মান ফাঁস হলে তবেই বোঝা যায়। তাই এখানে যাচাই সবচেয়ে জরুরি।

AI যা প্রায়ই করেকেন বিপজ্জনককীভাবে ধরবেন
Secret-এর YAML-এ আসল মান base64 করে দেয়, আর বলে “encoded, তাই নিরাপদ”Git-এ গেলে মান প্রকাশ্যbase64 -d চালিয়ে দেখুন, secret scanner রাখুন
Database Deployment-এ চালায়, একটি RWO PVC সব replica-য় দেয়একই ডেটায় একাধিক লেখক, বা Pod আটকে থাকাStateful কাজে StatefulSet বা managed service
storageClassName মনগড়া লেখেPVC চিরকাল Pendingkubectl get sc-এর সাথে মেলান
RWX চায় block storage-এProvisioning ব্যর্থDriver-এর docs-এ access mode দেখুন
Config-এ subPath দেয়, আর বলে মান আপনা থেকে বদলাবেপুরোনো মান চুপচাপ চলেমান বদলে Pod-এর ভেতরে পড়ে দেখুন
“জায়গা খালি করতে” kubectl delete pvc --all লিখে দেয়Delete policy-তে ডেটা শেষআগে kubectl get pv-এ policy দেখুন, backup নিশ্চিত করুন
latest tag, পুরোনো API version, অচেনা field দেয়পরে ভাঙে, বা field উপেক্ষিত হয়--dry-run=server, kubectl explain

প্রশ্নে পরিবেশ আর ঝুঁকি দুটোই বলে দিন, আর আসল secret কখনো prompt-এ দেবেন না:

আমার Kubernetes 1.34 cluster-এ একটি Postgres-সদৃশ stateful app চালাতে চাই। `kubectl get sc`-এর আউটপুট নিচে দিলাম। [এখানে storage class-এর তালিকা, কোনো secret নয়] ১. StatefulSet, headless Service আর volumeClaimTemplates-এর YAML লেখো। image-এ নির্দিষ্ট version দাও, latest নয়। ২. Password-এর জন্য আসল মান লিখবে না; কোন Secret-এর নাম ধরে নিচ্ছ, তা বলো। ৩. reclaim policy আর access mode কী হবে এবং PVC মুছলে কী হবে, তা ব্যাখ্যা করো। ৪. যাচাইয়ের জন্য `--dry-run=server`-সহ কমান্ড দাও। মোছার কোনো কমান্ড আলাদা করে সতর্কতাসহ দেখাও। ৫. যে field নিয়ে তুমি নিশ্চিত নও, তা স্পষ্ট করে বলো।

AI-এর লেখা manifest চালানোর আগে

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

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

প্র ০১Secret যদি শুধু base64 হয়, Kubernetes তাহলে Secret নামে আলাদা object রাখল কেন?

কারণ Secret-এর মূল্য encoding-এ নয়, আলাদা ব্যবহারে। Base64 এসেছে একটি সহজ প্রয়োজন থেকে: Kubernetes-এর API JSON-এ কথা বলে, আর certificate বা key-র মতো মান যেকোনো byte হতে পারে। JSON string-এ এমন byte সরাসরি রাখা যায় না, তাই base64. এটি লুকানোর জন্য ছিল না।

আলাদা object হওয়ায় অনেক কিছু সম্ভব হয়েছে। RBAC-এ ConfigMap পড়ার অনুমতি দিয়েও Secret আটকানো যায়। etcd encryption শুধু Secret-এর জন্য চালু করা যায়, যা উপরের config ফাইলেই দেখেছেন। Secret volume tmpfs-এ বসে, তাই node-এর disk-এ লেখা হয় না। kubelet শুধু সেই Pod-এর Secret আনে, যে Pod সেই node-এ চলছে। আর describe মান না দেখিয়ে শুধু আকার দেখায়।

এই স্তরগুলো একবারে আসেনি। etcd encryption-এর সুবিধা এসেছে Kubernetes-এর ইতিহাসে পরে, আর KMS-ভিত্তিক envelope encryption তার পরে পরিণত হয়েছে। KMS v2 ১.২৯ version-এ stable হয়। মানে নামটি আগে এসেছে, সুরক্ষা ধাপে ধাপে যোগ হয়েছে। সমালোচনাও ন্যায্য: “Secret” নামটি অনেককে নিরাপত্তার ভুল ধারণা দেয়। Kubernetes-এর নিজের docs-ও সতর্ক করে যে default-এ মান encrypted না-ও থাকতে পারে।

ফলে একজন সচেতন প্রকৌশলী প্রশ্ন বদলে নেন। তিনি জিজ্ঞেস করেন না “মান কি লুকানো?” তিনি জিজ্ঞেস করেন: disk চুরি হলে কী হবে, backup ফাঁস হলে কী হবে, কোন মানুষ আর কোন service account পড়তে পারে, আর ফাঁস হলে আমি জানব কীভাবে। প্রতিটি প্রশ্নের উত্তর আলাদা স্তর।

মূল উপলব্ধি: নিরাপত্তা একটি object-এর নামে থাকে না। এটি থাকে স্তরে: encryption, অনুমতি, audit আর বাইরের চাবি। Secret সেই স্তরগুলো জোড়ার জায়গা মাত্র।

প্র ০২Twelve-Factor বলে config env-এ রাখতে। তাহলে Kubernetes-এ ফাইলকে অনেকে ভালো বলেন কেন?

The Twelve-Factor App লেখা হয়েছিল ২০১১ সালের দিকে, Heroku-র Adam Wiggins-এর নেতৃত্বে। সেই পরিবেশে env ছিল সবচেয়ে সরল আর ভাষা-নিরপেক্ষ পথ। কোডে বসে থাকা config-এর তুলনায় এটি বিশাল উন্নতি, আর আজও সাধারণ config-এর জন্য চমৎকার। App শুরুতে একবার পড়ে, মান সারাজীবন একই থাকে, ফলে আচরণ অনুমান করা সহজ।

কিন্তু বিশেষ করে secret-এ env-এর দুর্বলতা আছে। Process-এর সম্পূর্ণ env অনেক সময় crash report বা debug পাতায় চলে যায়। Child process-ও পুরোটা উত্তরাধিকার পায়। Container inspect আর Pod-এর manifest-এও কখনো মান দেখা যায়, বিশেষত যদি মান সরাসরি YAML-এ লেখা হয়। আর এই পর্বের আসল run-এই দেখেছেন, চলমান Pod-এ env বদলায় না। তাই rotation-এ restart লাগে।

ফাইলের সুবিধা হলো অনুমতি দেওয়া যায় (0400), tmpfs-এ থাকে, আর নতুন মান চলমান Pod-এ পৌঁছায়। বড় বা গঠনযুক্ত config, যেমন একটি TLS certificate বা YAML ফাইল, env-এ রাখা এমনিতেই অসুবিধার। কিন্তু দাম আছে: app-কে ফাইল আবার পড়তে শিখতে হবে। অর্ধেক Pod নতুন মান আর অর্ধেক পুরোনো মান নিয়ে কিছুক্ষণ চলতে পারে। আর ভুল config restart ছাড়াই সব জায়গায় পৌঁছাতে পারে।

বাস্তব সমাধান তাই মিশ্র। সাধারণ, অ-গোপন মান env-এ। গোপন মান আর বড় config ফাইলে। আর দুটি ক্ষেত্রেই বদলের সময় সচেতন rolling update, যাতে পরিবর্তন ধাপে ধাপে যায় আর ফেরার পথ থাকে। অনেক দল ConfigMap-এর hash Pod template-এ বসায়, যাতে config বদলালে নিজে থেকেই নতুন rollout হয়। Kustomize এবং Helm-এর পরিচিত নকশায় এই কৌশল দেখা যায়, যা পর্ব ৫০-এর বিষয়।

মূল উপলব্ধি: “env না file” প্রশ্নের চেয়ে বড় প্রশ্ন হলো: config বদল কীভাবে নিয়ন্ত্রিত, ধাপে ধাপে আর ফেরতযোগ্যভাবে চলমান সিস্টেমে পৌঁছাবে।

প্র ০৩Dynamic PV-র default reclaim policy কেন Delete? এটি কি বিপজ্জনক নয়?

Default-টি একটি ছোট দর্শন প্রকাশ করে: যা স্বয়ংক্রিয়ভাবে তৈরি হলো, তা স্বয়ংক্রিয়ভাবে পরিষ্কারও হোক। Dynamic provisioning-এ মানুষ disk হাতে বানায় না। PVC-ই disk-এর জীবনের একমাত্র কারণ। Retain default হলে পরীক্ষার cluster-এ, CI-তে আর প্রতিদিনের কাজে হাজার হাজার disk পড়ে থাকত, আর কেউ তাদের মালিক খুঁজে পেত না। Cloud-এ সেই পরিত্যক্ত disk-গুলো প্রতি মাসে বিল করে যেত। হাতে বানানো PV-র default কিন্তু Retain, কারণ সেখানে মানুষ ইচ্ছা করে disk বানিয়েছেন।

তবে ঝুঁকি বাস্তব। একটি ভুল kubectl delete namespace বা delete pvc Delete policy-তে আসল disk মুছে দিতে পারে। Kubernetes কিছু সুরক্ষা দেয়: Pod ব্যবহার করছে এমন PVC একটি finalizer-এর কারণে তখনই মোছে না, Pod যাওয়া পর্যন্ত অপেক্ষা করে। কিন্তু এটি ভুল থেকে সম্পূর্ণ রক্ষা করে না, শুধু চলমান Pod-এর নিচ থেকে disk সরানো আটকায়।

মানুষের ভুল থেকে বড় দুর্ঘটনার একটি পরিচিত উদাহরণ GitLab-এর ২০১৭ সালের database ঘটনা। প্রকাশিত postmortem অনুযায়ী, একজন প্রকৌশলী ভুল server-এ ডেটা মুছে ফেলেন, আর তারপর দেখা যায় কয়েকটি backup ব্যবস্থা ঠিকমতো কাজ করছিল না। এটি Kubernetes-এর ঘটনা নয়, কিন্তু শিক্ষা একই: মোছা সহজ, আর ফেরার পথ না পরীক্ষা করলে সেটি নেই।

তাই production-এ একটি যুক্তিসঙ্গত নকশা হলো মূল ডেটার জন্য Retain policy-র আলাদা StorageClass, cloud-এর disk snapshot, আর কে Delete চালাতে পারে তার RBAC সীমা। সাথে একটি নিয়মিত হিসাব, যা Released অবস্থার PV আর মালিকহীন disk খুঁজে বের করে। Retain শুধু দরজা খোলা রাখে, ডেটার backup-এর জায়গা নেয় না।

মূল উপলব্ধি: Default-গুলো সাধারণ ক্ষেত্রের জন্য বানানো। আপনার সবচেয়ে দামি ডেটা সাধারণ ক্ষেত্র নয়, তাই তার policy নিজে হাতে ঠিক করুন।

প্র ০৪StatefulSet থাকতেও production database Kubernetes-এ রাখা নিয়ে এত তর্ক কেন?

শুরুতে Kubernetes-এর নকশা ছিল মূলত stateless কাজের জন্য, যেখানে Pod যেকোনো সময় বদলানো যায়। Stateful কাজের জন্য ২০১৬ সালে PetSet নামে একটি alpha সুবিধা আসে। এই নাম “pets vs cattle” উপমা থেকে: গবাদি পশু বদলানো যায়, পোষা প্রাণীকে আলাদা যত্ন লাগে। Kubernetes ১.৫-এ এটি StatefulSet নাম পায়, আর ১.৯-এ, ২০১৭ সালের শেষ দিকে, stable হয়। তাই “Kubernetes-এ database চলে না” কথাটি এক সময় অনেকটা সত্য ছিল, এখন তা আর ঠিক নয়।

তর্ক এখন অন্য জায়গায়। পক্ষের যুক্তি: একই deployment পদ্ধতি সব পরিবেশে, CloudNativePG-র মতো operator দিয়ে failover আর backup স্বয়ংক্রিয়, on-premises-এ চলার স্বাধীনতা, আর provider-এ বাঁধা না থাকা। বিপক্ষের যুক্তি: Pod সরে গেলে disk attach হতে সময় লাগে, node upgrade মানে ডেটাবেসেও ঝাঁকুনি, memory limit-এ OOM kill মানে হঠাৎ database বন্ধ, আর সমস্যার সময় Kubernetes আর database দুটিই একসাথে বুঝতে হয়।

Kubernetes শুধু “নাম আর disk” দেয়, এটি মনে রাখলে ছবিটি পরিষ্কার হয়। প্রকৃত database-এর কাজগুলো, যেমন কোন replica সবচেয়ে এগিয়ে, কীভাবে নতুন primary বাছা হবে, quorum আছে কি না, এসব StatefulSet জানে না। Operator সেই জ্ঞান কোডে লেখার চেষ্টা। তবে operator-ও একটি জটিল software, যাকে পরীক্ষা করতে আর upgrade করতে হয়।

সিদ্ধান্তটি তাই প্রযুক্তির চেয়ে দলের বিষয়। প্রশ্নগুলো হলো: রাত ৩টায় database failover ব্যর্থ হলে কে ঠিক করবে? শেষ কবে restore পরীক্ষা হয়েছে? Managed service-এর বাড়তি খরচ কি একজন প্রকৌশলীর সময়ের চেয়ে কম? ছোট দলের জন্য উত্তর প্রায়ই managed service. আর যাঁদের ডেটা আইনি কারণে নির্দিষ্ট জায়গায় রাখতে হয়, বা অনেক ছোট database চালাতে হয়, তাঁদের কাছে Kubernetes-এর আকর্ষণ বেশি।

মূল উপলব্ধি: “চালানো যায়” আর “চালানো উচিত” দুটি আলাদা প্রশ্ন। প্রথমটির উত্তর প্রযুক্তি দেয়, দ্বিতীয়টির উত্তর দেয় আপনার দলের দক্ষতা, খরচ আর ঝুঁকি সহ্য করার ক্ষমতা।

প্র ০৫StatefulSet-এ ক্রম আর স্থায়ী নাম কি সবসময় দরকার? Parallel মোড কখন ঠিক?

ক্রমের মূল্য বোঝা যায় একটি পুরোনো সমস্যা থেকে: cluster গঠন। ধরুন তিনটি node-এর একটি consensus-ভিত্তিক সিস্টেম, যেমন etcd. প্রথম সদস্য চালু হওয়ার পর অন্যরা তার ঠিকানা জেনে যোগ দেয়। সব একসাথে চালু হলে কে কার সাথে যোগ দেবে, তা অস্পষ্ট হয়ে যায়। সবচেয়ে খারাপ ক্ষেত্রে দুটি আলাদা cluster তৈরি হতে পারে। ক্রমিক শুরু এই সমস্যা সরল করে: web-0 সবসময় আগে আসে, তাই “প্রথম সদস্য কে” প্রশ্নের উত্তর থাকে।

স্থায়ী নামের কারণও একই ধরনের। Replication config-এ লেখা থাকে “db-0-এর কাছ থেকে নকল করুন।” IP বদলালে এই config ভাঙত, যা আপনি আসল run-এ দেখেছেন: 10.1.0.242 হয়ে গেছে 10.1.0.252, কিন্তু নামটি রয়ে গেছে। আর ডেটা ও নামের জোড়া নিশ্চিত করে যে replica নিজের পুরোনো log থেকে দ্রুত ধরে নিতে পারে, পুরো ডেটা নতুন করে নকল করতে হয় না।

উল্টো ক্রমের rolling update-এরও যুক্তি আছে। অনেক নকশায় db-0 primary. শেষে তাকে বদলালে failover-এর সংখ্যা কমে। আর partition দিয়ে আগে একটি replica-তে নতুন version পরীক্ষা করা যায়।

কিন্তু সব কাজে ক্রম লাগে না। Cassandra-র মতো কিছু সিস্টেমে সদস্যরা নিজেরাই একে অপরকে খুঁজে নেয়, আর ক্রম শুধু সময় নষ্ট করে। ৫০টি replica ক্রমে চালু হলে, প্রতিটিতে ৩০ সেকেন্ড ধরলে, ৫০ × ৩০ = ১,৫০০ সেকেন্ড, মানে ২৫ মিনিট লাগবে। তখন podManagementPolicy: Parallel দরকারি। আবার, ক্রম ঝুঁকিও তৈরি করে: একটি Pod আটকে গেলে পরের সবাই আটকে যায়, যা এই পর্বের gate উদাহরণে দেখেছেন। ভুল version-এ আটকে গেলে অনেক ক্ষেত্রে হাতে হস্তক্ষেপ লাগে। আর নাম স্থায়ী হলেও Pod আসলে নতুন, তাই app-কে ধরে নিতে হবে যে memory-র সব কিছু হারিয়েছে।

মূল উপলব্ধি: StatefulSet একটি যন্ত্র, পরিচয় আর ক্রমের দাম আছে। আপনার সিস্টেম ঠিক কোন নিশ্চয়তা চায়, তা জেনেই বেছে নিন। কোনো পরিচয় বা নিজস্ব disk না লাগলে Deployment-ই সরল আর ভালো।

অনুশীলন

প্রথম তিনটি কাগজ-কলমেই করা যায়। শেষটির জন্য নিজের local cluster থাকলে ভালো, না থাকলে কমান্ডগুলো লিখে যুক্তি মেলান।

  1. হিসাব করুন: ৫ replica-র একটি StatefulSet, প্রতিটি PVC ৫০ GiB, policy Retain. ২ replica-তে নামানো হলো, পরে পুরো StatefulSet মুছে ফেলা হলো, কিন্তু PVC কেউ মোছেনি। ধরে নিন disk-এর দাম মাসে GiB-প্রতি $০.১০। (ক) কমানোর পরে কতটি PVC অব্যবহৃত? (খ) মাসিক অপচয় কত? (গ) StatefulSet মোছার পরে মাসিক অপচয় কত? (ঘ) ৬ মাসে কত?

    (ক) ৫ − ২ = ৩টি PVC: data-…-2, -3, -4. StatefulSet কমালেও PVC মোছে না।

    (খ) ৩ × ৫০ × ০.১০ = $১৫ প্রতি মাসে।

    (গ) StatefulSet মোছার পর ৫টি PVC-ই অব্যবহৃত: ৫ × ৫০ × ০.১০ = $২৫ প্রতি মাসে।

    (ঘ) ৬ × ২৫ = $১৫০. দাম এখানে ধরে নেওয়া মান। মূল কথা হলো এই বিল চুপচাপ চলে।

  2. Manifest সমালোচনা করুন: একজন লিখেছেন: Deployment, replicas: 3, image postgres:latest, সব Pod একটি RWO PVC শেয়ার করে, আর ConfigMap-এ POSTGRES_PASSWORD: admin123. অন্তত পাঁচটি সমস্যা বের করুন, আর ভালো নকশা দিন।
    • Password ConfigMap-এ: Secret বা বাইরের secret manager লাগবে, আর মান git-এ থাকবে না।
    • দুর্বল password: নকল উদাহরণ হলেও এমন মান production-এ চলবে না।
    • Deployment: Database-এর স্থায়ী পরিচয় লাগে, তাই StatefulSet.
    • এক PVC তিন Pod: এক node-এ থাকলে তিনটি Postgres একই ডেটা ফোল্ডার ধরতে পারে, বা অন্য node-এ Pod আটকে যাবে। RWO এক node-এর কথা বলে, এক লেখকের নিশ্চয়তা নয়।
    • latest: অজানা major version আসতে পারে। নির্দিষ্ট version দরকার।
    • Replication নেই: তিনটি কপি মানেই high availability নয়, Postgres-এর replication লাগবে।
    • ভালো নকশা: Managed database, অথবা StatefulSet বা operator, replica-প্রতি PVC, Retain policy, নিয়মিত backup আর restore পরীক্ষা।
  3. ফল অনুমান করুন: ConfigMap c-তে X=1. Pod p-তে envFrom-এ c, env-এ Y=2, আর c /cfg-এ পুরো volume হিসেবে, /one/X-এ subPath দিয়ে mount করা। কমান্ড: kubectl patch করে X=9, দুই মিনিট অপেক্ষা, তারপর printenv X, cat /cfg/X, cat /one/X, printenv Y. এরপর Pod মুছে একই manifest আবার চালিয়ে একই চারটি কমান্ড। কী দেখবেন?

    একই Pod-এ: printenv X দেখায় 1, /cfg/X দেখায় 9, /one/X দেখায় 1, printenv Y দেখায় 2.

    নতুন Pod-এ: 9, 9, 9, 2. নতুন container শুরুতেই ConfigMap-এর এখনকার মান পায়।

  4. কমান্ড লিখুন: Retain policy-র একটি PV Released হয়ে আছে। ঐচ্ছিক local cluster-এ ক্রম লিখুন: (ক) অবস্থা দেখা (খ) পুরোনো PVC-র বন্ধন সরানো (গ) সেই PV-র নাম দিয়ে নতুন PVC বানানো (ঘ) ফাইল পড়ে যাচাই (ঙ) শেষে পরিষ্কার করা। Cloud-এ করলে খরচ হতে পারে, তাই পরিষ্কার করতে ভুলবেন না।
    kubectl get pv                                    # (a) STATUS Released
    kubectl patch pv PV_NAME --type json \
      -p '[{"op":"remove","path":"/spec/claimRef"}]'  # (b) becomes Available
    kubectl apply -f new-claim.yaml                   # (c) spec.volumeName: PV_NAME
    kubectl get pvc                                   #     STATUS Bound
    kubectl exec some-pod -- cat /data/orders.txt     # (d) read it
    kubectl delete pod some-pod                       # (e) clean up
    kubectl delete pvc new-claim
    kubectl delete pv PV_NAME

    নতুন PVC-তে StorageClass, access mode আর আকার PV-র সাথে মিলতে হবে। Retain policy-র PV মুছলে cloud-এর disk মোছে না, সেটি provider-এর console বা CLI থেকে আলাদা করে মুছতে হবে। এই পর্বের আসল run-এ পরিষ্কারের জন্য policy Delete-এ বদলে নেওয়া হয়েছিল, তখন PV আর local ডেটা দুটিই চলে যায়।

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

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

ক্রমImmediate binding-এ dynamic provisioning-এর ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. 📝 ব্যবহারকারী PVC বানালেন, status Pending
  2. 🗂️ Controller StorageClass দেখে provisioner-কে কাজ দিল
  3. 💽 Provisioner আসল storage আর PV বানাল
  4. 🔗 PV আর PVC Bound হলো
  5. 📦 Pod চালু হলো, kubelet volume mount করল

একটি Kubernetes Secret-এর মান ZGVtbw==. এটি থেকে কোনটি সত্য?

  • মানটি AES দিয়ে encrypted, চাবি ছাড়া পড়া যাবে না
  • মানটি একমুখী hash, আসল মান ফেরানো যাবে না
  • মানটি base64 encoding, চাবি ছাড়াই আসল মান ফেরানো যায়
  • এই মান দেখলেই বোঝা যায় etcd encryption চালু আছে

একটি ConfigMap-এর মান বদলানো হলো। চলমান Pod-এ একই মান env হিসেবে, পুরো volume হিসেবে আর subPath দিয়ে আছে। কোনো restart হয়নি। কিছু সময় পরে নতুন মান কোথায় দেখা যাবে?

  • তিনটিতেই
  • শুধু পুরো volume হিসেবে mount করা ফাইলে
  • শুধু env-এ
  • কোথাও নয়, Pod restart ছাড়া কিছুই বদলায় না

ReadWriteOnce PVC সম্পর্কে কোনটি ঠিক?

  • একটি PVC একসাথে একটিমাত্র Pod ব্যবহার করতে পারে
  • PVC-তে জীবনে একবারই লেখা যায়
  • অনেক node একসাথে পড়া-লেখা করতে পারে
  • একই সময়ে একটি node পড়া-লেখা করতে পারে, সেই node-এ একাধিক Pod থাকতে পারে

Retain policy-র একটি PV-র PVC মুছে ফেলা হলো। ঠিক পরে কী অবস্থা হবে?

  • PV Released হবে, ডেটা থাকবে, নতুন PVC আপনা থেকে জুড়বে না
  • PV আর disk দুটিই মুছে যাবে
  • PV নিজে থেকে Available হয়ে পরের PVC-কে দেবে
  • Disk ফাঁকা করে PV আবার ব্যবহারের জন্য তৈরি হবে

৩ replica-র একটি StatefulSet web-এ Pod web-1 মুছে দেওয়া হলো। কী ঘটবে?

  • নতুন এলোমেলো নামের Pod আসবে, আর নতুন PVC পাবে
  • একই নাম, কিন্তু ফাঁকা নতুন PVC
  • একই নামে নতুন Pod আসবে, আগের PVC লাগবে, তবে IP বদলাতে পারে
  • সব Pod ক্রমে restart হবে

Multi-zone cluster-এ block disk-এর StorageClass-এ সাধারণত WaitForFirstConsumer কেন ব্যবহার হয়?

  • এতে disk দ্রুততর হয়
  • Pod কোন zone-এ বসবে তা জানার পরে disk বানানোর জন্য
  • PVC মোছার পর ডেটা রেখে দেওয়ার জন্য
  • একটি disk অনেক zone-এর node-এ লাগানোর জন্য

শব্দকোষ

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

মনে রাখুন

  1. Config image-এর বাইরে: env শুরুতে একবার বসে, mounted ফাইল বদলায়, subPath বদলায় না।
  2. env জেতে envFrom-এর ওপর, আর envFrom-এ পরের উৎস জেতে। ফাইল বনাম env ঠিক করে app.
  3. Base64 encryption নয়। Secret রক্ষা পায় etcd encryption, RBAC আর বাইরের secret manager দিয়ে।
  4. Pod → PVC → PV → disk. Pod মুছলে ডেটা থাকে; PVC মুছলে reclaim policy ঠিক করে। Retain মানে ডেটাও থাকে, বিলও থাকে।
  5. StatefulSet দেয় স্থায়ী নাম, ক্রম আর replica-প্রতি PVC, কিন্তু failover বা backup দেয় না। PVC পেছনে থেকে যায়, আর block disk zone-এ বাঁধা।

পরের পর্বে: Pod-এর config আর ডেটা ঠিক হলো। এবার প্রশ্ন, Pod কোন node-এ বসবে আর কত CPU ও memory পাবে। পর্ব ৪৮-এ scheduling, request, limit, QoS আর autoscaling.

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

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