בדף הזה מתוארים הסוגים השונים של שירותים ב-Kubernetes, ומוסבר איך Google Kubernetes Engine (GKE) משתמש בשירותים כדי לקבץ נקודות קצה של Pod.
תלמדו איך ליצור שירותים באמצעות דוגמאות של YAML. כל סוג שירות משתמש בכתובת ה-IP הקבועה של השירות כדי לצמצם את המורכבות של משימות ספציפיות ברשת ובתקשורת. לדוגמה, איך להשתמש בכמה יציאות ונקודות קצה (endpoints), ואיך להגדיר אפשרויות של מחסנית יחידה או כפולה שתומכות ב-IPv4 וב-IPv6.
במאמר חשיפת אפליקציות באמצעות שירותים מוסבר איך ליצור שירות.
הדף הזה מיועד לאופרטורים ולמפתחים שמקצים ומגדירים משאבי ענן ופורסים אפליקציות ושירותים. במאמר תפקידים ומשימות נפוצים של משתמשי GKE מפורט מידע נוסף על תפקידים נפוצים ומשימות לדוגמה שמוזכרים בתוכן של Google Cloud.
מהו שירות Kubernetes?
הרעיון של שירות הוא לקבץ קבוצה של נקודות קצה (endpoint) של Pod למשאב אחד. אתם יכולים להגדיר דרכים שונות לגשת לקבוצה. כברירת מחדל, מקבלים כתובת IP יציבה של אשכול, שבעזרתה לקוחות בתוך האשכול יכולים ליצור קשר עם Pods בשירות. לקוח שולח בקשה לכתובת ה-IP הקבועה, והבקשה מנותבת לאחד מה-Pods בשירות.
שירות מזהה את ה-Pods ששייכים לו באמצעות בורר. כדי ש-Pod יהיה חבר בשירות, הוא צריך לכלול את כל התוויות שצוינו בסלקטור. תווית היא צמד מפתח/ערך שרירותי שמצורף לאובייקט.
במניפסט השירות הבא יש בורר שמציין שתי תוויות. בשדה selector מצוין שכל Pod עם התווית app: metrics והתווית department:engineering הוא חבר בשירות הזה.
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
# Use labels to select the member Pods of the Service.
selector:
app: metrics
department: engineering
ports:
...
למה כדאי להשתמש ב-Kubernetes Service?
באשכול Kubernetes, לכל Pod יש כתובת IP פנימית. אבל הפודים בפריסת Deployment מגיעים והולכים, וכתובות ה-IP שלהם משתנות. לכן, אין טעם להשתמש ישירות בכתובות ה-IP של ה-Pod. כשמשתמשים בשירות, מקבלים כתובת IP יציבה שתקפה למשך מחזור החיים של השירות, גם אם כתובות ה-IP של חברי ה-Pod משתנות.
שירות מספק גם איזון עומסים. הלקוחות מתקשרים לכתובת IP יציבה אחת, והבקשות שלהם מאוזנות בין ה-Pods ששייכים לשירות.
סוגים של שירותי Kubernetes
יש חמישה סוגים של שירותים:
ClusterIP (ברירת מחדל): לקוחות פנימיים שולחים בקשות לכתובת IP פנימית יציבה.
NodePort: לקוחות שולחים בקשות לכתובת ה-IP של צומת בערך אחד או יותר של
nodePortשצוינו על ידי השירות.LoadBalancer: לקוחות שולחים בקשות לכתובת ה-IP של מאזן עומסים ברשת.
ExternalName: לקוחות פנימיים משתמשים בשם ה-DNS של שירות ככינוי לשם DNS חיצוני.
Headless: אפשר להשתמש בשירות headless כשרוצים לקבץ Pod, אבל לא צריך כתובת IP יציבה.
הסוג NodePort הוא הרחבה של הסוג ClusterIP. לכן לשירות מסוג NodePort יש כתובת IP של אשכול.
הסוג LoadBalancer הוא הרחבה של הסוג NodePort. לכן, לשירות מסוג LoadBalancer יש כתובת IP של אשכול וערך אחד או יותר של nodePort.
שירותים מסוג ClusterIP
כשיוצרים שירות מסוג ClusterIP, Kubernetes יוצר כתובת IP יציבה שאפשר לגשת אליה מצמתים באשכול.
זוהי מניפסט של שירות מסוג ClusterIP:
apiVersion: v1
kind: Service
metadata:
name: my-cip-service
spec:
selector:
app: metrics
department: sales
# Create a ClusterIP Service. Kubernetes dynamically allocates an IP address
# to the Service.
type: ClusterIP
ports:
- protocol: TCP
port: 80
targetPort: 8080
אפשר ליצור את השירות באמצעות kubectl apply -f [MANIFEST_FILE]. אחרי שיוצרים את השירות, אפשר להשתמש ב-kubectl get service כדי לראות את כתובת ה-IP הקבועה:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
my-cip-service ClusterIP 10.11.247.213 none 80/TCP
לקוחות באשכול קוראים לשירות באמצעות כתובת ה-IP של האשכול ויציאת ה-TCP שצוינה בשדה port של מניפסט השירות. הבקשה מועברת לאחד מה-Pods של החברים ביציאת ה-TCP שצוינה בשדה targetPort. בדוגמה הקודמת, לקוח שולח קריאה לשירות בכתובת 10.11.247.213 ביציאת TCP מספר 80. הבקשה מועברת לאחד מ-Pods החברים ביציאת TCP 8080. ל-Pod של החבר חייב להיות מאגר שמקשיב ליציאת TCP 8080. אם אין מאגר שתומך בהאזנה ביציאה 8080, הלקוחות יראו הודעה כמו 'החיבור נכשל' או 'לא ניתן לגשת לאתר הזה'.
שירות מסוג NodePort
כשיוצרים שירות מסוג NodePort, Kubernetes נותן ערך של nodePort. אחר כך אפשר לגשת לשירות באמצעות כתובת ה-IP של כל צומת יחד עם הערך nodePort.
זוהי דוגמה למניפסט של שירות מסוג NodePort:
apiVersion: v1
kind: Service
metadata:
name: my-np-service
spec:
selector:
app: products
department: sales
# Kubernetes allocates a nodePort between 30000 and 32767 for external traffic
# that reaches the node IP address. Additionally, Kubernetes dynamically
# allocates a clusterIP IP address for internal traffic.
type: NodePort
ports:
- protocol: TCP
port: 80 # Route internal traffic that reaches the clusterIP IP address on this port.
targetPort: 8080
אחרי שיוצרים את השירות, אפשר להשתמש ב-kubectl get service -o yaml כדי לראות את המפרט שלו ואת הערך של nodePort.
spec:
clusterIP: 10.11.254.114
externalTrafficPolicy: Cluster
ports:
- nodePort: 32675
port: 80
protocol: TCP
targetPort: 8080
לקוחות חיצוניים מתקשרים עם השירות באמצעות כתובת ה-IP החיצונית של צומת, יחד עם יציאת ה-TCP שצוינה על ידי nodePort. הבקשה מועברת לאחד מ-Pods החברים ביציאת ה-TCP שצוינה בשדה targetPort.
לדוגמה, נניח שכתובת ה-IP החיצונית של אחד מצמתי האשכול היא 203.0.113.2. בדוגמה הקודמת, הלקוח החיצוני קורא לשירות בכתובת 203.0.113.2 ביציאת TCP 32675. הבקשה מועברת לאחד מה-Pods החברים ביציאת TCP 8080. ל-Pod של החבר חייב להיות מאגר תגים שמקשיב ליציאת TCP 8080.
סוג השירות NodePort הוא הרחבה של סוג השירות ClusterIP. לכן, ללקוחות פנימיים יש שתי דרכים לקרוא לשירות:
- משתמשים ב-
clusterIPוב-port. - משתמשים בכתובת ה-IP של הצומת וב-
nodePort.
בחלק מהגדרות האשכול, מאזן העומסים של אפליקציות (ALB) החיצוני משתמש בשירות מסוג NodePort.
מאזן עומסים חיצוני של אפליקציות (ALB) הוא שרת proxy, והוא שונה באופן מהותי ממאזן עומסי רשת חיצוני אזורי להעברת סיגנל ללא שינוי שמתואר בנושא הזה בקטע שירות מסוג LoadBalancer.
שירותים מסוג LoadBalancer
מידע נוסף על שירותים מסוג LoadBalancer זמין במאמר מושגים בנושא שירות LoadBalancer.
שירות מסוג ExternalName
שירות מסוג ExternalName מספק כינוי פנימי לשם DNS חיצוני. לקוחות פנימיים שולחים בקשות באמצעות שם ה-DNS הפנימי, והבקשות מופנות לשם החיצוני.
זוהי דוגמה למניפסט של שירות מסוג ExternalName:
apiVersion: v1
kind: Service
metadata:
name: my-xn-service
spec:
type: ExternalName
externalName: example.com
כשיוצרים שירות, Kubernetes יוצר שם DNS שבעזרתו לקוחות פנימיים יכולים לקרוא לשירות. בדוגמה שלמעלה, שם ה-DNS הוא my-xn-service.default.svc.cluster.local. כשלקוח פנימי שולח בקשה אל my-xn-service.default.svc.cluster.local, הבקשה מנותבת מחדש אל example.com.
סוג השירות ExternalName שונה באופן מהותי מסוגי השירותים האחרים. למעשה, שירות מסוג ExternalName לא מתאים להגדרה של שירות שמופיעה בתחילת הנושא הזה. שירות מסוג ExternalName לא משויך לקבוצת Pods, ואין לו כתובת IP יציבה. במקום זאת, Service מסוג ExternalName הוא מיפוי משם DNS פנימי לשם DNS חיצוני.
Headless Service
שירות ללא ממשק משתמש הוא סוג של שירות Kubernetes שלא מקצה כתובת IP לאשכול. במקום זאת, שירות ללא ראש משתמש ב-DNS כדי לחשוף את כתובות ה-IP של ה-Pods שמשויכים לשירות. כך אפשר להתחבר ישירות לפודים, במקום לעבור דרך שרת proxy.
שירותים ללא ממשק משתמש שימושיים במגוון תרחישים, כולל:
זיהוי שירותים: אפשר להשתמש בשירות ללא ראש כדי להטמיע זיהוי שירותים. כדי להטמיע את זה, יוצרים Service עם שם ובורר. רשומת ה-DNS של השירות ללא ראש מכילה את כל כתובות ה-IP של ה-Pods שמאחורי השירות שתואמות לבורר. לקוחות יכולים להשתמש ברשומות ה-DNS האלה כדי למצוא את כתובות ה-IP של ה-Pods שמשויכים לשירות.
גישה ישירה ל-Pod: לקוחות יכולים להתחבר ישירות ל-Pods שמשויכים לשירות headless, וזה יכול להיות שימושי לשירותים שדורשים גישה ישירה ל-Pods הבסיסיים, כמו מאזני עומסים ושרתי DNS.
גמישות: אפשר להשתמש בשירותים ללא ממשק משתמש כדי ליצור מגוון טופולוגיות שונות, כמו מאזני עומסים, שרתי DNS ומסדי נתונים מבוזרים.
אם יש לכם דרישות מיוחדות לגבי הרשת עבור עומסי העבודה שלכם, שלא ניתן לפתור באמצעות שירותים ללא ראש עם סלקטורים, יש גם אפשרות להשתמש בשירותים ללא ראש ללא סלקטורים. שירותים ללא ראש הם כלי שימושי לגישה לשירותים שלא נמצאים באשכול Kubernetes עצמו, כי מישור הבקרה לא יוצר אובייקטים של EndpointSlice. מידע נוסף זמין במאמר שירות ללא סלקטורים.
הדוגמה הבאה היא מניפסט של שירות ללא ממשק משתמש:
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
# Prevent Kubernetes from allocating an IP address to the Service.
clusterIP: None
selector:
app: nginx
ports:
- name: http
port: 80
targetPort: 80
אחרי שיוצרים שירות ללא ראש, אפשר לשלוח שאילתה ל-DNS כדי למצוא את כתובות ה-IP של ה-Pods שמשויכים לשירות. לדוגמה, הפקודה הבאה מציגה את כתובות ה-IP של הפודים שמשויכים לשירות nginx:
dig +short nginx.default.svc.cluster.local
דוגמה נוספת שמשתמשת בהרחבת שאילתה של Kubernetes:
dig +short +search nginx
אפשר ליצור שירות ללא ממשק משתמש באמצעות פקודה אחת, וקל לעדכן ולהרחיב שירותים ללא ממשק משתמש.
kubectl create service clusterip my-svc --clusterip="None" --dry-run=client -o yaml > [file.yaml]
הפשטת השירות
Service הוא הפשטה במובן שהוא לא תהליך שמאזין לממשק רשת כלשהו. חלק מההפשטה מיושם בכללי iptables של צמתי האשכול. בהתאם לסוג השירות, חלקים אחרים של האבסטרקציה מיושמים על ידי מאזן עומסים אזורי חיצוני להעברת סיגנל ללא שינוי או על ידי מאזן עומסים של אפליקציות (ALB) חיצוני.
יציאות שירות שרירותיות
הערך של השדה port במניפסט של שירות הוא שרירותי. עם זאת, הערך של targetPort לא שרירותי. לכל פוד של חבר צריך להיות קונטיינר שמקשיב ב-targetPort.
הנה דוגמה לשירות מסוג LoadBalancer, עם ערך port של 50, 000:
apiVersion: v1
kind: Service
metadata:
name: my-ap-service
spec:
# Set your own IP address for internal traffic instead of letting Kubernetes
# select one for you.
clusterIP: 10.11.241.93
externalTrafficPolicy: Cluster
ports:
- nodePort: 30641 # Set a port for external traffic that reaches the node IP address.
port: 50000 # Set a port for internal traffic that reaches the clusterIP IP address.
protocol: TCP
targetPort: 8080
selector:
app: parts
department: engineering
sessionAffinity: None
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 203.0.113.200
לקוח שולח קריאה לשירות בכתובת 203.0.113.200 ביציאת TCP מספר 50000. הבקשה מועברת לאחד מ-Pods החברים ביציאת TCP 8080.
כמה יציאות
השדה ports של Service הוא מערך של אובייקטים מהטיפוס ServicePort. לאובייקט ServicePort יש את השדות הבאים:
nameprotocolporttargetPortnodePort
אם יש לכם יותר מ-ServicePort אחד, לכל ServicePort צריך להיות שם ייחודי.
הנה שירות, מסוג LoadBalancer, שיש לו שני אובייקטים מסוג ServicePort:
apiVersion: v1
kind: Service
metadata:
name: my-tp-service
spec:
clusterIP: 10.11.242.196
externalTrafficPolicy: Cluster
ports:
# Forward traffic that reaches port 31233 on the node IP address, or port
# 60000 on the clusterIP IP address, to port 50000 on a member Pod.
- name: my-first-service-port
nodePort: 31233
port: 60000
protocol: TCP
targetPort: 50000
# Forward traffic that reaches port 31081 on the node IP address, or port
# 60001 on the clusterIP IP address, to port 8080 on a member Pod.
- name: my-second-service-port
nodePort: 31081
port: 60001
protocol: TCP
targetPort: 8080
selector:
app: tests
department: engineering
sessionAffinity: None
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 203.0.113.201
בדוגמה הקודמת, אם לקוח מתקשר לשירות בכתובת 203.0.113.201 ביציאת TCP מספר 60000, הבקשה מועברת ל-Pod של חבר ביציאת TCP מספר 50000. אבל אם לקוח קורא לשירות ב-203.0.113.201 ביציאת TCP 60001, הבקשה מועברת ל-Pod של חבר ביציאת TCP 8080.
לכל Pod של חבר צריך להיות מאזין של קונטיינר ביציאת TCP 50000 ומאזין של קונטיינר ביציאת TCP 8080. יכול להיות שזה מאגר תגים יחיד עם שני שרשורים, או שני מאגרי תגים שפועלים באותו Pod.
נקודות קצה של שירותים
כשיוצרים שירות, Kubernetes יוצר אובייקט Endpoints עם אותו שם כמו של השירות. מערכת Kubernetes משתמשת באובייקט Endpoints כדי לעקוב אחרי קבוצות ה-Pod ששייכות לשירות.
שירותים עם ערימה יחידה וערימה כפולה
אפשר ליצור שירות IPv6 מסוג
ClusterIP
או NodePort.
לכל אחד מסוגי השירותים האלה, אפשר להגדיר את השדות ipFamilies ו-ipFamilyPolicy בתור IPv4, IPv6 או שירות במצב דואלי.