পাঠ ৩২ · ৫৭-এর মধ্যে · মডিউল ৭
Home / Courses / Cloud Computing & DevOps / অপারেটর ও CRD

Kubernetes অপারেটর ও কাস্টম রিসোর্স পরিচিতি

Kubernetes operators & custom resources intro
৮ মিনিট পড়া উন্নত · Advanced Python কোডসহ সম্পূর্ণ বাংলায়

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

  • বিল্ট-ইন Kubernetes অবজেক্ট কেন জটিল স্টেটফুল অ্যাপ্লিকেশনের জন্য যথেষ্ট নয়
  • CRD কী এবং এটি কীভাবে Kubernetes API-কে সম্প্রসারিত করে
  • অপারেটর কীভাবে reconciliation loop-এর একই প্যাটার্ন পুনরায় ব্যবহার করে, কিন্তু ডোমেইন-স্পেসিফিক লজিকসহ
  • Python দিয়ে একটি টয় "ডাটাবেস অপারেটর" সিমুলেট করা — রেপ্লিকা স্কেলিং ও ব্যাকআপ শিডিউলিং একসাথে

১ · সমস্যা — জেনেরিক অবজেক্ট যথেষ্ট নয়

L28-এ আমরা দেখেছি Deployment কীভাবে ReplicaSet ও Pod ব্যবস্থাপনা করে — এই প্যাটার্ন স্টেটলেস ওয়েব অ্যাপ্লিকেশনের জন্য চমৎকার কাজ করে। কিন্তু ধরুন আপনার একটি ডাটাবেস ক্লাস্টার চালাতে হবে — শুধু "৩টি পড চালু রাখো" যথেষ্ট নয়। আপনার দরকার নিয়মিত ব্যাকআপ শিডিউল মেনে চলা, একটি নোড ডাউন হলে সঠিকভাবে ফেইলওভার করা (কোন রেপ্লিকা নতুন প্রাইমারি হবে তা ঠিকভাবে নির্বাচন করা), এবং ভার্সন আপগ্রেডের সময় ডেটা হারানো এড়ানো — এগুলো সাধারণ Kubernetes বিল্ট-ইন অবজেক্টে নেই, কারণ এই লজিক সম্পূর্ণভাবে সেই নির্দিষ্ট অ্যাপ্লিকেশনের (এই ক্ষেত্রে, একটি নির্দিষ্ট ডাটাবেস ইঞ্জিনের) উপর নির্ভরশীল।

২ · Custom Resource Definition (CRD) — API সম্প্রসারণ

CRDCustom Resource DefinitionKubernetes API-তে একটি সম্পূর্ণ নতুন অবজেক্ট টাইপ যোগ করার মেকানিজম — যেমন বিল্ট-ইন "Pod" বা "Service"-এর মতোই একটি "DatabaseCluster" টাইপ তৈরি করা যায়। হলো সেই মেকানিজম যা দিয়ে আপনি Kubernetes API-তে সম্পূর্ণ নতুন একটি অবজেক্ট টাইপ যোগ করতে পারেন — যেমন একটি DatabaseCluster টাইপ, যার নিজস্ব ফিল্ড থাকবে (replicas, backup_interval_hours, version)। একবার একটি CRD ইনস্টল করা হলে, আপনি ঠিক Pod বা Deployment-এর মতোই DatabaseCluster অবজেক্ট তৈরি করতে পারবেন — কিন্তু শুধু ডেটা সংরক্ষণ করা ছাড়া CRD নিজে থেকে কিছুই "করে" না।

৩ · অপারেটর — একই reconciliation loop, বিশেষায়িত লজিক

অপারেটরOperatorএকটি কাস্টম কন্ট্রোলার যা একটি CRD-এর ইনস্ট্যান্স পর্যবেক্ষণ করে এবং সেই অ্যাপ্লিকেশনের জন্য বিশেষায়িত অপারেশনাল লজিক চালায়, ঠিক L27/L28-এর reconciliation loop-এর মতোই। হলো একটি কাস্টম কন্ট্রোলার — এটি CRD-এর ইনস্ট্যান্সগুলো পর্যবেক্ষণ করে (ঠিক যেমন controller manager Deployment পর্যবেক্ষণ করে), এবং desired spec ও observed status-এর মধ্যে পার্থক্য দেখলে প্রয়োজনীয় অ্যাকশন নেয়। পার্থক্য হলো — একটি সাধারণ Deployment শুধু "পড কাউন্ট মেলাও" জানে, কিন্তু একটি অপারেটর মানুষের অপারেশনাল বিশেষজ্ঞতা কোডে এনকোড করে রাখে (কখন ব্যাকআপ নিতে হবে, কীভাবে নিরাপদে ফেইলওভার করতে হবে) — একই প্যাটার্ন, কিন্তু অনেক বেশি ডোমেইন-স্পেসিফিক।

Operator = reconciliation loop + ডোমেইন-স্পেসিফিক জ্ঞান

মনে রাখুন L27-এ আমরা দেখেছিলাম controller manager ক্রমাগত actual state-কে desired state-এর দিকে নিয়ে যায় — এটাই Kubernetes-এর মূল দর্শন। অপারেটর সেই একই ধারণা নেয়, কিন্তু "কীভাবে মেলাতে হবে" এই প্রশ্নের উত্তরে সাধারণ স্কেলিং লজিকের বদলে জটিল, অ্যাপ্লিকেশন- স্পেসিফিক সিদ্ধান্ত যোগ করে। এই কারণেই অপারেটরকে প্রায়ই বলা হয় "মানুষের অপারেশনাল অভিজ্ঞতা সফটওয়্যারে রূপান্তরিত করা।"

Python
# টয় "অপারেটর" — একটি DatabaseCluster কাস্টম রিসোর্সের রিকনসিলিয়েশন লজিক
# (বাস্তব কোনো ক্লাস্টার/ডাটাবেসে কিছুই ঘটছে না — শুধুই ধারণা বোঝানোর সিমুলেশন)

def reconcile_database_operator(desired_spec, current_status):
    actions = []

    # ১) রেপ্লিকা কাউন্ট মেলানো — L27/L28-এর মতোই জেনেরিক রিকনসিলিয়েশন
    desired_replicas = desired_spec["replicas"]
    actual_replicas = current_status["running_replicas"]
    if actual_replicas < desired_replicas:
        actions.append(
            f"স্কেল আপ: {desired_replicas - actual_replicas}টি নতুন রেপ্লিকা তৈরি করো"
        )
    elif actual_replicas > desired_replicas:
        actions.append(
            f"স্কেল ডাউন: {actual_replicas - desired_replicas}টি রেপ্লিকা বন্ধ করো"
        )

    # ২) ডোমেইন-স্পেসিফিক লজিক — ব্যাকআপ শিডিউল (এটাই "অপারেটর"-কে স্পেশাল করে)
    backup_interval_hours = desired_spec["backup_interval_hours"]
    hours_since_last_backup = current_status["hours_since_last_backup"]
    if hours_since_last_backup >= backup_interval_hours:
        actions.append(
            f"এখনই ব্যাকআপ ট্রিগার করো (শেষ ব্যাকআপের {hours_since_last_backup} ঘণ্টা পর, "
            f"শিডিউল প্রতি {backup_interval_hours} ঘণ্টায়)"
        )

    if not actions:
        actions.append("কোনো অ্যাকশন দরকার নেই — actual state ইতিমধ্যে desired spec-এর সাথে মিলছে")

    return actions


desired = {"replicas": 3, "backup_interval_hours": 6}

scenarios = {
    "সব ঠিক আছে": {"running_replicas": 3, "hours_since_last_backup": 2},
    "একটি রেপ্লিকা ডাউন + ব্যাকআপের সময় পার হয়ে গেছে": {
        "running_replicas": 2,
        "hours_since_last_backup": 7,
    },
    "অতিরিক্ত রেপ্লিকা আছে": {"running_replicas": 5, "hours_since_last_backup": 1},
}

for name, status in scenarios.items():
    print(f"--- {name} ---")
    for action in reconcile_database_operator(desired, status):
        print(f"  → {action}")

    
লক্ষ্য করুন reconcile_database_operator-এর প্রথম অংশ (রেপ্লিকা মেলানো) হুবহু L27/L28-এর reconciliation লজিকের মতো — কিন্তু দ্বিতীয় অংশ (ব্যাকআপ শিডিউলিং) সম্পূর্ণ নতুন, ডাটাবেস-স্পেসিফিক জ্ঞান। বাস্তব অপারেটরগুলো (যেমন PostgreSQL বা Kafka অপারেটর) এই একই প্যাটার্নে আরও অনেক জটিল সিদ্ধান্ত (কোন রেপ্লিকা প্রাইমারি হবে, কীভাবে নিরাপদে রোলিং আপগ্রেড করতে হবে) যোগ করে।

৪ · কখন নিজে অপারেটর লিখবেন

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

মূল কথা · Key takeaway

CRD Kubernetes API-কে সম্প্রসারিত করে, আর অপারেটর সেই সম্প্রসারিত অবজেক্টের জন্য L27-এর reconciliation loop প্যাটার্ন পুনরায় ব্যবহার করে ডোমেইন-স্পেসিফিক অপারেশনাল লজিক স্বয়ংক্রিয় করে। এটি Kubernetes অর্কেস্ট্রেশনের সবচেয়ে উন্নত ও পরিণত প্যাটার্নগুলোর একটি — এবং M7 (Kubernetes মডিউল)-এর সমাপ্তি চিহ্নিত করে। পরবর্তী মডিউল, M8, দেখাবে কীভাবে এই সব ম্যানিফেস্ট/চার্ট/অপারেটর CRD সহ CI/CD পাইপলাইনের মাধ্যমে স্বয়ংক্রিয়ভাবে বিল্ড, টেস্ট ও ডিপ্লয় হয়।

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

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

প্র ০১ একটি স্টেটলেস ওয়েব অ্যাপ্লিকেশনের জন্য কেন সাধারণত কাস্টম অপারেটর লেখার প্রয়োজন হয় না?

একটি স্টেটলেস ওয়েব অ্যাপ্লিকেশনের অপারেশনাল প্রয়োজন সাধারণ — শুধু নির্দিষ্টসংখ্যক পড চালু রাখা ও ক্র্যাশ করলে প্রতিস্থাপন করা, যা বিল্ট-ইন Deployment ইতিমধ্যেই ভালোভাবে করে। এর কোনো জটিল, অ্যাপ্লিকেশন- স্পেসিফিক অপারেশনাল জ্ঞান (ব্যাকআপ শিডিউল, ফেইলওভার সিদ্ধান্ত) দরকার হয় না — তাই একটি কাস্টম অপারেটর তৈরির অতিরিক্ত জটিলতা ও রক্ষণাবেক্ষণ-খরচ ন্যায্য নয়।

প্র ০২ যদি একটি অপারেটর নিজেই ক্র্যাশ করে বা কিছু সময়ের জন্য বন্ধ থাকে, তাহলে কী হবে?

reconciliation loop প্যাটার্নের একটি বড় সুবিধা হলো এটি এক-বারের কমান্ড নয়, বরং ক্রমাগত পুনরাবৃত্তিমূলক চেক — অপারেটর আবার চালু হলে, এটি আবার desired spec ও বর্তমান status তুলনা করে যেখান থেকে দরকার সেখান থেকে কাজ চালিয়ে যায়। এই সময়ের মধ্যে হয়তো একটি ব্যাকআপ দেরিতে হবে বা একটি ব্যর্থ পড কিছুক্ষণ বেশি সময় ধরে ডাউন থাকবে, কিন্তু সিস্টেম নিজে থেকে ভেঙে পড়ে না — অপারেটর ফিরে এলেই স্বয়ংক্রিয়ভাবে সামঞ্জস্য পুনরুদ্ধার হয়।

প্র ০৩ একটি টিমের নিজস্ব কাস্টম অপারেটর লেখার আগে প্রথমে কী খুঁজে দেখা উচিত, এবং কেন?

একটি বিদ্যমান, কমিউনিটি বা ভেন্ডর-নির্মিত অপারেটর ইতিমধ্যেই সেই অ্যাপ্লিকেশনের জন্য পাওয়া যায় কি না — কারণ একটি নির্ভরযোগ্য অপারেটর লেখা এবং তা দীর্ঘমেয়াদে রক্ষণাবেক্ষণ করা যথেষ্ট বিশেষজ্ঞতা ও সময়ের দাবি রাখে (ভুল ফেইলওভার লজিক বাস্তব ডেটা হারানোর কারণ হতে পারে)। বেশিরভাগ জনপ্রিয় স্টেটফুল অ্যাপ্লিকেশনের (ডাটাবেস, মেসেজ ব্রোকার) জন্য ইতিমধ্যে পরীক্ষিত অপারেটর বিদ্যমান — নিজে লেখা তখনই যুক্তিসঙ্গত যখন এমন কোনো সমাধান নেই বা প্রতিষ্ঠানের সত্যিই অনন্য প্রয়োজন আছে।

অনুশীলন

  1. চিন্তা করুন: উপরের কোড সেলের "অতিরিক্ত রেপ্লিকা আছে" দৃশ্যে running_replicas ৫ কিন্তু desired_replicas ৩ — ফাংশনটি কী অ্যাকশন প্রস্তাব করছে এবং কেন এটি L27-এর reconciliation লজিকের সাথে সামঞ্জস্যপূর্ণ?

    যেহেতু actual (৫) desired (৩)-এর চেয়ে বেশি, ফাংশনটি "স্কেল ডাউন: ২টি রেপ্লিকা বন্ধ করো" প্রস্তাব করে — এটি ঠিক L27-এর reconciliation loop-এর overprovisioned দৃশ্যের মতোই আচরণ, শুধু এখানে এটি একটি ডাটাবেস কাস্টম রিসোর্সের ক্ষেত্রে প্রয়োগ করা হয়েছে।

  2. পরীক্ষা করুন: reconcile_database_operator ফাংশনে একটি নতুন চেক যোগ করুন — যদি current_status-এ "version" ফিল্ড desired_spec-এর "version"-এর সাথে না মেলে, তাহলে "ভার্সন আপগ্রেড ট্রিগার করো" অ্যাকশন যোগ করুন। একটি নতুন দৃশ্য দিয়ে পরীক্ষা করুন যেখানে ভার্সন মেলে না।

    desired_spec-এ "version": "14.2" যোগ করে এবং একটি দৃশ্যে current_status["version"] = "13.9" রেখে, একটি if desired_spec["version"] != current_status["version"]: চেক যোগ করলে ফাংশনটি "ভার্সন আপগ্রেড ট্রিগার করো" প্রিন্ট করবে — এটি দেখায় কীভাবে একটি অপারেটরে সহজেই আরও ডোমেইন-স্পেসিফিক অপারেশনাল লজিক যোগ করা যায়, একই reconciliation প্যাটার্নের মধ্যে থেকেই।

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

আগের পাঠ
L31 · Helm — Kubernetes প্যাকেজ ম্যানেজমেন্ট