نسخه آنلاین در حال بارگذاری زمان... تهران: ۲۶°C
۲۸ کاربر آنلاین

PNo.30Light

نشریه تخصصی هوش مصنوعی، سیستم‌های سرور و مهندسی داده

تازه ترین‌ها
زیرساخت و سرور
زمان مطالعه: ۳۲ دقیقه ۰ بازدید

Service در کوبرنتیز: Deep Dive انواع ClusterIP، NodePort، LoadBalancer و ExternalName

نویسنده: تحریریه فنی P30Light
Service در کوبرنتیز: Deep Dive انواع ClusterIP، NodePort، LoadBalancer و ExternalName
✦ خلاصه نکات کلیدی مقاله
  • Service = نقطهٔ دسترسی پایدار شبکه به مجموعه‌ای از endpointها (معمولاً Podها) با سیاست routing.
  • چهار type اصلی: ClusterIP، NodePort، LoadBalancer، ExternalName — به‌علاوه headless با clusterIP: None.
  • selector روی labels پادهاست؛ readiness تعیین می‌کند ترافیک به کدام endpoint برسد.

Podها ephemeralاند: ساخته می‌شوند، می‌میرند، IP عوض می‌شود، تعداد replica بالا و پایین می‌رود. اگر frontend بخواهد مستقیم به IP لحظه‌ای backend وصل شود، سرویس‌کشف شکننده می‌شود.

طبق مستندات رسمی Service:

Service روشی است برای expose کردن یک اپ شبکه که به‌صورت یک یا چند Pod در cluster اجرا می‌شود — پشت یک endpoint روبه‌بیرون پایدار، حتی وقتی workload روی چند backend پخش است.

این مقاله deep dive روی Service و typeها است. بخش labels/selector به metadata وصل می‌شود؛ خود ObjectMeta را دوباره تکرار نمی‌کنیم.


مشکل و راه‌حل

بدون Serviceبا Service
IP پاد عوض می‌شودVIP یا DNS پایدار
باید لیست پادها را خودتان track کنیدselector + EndpointSlice خودکار
load balancing دستیkube-proxy / dataplane
کشف سرویس سفارشیDNS داخلی name.ns.svc.cluster.local

Service یک انتزاع شبکه است: مجموعه منطقی endpointها + سیاست دسترسی.


آناتومی یک Service

apiVersion: v1
kind: Service
metadata:
  name: web                    # باید DNS label معتبر (RFC 1123) باشد
  namespace: production
  labels:
    app.kubernetes.io/name: web
spec:
  type: ClusterIP              # پیش‌فرض اگر ننویسید
  selector:
    app: web                   # labels روی Pod — نه لزوماً روی Deployment
  ports:
    - name: http
      protocol: TCP
      port: 80                 # پورتی که کلاینت به Service می‌زند
      targetPort: http         # پورت/نام پورت روی Pod
  sessionAffinity: None

metadata مهم برای Service

فیلدنقش در Service
nameپایهٔ DNS داخلی سرویس
namespaceمحدودهٔ کشف (web.production.svc...)
labelsسازمان‌دهی خود شیء Service (برای ابزارها/سیاست)
annotationsاغلب برای cloud LB، Ingress controller، mesh

جزئیات عمومی ObjectMeta: مقاله metadata.

قلب اتصال: spec.selector و labels پاد

Service.spec.selector  →  match روی Pod.metadata.labels
                       →  فقط Podهای Ready معمولاً در Endpoints می‌آیند

تله کلاسیک: label روی metadata دیپلویمنت با selector سرویس یکی نیست. Service به Pod template labels می‌چسبد. جزئیات labels در مقاله metadata؛ الگوی selector در spec.

kubectl get endpoints web -n production
kubectl get endpointslice -l kubernetes.io/service-name=web -n production

پورت‌ها: port · targetPort · nodePort

فیلدمعنی
portپورت مجازی Service (آنچه کلاینت می‌بیند)
targetPortپورت روی Pod (عدد یا نام containerPort)
nodePortفقط برای NodePort/LoadBalancer — پورت روی هر Node
protocolپیش‌فرض TCP؛ UDP / SCTP هم ممکن
nameوقتی چند پورت دارید اجباری‌تر؛ برای ارجاع
appProtocolراهنما برای پیاده‌سازی‌ها (مثلاً http)
ports:
  - name: http
    port: 80
    targetPort: http-web-svc   # نام پورت در Pod
    protocol: TCP

مزیت targetPort نام‌دار: می‌توانید شماره پورت داخل container را عوض کنید بدون شکستن کلاینت‌هایی که به port: 80 سرویس می‌زنند.

یک Service می‌تواند چند پورت داشته باشد (مثلاً http + metrics).


جدول یک‌نگاه: انواع Service

TypeدسترسیVIP؟کاربرد典型
ClusterIPفقط داخل clusterبله (مگر headless)سرویس‌به‌سرویس — پیش‌فرض
NodePortاز بیرون via IP نود + پورت ثابتبله + NodePortlab، bare metal بدون LB ابری
LoadBalancerIP/hostname بیرونی LBبله + معمولاً NodePortcloud production برای TCP/UDP
ExternalNameDNS CNAME به خارجخیر — فقط DNSوابستگی خارجی (DB/API مدیریت‌شده)
Headless (clusterIP: None)DNS مستقیم به Pod IPهاخیرStatefulSet، کشف per-pod

Ingress و Gateway type سرویس نیستند؛ معمولاً جلوی ClusterIP می‌نشینند برای HTTP(S).


۱. ClusterIP (پیش‌فرض)

spec:
  type: ClusterIP
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080
  • یک Cluster IP داخلی تخصیص می‌یابد
  • فقط از داخل cluster (و شبکه هم‌تراز) reachable است
  • برای اینترنت عمومی: جلوی آن Ingress / Gateway / mesh بگذارید

کشف DNS (CoreDNS):

web.production.svc.cluster.local
# کوتاه داخل همان ns: web

این نوع برای اکثر ترافیک east-west کافی است.

تخصیص IP

spec:
  clusterIP: 10.96.100.10   # اختیاری؛ معمولاً خودکار
  # یا clusterIP: None برای headless

بعد از create، clusterIP معمولاً immutable است.


۲. NodePort

روشن‌کردن Service روی هر Node روی یک پورت استاتیک (بازه پیش‌فرض رایج: ۳۰۰۰۰–۳۲۷۶۷).

spec:
  type: NodePort
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 30080        # اختیاری؛ وگرنه خودکار

لایه‌ها:

Client → <NodeIP>:30080 → (kube-proxy) → ClusterIP → Pod
  • خودبه‌خود یک ClusterIP هم دارد
  • مناسب تست، on-prem بدون CCM، یا جلوی LB سخت‌افزاری خودتان
  • هر Node همان nodePort را advertise می‌کند؛ ترافیک ممکن است به نودی برود که Pod ندارد (سپس hop داخلی) مگر externalTrafficPolicy: Local

محدودیت‌ها: تعداد پورت محدود، امنیتی بودن باز کردن روی همه نودها، نیاز به دانستن IP نود.


۳. LoadBalancer

برای expose بیرونی با load balancer خارجی (معمولاً cloud provider یا MetalLB / K3s ServiceLB).

spec:
  type: LoadBalancer
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080
  # loadBalancerIP: ...     # وابسته به پیاده‌سازی؛ در dual-stack ضعیف است
  allocateLoadBalancerNodePorts: true   # پیش‌فرض؛ بعضی LB مستقیم به Pod می‌روند

سلسله‌مراتب مفهومی:

LoadBalancer ⊃ NodePort ⊃ ClusterIP

یعنی معمولاً هم VIP ابری دارید، هم NodePort، هم ClusterIP.

kubectl get svc web -w
# EXTERNAL-IP از <pending> به IP واقعی

بدون cloud controller / LB پیاده‌سازی‌شده، EXTERNAL-IP تا ابد pending می‌ماند.

فیلدهای مرتبط cloud (بسته به provider):

  • annotations مخصوص AWS/GCP/Azure
  • loadBalancerSourceRanges
  • externalTrafficPolicy برای حفظ client IP و health check

allocateLoadBalancerNodePorts: false فقط وقتی LB مستقیماً به Podها route می‌کند معنا دارد؛ روی سرویس موجود، nodePortهای قبلی خودکار آزاد نمی‌شوند مگر از ports حذفشان کنید.


۴. ExternalName

بدون proxy به Pod — فقط DNS alias (CNAME) داخل cluster:

apiVersion: v1
kind: Service
metadata:
  name: external-db
spec:
  type: ExternalName
  externalName: db.prod.example.com
  # selector نگذارید
external-db.default.svc.cluster.local  →  CNAME → db.prod.example.com

کاربرد: یک نام ثابت داخل cluster برای وابستگی خارج (RDS، API شریک، …) تا بین envها فقط externalName عوض شود.

  • هیچ ClusterIP / kube-proxy برای این type نیست
  • فیلدهایی مثل selector برای ساخت ExternalName معنا ندارند / پاک می‌شوند

۵. Headless Service (clusterIP: None)

گاهی VIP و proxy نمی‌خواهید — کلاینت باید مستقیم به Pod وصل شود (مثلاً quorum دیتابیس، Sticky identity).

apiVersion: v1
kind: Service
metadata:
  name: mydb
spec:
  clusterIP: None
  selector:
    app: mydb
  ports:
    - port: 5432
      targetPort: 5432

رفتار:

  • ClusterIP تخصیص نمی‌شود
  • kube-proxy این سرویس را handle نمی‌کند (بدون LB پلتفرمی)
  • با selector: DNS رکوردهای A/AAAA به IP پادها
  • برای StatefulSet + serviceName: نام‌های پایدار مثل mydb-0.mydb.ns.svc.cluster.local

بدون selector: DNS ممکن است به externalName یا رکوردهای تعریف‌شدهٔ دیگر وابسته باشد (طبق docs) — EndpointSlice خودکار ساخته نمی‌شود.


EndpointSlice (و Endpoints قدیمی)

کنترلر به‌طور پیوسته Podهای مطابق selector را می‌خواند و EndpointSlice را به‌روز می‌کند.

APIوضعیت
EndpointSliceمسیر مدرن؛ dual-stack؛ مقیاس‌پذیر (slice حدود ۱۰۰ endpoint)
Endpointsdeprecated نسبی؛ محدودیت ~۱۰۰۰؛ برای کلاینت‌های جدید توصیه نمی‌شود

Service بدون selector

برای backend خارج از cluster یا مهاجرت تدریجی:

# Service بدون selector
spec:
  ports:
    - port: 80
      targetPort: 9376
---
# EndpointSlice دستی با label
# kubernetes.io/service-name: my-service

محدودیت: kubectl port-forward service/... به endpoint غیر-Pod معمولاً کار نمی‌کند (امنیتی).

IPهای loopback و link-local به‌عنوان endpoint مجاز نیستند؛ ClusterIP سرویس دیگر هم مقصد معتبر برای kube-proxy نیست.


Discovery: DNS و متغیر محیطی

DNS (ترجیح مدرن)

<service>.<namespace>.svc.cluster.local

Headless: چند A/AAAA یا SRV به‌جای یک VIP.

Podهایی که بعد از Service ساخته شوند ممکن است WEB_SERVICE_HOST / PORT بگیرند. شکننده است (ترتیب create) — DNS را ترجیح دهید.


سیاست ترافیک و affinity

sessionAffinity

spec:
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800

اتصال‌های بعدی از همان client IP (آنگونه که proxy می‌بیند) به همان Pod. برای headless بی‌معناست (proxy نیست).

با externalTrafficPolicy: Cluster، IP واقعی کلاینت ممکن است SNAT شود و affinity «کلاینت واقعی» را نبیند.

externalTrafficPolicy / internalTrafficPolicy

مقداررفتار تقریبی
Cluster (پیش‌فرض خارجی)ترافیک ممکن است به نود دیگر برود؛ SNAT محتمل
Localفقط به Pod روی همان نود؛ اگر نباشد ممکن است drop؛ client IP بهتر حفظ می‌شود

برای LB ابری، Local با health check نودهایی که Pod ندارند را از pool خارج می‌کند.

internalTrafficPolicy: Local مشابه برای ترافیک داخل cluster (کاهش hop، با ریسک endpoint خالی محلی).


Dual-stack و خانواده IP

Clusterهای dual-stack می‌توانند:

spec:
  ipFamilyPolicy: PreferDualStack   # یا SingleStack / RequireDualStack
  ipFamilies:
    - IPv4
    - IPv6

جزئیات به CNI و نسخه cluster بستگی دارد — با kubectl explain service.spec.ipFamilyPolicy چک کنید.


proxy dataplane: kube-proxy و فراتر

به‌طور کلاسیک kube-proxy (iptables / ipvs / nftables) VIP را به Podها می‌رساند. جایگزین‌ها:

  • Cilium eBPF بدون kube-proxy
  • mesh (Istio، Linkerd) برای L7 روی همان Service یا sidecar

رفتار NodePort روی localhost و آدرس‌های نود به mode و فلگ‌های kube-proxy وابسته است.


Service در برابر Ingress / Gateway

ServiceIngress / Gateway API
لایهعمدتاً L4 (TCP/UDP)L7 HTTP(S)
host/pathخیر (جز appProtocol hint)بله
TLS terminationمعمولاً در LB/Ingressرایج
typeClusterIP/NodePort/LB/…شیء جدا

الگوی رایج production HTTP:

Internet → LoadBalancer یا NodePort روی Ingress Controller
         → ClusterIP سرویس‌های اپ
         → Pods

مقاله مرتبط لبه: Contour.


انتخاب type — درخت تصمیم

فقط داخل cluster؟
  ├─ نیاز به VIP و LB؟ → ClusterIP
  └─ نیاز به DNS مستقیم به هر Pod؟ → Headless (clusterIP: None)

از بیرون cluster؟
  ├─ HTTP(S) با host/path/TLS؟ → Ingress/Gateway جلوی ClusterIP
  ├─ cloud با CCM؟ → LoadBalancer (یا Ingress + LB یک‌بار)
  ├─ bare metal / lab؟ → NodePort یا MetalLB به‌عنوان LoadBalancer
  └─ فقط alias به سرویس خارجی؟ → ExternalName

نمونهٔ کامل‌تر

apiVersion: v1
kind: Service
metadata:
  name: payment-api
  namespace: production
  labels:
    app.kubernetes.io/name: payment
  annotations:
    # مثال وابسته به cloud — مقدار واقعی از docs provider
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  selector:
    app.kubernetes.io/name: payment
    app.kubernetes.io/component: api
  ports:
    - name: http
      protocol: TCP
      port: 443
      targetPort: http
  sessionAffinity: None

دیباگ روزمره

kubectl get svc,endpoints,endpointslice -n production
kubectl describe svc payment-api -n production

# از یک Pod داخل cluster
curl -v http://payment-api.production.svc.cluster.local

# آیا selector پاد Ready دارد؟
kubectl get pods -l app.kubernetes.io/name=payment -n production
علامتعلت محتمل
Endpoints خالیselector غلط؛ پاد NotReady؛ namespace اشتباه
Connection refusedtargetPort غلط
LB pendingنبود CCM / MetalLB
NodePort timeoutفایروال نود؛ policy Local بدون Pod محلی
سرویس هست ولی DNS نهCoreDNS؛ نام/ns غلط

ارتباط با metadata و بقیهٔ مقالات

Pod.metadata.labels  ←── selector ──  Service.spec
Service.metadata.name/ns  →  DNS
Service.spec.type         →  نحوهٔ expose
readiness (در Pod spec)   →  عضویت در EndpointSlice

Best practices

  1. پیش‌فرض داخلی: ClusterIP؛ لبه HTTP را با Ingress جدا کنید
  2. selector را با app.kubernetes.io/* پایدار نگه دارید
  3. targetPort نام‌دار برای انعطاف نسخه
  4. readinessProbe را جدی بگیرید — وگرنه ترافیک به پاد سرد می‌رود
  5. برای client IP واقعی روی LB: externalTrafficPolicy: Local را بفهمید و تست کنید
  6. ExternalName برای رمز/اتصال DB کافی نیست — Secret جدا لازم است
  7. Headless را فقط وقتی per-pod identity لازم است استفاده کنید
  8. از Endpoints قدیمی در ابزار جدید پرهیز کنید؛ EndpointSlice
  9. در GitOps، type و annotationهای cloud را صریح و محیطی کنید
  10. قبل از production، kubectl describe svc و تعداد endpoints را در چک‌لیست بگذارید

جمع‌بندی

مفهومیک جمله
Serviceدسترسی پایدار شبکه به مجموعه endpointها
ClusterIPVIP داخلی — پیش‌فرض
NodePortپورت ثابت روی هر نود
LoadBalancerLB خارجی (+ معمولاً NodePort)
ExternalNameCNAME بدون proxy
Headlessبدون VIP؛ DNS به Podها
EndpointSliceفهرست واقعی backendهای Ready
selectorپل metadata labels پاد ↔ ترافیک

Service مشکل «Pod می‌میرد، IP عوض می‌شود» را با یک قرارداد پایدار حل می‌کند. انتخاب type یعنی انتخاب کجا و چطور آن قرارداد دیده شود — از فقط-داخل-cluster تا لبه‌های ابری و aliasهای خارجی.


قدم بعدی

  1. یک Deployment + ClusterIP بسازید و از Pod دیگر curl بزنید
  2. عمداً selector را غلط کنید و Endpoints خالی را ببینید
  3. همان سرویس را NodePort کنید و از IP نود تست کنید
  4. یک Headless برای StatefulSet نمونه بسازید و DNS pod-0.svc را resolve کنید
  5. برای HTTP لبه، Ingress جلوی ClusterIP بگذارید — نه لزوماً LoadBalancer جدا برای هر اپ

منابع


منتشر شده در P30Light — بخش زیرساخت و سرور.

لینک گزارش با موفقیت کپی گردید!