পাঠ ২৮ · ৫৭-এর মধ্যে · মডিউল ৭

Pod, Deployment ও ReplicaSet

Pods, Deployments & ReplicaSets
৯ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • Pod আসলে কী এবং কেন এটি ephemeral হিসেবে ডিজাইন করা হয়েছে
  • ReplicaSet কীভাবে ক্র্যাশ হওয়া পড স্বয়ংক্রিয়ভাবে প্রতিস্থাপন করে
  • Deployment, ReplicaSet ও Pod-এর মধ্যে স্তরবিন্যাস (hierarchy) সম্পর্ক
  • L27-এর reconcile() লজিক পুনর্ব্যবহার করে একটি self-healing Deployment সিমুলেট করা

১ · Pod — সবচেয়ে ছোট ডিপ্লয়যোগ্য একক

PodPodKubernetes-এর সবচেয়ে ছোট ডিপ্লয়যোগ্য একক — এক বা একাধিক কন্টেইনার যারা নেটওয়ার্ক ও স্টোরেজ শেয়ার করে, একসাথে শিডিউল ও একসাথে সরানো হয়। হলো Kubernetes-এর সবচেয়ে ছোট একক যা আপনি ডিপ্লয় করতে পারেন — সরাসরি একটি কন্টেইনার নয়। একটি পডে এক বা একাধিক শক্তভাবে-সম্পর্কিত কন্টেইনার থাকতে পারে যারা নেটওয়ার্ক (একই IP ঠিকানা শেয়ার করে) ও স্টোরেজ শেয়ার করে — যদিও প্র্যাকটিসে বেশিরভাগ পডে মাত্র একটিই কন্টেইনার থাকে। গুরুত্বপূর্ণ ধারণা — পড ephemeral (ক্ষণস্থায়ী) ও ডিসপোজেবল: প্রোডাকশনে আপনি সরাসরি নিজে পড তৈরি/ম্যানেজ করেন না, বরং উচ্চ-স্তরের একটি অবজেক্ট দিয়ে (নিচে দেখুন)।

২ · ReplicaSet — নির্দিষ্টসংখ্যক রেপ্লিকা বজায় রাখা

ReplicaSetReplicaSetএকটি নির্দিষ্টসংখ্যক অভিন্ন পড রেপ্লিকা সবসময় চলছে তা নিশ্চিত করে এমন একটি Kubernetes অবজেক্ট — একটি পড ক্র্যাশ করলে স্বয়ংক্রিয়ভাবে একটি নতুন পড তৈরি করে। একটি নির্দিষ্ট সংখ্যক অভিন্ন পড রেপ্লিকা সবসময় চলছে তা নিশ্চিত করে। এটি হলো L27-এর reconciliation লজিকের সরাসরি একটি প্রয়োগ — একটি পড ক্র্যাশ করলে, ReplicaSet সেটি লক্ষ্য করে (actual < desired) এবং স্বয়ংক্রিয়ভাবে একটি প্রতিস্থাপন পড তৈরি করে। কিন্তু ReplicaSet একাই একটি সীমাবদ্ধতা বহন করে — এটি "কতগুলো পড থাকা উচিত" জানে, কিন্তু অ্যাপ্লিকেশনের নতুন ভার্সন নিরাপদে রোলআউট করার কোনো বিল্ট-ইন উপায় দেয় না।

৩ · Deployment — রোলিং-আপডেট যোগ করা

DeploymentDeploymentএকটি উচ্চ-স্তরের Kubernetes অবজেক্ট যা একটি ReplicaSet ম্যানেজ করে এবং রোলিং-আপডেট ক্ষমতা যোগ করে — অ্যাপ্লিকেশনের নতুন ভার্সন ধাপে ধাপে, ডাউনটাইম ছাড়া রোলআউট করা যায়। হলো সেই সমাধান — এটি একটি ReplicaSet ম্যানেজ করে এবং তার উপরে রোলিং-আপডেট ক্ষমতা যোগ করে (আপনি অ্যাপ্লিকেশনের একটি নতুন ভার্সনে আপডেট করলে, Deployment ধাপে ধাপে পুরনো পড-গুলোকে নতুন পড দিয়ে প্রতিস্থাপন করে, একসাথে সব বন্ধ না করে — M8/L37-এ deployment strategy নিয়ে আরও বিস্তারিত দেখব)। প্র্যাকটিসে আপনি প্রায় সবসময় সরাসরি একটি Deployment তৈরি করেন, যা নিজে থেকেই একটি ReplicaSet তৈরি ও ম্যানেজ করে, যা আবার Pod-গুলো ম্যানেজ করে — একটি স্তরবিন্যস্ত (layered) হায়ারার্কি।

Deployment রোলিং-আপডেট ReplicaSet রেপ্লিকা কাউন্ট বজায় রাখে Pods আসল কন্টেইনার চলে
Deployment → ReplicaSet → Pod — প্রতিটি স্তর নিচেরটি ম্যানেজ করে, আর প্রতিটি স্তরেই L27-এর reconciliation দর্শন কাজ করে।

৪ · Self-healing সিমুলেশন — L27-এর reconcile() পুনর্ব্যবহার

নিচের কোডে আমরা L27-এর reconcile() ফাংশনটি হুবহু পুনর্ব্যবহার করছি একটি Deployment ক্লাসের ভেতরে — এটি দেখায় কীভাবে একই অন্তর্নিহিত প্যাটার্ন (desired বনাম actual-এর ফাঁক গণনা করে create/terminate করা) Kubernetes-এর প্রতিটি স্তরে পুনরাবৃত্ত হয়।

Python
# L27-এর reconcile() লজিক পুনর্ব্যবহার করে একটি self-healing Deployment সিমুলেশন
# (fake in-memory ডেটা — বাস্তব kubectl/cluster-এর বিরুদ্ধে চলছে না)

def reconcile(desired_replicas, actual_running_pods):
    """L27 থেকে হুবহু পুনর্ব্যবহৃত — desired ও actual-এর ফাঁক বন্ধ করার অ্যাকশন গণনা করে।"""
    actual_count = len(actual_running_pods)
    shortfall = desired_replicas - actual_count
    actions = []

    if shortfall > 0:
        # নতুন id সবসময় সর্বোচ্চ বিদ্যমান সংখ্যার পরের থেকে শুরু হয় -- শুধু len()
        # ব্যবহার করলে মাঝখানের কোনো pod সরে গেলে (gap) নাম সংঘর্ষ হতে পারত।
        existing_numbers = [int(p.split("-")[1]) for p in actual_running_pods]
        next_number = max(existing_numbers, default=0) + 1
        for i in range(shortfall):
            actions.append(("CREATE", f"pod-{next_number + i}"))
    elif shortfall < 0:
        for pod_id in actual_running_pods[shortfall:]:
            actions.append(("TERMINATE", pod_id))
    else:
        actions.append(("NO-OP", "already matches desired state"))

    return actions


class Deployment:
    """L27-এর reconcile() লজিক ব্যবহার করে self-healing বাস্তবায়নকারী একটি সরল Deployment।"""

    def __init__(self, name, desired_replicas):
        self.name = name
        self.desired_replicas = desired_replicas
        self.pods = [f"pod-{i + 1}" for i in range(desired_replicas)]

    def simulate_pod_crash(self, pod_id):
        if pod_id in self.pods:
            self.pods.remove(pod_id)
            print(f"[CRASH] {pod_id} ক্র্যাশ করেছে এবং তালিকা থেকে সরে গেছে")

    def reconcile(self):
        # ঠিক L27-এর ফাংশনটিই এখানে পুনর্ব্যবহার হচ্ছে — নতুন কোনো লজিক নয়
        actions = reconcile(self.desired_replicas, self.pods)
        for action, target in actions:
            if action == "CREATE":
                self.pods.append(target)
                print(f"[RECONCILE] নতুন পড তৈরি হলো: {target}")
            elif action == "TERMINATE":
                self.pods.remove(target)
                print(f"[RECONCILE] অতিরিক্ত পড বন্ধ হলো: {target}")
            else:
                print(f"[RECONCILE] {target}")


dep = Deployment("web-app", desired_replicas=3)
print("প্রাথমিক অবস্থা:", dep.pods)

dep.simulate_pod_crash("pod-2")
print("ক্র্যাশের ঠিক পরে:", dep.pods)

dep.reconcile()
print("reconcile()-এর পরে (self-healed):", dep.pods)

    
লক্ষ্য করুন — Deployment.reconcile() নিজে কোনো নতুন গণনা-লজিক লেখেনি, শুধু L27-এর reconcile() ফাংশনটি কল করেছে এবং ফলাফল অনুযায়ী কাজ করেছে। বাস্তব Kubernetes-এও ঠিক এই একই অন্তর্নিহিত reconciliation প্যাটার্ন প্রতিটি স্তরে (ReplicaSet, Deployment, এবং L32-এর কাস্টম অপারেটর পর্যন্ত) পুনরাবৃত্ত হয় — শুধু "কী তৈরি হবে" তার প্রসঙ্গ পাল্টায়, মূল যুক্তি একই থাকে।
মূল কথা · Key takeaway

Pod হলো একক, ReplicaSet তার সংখ্যা বজায় রাখে, আর Deployment তার উপরে রোলিং-আপডেট যোগ করে — এই তিনটি মিলে একটি স্তরবিন্যস্ত সিস্টেম তৈরি করে যেখানে প্রতিটি স্তরই স্বয়ংক্রিয়ভাবে সুস্থ থাকার চেষ্টা করে। পরবর্তী পাঠে আমরা দেখব কীভাবে এই ক্রমাগত-পরিবর্তনশীল পড-সেটকে একটি স্থিতিশীল ঠিকানার (Service) মাধ্যমে অন্যরা নির্ভরযোগ্যভাবে খুঁজে পায়।

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

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

প্র ০১ কেন প্রোডাকশনে সরাসরি নিজে একটি Pod তৈরি না করে সবসময় Deployment ব্যবহার করা উচিত?

সরাসরি একটি Pod তৈরি করলে সেটির কোনো "মালিক" থাকে না — সেই Pod ক্র্যাশ করলে কেউ এটি প্রতিস্থাপন করবে না, কারণ কোনো ReplicaSet এটির সংখ্যা ট্র্যাক করছে না। Deployment ব্যবহার করলে আপনি স্বয়ংক্রিয়ভাবে self-healing (ReplicaSet-এর মাধ্যমে) এবং নিরাপদ রোলআউট (Deployment-এর মাধ্যমে) — দুটোই পান, বাড়তি কোনো কাজ ছাড়াই।

প্র ০২ ReplicaSet ও Deployment-এর মধ্যে সম্পর্ক ঠিক কী — Deployment কি ReplicaSet-কে প্রতিস্থাপন করে?

না, প্রতিস্থাপন করে না — Deployment ReplicaSet-এর উপরে বসে, এটিকে ম্যানেজ করে। যখন আপনি একটি Deployment আপডেট করেন (যেমন একটি নতুন ইমেজ ভার্সন), Deployment একটি নতুন ReplicaSet তৈরি করে (নতুন ভার্সনের জন্য) এবং ধাপে ধাপে পুরনো ReplicaSet-এর পড-গুলোকে নতুনটির পড দিয়ে প্রতিস্থাপন করে — উভয় ReplicaSet সাময়িকভাবে একসাথে থাকতে পারে রোলআউটের সময়।

প্র ০৩ self-healing মানে কি "কখনও ডাউনটাইম হবে না" — নাকি এর একটি সীমা আছে?

এর একটি সীমা আছে। self-healing রেপ্লিকা সংখ্যা পুনরুদ্ধার করে, কিন্তু ক্র্যাশ শনাক্তকরণ ও নতুন পড শিডিউল/চালু হওয়ার মধ্যে একটি ছোট সময়ের ব্যবধান থাকে — তাৎক্ষণিক নয়। এছাড়া যদি একসাথে একাধিক পড ক্র্যাশ করে বা পুরো ওয়ার্কার নোডই ডাউন হয়ে যায় (L27), পুনরুদ্ধারে বেশি সময় লাগতে পারে। এই কারণেই একাধিক রেপ্লিকা একাধিক নোডে ছড়িয়ে রাখা এবং পর্যাপ্ত মনিটরিং (M10) গুরুত্বপূর্ণ থাকে।

অনুশীলন

  1. চিন্তা করুন: উপরের সিমুলেশনে যদি একই reconcile() কলের আগে দুটি পড (pod-1 ও pod-3) একসাথে ক্র্যাশ করত, ফলাফল কী হতো?

    দুটি ক্র্যাশের পর self.pods-এ শুধু একটি পড (pod-2) বাকি থাকবে, তাই shortfall = 3 - 1 = 2। reconcile() দুটি CREATE অ্যাকশন ফেরত দেবে — মূল লজিক অপরিবর্তিত থাকে, কতগুলো পড ক্র্যাশ করেছে তার সংখ্যা যাই হোক না কেন, ফাংশনটি সবসময় সঠিক ফাঁকটুকু গণনা করে।

  2. পরীক্ষা করুন: Deployment-এ একটি নতুন মেথড scale(new_desired_replicas) যোগ করুন যা self.desired_replicas আপডেট করে তারপর self.reconcile() কল করে — এটি ব্যবহার করে dep-কে ৫টি রেপ্লিকায় স্কেল-আপ করুন এবং ফলাফল দেখুন।

    desired_replicas ৫-এ পরিবর্তন করার পর বর্তমানে ৩টি পড চলছে ধরে নিলে reconcile() দুটি নতুন CREATE অ্যাকশন করবে — এটি দেখায় স্কেল-আপ (আরও রেপ্লিকা চাওয়া) আর ক্র্যাশ-রিকভারি (হারানো রেপ্লিকা ফিরিয়ে আনা) আসলে একই অন্তর্নিহিত reconciliation যুক্তির দুটি ভিন্ন প্রকাশ মাত্র।

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

পাঠ ২৭
Kubernetes আর্কিটেকচার — কন্ট্রোল প্লেন ও নোড