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

Ingress আর Gateway API — বাইরের traffic ভেতরে আনা

Ingress and Gateway API — Bringing External Traffic Into the Cluster

Taught in Bangla, this lesson compares many load balancers with one Ingress, explains Ingress objects, IngressClass and controllers, host and path routing with Exact and Prefix matching, TLS termination with a self-signed certificate and cert-manager, ingress-nginx retirement, Gateway API roles and traffic splitting, and how to trace 404 versus 503 responses layer by layer.

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

  • N-টি load balancer আর একটি Ingress-এর খরচ হিসাব করতে
  • Ingress object, IngressClass আর controller-এর সম্পর্ক বুঝতে
  • Host আর path দিয়ে দুটি Service-এ traffic পাঠিয়ে curl দিয়ে যাচাই করতে
  • Self-signed certificate দিয়ে TLS termination পরীক্ষা করতে, আর production-এ cert-manager কেন, তা জানতে
  • Gateway API-র তিন object, দায়িত্ব ভাগ আর traffic splitting বুঝতে
  • 404 আর 503 দেখে সমস্যা কোন স্তরে, তা ধাপে ধাপে খুঁজে বের করতে

এই পর্বের আউটপুট সম্পর্কে: kubectl, helm, curl আর openssl-এর আউটপুট একটি স্থানীয় single-node cluster-এর (Kubernetes ১.৩৪) আসল run থেকে নেওয়া। Controller হিসেবে ছিল Traefik (chart 42.2.0, app v3.7.16). নাম, বয়স, IP আর সময় আপনার ক্ষেত্রে আলাদা হবে। লম্বা অংশ … দিয়ে ছাঁটা হয়েছে। Cloud load balancer এই cluster-এ নেই, তাই তার জায়গায় 127.0.0.1-এ kubectl port-forward ব্যবহার করা হয়েছে, আর cloud-এর ধাপগুলো “নমুনা” লেখা আছে। Gateway API-র কোনো controller চালানো হয়নি: YAML শুধু API server-এর --dry-run=server যাচাই পার করেছে।

১ · প্রতিটি Service-এ একটি load balancer কেন অপচয়

পর্ব ৪৫-এ দেখেছেন, LoadBalancer Service বানালে cloud একটি বাইরের load balancer দেয়। একটি Service থাকলে এটি দারুণ সহজ। কিন্তু একটি কাল্পনিক অনলাইন বাজার অ্যাপের কথা ভাবুন: দোকানের পাতা, API, ছবি, অ্যাডমিন, পেমেন্টের ফিরতি ঠিকানা। পাঁচটি Service মানে পাঁচটি load balancer, পাঁচটি বাইরের ঠিকানা, পাঁচটি certificate-এর জায়গা, পাঁচ সেট firewall নিয়ম। অথচ সবগুলোই একই HTTPS ট্রাফিক।

একটি বড় শপিং মলের কথা ভাবুন। প্রতিটি দোকানের জন্য রাস্তায় আলাদা ফটক, আলাদা দারোয়ান রাখলে খরচ অনেক, আর ক্রেতাও বিভ্রান্ত হবেন। তাই মলে থাকে একটি মূল ফটক, ভেতরে একটি নির্দেশিকা বোর্ড (“জুতা: তিনতলা, খাবার: ছয়তলা”), আর তথ্যকেন্দ্রের একজন কর্মী। Ingress হলো সেই বোর্ড। Ingress controller হলো কর্মীটি। বোর্ড টাঙালেই কেউ পথ দেখাবে না, কর্মী থাকতে হবে।

L4 load balancer শুধু IP আর port দেখে, তাই সে HTTP-র ভেতরের host বা path পড়তে পারে না। Ingress controller কাজ করে L7-এ: reverse proxy হিসেবে অনুরোধ খুলে পড়ে, তারপর host-based routing আর path-based routing করে। পর্ব ১৮-তে cloud L7 load balancer-এ যে ধারণা দেখেছেন, আর পর্ব ১৪-তে Nginx virtual host-এ যা হাতে সাজিয়েছেন, এখানে সেটিই cluster-এর ভেতরে YAML দিয়ে ঘোষণা করা হয়।

খরচের হিসাব: কতটি Service থেকে Ingress লাভজনক

নিচের দামগুলো ধরে নেওয়া গোল সংখ্যা, কোনো provider-এর আসল দাম নয়। আসল দাম জানতে provider-এর pricing পাতা দেখুন। হাতে একবার করা যাক: ধরি ঘণ্টায় $০.০৩ দরে একটি load balancer, ৩০ দিনের মাসে ৭২০ ঘণ্টা। তাহলে মাসে ০.০৩ × ৭২০ = $২১.৬০। ৫০০ GB ডেটায় প্রতি GB $০.০০৮ ধরলে ডেটার খরচ $৪, যা দুই নকশাতেই সমান। Ingress নকশায় বাড়তি লাগে controller-এর দুটি Pod, ধরি প্রতিটি মাসে $১০।

// N LoadBalancer Services versus one Ingress: all prices are ASSUMED
const lbPerHour = 0.03;      // assumed: one cloud load balancer, per hour
const gbPrice   = 0.008;     // assumed: data processed, per GB
const gbTotal   = 500;       // assumed: total monthly traffic, GB
const podCost   = 10;        // assumed: monthly cost of one controller Pod
const pods      = 2;         // two controller replicas
const hours     = 720;       // 30-day month

const lbMonth = lbPerHour * hours;
const data    = gbPrice * gbTotal;      // same bytes either way

const perService = n => n * lbMonth + data;
const oneIngress = () => lbMonth + pods * podCost + data;

console.log(`one LB = $${lbMonth.toFixed(2)}/month, data = $${data.toFixed(2)}/month`);
console.log("Services  N LBs($)  1 Ingress($)  saved($)");
for (const n of [1, 2, 3, 5, 10, 20, 50]) {
  const a = perService(n), b = oneIngress();
  console.log(String(n).padEnd(10) + a.toFixed(2).padEnd(10) +
              b.toFixed(2).padEnd(14) + (a - b).toFixed(2));
}
const breakEven = 1 + (pods * podCost) / lbMonth;
console.log("break-even N = " + breakEven.toFixed(2));

আউটপুটের মূল লাইন: 1 … 25.60 … 45.60 … -20.00, 2 … 47.20 … 45.60 … 1.60, আর 20 … 436.00 … 45.60 … 390.40. শেষে break-even N = 1.93. অর্থাৎ এই ধরে নেওয়া দামে একটি মাত্র Service-এ Ingress $২০ বেশি খরচ করে, কিন্তু দুটি থেকেই লাভ শুরু। সূত্রটি সহজ: N × ২১.৬০ = ২১.৬০ + ২০ থেকে N = ১.৯৩. বিশটি Service-এ মাসে $৩৯০.৪০ বাঁচে। দাম বদলে নিজে চালিয়ে দেখুন।

খরচ বাঁচলেও একটি দাম আছে: একটি দরজা মানে সবার ঝুঁকি একসাথে। Controller-এর একটি ভুল config বা ভুল upgrade সব Service-কে একসাথে নামিয়ে দিতে পারে। তাই অন্তত দুটি replica রাখুন। আর HTTP নয় এমন traffic (যেমন ডেটাবেসের TCP বা গেমের UDP) Ingress দিয়ে চলে না; সেখানে Service-এর L4 load balancer বা Gateway API-র অন্য route লাগে।

২ · Ingress object আর ingress controller

এই দুটি শব্দ আলাদা করতে পারাই প্রথম বড় ধাপ।

Ingress object

API server-এ রাখা একটি YAML রেকর্ড। লেখা থাকে কোন host আর path কোন Service-এ যাবে। সে নিজে কোনো port খোলে না।

নির্দেশিকা বোর্ড

Ingress controller

সাধারণত cluster-এ চলা একটি proxy Pod. Ingress object দেখে নিজের routing বদলায়, আর আসল traffic বহন করে।

তথ্যকেন্দ্রের কর্মী

IngressClass

Cluster-এ একাধিক controller থাকলে কে কোন Ingress দেখবে, সেটি ঠিক করে। Ingress-এ লেখা হয় ingressClassName.

কোন কর্মীর দায়িত্ব

Kubernetes-এর core অংশে কোনো ingress controller নেই। আপনাকে বা আপনার cloud provider-কে একটি বসাতে হয়। তাই Ingress লিখে কিছু না হলে প্রথম প্রশ্ন: “Controller আছে তো?” Controller-ও reconciliation loop-এ চলে, যা পর্ব ৪২-এ দেখেছেন: Ingress আর EndpointSlice নজরে রাখে, কিছু বদলালে নিজের routing নতুন করে বানায়।

একটি দরজা, দুটি Service

Traffic enters through one controller and reaches two Services

Cloud load balancer থেকে controller হয়ে দুটি Service আর তাদের Pod ব্যবহারকারী HTTPS 443 Cloud load balancer মাত্র একটি বাইরের ঠিকানা Kubernetes cluster Ingress controller TLS শেষ করে, Host আর path দেখে Service বাছে Pod-এ চলা proxy Ingress object API server-এ রাখা নিয়ম / (বাকি সব) /api Service shop-web Service shop-api Pod web-1 Pod web-2 Pod api-1 Pod api-2
ছবি ৪৬.১ — বাইরের ঠিকানা একটি। Controller Ingress নিয়ম পড়ে (ডটেড দাগ), আর দুটি Service-এ traffic ভাগ করে। অনেক controller সরাসরি Pod IP-তে পাঠায়, Service-এর ClusterIP হয়ে নয়।

Ingress API অনেক আগে beta হিসেবে আসে, আর Kubernetes ১.১৯-এ networking.k8s.io/v1 নামে stable হয়। পুরোনো extensions/v1beta1 আর networking.k8s.io/v1beta1 Ingress ১.২২-এ সরানো হয়েছে। Kubernetes-এর বর্তমান documentation জানায়, Ingress API এখন হিমায়িত: আর নতুন কিছু যোগ হবে না, তবে GA নিশ্চয়তা আছে আর সরানোর পরিকল্পনা নেই। একই পাতায় Ingress-এর বদলে Gateway ব্যবহারের সুপারিশ আছে।

একটি controller বসানো (আসল run)

আমরা Helm দিয়ে Traefik বসিয়েছি। Traefik বেছে নেওয়া মানে এটি সেরা বলা নয়; এই পরীক্ষার জন্য বাছা হয়েছে এমন একটি controller, যা লেখার সময় রক্ষণাবেক্ষণে আছে আর সহজে বসে। Helm নিয়ে বিস্তারিত পর্ব ৫০-এ।

global:
  checkNewVersion: false
  sendAnonymousUsage: false
deployment:
  replicas: 1
ingressClass:
  enabled: true
  name: abcl-ki
  isDefaultClass: false
service:
  spec:
    type: ClusterIP      # no cloud LB on this local cluster
providers:
  kubernetesCRD:
    enabled: false
  kubernetesIngress:
    enabled: true
    allowEmptyServices: true
    namespaces:
    - abcl-ki
  kubernetesGateway:
    enabled: false
resources:
  requests: {cpu: 50m, memory: 64Mi}
  limits: {cpu: 200m, memory: 128Mi}
$ helm install abcl-ki-ingress traefik \
    --repo https://traefik.github.io/charts --version 42.2.0 \
    -n abcl-ki --skip-crds -f values.yaml --wait
NAME: abcl-ki-ingress
…
STATUS: deployed
REVISION: 1
…
$ kubectl get ingressclass abcl-ki
NAME      CONTROLLER                      PARAMETERS   AGE
abcl-ki   traefik.io/ingress-controller   <none>       63s

$ kubectl -n abcl-ki get pods,svc
NAME                                           READY   STATUS    RESTARTS   AGE
pod/abcl-ki-ingress-traefik-667c9875b6-m7rzm   1/1     Running   0          63s
pod/shop-api-55478bc64d-nc22f                  1/1     Running   0          3s
pod/shop-api-55478bc64d-rkz9v                  1/1     Running   0          3s
pod/shop-web-787f666fc6-kn2fs                  1/1     Running   0          3s
pod/shop-web-787f666fc6-skcbj                  1/1     Running   0          3s

NAME                              TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
service/abcl-ki-ingress-traefik   ClusterIP   10.108.211.165   <none>        80/TCP,443/TCP   63s
service/shop-api                  ClusterIP   10.108.221.88    <none>        80/TCP           3s
service/shop-web                  ClusterIP   10.97.157.85     <none>        80/TCP           3s

Cloud-এ এই controller-এর Service সাধারণত LoadBalancer ধরনের হয়, আর সেই একটি load balancer-ই পুরো cluster-এর দরজা। এখানে স্থানীয় cluster বলে ClusterIP রেখে 127.0.0.1-এ port-forward করা হয়েছে। সব কমান্ড চলেছে namespace abcl-ki-তে; shop-web আর shop-api হলো দুটি ছোট nginx Deployment, যারা নিজের নাম, Pod-এর নাম আর পাওয়া path ফেরত দেয়।

৩ · Host আর path routing: Exact বনাম Prefix

নকল domain হিসেবে আমরা .test ব্যবহার করছি, যা পরীক্ষার জন্য সংরক্ষিত। নিচের Ingress-এ দুটি host আর তিনটি path নিয়ম আছে। প্রতিটি path-এ pathType বাধ্যতামূলক; না লিখলে validation ব্যর্থ হয়।

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: shop
spec:
  ingressClassName: abcl-ki
  rules:
  - host: shop.example.test
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: shop-web
            port:
              number: 80
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: shop-api
            port:
              number: 80
      - path: /status
        pathType: Exact
        backend:
          service:
            name: shop-api
            port:
              number: 80
  - host: api.example.test
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: shop-api
            port:
              number: 80
pathTypeমেলার নিয়মউদাহরণ
Exactপুরো path হুবহু, বড়-ছোট হাতের অক্ষরসহ/foo মেলে না /foo/-এর সাথে
Prefix/ দিয়ে ভাগ করা অংশ ধরে ধরে, শেষের / উপেক্ষা/foo মেলে /foo/bar-এ, /foobar-এ নয়
ImplementationSpecificController যা ঠিক করেবহনযোগ্য নয়, এড়িয়ে চলা ভালো

একাধিক নিয়ম মিললে documentation-এর নিয়ম হলো: আগে সবচেয়ে লম্বা path জেতে। সমান লম্বা হলে Exact জেতে Prefix-এর ওপর। কোনো নিয়ম না মিললে defaultBackend-এ যায়, আর সেটি না থাকলে কী হবে, তা controller ঠিক করে। নিচের সিমুলেটরে spec-এর নিয়মের পাশে সরল “string prefix” তুলনা রেখেছি। নিয়ম বা অনুরোধ বদলে চালিয়ে দেখুন।

// Host and path router: the Ingress spec versus a plain string prefix
const rules = [
  { host: "shop.example.test", path: "/",        type: "Prefix", svc: "shop-web" },
  { host: "shop.example.test", path: "/api",     type: "Prefix", svc: "shop-api" },
  { host: "shop.example.test", path: "/status",  type: "Exact",  svc: "shop-api" },
  { host: "api.example.test",  path: "/",        type: "Prefix", svc: "shop-api" },
];
const parts = p => p.split("/").filter(Boolean);

function matches(rule, path, stringPrefix) {
  if (rule.type === "Exact") return path === rule.path;
  if (stringPrefix) return path.startsWith(rule.path);
  const want = parts(rule.path), got = parts(path);   // element by element
  return want.every((seg, i) => seg === got[i]);
}
function route(host, path, stringPrefix = false) {
  const hits = rules.filter(r => r.host === host && matches(r, path, stringPrefix));
  if (hits.length === 0) return "404";
  hits.sort((a, b) => b.path.length - a.path.length       // longest path first
                   || (a.type === "Exact" ? -1 : 1));     // then Exact over Prefix
  return hits[0].svc;
}
const tests = [
  ["shop.example.test", "/"], ["shop.example.test", "/api/orders"],
  ["shop.example.test", "/apix"], ["shop.example.test", "/API"],
  ["shop.example.test", "/status"], ["shop.example.test", "/status/"],
  ["api.example.test", "/anything"], ["unknown.example.test", "/"],
];
console.log("host".padEnd(21) + "path".padEnd(13) + "spec".padEnd(10) + "string prefix");
for (const [h, p] of tests) {
  const a = route(h, p), b = route(h, p, true);
  console.log(h.padEnd(21) + p.padEnd(13) + a.padEnd(10) + b + (a !== b ? "  <- differ" : ""));
}

একটিমাত্র সারিতে দুই পদ্ধতির ফল আলাদা: /apix shop-web shop-api <- differ. Spec-এর নিয়মে /apix-এর প্রথম অংশ apix, api নয়, তাই নিয়ম মেলে না, আর অনুরোধ /-এ পড়ে shop-web-এ যায়। /status/-এ শেষের / থাকায় Exact মেলে না, তাই সেটিও shop-web. /API বড় হাতের বলে /api-তে মেলে না। আর অচেনা host পায় 404.

আসল controller কী করল? একটি অপ্রত্যাশিত ফল

এবার সত্যিকারের curl. -H "Host: …" দিয়ে আমরা DNS ছাড়াই controller-কে বলছি কোন host চাই। লেখা ছোট রাখতে একটি ছোট shell function বানানো হয়েছে, আর $LB হলো port-forward-এর স্থানীয় ঠিকানা (প্রতি run-এ port আলাদা ছিল)।

$ kubectl -n abcl-ki port-forward svc/abcl-ki-ingress-traefik 18461:80 18462:443 &
$ LB=127.0.0.1:18461
$ c() { curl -s -H "Host: $1" -w '[HTTP %{http_code}]\n' "$LB$2"; }

$ c shop.example.test /
shop-web pod=shop-web-787f666fc6-kn2fs path=/
[HTTP 200]
$ c shop.example.test /api/orders
shop-api pod=shop-api-55478bc64d-nc22f path=/api/orders
[HTTP 200]
$ c shop.example.test /apix
shop-api pod=shop-api-55478bc64d-rkz9v path=/apix        # spec says shop-web
[HTTP 200]
$ c shop.example.test /status
shop-web pod=shop-web-787f666fc6-skcbj path=/status      # spec says shop-api
[HTTP 200]
$ c unknown.example.test /
404 page not found
[HTTP 404]

দুটি ফলই documentation-এর নিয়মের সাথে মেলে না। ডান পাশের মন্তব্য দুটি আমরা যোগ করেছি। কারণ খুঁজতে Traefik-এর debug API চালু করে (api.insecure, শুধু এই পরীক্ষায়, স্থানীয় port-forward-এ) router-এর তালিকা দেখা হলো। সাজিয়ে লেখা অংশ:

# priority  rule                                         service
47  Host("shop.example.test") && PathPrefix("/api")   shop-api
44  Host("shop.example.test") && Path("/status")      shop-api
44  Host("shop.example.test") && PathPrefix("/")      shop-web
43  Host("api.example.test") && PathPrefix("/")       shop-api
  • /apix: default-এ নিয়মটি হয়েছে PathPrefix("/api"), যা অক্ষর ধরে মেলে। Chart-এ strictPrefixMatching নামে একটি বিকল্প আছে, যার মান default-এ false.
  • /status: তালিকায় দেখা যাচ্ছে priority-র মান নিয়মের লেখার দৈর্ঘ্যের সমান। Path("/status") আর PathPrefix("/") দুটিই ১৫ অক্ষর, তাই দুই নিয়মের priority সমান, ৪৪। লম্বা path জেতার নিশ্চয়তা এখানে নেই।

এবার strictPrefixMatching=true দিয়ে আবার চালানো হলো:

$ helm upgrade abcl-ki-ingress traefik … -f values.yaml \
    --set providers.kubernetesIngress.strictPrefixMatching=true --wait
…
# router rule for /api is now:
66  Host("shop.example.test") && (Path("/api") || PathPrefix("/api/"))   shop-api
44  Host("shop.example.test") && Path("/status")                        shop-api
44  Host("shop.example.test") && PathPrefix("/")                        shop-web

$ c shop.example.test /api/
shop-api pod=shop-api-55478bc64d-nc22f path=/api/
[HTTP 200]
$ c shop.example.test /apix
shop-web pod=shop-web-787f666fc6-kn2fs path=/apix
[HTTP 200]
$ c shop.example.test /API
shop-web pod=shop-web-787f666fc6-skcbj path=/API
[HTTP 200]
$ c shop.example.test /status
shop-api pod=shop-api-55478bc64d-nc22f path=/status
[HTTP 200]
$ c shop.example.test /status/
shop-web pod=shop-web-787f666fc6-kn2fs path=/status/
[HTTP 200]

/apix এবার ঠিক আছে। /status-ও এবার shop-api-তে গেছে, কিন্তু খেয়াল করুন: তার priority এখনো ৪৪, /-এর সমান। অর্থাৎ দুই run-এ একই সমতার ফল আলাদা হয়েছে, তাই এর ওপর ভরসা করা যায় না। পাকা সমাধান হিসেবে আলাদা একটি Ingress-এ Traefik-এর নিজস্ব annotation traefik.ingress.kubernetes.io/router.priority: "100" বসিয়ে দেখা হয়েছে: router-এর priority ১০০ হয়, আর /status যায় shop-api-তে। আমরা Traefik-এর source পড়ে সমতা ভাঙার নিয়ম যাচাই করিনি; যা দেখেছি, শুধু সেটুকুই লিখলাম।

মূল শিক্ষা

API-র নিয়ম আর controller-এর আচরণ এক জিনিস নয়। নিজের controller-এ curl দিয়ে প্রান্তিক path (/apix, শেষে /, বড় হাতের অক্ষর) যাচাই না করে “Prefix মানে এই” ধরে নেবেন না। এই পরীক্ষাটি এক version-এর একটি controller-এর ফল; অন্য version বা controller-এ আলাদা হতে পারে।

৪ · TLS termination: certificate থাকে Secret-এ

TLS termination মানে এনক্রিপ্ট করা সংযোগ কোথাও এসে শেষ হওয়া। Ingress-এ সেটি সাধারণত controller-এ হয়, ফলে certificate রাখতে হয় একটি জায়গায়, প্রতিটি app-এ নয়। Certificate আর private key থাকে kubernetes.io/tls ধরনের Secret-এ, যা নিয়ে আরও আলোচনা পর্ব ৪৭-এ। TLS-এর ভেতরের কাজ জানতে Computer Networks কোর্সের TLS পর্বটি দেখুন।

পরীক্ষার জন্য নকল domain-এ একটি self-signed certificate বানানো হলো, আর Ingress-এর spec-এ এই অংশটুকু যোগ করা হলো:

spec:
  ingressClassName: abcl-ki
  tls:
  - hosts:
    - shop.example.test
    secretName: shop-tls
  rules:
  # ... same rules as before ...
$ openssl req -x509 -newkey rsa:2048 -nodes -keyout shop.key -out shop.crt \
    -days 30 -subj "/CN=shop.example.test" \
    -addext "subjectAltName=DNS:shop.example.test"
$ kubectl -n abcl-ki create secret tls shop-tls --cert=shop.crt --key=shop.key
secret/shop-tls created
$ kubectl -n abcl-ki get secret shop-tls
NAME       TYPE                DATA   AGE
shop-tls   kubernetes.io/tls   2      0s

$ kubectl -n abcl-ki describe ingress shop
…
TLS:
  shop-tls terminates shop.example.test
Rules:
  Host               Path  Backends
  ----               ----  --------
  shop.example.test
                     /         shop-web:80 (10.1.0.81:80,10.1.0.82:80)
                     /api      shop-api:80 (10.1.0.83:80,10.1.0.84:80)
                     /status   shop-api:80 (10.1.0.83:80,10.1.0.84:80)
…
$ H=shop.example.test:18462
$ curl -sS --resolve $H:127.0.0.1 https://$H/
curl: (60) … The certificate chain was issued by an authority that is not trusted.

$ curl -sS --resolve $H:127.0.0.1 --cacert shop.crt https://$H/api/orders
shop-api pod=shop-api-55478bc64d-nc22f path=/api/orders

$ echo | openssl s_client -connect 127.0.0.1:18462 -servername shop.example.test
…
subject=CN=shop.example.test
issuer=CN=shop.example.test
…
Protocol: TLSv1.3

$ echo | openssl s_client -connect 127.0.0.1:18462 -servername other.example.test
…
subject=CN=TRAEFIK DEFAULT CERT
issuer=CN=TRAEFIK DEFAULT CERT

চারটি বিষয় লক্ষ করুন। (১) --cacert ছাড়া curl থামল, কারণ এই certificate-এ কোনো স্বীকৃত CA-র স্বাক্ষর নেই; ত্রুটির ভাষা আপনার curl-এর TLS library-ভেদে আলাদা হবে। (২) নিজের shop.crt-কে বিশ্বাসযোগ্য বলে দিতেই যাচাই পার হলো। -k দিয়ে যাচাই বন্ধ করার চেয়ে এটি ভালো অভ্যাস। (৩) subject আর issuer একই, এটাই self-signed-এর চিহ্ন। (৪) অন্য SNI নামে controller নিজের default certificate দেখাল: নাম দেখেই সে certificate বাছে। আর সাধারণ HTTP-তে অনুরোধ এই run-এ 200 পেয়েছিল: TLS যোগ করলেই HTTP বন্ধ বা redirect হয়ে যায় না, সেটি আলাদা করে সাজাতে হয়, আর নিয়ম controller-ভেদে আলাদা।

Self-signed certificate শুধু পরীক্ষার জন্য। আর Kubernetes Secret নিজে থেকে encryption নয়, শুধু base64; তাই Secret পড়ার RBAC অনুমতি সীমিত রাখুন (পর্ব ৪৯)। Private key কখনো git-এ রাখবেন না। এই পরীক্ষার key কাজ শেষে মুছে ফেলা হয়েছে।

Production-এ: cert-manager

হাতে certificate নবায়ন মানে একদিন ভুলে যাওয়া। cert-manager একটি open source controller, যা Let's Encrypt-এর মতো ACME CA থেকে certificate আনে, Secret-এ রাখে, আর মেয়াদ ফুরানোর আগে নবায়ন করে। Domain যাচাই হয় HTTP-01 বা DNS-01 দিয়ে (পর্ব ১৪ আর ৩০)। সাধারণত Ingress-এ একটি annotation বসালেই কাজ চলে। আমরা cert-manager বসাইনি; নিচের YAML শুধু API server-এর --dry-run=server পার করেছে, যা YAML-টি বৈধ কিনা বলে, certificate আসবে কিনা বলে না।

metadata:
  name: shop-acme-sample
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-example
spec:
  tls:
  - hosts:
    - shop.example.test
    secretName: shop-tls-auto    # cert-manager creates this Secret

একটি HTTPS অনুরোধের পুরো যাত্রা

নিচের ধাপে ধাপে অনুসরণে DNS আর cloud load balancer-এর মান নমুনা (ঠিকানাটি documentation-এর জন্য সংরক্ষিত)। Controller থেকে Pod পর্যন্ত মানগুলো ওপরের আসল run-এর।

  1. DNSAঠিকানা
  2. Cloud LB:443দরজা
  3. ControllerTLSখোলা
  4. নিয়ম/apiবাছাই
  5. EndpointPod IPready তালিকা
  6. Pod:80উত্তর
DNSনমুনা
shop.example.test  A  203.0.113.10
Cloud LBনমুনা
listener  :443 (TCP pass-through)
target    controller Pods / node ports
এখানে TLS খোলা হয়নি
Controllerআসল run
SNI          shop.example.test
certificate  Secret shop-tls
Protocol     TLSv1.3
Routingআসল run
Host  shop.example.test
Path  /api/orders
নিয়ম  /api (Prefix)  ->  shop-api:80
Endpointআসল run
shop-api:80 (10.1.0.83:80,10.1.0.84:80)
শুধু ready Pod নির্বাচনের যোগ্য
Pod-এর উত্তরআসল run
shop-api pod=shop-api-55478bc64d-nc22f path=/api/orders
এই শেষ অংশে HTTP, TLS নয়
  1. DNS. Browser shop.example.test-এর ঠিকানা চায়। উত্তরে আসে cloud load balancer-এর একটি ঠিকানা। সব host একই ঠিকানায় আসতে পারে; এটাই “একটি দরজা”।
  2. Cloud load balancer. সংযোগ আসে :443-তে। এই নমুনায় LB শুধু TCP চালান করে। অনেক নকশায় LB নিজেই TLS খোলে; সেক্ষেত্রে পরের ধাপের কাজ এখানে হয়।
  3. Controller, TLS. Client-এর SNI দেখে controller Secret shop-tls-এর certificate দেখায়, আর TLSv1.3 সংযোগ শেষ করে। এখন অনুরোধ পড়া যায়।
  4. Controller, নিয়ম. Host আর /api/orders মিলিয়ে সবচেয়ে উপযুক্ত নিয়ম হলো /api Prefix. গন্তব্য shop-api:80.
  5. Endpoint. Controller Service-এর EndpointSlice থেকে ready Pod-এর তালিকা জানে। এখানে দুটি ঠিকানা, আর এই অনুরোধের জন্য একটি বাছা হলো। Traefik default-এ সরাসরি Pod ঠিকানায় যায়।
  6. Pod. Pod সাধারণ HTTP-তে অনুরোধ পায় আর উত্তর দেয়। উত্তর একই পথে ফেরে। TLS শেষ হয়েছে controller-এ, তাই Pod পর্যন্ত নিজে থেকে এনক্রিপশন নেই, যদি না আবার TLS বা mesh সাজানো হয়।

৫ · Annotation-এর দুর্বলতা আর ingress-nginx-এর অবসর

Ingress API ছোট: host, path, TLS, এটুকুই। Rewrite, redirect, rate limit, timeout, canary, CORS এসব রাখার ঘর নেই। তাই প্রতিটি controller নিজের নিজের annotation বানিয়েছে। Annotation হলো metadata-র সাধারণ লেখা, API server তার অর্থ বোঝে না। ফলে ভুল annotation-এ কোনো ত্রুটিও আসে না। নিচে ingress-nginx-এর একটি annotation Traefik-এ পরীক্ষা করা হলো:

# Ingress "legacy": path /old, Prefix, host legacy.example.test
#   annotations:
#     nginx.ingress.kubernetes.io/rewrite-target: /

$ kubectl -n abcl-ki apply -f ing-rewrite.yaml
ingress.networking.k8s.io/legacy created

$ c legacy.example.test /old/page
shop-web pod=shop-web-787f666fc6-skcbj path=/old/page
[HTTP 200]

Apply সফল, কোনো সতর্কবার্তা নেই, অনুরোধও 200. কিন্তু Pod পেয়েছে /old/page: rewrite হয়নি। এই controller-এ এই annotation-এর কোনো অর্থ নেই, আর সে চুপচাপ উপেক্ষা করেছে। বড় কোনো migration-এ ঠিক এই ধরনের নীরব পরিবর্তনই বিপদ। Traefik-এর সাম্প্রতিক chart-এ ingress-nginx annotation বোঝার আলাদা provider আছে, যা এখানে চালু ছিল না; কতটা মেলে তা নিজে পরীক্ষা করে দেখতে হবে।

ingress-nginx প্রকল্পের কী হলো

এখানে দুটি জিনিস আলাদা রাখুন: Ingress API Kubernetes-এর অংশ, সেটি হিমায়িত কিন্তু আছে। ingress-nginx একটি controller প্রকল্প, যার অবসর হয়েছে।

তারিখঘটনা
১১ নভেম্বর ২০২৫Kubernetes SIG Network আর Security Response Committee অবসরের ঘোষণা দেয়: best-effort রক্ষণাবেক্ষণ চলবে মার্চ ২০২৬ পর্যন্ত। কারণ হিসেবে বলা হয়, উন্নয়ন নির্ভর করছিল মাত্র এক-দুজনের নিজের সময়ের ওপর। উত্তরসূরি InGate-ও পরিণত হয়নি।
২৯ জানুয়ারি ২০২৬Steering আর Security Response Committee-র বিবৃতি: অবসরের পর bug fix বা নিরাপত্তা patch কিছুই আসবে না। Datadog-এর গবেষণার বরাতে বলা হয়, প্রায় ৫০% cloud native পরিবেশ এটির ওপর নির্ভরশীল।
১৯ মার্চ ২০২৬GitHub-এর তালিকা অনুযায়ী এই তারিখে controller-v1.15.1 প্রকাশিত হয়। আমাদের দেখা তালিকায় এটিই সর্বশেষ।
লেখার সময় (অক্টোবর ২০২৬)GitHub-এ kubernetes/ingress-nginx repository archived অবস্থায় আছে। Chart এখনো ডাউনলোড করা যাচ্ছিল।

ঘোষণা অনুযায়ী, চলমান deployment ভেঙে পড়বে না, আর পুরোনো image ও chart পাওয়া যাবে। ঝুঁকিটি অন্য জায়গায়: নতুন নিরাপত্তা-ত্রুটি ধরা পড়লে upstream থেকে আর সমাধান আসবে না। কিছু vendor আলাদাভাবে সহায়তা দিতে পারে, তাই নিজের vendor-এর ঘোষণা দেখে নিন। ব্যবহার আছে কিনা জানার জন্য বিবৃতিতে এই কমান্ডটি দেওয়া আছে, যা cluster-admin অনুমতিতে চালাতে হয়:

kubectl get pods --all-namespaces \
  --selector app.kubernetes.io/name=ingress-nginx

নামের ফাঁদ: ingress-nginx (Kubernetes প্রকল্প, kubernetes/ingress-nginx) আর NGINX Ingress Controller (F5/NGINX-এর প্রকল্প, nginx/kubernetes-ingress) আলাদা। দ্বিতীয়টির chart লেখার সময় নিয়মিত পাওয়া যাচ্ছিল, আর annotation-ও আলাদা। পুরোনো টিউটোরিয়াল বা AI-এর পরামর্শে nginx.ingress.kubernetes.io/… দেখলে সেটি অবসরপ্রাপ্ত প্রকল্পটির কথা বলছে।

৬ · Gateway API: দায়িত্ব ভাগ করা নকশা

Gateway API তৈরি হয়েছে Ingress-এর সীমা মাথায় রেখে। Kubernetes-এর core-এ এটি নেই; CRD হিসেবে আলাদা বসাতে হয়। ২০২৩ সালের ৩১ অক্টোবরের Kubernetes blog অনুযায়ী v1.0-এ Gateway, GatewayClass আর HTTPRoute GA হয়। এরপর নিয়মিত নতুন version এসেছে; লেখার সময় GitHub-এ সর্বশেষ দেখা release v1.7.0 (অক্টোবর ২০২৬)। সংখ্যাটি দ্রুত বদলায়, আর কোন ক্ষমতা কোন channel-এ, তা version-ভেদে আলাদা, তাই বসানোর আগে নিজের controller-এর সমর্থিত version দেখে নিন।

শপিং মলে তিন রকম মানুষের তিন রকম দায়িত্ব। মালিক ঠিক করেন কোন নিরাপত্তা কোম্পানি ফটক চালাবে। মল ব্যবস্থাপক ঠিক করেন কোন ফটক খোলা, কোন চাবি, কোন দোকান ঢুকতে পারবে। আর দোকানদার শুধু নিজের দোকানের পথনির্দেশ লেখেন। কেউ অন্যের কাজে হাত দেন না।

Objectকে সাধারণত দেখেনকী লেখা থাকেScope
GatewayClassInfrastructure দল বা providerকোন controller চালাবেCluster
GatewayCluster পরিচালনাকারী দলPort, protocol, certificate, কোন route ঢুকতে পারবেNamespace
HTTPRouteApp developerHost, path, header মিলিয়ে কোন Service-এ, কত ওজনেNamespace

Ingress-এ এক object-এই TLS আর routing মিশে থাকে, তাই যিনি path বদলান, তিনি certificate-ও ছুঁতে পারেন। Gateway API-তে app দল শুধু নিজের route লেখে, আর নিরাপত্তা-সংবেদনশীল সিদ্ধান্ত Gateway-তে থাকে। Gateway API-র documentation এই ভাগকে role-oriented নকশা বলে, আর উদাহরণ দেয় Ian, Chihiro, Ana নামের তিনটি কল্পিত চরিত্র দিয়ে।

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: abcl-ki-gw
spec:
  controllerName: example.net/gateway-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: shop-gw
spec:
  gatewayClassName: abcl-ki-gw
  listeners:
  - name: http
    protocol: HTTP
    port: 80
    hostname: "*.example.test"
    allowedRoutes:
      namespaces:
        from: Same
  - name: https
    protocol: HTTPS
    port: 443
    hostname: "*.example.test"
    tls:
      mode: Terminate
      certificateRefs:
      - name: shop-tls
    allowedRoutes:
      namespaces:
        from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: shop
spec:
  parentRefs:
  - name: shop-gw
  hostnames:
  - shop.example.test
  rules:
  - matches:
    - path:
        type: Exact
        value: /status
    backendRefs:
    - name: shop-api
      port: 80
  - matches:
    - path:
        type: PathPrefix
        value: /api
    backendRefs:
    - name: shop-api
      port: 80
      weight: 90
    - name: shop-api-v2
      port: 80
      weight: 10
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: shop-web
      port: 80
# standard CRDs of Gateway API v1.7.0 were applied only for this check
$ kubectl -n abcl-ki apply --dry-run=server -f gateway.yaml
gatewayclass.gateway.networking.k8s.io/abcl-ki-gw created (server dry run)
gateway.gateway.networking.k8s.io/shop-gw created (server dry run)
httproute.gateway.networking.k8s.io/shop created (server dry run)

$ kubectl api-resources --api-group=gateway.networking.k8s.io
NAME             SHORTNAMES   APIVERSION                     NAMESPACED   KIND
gatewayclasses   gc           gateway.networking.k8s.io/v1   false        GatewayClass
gateways         gtw          gateway.networking.k8s.io/v1   true         Gateway
httproutes                    gateway.networking.k8s.io/v1   true         HTTPRoute

created (server dry run) মানে API server YAML যাচাই করেছে, কিছু তৈরি করেনি। পরীক্ষার পর CRD তিনটিও মুছে ফেলা হয়েছে। এই cluster-এ CRD ছাড়া Gateway API ছিল না, এটিও খেয়াল করুন: kind: Gateway চেনানোর জন্য আগে CRD লাগে। এখানে কোনো Gateway controller চলেনি, তাই নিচের routing-এর ফল সিমুলেটরের, cluster-এর নয়।

Traffic splitting: ওজনের হিসাব

Ingress-এ ওজন দিয়ে ভাগ করা API-র অংশ নয়; করতে হলে controller-এর annotation লাগত। HTTPRoute-এর backendRefs-এ weight সরাসরি লেখা যায়। Documentation অনুযায়ী weight না দিলে মান ১, আর ভাগ হয় অনুপাতে: একটি backend-এর অংশ = তার ওজন ÷ মোট ওজন। 90 আর 10 মানে ৯০/১০০ = ৯০% আর ১০/১০০ = ১০%. ওজন ০ মানে সেই backend কোনো traffic পায় না। Canary কৌশল নিয়ে বিস্তারিত পর্ব ৫৯-এ।

এই ভাগ নির্দিষ্ট গোনা নয়, সম্ভাবনার (controller-ভেদে পদ্ধতি আলাদা হতে পারে)। নিচের সিমুলেটরটি নমুনা: একটি বীজ-নির্দিষ্ট PRNG দিয়ে অনুরোধ ভাগ করে, তাই প্রতিবার একই ফল আসে।

// Weighted HTTPRoute split: share = weight / sum of weights
function mulberry32(seed) {             // small seeded PRNG, same run every time
  return function () {
    seed = (seed + 0x6D2B79F5) | 0;
    let t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}
const backends = [
  { name: "shop-api",    weight: 90 },
  { name: "shop-api-v2", weight: 10 },
];
const total = backends.reduce((s, b) => s + b.weight, 0);

function pick(rand) {                   // walk the cumulative weights
  let x = rand() * total;
  for (const b of backends) {
    if (x < b.weight) return b.name;
    x -= b.weight;
  }
}
for (const n of [20, 100, 1000, 10000, 100000]) {
  const rand = mulberry32(46);
  const count = {};
  for (const b of backends) count[b.name] = 0;
  for (let i = 0; i < n; i++) count[pick(rand)]++;
  const v2 = count["shop-api-v2"];
  const pct = (100 * v2 / n).toFixed(2);
  const sd = (100 * Math.sqrt(0.1 * 0.9 / n)).toFixed(2);   // expected spread
  console.log(`n=${String(n).padEnd(7)} v1=${String(count["shop-api"]).padEnd(6)} ` +
              `v2=${String(v2).padEnd(6)} v2 share=${pct}%  expected spread +-${sd}%`);
}

আউটপুটের কয়েকটি লাইন: n=100 … v2=9 … 9.00% expected spread +-3.00%, n=10000 … v2=961 … 9.61% … +-0.30%, n=100000 … v2=10105 … 10.11% … +-0.09%. Spread এসেছে √(p(1 − p)/n) সূত্র থেকে: n = ১০০ হলে √(০.১ × ০.৯ / ১০০) = ০.০৩, অর্থাৎ ৩%. কম traffic-এ “১০%” বেশ নড়াচড়া করে, আর অনুরোধ বাড়লে ১০%-এর কাছে আসে। তাই ২০টি অনুরোধ দেখে canary-র ত্রুটির হার নিয়ে সিদ্ধান্ত নেওয়া ঠিক নয়।

৭ · Cloud-এর controller আর Ingress থেকে Gateway-তে যাওয়া

Controller দুই ধরনের হতে পারে। Cluster-এর ভেতরের proxy (Traefik, HAProxy, Envoy-ভিত্তিক controller ইত্যাদি): সামনে একটি L4 LB থাকে, আর routing করে Pod-এ চলা proxy. Cloud-এর controller: controller নিজে traffic বহন করে না; Ingress পড়ে cloud-এর L7 load balancer সাজিয়ে দেয়। তখন routing হয় cluster-এর বাইরে, cloud-এর পরিষেবায়।

Providerসাধারণ controllerযা বানায়
AWSAWS Load Balancer ControllerIngress থেকে Application Load Balancer
AzureApplication Gateway Ingress ControllerAzure Application Gateway
Google CloudGKE Ingress (ingress-gce)Google Cloud-এর Application Load Balancer

এই তালিকা লেখার সময়ের সাধারণ চিত্র; নাম আর ক্ষমতা বদলায়, আর তিন provider-ই Gateway API-র দিকে নিজস্ব পথ তৈরি করছে, তাই provider-এর documentation দেখে নিন। Cloud controller-এ অনেক সময় প্রতিটি Ingress-এ একটি করে LB হতে পারে, যদি না একসাথে জোড়ার ব্যবস্থা করা হয়। তখন সেকশন ১-এর হিসাব বদলে যায়। সুবিধা হলো WAF আর certificate manager-এর সাথে সরাসরি জোড় (পর্ব ৩০), অসুবিধা হলো provider-নির্ভর annotation.

Migration: ধাপে ধাপে

Ingress থেকে Gateway API-তে যাওয়া একবারে সুইচ টেপার কাজ নয়। দুটি পথ পাশাপাশি চালিয়ে ধীরে ধীরে সরানো হয়। ingress2gateway নামে kubernetes-sigs-এর একটি টুল Ingress থেকে Gateway API object বানানোর খসড়া দিতে পারে, তবে annotation-এর অর্থ কতটা ধরবে, তা নিজে যাচাই করতে হয়। নিচের ধাপগুলো পরিকল্পনার নমুনা, আসল run নয়; শতকরা হিসাবগুলো একটি কাল্পনিক উদাহরণ।

ধাপ ১Ingress ১০০% · Gateway ০%
$ kubectl get ingress -A
# তালিকা: host, path, TLS, annotation
rewrite-target, timeout, rate limit -> কে কীভাবে হবে?
ধাপ ২Ingress ১০০% · Gateway ০%
CRD + নতুন Gateway controller বসানো
GatewayClass, Gateway  -> নতুন আলাদা ঠিকানা
পুরোনো controller অক্ষত
ধাপ ৩Ingress ১০০% · Gateway ০%
$ ingress2gateway ...      # খসড়া
HTTPRoute পর্যালোচনা, annotation হাতে অনুবাদ
$ kubectl apply --dry-run=server -f routes.yaml
ধাপ ৪Ingress ১০০% · Gateway পরীক্ষা
$ curl --resolve shop.example.test:443:<gateway-ip> \
    https://shop.example.test/api/orders
# DNS না বদলে নতুন পথে প্রান্তিক path যাচাই
ধাপ ৫Ingress ৯০% · Gateway ১০%
DNS weighted record  old 90, new 10
TTL আগেই কমানো
error rate, latency, 404/503 তুলনা
ধাপ ৬Ingress ০% · Gateway ১০০%
DNS সম্পূর্ণ নতুন ঠিকানায়
কিছুদিন পুরোনো পথ চালু রেখে নজর
তারপর Ingress আর পুরোনো controller সরানো
  1. তালিকা বানানো। সব Ingress, host, TLS আর বিশেষ করে annotation লিখে ফেলুন। প্রতিটি annotation-এর অর্থ কী, Gateway API-তে তার সমতুল্য আছে কি না, তা ঠিক করুন। Traffic এখনো পুরোটাই Ingress-এ।
  2. পাশাপাশি বসানো। CRD আর নতুন controller বসান, নতুন Gateway-র নিজস্ব ঠিকানা পান। পুরোনো পথে হাত পড়ে না, তাই ব্যবহারকারীর কিছু বদলায় না।
  3. অনুবাদ। টুল দিয়ে খসড়া নিন, HTTPRoute পড়ে দেখুন, annotation-এর আচরণ হাতে মেলান, --dry-run=server করুন। ভুলে হারানো একটি rewrite মানে নীরব ত্রুটি।
  4. নতুন পথে পরীক্ষা। DNS না বদলে curl --resolve দিয়ে নতুন ঠিকানায় অনুরোধ পাঠান। Host, path, certificate আর সেকশন ৩-এর প্রান্তিক path যাচাই করুন। Traffic তখনো ১০০% পুরোনো পথে।
  5. অল্প traffic সরানো। Weighted routing দিয়ে ১০% নতুন পথে পাঠান (পর্ব ২৯)। দুই পথের error rate আর latency তুলনা করুন। সমস্যা হলে ওজন ০ করলেই ফেরা যায়, TTL পেরোনোর পর।
  6. পুরোটা সরানো। ধীরে ১০০% করুন। পুরোনো পথ কিছুদিন রেখে দিন, তারপর Ingress আর পুরোনো controller সরান। আর certificate-এর নবায়ন নতুন পথে চলছে কি না দেখে নিন।

আরও গভীরে: Ingress আর Gateway API-র একটি মূল পার্থক্য extensibility-র ধরনে: annotation হলো অনিয়ন্ত্রিত লেখা, আর Gateway API-তে filter, ReferenceGrant আর policy attachment-এর মতো ধরনযুক্ত ব্যবস্থা। HTTPRoute পাশাপাশি GRPCRoute-এর মতো route-ও আছে, আর GAMMA উদ্যোগে একই route দিয়ে cluster-এর ভেতরের east-west traffic-ও বর্ণনা করা হয় (service mesh). v1.7.0-র standard-install.yaml-এ আমরা ১০টি CRD আর একটি ValidatingAdmissionPolicy দেখেছি, তবে এই পরীক্ষায় শুধু তিনটি CRD বসানো হয়েছিল। Controller কতটা spec মানে, তা Gateway API-র conformance report-এ দেখা যায়।

৮ · Controller-এর 404 বনাম 503: সমস্যা খোঁজা

“সাইট খুলছে না” শুনলে প্রথম কাজ status code আর উত্তরের body পড়া। Controller-এর নিজের উত্তর আর app-এর উত্তর আলাদা দেখতে হয়। এই run-এ 404 page not found আর no available server এসেছে controller থেকে, আর shop-api pod=… এসেছে app থেকে। সব controller-এর লেখা এক নয়। নিচের ফলাফলগুলো এই controller-এ পরীক্ষা করা:

পরীক্ষাফলঅর্থ
অচেনা Host404 page not foundকোনো নিয়ম মেলেনি
ingressClassName-এ বানান ভুল404, Ingress তৈরি হয়েছেController Ingress-টি নেয়নি
Backend Service নেই404, Ingress তৈরি হয়েছেএই controller নিয়মটি বাদ দিয়েছে
Service আছে, ready Pod নেই503 no available serverনিয়ম মিলেছে, পাঠানোর জায়গা নেই
একই অবস্থা, allowEmptyServices=false404Ready Pod না থাকলে নিয়মটিই বাদ

শেষ সারিটি মন দিয়ে দেখুন: একই সমস্যা, শুধু একটি সেটিংয়ে 503 হয়ে গেল 404. তাই “404 মানে নিয়ম নেই” একটি ভালো প্রথম অনুমান, চূড়ান্ত প্রমাণ নয়। লক্ষ করুন, Service-হীন Ingress বা ভুল class-এও kubectl apply সফল হয়। Kubernetes শুধু YAML রেখে দেয়। আর 502 সাধারণত বোঝায় Pod-এ সংযোগ বা উত্তরে সমস্যা, যেমন ভুল port; এটি এই run-এ পরীক্ষা করা হয়নি, তাই আচরণ নিজের controller-এ যাচাই করুন। Pod-এর আসল ব্যর্থতা ধরতে readiness probe কীভাবে কাজ করে, তা পর্ব ৪৩ আর ৪৫-এ দেখেছেন। নিচের গল্পটি আসল run:

  1. Rolling200পুরোনো Pod
  2. Recreate0/2সব নতুন
  3. Controller503লক্ষণ
  4. Ingress()ফাঁকা
  5. Slicefalseফাঁদ
  6. সমাধান200ফিরল
ভুল probe port 8080, RollingUpdateআসল run
NAME                        READY   STATUS    RESTARTS   AGE
shop-web-787f666fc6-2jxfg   1/1     Running   0          12s
shop-web-787f666fc6-tqsv8   1/1     Running   0          16s
shop-web-7dd98c95b-x8p84    0/1     Running   0          9s

$ c shop.example.test /
shop-web pod=shop-web-787f666fc6-2jxfg path=/
[HTTP 200]
strategy: Recreate, একই ভুল probeআসল run
NAME                        READY   STATUS    RESTARTS   AGE
shop-web-7d4b45dd5c-77bl7   0/1     Running   0          7s
shop-web-7d4b45dd5c-nfbdx   0/1     Running   0          7s

NAME       READY   UP-TO-DATE   AVAILABLE   AGE
shop-web   0/2     2            0           2m59s
লক্ষণআসল run
$ c shop.example.test /
no available server
[HTTP 503]
$ c shop.example.test /api/orders
shop-api pod=shop-api-55478bc64d-nc22f path=/api/orders
[HTTP 200]
Ingress আর Serviceআসল run
$ kubectl -n abcl-ki describe ingress shop
…
    /         shop-web:80 ()
    /api      shop-api:80 (10.1.0.83:80,10.1.0.84:80)
$ kubectl -n abcl-ki describe svc shop-web
Selector:     app=shop-web
Endpoints:    (ফাঁকা)
EndpointSliceআসল run
NAME             ADDRESSTYPE   PORTS   ENDPOINTS
shop-web-x87rm   IPv4          80      10.1.0.135,10.1.0.134

$ kubectl … -o jsonpath=…
10.1.0.135 ready=false
10.1.0.134 ready=false

Warning  Unhealthy  Readiness probe failed:
  Get "http://10.1.0.135:8080/healthz": dial tcp …
Probe port 80-তে ফেরানোআসল run
deployment "shop-web" successfully rolled out
10.1.0.151 ready=true
10.1.0.150 ready=true

$ c shop.example.test /
shop-web pod=shop-web-68d7bff989-smm28 path=/
[HTTP 200]
  1. RollingUpdate রক্ষা করল। Readiness probe-এ ভুল port 8080 দেওয়া হলো। নতুন Pod 0/1-এ আটকে রইল, কিন্তু পুরোনো দুটি Pod চলছে, তাই ব্যবহারকারী 200 পাচ্ছেন। পর্ব ৪৪-এর rolling update এখানেই কাজের।
  2. Recreate সব সরাল। এবার strategy: Recreate. পুরোনো Pod আগে মুছে যায়, তারপর একই ভুল probe নিয়ে নতুন দুটি আসে। Pod Running হলেও READY 0/1; Deployment 0/2. Running মানেই ready নয়।
  3. লক্ষণ। /-এ controller বলছে no available server, 503. কিন্তু /api/orders এখনো 200. অর্থাৎ controller ঠিক আছে, নিয়ম মিলেছে, সমস্যা শুধু shop-web-এ।
  4. ওপর থেকে নিচে। describe ingress-এ shop-web-এর পাশে ফাঁকা (), আর Service-এর Endpoints ফাঁকা। Selector ঠিক আছে, app=shop-web. তাহলে Pod আছে, কিন্তু ready নয়।
  5. ফাঁদটি দেখুন। get endpointslices-এর ENDPOINTS কলামে দুটি IP দেখাচ্ছে, যেন সব ঠিক! আসল কথা আছে ভেতরের ready=false-এ। আর event বলছে probe 8080-তে সংযোগ পাচ্ছে না। মূল কারণ পাওয়া গেল।
  6. সমাধান। Probe port 80-তে ফেরাতেই rollout শেষ হলো, ready=true, আর 200 ফিরল। Controller বা Ingress-এ কিছুই বদলাতে হয়নি, যদিও লক্ষণটি দেখা গিয়েছিল controller-এ।

প্রতিবারের জন্য একটি মই:

  1. উত্তর পড়ুন: কোড কত, body কার, কোন host আর path.
  2. Controller: সে চলছে কি, kubectl get ingress-এ class ঠিক কি, log কী বলে।
  3. নিয়ম: curl -H "Host: …" দিয়ে DNS বাদ দিয়ে সরাসরি controller-এ পরীক্ষা, describe ingress.
  4. Service: Selector মেলে কি, EndpointSlice-এ ready কি।
  5. Pod: kubectl port-forward দিয়ে controller এড়িয়ে Service বা Pod-এ সরাসরি অনুরোধ, event, log.

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

  • Ingress আর ingress controller-এর পার্থক্য? প্রথমটি নিয়মের object, দ্বিতীয়টি সেই নিয়ম মেনে traffic চালানো প্রোগ্রাম।
  • Exact /foo কি /foo/-এ মেলে? না। Prefix /foo মেলে, তবে /foobar-এ মেলে না।
  • Ingress কেন, LoadBalancer Service নয়? একটি ঠিকানায় অনেক HTTP Service, কম খরচ, TLS এক জায়গায়। HTTP ছাড়া traffic-এ LoadBalancer Service লাগতে পারে।
  • Gateway API কী উন্নতি আনে? Role ভিত্তিক দায়িত্ব ভাগ, ওজন ও header matching API-র অংশ, annotation-এর ওপর কম নির্ভরতা।
  • Controller থেকে 503 এলে প্রথমে কী দেখবেন? সেই Service-এর ready Pod আর EndpointSlice-এর ready অবস্থা।

৯ · AI-এর যুগে Ingress লেখা

AI অনেক সময় পুরোনো টিউটোরিয়াল থেকে শেখা YAML দেয়। Ingress-এ এটি বিশেষভাবে বিপজ্জনক, কারণ ভুল annotation-ও চুপচাপ apply হয়। এই পর্ব লেখার সময়েই একটি ভুল ধরা পড়েছিল: আমরা chart-এ service.type: ClusterIP দিয়েছিলাম, কিন্তু render করা YAML-এ দেখা গেল type: LoadBalancer. এই chart-এ আসল ঘর service.spec.type. ভুল ঘর কোনো ত্রুটি দেখায়নি। helm template দিয়ে আগে দেখে না নিলে অজান্তেই একটি LoadBalancer চেয়ে বসতাম।

AI-এর সাধারণ ভুলফলযাচাই
extensions/v1beta1 Ingress১.২২ থেকে API নেইkubectl api-resources
pathType বাদ, বা পুরোনো kubernetes.io/ingress.class annotationValidation ব্যর্থ, বা controller হয়তো নেবে না--dry-run=server
অবসরপ্রাপ্ত ingress-nginx বসানোর পরামর্শনিরাপত্তা patch নেইপ্রকল্পের অবস্থা দেখা
অন্য controller-এর annotationনীরবে উপেক্ষাcurl দিয়ে আচরণ পরীক্ষা
Chart-এর ভুল value বা বানানো flagনীরবে default, হয়তো নতুন LoadBalancer ও বিলhelm template, chart-এর values
সব Service-এ type: LoadBalancerখরচ বাড়ে, বাইরের দরজা বাড়েখরচের হিসাব, প্রয়োজন যাচাই
-k দিয়ে TLS যাচাই বন্ধ, key YAML-এ বসানোত্রুটি লুকায়, গোপন key ফাঁস--cacert, Secret, cert-manager
অ্যাডমিন path উন্মুক্ত করাঅনিচ্ছায় বাইরে প্রকাশপ্রতিটি নিয়ম পড়া, আলাদা সুরক্ষা

AI-কে বলার সময় cluster version আর controller-এর নাম দিন, আর যাচাইয়ের ধাপ চান:

আমার Kubernetes version ১.৩৪, ingress controller: [নাম ও version]. shop.example.test-এর জন্য একটি Ingress বানাও: /api যাবে shop-api-তে, বাকি সব shop-web-এ, TLS Secret shop-tls. শর্ত: ১. শুধু networking.k8s.io/v1 ব্যবহার করবে, আর pathType লিখবে। ২. কোনো annotation দিলে বলবে সেটি কোন controller-এর, আর documentation-এর কোন অংশে আছে। ৩. /apix, /api/, /API আর অচেনা host-এ কী হওয়া উচিত, তার একটি curl পরীক্ষার তালিকা দাও। ৪. প্রথমে kubectl apply --dry-run=server, তারপর কী দেখব, সেটি বলো। ৫. কোনো secret, key বা delete কমান্ড দেবে না, দরকার হলে আগে সতর্ক করবে।

AI-এর লেখা Ingress বা Gateway YAML হাতে পেলে

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

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

প্র ০১Ingress API-কে আরও বড় না করে কেন হিমায়িত করা হলো, আর একেবারে নতুন API বানানো হলো?

Ingress নকশা করা হয়েছিল সবচেয়ে সাধারণ HTTP ব্যবহারের জন্য: host, path, TLS. এই সরলতাই তার জনপ্রিয়তার কারণ। কিন্তু বাস্তবে সবাই আরও চাইল: rewrite, timeout, header match, canary, TCP. Kubernetes প্রকল্প যদি প্রতিটি ক্ষমতা API-তে যোগ করত, তাহলে সেটি সব controller-কে মানতে হতো, আর controller-গুলোর ক্ষমতা ছিল খুব আলাদা। তাই শূন্যস্থান ভরল annotation: প্রতিটি controller-এর নিজের ভাষা।

এর ফল তিনটি। প্রথমত, YAML আর বহনযোগ্য রইল না; এক controller থেকে অন্যটিতে গেলে নীরবে আচরণ বদলায়, যা এই পর্বের rewrite পরীক্ষায় দেখেছেন। দ্বিতীয়ত, annotation লেখা স্ট্রিং, API server তা যাচাই করে না। তৃতীয়ত, এক object-এ সব কিছু মিশে থাকায় দায়িত্ব ভাগ করা কঠিন: একই ফাইলে TLS আর path. কিছু controller-এ annotation দিয়ে সরাসরি proxy config ঢোকানোর ক্ষমতা ছিল, যা নিরাপত্তার ঝুঁকিও তৈরি করে।

একটি GA API-কে ভেতর থেকে বদলানো কঠিন, কারণ পুরোনো ব্যবহারকারীদের ভাঙা যায় না। Gateway API তাই নতুন করে শুরু হলো, CRD হিসেবে, যাতে Kubernetes-এর release-এর অপেক্ষা ছাড়াই আলাদা গতিতে এগোতে পারে। এর আলাদা channel আছে, আর conformance পরীক্ষা দিয়ে controller-গুলোর আচরণ মেলানোর চেষ্টা হয়।

হিমায়িত হওয়া মানে মৃত্যু নয়। Documentation অনুযায়ী Ingress GA, সরানোর পরিকল্পনা নেই, শুধু নতুন ক্ষমতা আসবে না। ছোট, সাধারণ ব্যবহারে এটি এখনো চলে। তবে নতুন নকশা শুরু করলে Gateway API আগে বিবেচনা করা যুক্তিসংগত।

মূল উপলব্ধি: একটি API-র সবচেয়ে কঠিন সিদ্ধান্ত হলো কী বাদ রাখা। বাদ দেওয়া ঘর কেউ না কেউ ভরবেই, আর সেই ভরাট অংশই পরে সবচেয়ে কঠিন ঋণ হয়ে দাঁড়ায়।

প্র ০২প্রায় অর্ধেক পরিবেশে ব্যবহৃত বলে উল্লিখিত একটি প্রকল্প এক-দুজনের ওপর চলল কী করে? আপনার দল এখান থেকে কী শিখবে?

Open source-এ “ব্যবহার” আর “রক্ষণাবেক্ষণ” দুটি আলাদা সংখ্যা। একটি প্রকল্প বিনা খরচে পাওয়া যায়, নিয়মিত কাজ করে, তাই ব্যবহারকারীর সংখ্যা বাড়তেই থাকে। কিন্তু প্রতিটি নতুন ব্যবহারকারী রক্ষকদের কাজও বাড়ায়, আর সাহায্যের হাত সে হারে বাড়ে না। Kubernetes-এর নভেম্বর ২০২৫-এর ঘোষণা অনুযায়ী উন্নয়ন নির্ভর করছিল এক-দুজনের নিজের সময়ের ওপর। জানুয়ারি ২০২৬-এর বিবৃতিতে Datadog-এর গবেষণার বরাতে প্রায় ৫০% cloud native পরিবেশের কথা বলা হয়েছে; সংখ্যাটি একটি প্রতিষ্ঠানের অভ্যন্তরীণ গবেষণা, পুরো পৃথিবীর জরিপ নয়।

একটি edge proxy-র ঝুঁকি সাধারণ library-র চেয়ে বেশি, কারণ সে ইন্টারনেটের সামনে দাঁড়িয়ে থাকে, TLS key আর cluster-এর বহু Secret দেখতে পারে। তাই এখানে নিরাপত্তা patch বন্ধ হওয়ার ফল গুরুতর। ২০২৪ সালের xz ঘটনাতেও দেখা গেছে, একজন ক্লান্ত রক্ষকের ওপর নির্ভরতা নিজেই নিরাপত্তার সমস্যা হতে পারে।

আপনার দলের জন্য কয়েকটি বাস্তব শিক্ষা। এক, dependency-র তালিকায় শুধু version নয়, “এটির রক্ষক কারা, শেষ release কবে” এটুকুও রাখুন। দুই, edge-এর মতো গুরুত্বপূর্ণ অংশে annotation আর controller-নির্ভর আচরণ যত কম হবে, পালানোর পথ তত সহজ। তিন, migration-এর পরিকল্পনা অবসরের ঘোষণার পরে নয়, আগে থেকেই ভাবুন। চার, ব্যবহৃত open source-এ সময় বা অর্থ দিয়ে অবদান রাখাও একটি ঝুঁকি কমানোর উপায়, দান নয়।

মূল উপলব্ধি: “বিনা মূল্যে” মানে দাম নেই, এমন নয়। দাম অন্য কেউ দিচ্ছিলেন, আর একদিন সেটি আপনার migration-এর সময় হিসেবে ফিরে আসে।

প্র ০৩Cluster-এর ভেতরের proxy, না cloud-এর L7 load balancer: কোনটি বেছে নেবেন?

দুটিই একই কাজ করে, পার্থক্য হলো routing-এর কাজটি কোথায় হচ্ছে, আর আপনি কার ওপর নির্ভর করছেন।

  • Cluster-এর ভেতরের proxy: সামনে একটি সস্তা L4 LB, পেছনে আপনার proxy Pod. একই ধরনের YAML বিভিন্ন cloud-এ বা নিজের data center-এ চলতে পারে। নিয়ম বদলালে সাধারণত দ্রুত কার্যকর হয়। কিন্তু proxy-র scaling, upgrade, নিরাপত্তা patch আর CPU খরচ আপনার দায়িত্ব। আবার একটি বাড়তি hop যোগ হয়।
  • Cloud-এর L7 load balancer: চালানোর ঝামেলা provider-এর, WAF আর certificate সেবার সাথে সহজে জোড়ে, আর সে নিজে থেকে zone জুড়ে থাকে। কিন্তু config বদলাতে সময় লাগতে পারে, প্রতিটি LB-র নিজস্ব খরচ আছে, annotation আর ক্ষমতা provider-নির্ভর, আর স্থানীয়ভাবে পরীক্ষা করা কঠিন।

সিদ্ধান্তের জন্য কয়েকটি প্রশ্ন করুন। কয়টি Service, আর কয়টি দল? যদি এক cloud-এ থাকবেন, দলে Kubernetes-এর গভীর অভিজ্ঞতা কম, আর WAF দরকার, তাহলে cloud controller সহজ। যদি একাধিক cloud বা on-premises-এ একই নিয়ম লাগে, অথবা উন্নত routing দরকার, তাহলে ভেতরের proxy উপযুক্ত। দুটি মেশানোও সাধারণ: সামনে CDN বা cloud WAF, তারপর L7 LB, ভেতরে Gateway. শুধু মনে রাখবেন প্রতিটি স্তরে নতুন timeout, নতুন header আর নতুন খরচ যোগ হয়।

Gateway API এই দ্বন্দ্ব কিছুটা কমাতে পারে, কারণ একই ধরনের HTTPRoute বিভিন্ন implementation-এ চলতে পারে। তবে “চলে” মানে সব আচরণ এক নয়; সেকশন ৩-এর পরীক্ষাই তার প্রমাণ।

মূল উপলব্ধি: এখানে প্রশ্নটি প্রযুক্তির নয়, দায়িত্বের: আপনি একটি proxy চালাতে চান, না একটি provider-এর সীমার ভেতরে থাকতে চান। দুটিরই দাম আছে, শুধু দাম দেওয়ার মুদ্রা আলাদা।

প্র ০৪TLS কোথায় শেষ হওয়া উচিত: load balancer-এ, controller-এ, না Pod-এ?

পথে যত আগে TLS শেষ হবে, তত সহজ চালানো; যত পরে, তত কম জায়গায় ডেটা খোলা অবস্থায় থাকে। তিনটি প্রধান নকশা:

  • Cloud LB-তে শেষ: Certificate provider-এর সেবায়, নবায়ন স্বয়ংক্রিয়, WAF অনুরোধ পড়তে পারে। পরের অংশ private network-এ, তবে সেখানে ডেটা খোলা, যদি না আবার এনক্রিপ্ট করা হয়।
  • Controller-এ শেষ: এই পর্বের নকশা। Cloud LB শুধু TCP চালান করে, private key থাকে cluster-এর Secret-এ, আর cert-manager নবায়ন সামলায়। Key-র দায়িত্ব আপনার, তাই Secret-এর RBAC গুরুত্বপূর্ণ।
  • Pod-এ শেষ (passthrough): Controller শুধু SNI দেখে সংযোগ চালান করে। পথের মাঝে কেউ অনুরোধ পড়তে পারে না। কিন্তু path দেখে routing, WAF আর সহজ log সম্ভব নয়, আর প্রতিটি app-কে certificate সামলাতে হয়।

আসল প্রশ্ন: আপনার threat model কী? কোনো নিয়ন্ত্রক শর্ত বা দলের নীতি যদি বলে “in-transit সব জায়গায় এনক্রিপ্ট”, তাহলে edge-এ termination-এর পর আবার TLS (re-encryption) বা service mesh-এর mTLS দরকার হতে পারে। Cloud-এর ভেতরের network-কেও পুরোপুরি বিশ্বাসযোগ্য ধরে নেওয়া এখন অনেকের কাছে যথেষ্ট নয়। আবার ছোট একটি app-এ সবখানে TLS মানে certificate নবায়নের অনেক জায়গায় ব্যর্থতার সুযোগ। বাস্তবে কিছু certificate-এর মেয়াদ ফুরিয়ে যাওয়াই বড় outage-এর পরিচিত কারণ, তাই স্বয়ংক্রিয় নবায়ন আর মেয়াদের alert রাখা মূল শর্ত।

আরও একটি সূক্ষ্ম কথা: TLS শেষ হওয়ার পর app জানে না আসল client-এর ঠিকানা বা protocol, যদি না X-Forwarded-For আর X-Forwarded-Proto-এর মতো header ঠিকভাবে পাঠানো হয়, আর app শুধু বিশ্বাসযোগ্য proxy থেকে সেগুলো নেয়।

মূল উপলব্ধি: TLS কোথায় শেষ হবে, সেটি একটি নিরাপত্তা-সীমার সিদ্ধান্ত। প্রথমে ঠিক করুন কাকে বিশ্বাস করছেন, তারপর certificate কোথায় রাখবেন।

প্র ০৫weight: 10 লিখলে কি নতুন version সত্যিই ১০% ব্যবহারকারী পায়? Canary-তে কী ভুল বোঝাবুঝি হতে পারে?

weight বলে অনুরোধের অনুপাত, মানুষের নয়। একই ব্যবহারকারীর একটি পাতা লোড হতে পারে দশটি অনুরোধে। ওজন অনুরোধ ধরে ভাগ হলে তিনি কখনো পুরোনো, কখনো নতুন version পাবেন। পুরোনো পাতা নতুন API ডাকলে তখনই সমস্যা হতে পারে। ব্যবহারকারী ধরে ভাগ করতে চাইলে session affinity বা header দেখে নির্দিষ্ট দলকে পাঠানো ভালো, যেমন HTTPRoute-এ header match দিয়ে শুধু ভেতরের কর্মীদের নতুন version দেখানো। তবে session affinity-র সমর্থন implementation-ভেদে আলাদা।

দ্বিতীয়ত, সংখ্যার ভুল। আমাদের সিমুলেটরে ১০০ অনুরোধে v2 পেয়েছে ৯টি, আর প্রত্যাশিত ছড়ানো ±৩%. যদি canary-তে স্বাভাবিক ত্রুটির হার ০.১% হয়, তাহলে ১০০টি অনুরোধে একটি ত্রুটিও না দেখা স্বাভাবিক, তাই এটুকু দেখে “সব ঠিক” বলা যায় না। যত দুর্লভ ত্রুটি ধরতে চান, তত বেশি অনুরোধ দরকার। তাই canary-র ধাপের সময় ঠিক হয় traffic দেখে, ঘড়ি দেখে নয়। রাতে কম traffic-এ ১৫ মিনিট যথেষ্ট না-ও হতে পারে।

তৃতীয়ত, HTTP/2 বা keep-alive-এ L4 স্তরের সংযোগ ধরে ভাগ হলে অনুরোধের অনুপাত আলাদা হতে পারে; HTTPRoute-এর L7 proxy অনুরোধ ধরে ভাগ করতে পারে, তবে implementation দেখে নিন। চতুর্থত, ওজন দিয়ে backend-এর ক্ষমতা নিশ্চিত হয় না: v2-এর একটি Pod যদি ১০% traffic নিতে না পারে, তাহলে ত্রুটি আসবে version-এর দোষে নয়, ক্ষমতার অভাবে।

স্বয়ংক্রিয় canary টুল ওজন বদলানো, metric দেখা আর ফিরিয়ে আনার কাজ একসাথে করে। কিন্তু metric-এর সংজ্ঞা ঠিক না হলে টুলও ভুল সিদ্ধান্ত নেবে।

মূল উপলব্ধি: ওজন হলো একটি যন্ত্রের সেটিং, সিদ্ধান্ত নয়। সিদ্ধান্ত আসে যথেষ্ট নমুনা, ঠিক metric আর ফেরার পরিষ্কার পথ থেকে।

অনুশীলন

সবগুলো খাতা-কলমে করা যায়, cluster লাগে না। নিজে cloud-এ পরীক্ষা করলে খরচ হতে পারে; কাজ শেষে load balancer আর cluster মুছে ফেলুন।

  1. খরচের হিসাব: ধরে নিন একটি load balancer-এর দাম ঘণ্টায় $০.০৩, ৩০ দিনের মাস, আর controller-এর দুটি Pod মিলিয়ে মাসে $২০. ডেটার খরচ দুই নকশায় সমান বলে বাদ দিন। ১২টি Service-এ প্রতিটিতে আলাদা LB আর একটি Ingress-এর মাসিক পার্থক্য কত? কত Service থেকে Ingress সস্তা?

    এক LB = ০.০৩ × ৭২০ = $২১.৬০। ১২টি LB = ১২ × ২১.৬০ = $২৫৯.২০। একটি Ingress = ২১.৬০ + ২০ = $৪১.৬০। পার্থক্য = ২৫৯.২০ − ৪১.৬০ = $২১৭.৬০ মাসে।

    Break-even: N × ২১.৬০ = ৪১.৬০, তাই N = ১.৯৩. অর্থাৎ ২টি Service থেকেই এই ধরে নেওয়া দামে Ingress সস্তা। আসল দাম provider-ভেদে আলাদা, আর cloud controller প্রতিটি Ingress-এ আলাদা LB বানালে হিসাবটি বদলে যাবে।

  2. ফল অনুমান করুন: একটি host-এ তিনটি নিয়ম: / (Prefix) যায় web-এ, /shop (Prefix) যায় shop-এ, /shop/cart (Exact) যায় cart-এ। Kubernetes documentation-এর নিয়মে এই ছয়টি অনুরোধ কোথায় যাবে? /shop, /shop/, /shopping, /shop/cart, /shop/cart/, /SHOP.
    • /shop → shop: Prefix মিলেছে, /-এর চেয়ে লম্বা।
    • /shop/ → shop: Prefix শেষের / মেনে নেয়।
    • /shopping → web: অংশ shopping, shop নয়।
    • /shop/cart → cart: Exact-ই সবচেয়ে লম্বা মিল।
    • /shop/cart/ → shop: শেষের /-এর জন্য Exact ব্যর্থ, Prefix /shop মেলে।
    • /SHOP → web: মেলানো case-sensitive.

    সিমুলেটরের rules বদলে মিলিয়ে নিন। আর মনে রাখবেন, আসল controller-এর default ভিন্ন হতে পারে, তাই curl-ও দরকার।

  3. Debugging: একটি Ingress app.example.test-কে Service app-এ পাঠায়। curl -H "Host: app.example.test" $LB/ দেয় 503, আর একই controller-এর অন্য host কাজ করছে। কোন কোন কমান্ড কোন ক্রমে চালাবেন, আর প্রতিটি থেকে কী শিখবেন? (Service-এর selector app=ap হওয়ার ক্ষেত্রও ভাবুন।)

    অন্য host চলছে, তাই controller সচল। ক্রম:

    1. kubectl describe ingress …: backend-এর পাশে ফাঁকা () দেখছেন কি না।
    2. kubectl get svc app -o wide বা describe svc app: selector আর Endpoints.
    3. kubectl get pods --show-labels: label-এর সাথে selector মেলে কি না। app=ap হলে কোনো Pod মিলবে না, তাই EndpointSlice-এ কিছুই থাকবে না।
    4. Selector ঠিক থাকলে get endpointslices … -o yaml-এ ready, আর kubectl get events-এ probe-এর ত্রুটি।
    5. মেরামতের পর আবার curl.

    সতর্কতা: ENDPOINTS কলামের IP দেখে সন্তুষ্ট হবেন না, ওই কলামে not-ready ঠিকানাও আসতে পারে। ভুল selector-এর ক্ষেত্রটি আমরা run করে দেখিনি; এটি নিয়ম থেকে অনুমান, আর controller ভেদে 503-এর বদলে 404-ও হতে পারে।

  4. সমালোচনা ও নকশা: AI একটি অ্যাপের জন্য ৮টি type: LoadBalancer Service দিয়েছে, ingress-nginx বসাতে বলেছে, আর Ingress-এ nginx.ingress.kubernetes.io/rewrite-target আর extensions/v1beta1 ব্যবহার করেছে। এই নকশার সমস্যা লিখুন, আর উন্নত একটি নকশা প্রস্তাব করুন।
    • খরচ ও আক্রমণের ক্ষেত্র: ৮টি বাইরের দরজা, প্রতিটির নিজস্ব খরচ আর firewall. শুধু controller বা Gateway-কে বাইরে রেখে বাকিগুলো ClusterIP করা উচিত।
    • অবসরপ্রাপ্ত controller: ingress-nginx-এ আর নিরাপত্তা patch আসার কথা নয়। রক্ষণাবেক্ষণে আছে এমন controller বা cloud controller বাছুন, নতুন কাজে Gateway API আগে ভাবুন।
    • মুছে ফেলা API: extensions/v1beta1 ১.২২ থেকে নেই, apply-ই ব্যর্থ হবে। networking.k8s.io/v1 বা HTTPRoute লাগবে।
    • Annotation: Rewrite-এর প্রয়োজন আসলেই আছে কি না দেখুন। Gateway API-তে filter দিয়ে URL rewrite লেখা যায় (implementation সমর্থন দেখে নিন)।

    নকশা: একটি Gateway (দুটি বা বেশি controller replica), wildcard বা নির্দিষ্ট certificate, cert-manager, প্রতিটি দলের নিজের namespace-এ HTTPRoute, আর allowedRoutes দিয়ে কে যুক্ত হতে পারবে, তা নিয়ন্ত্রণ। প্রকাশের আগে dry-run, প্রান্তিক path-এর curl, আর খরচের হিসাব।

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

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

ক্রমIngress controller TLS শেষ করে, এমন cluster-এ একটি HTTPS অনুরোধের পথ সাজান (▲▼ চেপে সরান)

  1. DNS থেকে cloud load balancer-এর ঠিকানা পাওয়া
  2. Cloud load balancer সংযোগ controller-এর দিকে পাঠায়
  3. Controller SNI দেখে certificate দেখায়, TLS শেষ করে
  4. Controller Host আর path মিলিয়ে Service বাছে
  5. Ready Pod-এর তালিকা থেকে একটি Pod-এ অনুরোধ পৌঁছায়

একটি নতুন cluster-এ বৈধ Ingress object apply করা হলো, কিন্তু কোনো ingress controller নেই। কী হবে?

  • API server নিজেই traffic সরবরাহ শুরু করবে
  • Object তৈরি হবে, কিন্তু traffic-এর কিছুই বদলাবে না
  • Apply ত্রুটি দিয়ে ব্যর্থ হবে
  • Service-গুলো LoadBalancer ধরনে বদলে যাবে

Kubernetes documentation অনুযায়ী path: /foo, pathType: Prefix নিচের কোনটিতে মেলে না?

  • /foo/
  • /foo/bar
  • /foobar
  • /foo

একটি Ingress-এ দুটি নিয়ম: / (Prefix) আর /status (Exact), দুটি ভিন্ন Service-এ। Documentation-এর নিয়মে /status/ কোথায় যাবে?

  • /status-এর Service-এ
  • defaultBackend-এ
  • সবসময় 404
  • /-এর Service-এ

Gateway API-র কোন object-এ সাধারণত app developer host, path আর backend Service-এর ওজন লেখেন?

  • HTTPRoute
  • Gateway
  • GatewayClass
  • IngressClass

HTTPRoute-এর একটি rule-এ তিনটি backend-এর weight 6, 3 আর 1. দ্বিতীয় backend প্রত্যাশিতভাবে কত শতাংশ অনুরোধ পাবে?

  • ৩%
  • ৩০%
  • প্রায় ৩৩%
  • ৬০%

একটি controller /-এ 503 দিচ্ছে, অথচ একই host-এর /api কাজ করছে। Deployment দেখাচ্ছে 0/2, আর EndpointSlice-এর ENDPOINTS কলামে দুটি IP আছে। সবচেয়ে সম্ভাব্য অবস্থা কোনটি?

  • Ingress controller পুরোপুরি বন্ধ
  • অনুরোধের Host ভুল
  • Pod চলছে কিন্তু ready নয়, যেমন readiness probe ব্যর্থ
  • TLS certificate-এর মেয়াদ শেষ

শব্দকোষ

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

মনে রাখুন

  1. Ingress শুধু নিয়ম; traffic চালায় controller. Controller না থাকলে কিছুই ঘটে না।
  2. Prefix মেলে অংশ ধরে, Exact হুবহু। লম্বা path আগে। তবে নিজের controller-এ curl দিয়ে যাচাই করুন।
  3. TLS সাধারণত controller-এ শেষ হয়, certificate থাকে Secret-এ, নবায়ন সামলায় cert-manager.
  4. ingress-nginx মার্চ ২০২৬-এ অবসরে গেছে। Ingress API হিমায়িত, আর নতুন কাজে Gateway API (GatewayClass, Gateway, HTTPRoute) আগে ভাবুন।
  5. Debugging: সাধারণত 404 মানে নিয়ম মেলেনি, 503 মানে ready Pod নেই। আর EndpointSlice-এ ready দেখুন, শুধু IP নয়।

পরের পর্বে: Traffic ভেতরে এলো। এবার Pod-এর config আর ডেটা: ConfigMap, Secret, PV, PVC আর StatefulSet.

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

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