পাঠ ২৯ · ৫৭-এর মধ্যে · মডিউল ৭
Home / Courses / Cloud Computing & DevOps / Service ও Ingress

Kubernetes Service ও Ingress

Kubernetes Services & Ingress
৮ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

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

  • Pod-এর ক্রমাগত-পরিবর্তনশীল IP ঠিকানার সমস্যা এবং Service কীভাবে তা সমাধান করে
  • label selector দিয়ে Service কীভাবে সঠিক পড-গুলো খুঁজে পায় — একটি Python সিমুলেশনের মাধ্যমে
  • ClusterIP, NodePort ও LoadBalancer — তিনটি Service টাইপের পার্থক্য
  • Ingress কীভাবে একটি একক এন্ট্রি পয়েন্ট থেকে অনেকগুলো Service-এ রাউট করে

১ · সমস্যা — পড-এর ঠিকানা ক্রমাগত পরিবর্তিত হয়

L28-এ আমরা দেখেছি Pod-গুলো ephemeral — একটি পড ক্র্যাশ করলে ReplicaSet একটি সম্পূর্ণ নতুন পড তৈরি করে, যেটি একটি সম্পূর্ণ নতুন IP ঠিকানা পায়। তাহলে অন্য একটি পড বা বাইরের একজন ক্লায়েন্ট কীভাবে নির্ভরযোগ্যভাবে এই ক্রমাগত-পরিবর্তনশীল পড-সেটের সাথে যোগাযোগ করবে? সরাসরি IP হার্ডকোড করা কাজ করবে না — সেই IP যেকোনো মুহূর্তে অকেজো হয়ে যেতে পারে।

২ · Service — একটি স্থিতিশীল ঠিকানা

ServiceKubernetes Serviceএকটি স্থিতিশীল ভার্চুয়াল IP/DNS নাম যা একটি label selector-এর সাথে মিলে যাওয়া বর্তমান পড-গুলোর মধ্যে স্বয়ংক্রিয়ভাবে ট্রাফিক লোড-ব্যালেন্স করে। এই সমস্যার সমাধান দেয় — এটি একটি স্থিতিশীল ভার্চুয়াল IP ঠিকানা (এবং একটি DNS নাম) প্রদান করে যা কখনও পরিবর্তন হয় না, এবং label selectorLabel Selectorএক বা একাধিক key-value জোড়া যা দিয়ে একটি Service ঠিক করে কোন পড-গুলো তার ট্রাফিকের লক্ষ্য — শুধু যেসব পডের লেবেল সব শর্ত মেলে সেগুলোই অন্তর্ভুক্ত হয়। দিয়ে বর্তমানে কোন পড-গুলো ম্যাচ করে তা ক্রমাগত পুনর্গণনা করে। এটি সরাসরি L15-এর সার্ভিস-ডিসকভারি ধারণার একটি অভ্যন্তরীণ, ক্লাস্টার-নেটিভ বাস্তবায়ন — পড আসুক বা যাক, Service-এর ঠিকানা কখনও বদলায় না।

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

৩ · Ingress — একটি একক প্রবেশপথ, অনেক Service

IngressIngressক্লাস্টারে বাইরের HTTP(S) ট্রাফিক প্রবেশের ব্যবস্থাপনা — পাথ বা হোস্টনেম অনুযায়ী ভিন্ন ভিন্ন Service-এ রাউট করে, একটি একক এন্ট্রি পয়েন্ট থেকে। প্রতিটি Service-এর জন্য আলাদা LoadBalancer ব্যবহার করা ব্যয়বহুল ও ব্যবস্থাপনা-জটিল হতে পারে। Ingress একটি একক প্রবেশপথ দেয় যা পাথ (যেমন /api বনাম /images) বা হোস্টনেম অনুযায়ী ভিন্ন ভিন্ন Service-এ ট্রাফিক রাউট করে — ধারণাগতভাবে system-design কোর্সের API gateway-এর সাথে মিল রয়েছে। এটি অনেকগুলো ব্যাকএন্ড Service-কে একটি একক, সুসংগঠিত বাইরের ঠিকানার পেছনে সাজিয়ে রাখে।

ক্লায়েন্ট Ingress Service Pods
Ingress পাথ/হোস্ট দেখে সঠিক Service বেছে নেয়; Service তার লেবেল সিলেক্টর মিলিয়ে বর্তমান পড-সেটে লোড-ব্যালেন্স করে।
Python
# Label-selector-ভিত্তিক Service রাউটিং সিমুলেশন
# (fake in-memory ডেটা — বাস্তব kubectl/cluster-এর বিরুদ্ধে চলছে না)

pods = [
    {"id": "pod-1", "labels": {"app": "web", "version": "v1"}},
    {"id": "pod-2", "labels": {"app": "web", "version": "v2"}},
    {"id": "pod-3", "labels": {"app": "web", "version": "v2"}},
    {"id": "pod-4", "labels": {"app": "worker", "version": "v2"}},
]


def get_matching_pods(pods, selector):
    """selector-এর সবগুলো key-value শর্ত মেলে এমন পড-গুলোর id ফেরত দেয়।"""
    matches = []
    for pod in pods:
        if all(pod["labels"].get(k) == v for k, v in selector.items()):
            matches.append(pod["id"])
    return matches


service_selector = {"app": "web", "version": "v2"}
print("Service selector:", service_selector)
print("বর্তমান ম্যাচিং পড:", get_matching_pods(pods, service_selector))

# pod-2 রোলিং-আপডেটে সরে গেল, নতুন pod-5 (v2) যোগ হলো — L28-এর মতোই একটি স্বাভাবিক পরিবর্তন
pods = [p for p in pods if p["id"] != "pod-2"]
pods.append({"id": "pod-5", "labels": {"app": "web", "version": "v2"}})

print("\nপড-সেট পরিবর্তনের পরে (pod-2 সরে গেছে, pod-5 যোগ হয়েছে):")
print("নতুন ম্যাচিং পড:", get_matching_pods(pods, service_selector))
print("Service-এর ঠিকানা কিন্তু এই পুরো সময়ে একটুও বদলায়নি")

    
লক্ষ্য করুন — get_matching_pods() প্রতিবার নতুন করে চালানো হয়েছে, কিন্তু কোনো পরিবর্তন Service-এর নিজের ঠিকানায় প্রভাব ফেলেনি। বাস্তবে Kubernetes এই ম্যাচিং প্রক্রিয়া ক্রমাগত, স্বয়ংক্রিয়ভাবে চালায় — একটি পড যোগ/অপসারণ/রিলেবেল হওয়া মাত্র Service-এর লক্ষ্য-তালিকা আপডেট হয়ে যায়, কোনো ম্যানুয়াল হস্তক্ষেপ ছাড়াই।
মূল কথা · Key takeaway

Service ও Ingress একসাথে Pod-এর অস্থিরতাকে অন্য সবার কাছে অদৃশ্য করে দেয় — Service ভেতরের জন্য একটি স্থিতিশীল ঠিকানা দেয়, Ingress বাইরের জন্য একটি সুসংগঠিত প্রবেশপথ দেয়। পরবর্তী পাঠে আমরা দেখব কীভাবে অ্যাপ্লিকেশনের কনফিগারেশন ও সংবেদনশীল ডেটা (পাসওয়ার্ড, API কী) একইভাবে ইমেজ থেকে বাইরে রাখা যায়।

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

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

প্র ০১ Service-এর ঠিকানা পড-এর ঠিকানার চেয়ে "স্থিতিশীল" — এই স্থিতিশীলতা ছাড়া বাস্তবে কী সমস্যা হতো?

Service ছাড়া, একটি অ্যাপ্লিকেশনকে অন্য মাইক্রোসার্ভিসের সাথে যোগাযোগ করতে হলে প্রতিবার পড-এর IP পরিবর্তন হওয়ার সাথে সাথে নিজেই ট্র্যাক করে রাখতে হতো — যেটি Pod-এর ephemeral প্রকৃতির কারণে ক্রমাগত ঘটে (L28)। প্রতিটি ক্লায়েন্টকে নিজে থেকে এই আপডেট ট্র্যাক করতে হলে সিস্টেম ভঙ্গুর ও জটিল হয়ে যেত — Service এই পুরো সমস্যা একটি স্থিতিশীল ঠিকানার পেছনে লুকিয়ে ফেলে।

প্র ০২ ClusterIP, NodePort ও LoadBalancer-এর মধ্যে কখন কোনটা বেছে নেবেন?

অভ্যন্তরীণ মাইক্রোসার্ভিস যোগাযোগের জন্য (যেমন একটি ব্যাকএন্ড সার্ভিস অন্য একটি ইন্টারনাল সার্ভিসকে কল করছে) ClusterIP যথেষ্ট ও সবচেয়ে নিরাপদ (least-privilege ধারণা, L16)। বাইরের ব্যবহারকারীদের জন্য প্রোডাকশনে সাধারণত LoadBalancer (বা তার উপরে Ingress) ব্যবহৃত হয়। NodePort মূলত ডেভেলপমেন্ট/টেস্টিং বা এমন পরিবেশে ব্যবহৃত হয় যেখানে একটি সম্পূর্ণ ক্লাউড লোড ব্যালেন্সার প্রভিশন করার সুবিধা নেই।

প্র ০৩ Ingress আর একটি Service-এর মধ্যে পার্থক্য কী — একটির বদলে শুধু আরেকটি ব্যবহার করলেই কি চলে না?

একটি একক LoadBalancer Service একটি একক ব্যাকএন্ডের দিকে ট্রাফিক পাঠায়, কিন্তু বাস্তব অ্যাপ্লিকেশনে প্রায়ই একাধিক ব্যাকএন্ড Service থাকে (যেমন /api এক Service-এ, /images আরেক Service-এ)। Ingress একটি একক এন্ট্রি পয়েন্ট থেকে পাথ/হোস্ট দেখে সঠিক Service বেছে নেওয়ার এই "রাউটিং লজিক" যোগ করে — প্রতিটি ব্যাকএন্ডের জন্য আলাদা, ব্যয়বহুল LoadBalancer তৈরি না করেই।

অনুশীলন

  1. চিন্তা করুন: যদি service_selector-এ শুধু {"app": "web"} থাকত (version না থাকত), তাহলে pods তালিকায় কোন কোন পড ম্যাচ করত?

    তখন pod-1 (v1) এবং pod-2/pod-3 (v2) সবগুলোই ম্যাচ করত, কারণ selector-এ শুধু app কী-এর শর্ত আছে। এটি দেখায় কেন একটি Service-এর selector-এ কতগুলো কী রাখা হয়েছে তা সরাসরি নির্ধারণ করে কতটা "সংকীর্ণ" বা "বিস্তৃত" তার লক্ষ্য-গোষ্ঠী — উদাহরণস্বরূপ version বাদ দিলে v1 ও v2 উভয় ভার্সনই একসাথে ট্রাফিক পেত, যা একটি ক্যানারি রোলআউটের (M8/L37) সময় অনিচ্ছাকৃতভাবে ঘটলে সমস্যা তৈরি করতে পারে।

  2. পরীক্ষা করুন: pods তালিকায় একটি নতুন পড যোগ করুন যার লেবেল {"app": "web", "version": "v3"} — নিশ্চিত করুন এটি service_selector = {"app": "web", "version": "v2"}-এর সাথে ম্যাচ করে না।

    v3 পড get_matching_pods()-এর ফলাফলে আসবে না, কারণ version কী-এর মান ("v3") selector-এর প্রত্যাশিত মানের ("v2") সাথে মেলে না — all() শর্তটি একটি key-value-ও না মিললে পুরো পড বাদ দেয়। এটিই একটি Service-কে নির্দিষ্ট একটি ভার্সনের দিকেই ট্রাফিক পাঠাতে সাহায্য করে, বাকি ভার্সনের পড থাকলেও।

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

পাঠ ২৮
Pod, Deployment ও ReplicaSet