הגדרת הכוונת שירותים

בדף הזה מוסבר איך להגדיר ניתוב תעבורת נתונים לפודים.

כדי להבין איך פועל ניהול התנועה לשירותים, אפשר לעיין במאמר איך פועל ניהול התנועה לשירותים.

דרישות

  • ‫GKE בגרסה 1.30 ואילך.

מגבלות

  • ל-ServiceFunctionChain יכולה להיות פונקציית שירות אחת לכל היותר.
  • מומלץ להשתמש ב-100 צמתים לכל היותר, בנוסף ל-10 זוגות של ServiceFunctionChain ו-TrafficSelector.
  • התכונה "ניתוב תנועה לשירותים ב-GKE" זמינה רק בצמתים שפועלת בהם תמונת הצומת של מערכת הפעלה שמותאמת לקונטיינרים.
  • התכונה "הפניית תנועה לשירות ב-GKE" תומכת רק בכתובות IP של תעבורת נתונים יוצאת (egress) ושל יעדים.
  • התכונה 'הפניית תנועה לשירותים' לא מטפלת בקונפליקטים שמתעוררים כשמחילים על אותו נושא כמה TrafficSelector עם אורך קידומת זהה. כדי למנוע התנגשויות, כדאי לתכנן מראש את בוררי התנועה עם טווחי כתובות IP שלא חופפים וקריטריונים ברורים לבחירה.

הטמעה של ניהול תעבורה

באמצעות GKE Service Steering אתם יכולים להתאים אישית ולשלוט בתנועה ברשת בתוך אשכול. בקטע הזה מוסבר איך להטמיע את Service Steering באמצעות דוגמה של שער אינטרנט.

נניח שאתם רוצים ליצור שער אינטרנט שמאבטח את התנועה ממכשירי הלקוח של משתמשי הקצה לאינטרנט. מסיים VPN מושך תנועה אל השער המנוהל באמצעות מנהרה מאובטחת. תנועת משתמשי הקצה מופנית מחדש לחומת האש ואז לשרת ה-proxy. הפרוקסי מבצע תרגום של כתובת הרשת של המקור (SNAT) בתעבורת הנתונים, מסווה את כתובת המקור המקורית ושולח אותה לאינטרנט.

כדי להטמיע את GKE Service Steering:

  1. יוצרים VPC עם MTU של 8896.
  2. יוצרים אשכול GKE.
  3. יוצרים את ה-Pods של פונקציית השירות ואת השירות.
  4. יוצרים את ServiceFunctionChain.
  5. יוצרים את המשאב TrafficSelector עם הפניה אל ServiceFunctionChain.

לפני שמתחילים

לפני שמתחילים, חשוב לוודא שביצעתם את המשימות הבאות:

  • מפעילים את ממשק ה-API של Google Kubernetes Engine.
  • הפעלת Google Kubernetes Engine API
  • כדי להשתמש ב-CLI של Google Cloud למשימה הזו, צריך להתקין ואז לאתחל את ה-CLI של gcloud. אם התקנתם בעבר את ה-CLI של gcloud, מריצים את הפקודה gcloud components update כדי לקבל את הגרסה העדכנית. יכול להיות שבגרסאות קודמות של ה-CLI של gcloud אי אפשר להריץ את הפקודות שבמסמך הזה.

הכנת VPC

מכינים VPC. ב-Service Steering נעשה שימוש בהכמסה כדי להפנות את התנועה לפונקציות השירות המתאימות. הצפנה כוללת הוספה של כותרות נוספות לכל מנה, מה שמגדיל את גודל המנה. לא צריך לבצע הגדרה מיוחדת ב-VPC כדי להשתמש בהפניית תנועה לשירותים. כשמכינים את ה-VPC, מומלץ להביא בחשבון את התקורה של ה-encapsulation כשמחליטים על גודל ה-MTU. מידע נוסף זמין במאמר רשת VPC עם MTU מוגדר.

הפקודה הבאה מגדירה את גודל ה-MTU ב-VPC:

gcloud compute networks create VPC_NETWORK_NAME --mtu=8896

מחליפים את VPC_NETWORK_NAME בשם של רשת ה-VPC שמכילה את רשת המשנה.

יצירת אשכול GKE

כדי להפעיל את היכולות המתקדמות של ניהול כתובות IP וניתוב ברשת שנדרשות להטמעה של Service Steering ב-GKE, צריך ליצור אשכול GKE עם GKE Dataplane V2 באופן הבא:

gcloud container clusters create CLUSTER_NAME \
    --network VPC_NAME \
    --release-channel RELEASE_CHANNEL \
    --cluster-version CLUSTER_VERSION \
    --enable-dataplane-v2 \
    --enable-ip-alias

מחליפים את מה שכתוב בשדות הבאים:

  • ‫CLUSTER_NAME: שם האשכול.
  • ‫VPC_NAME: השם של ה-VPC שאליו רוצים לשייך את האשכול.
  • ‫RELEASE_CHANNEL: השם של ערוץ ההפצה.
  • ‫VERSION: גרסת GKE, שצריכה להיות 1.30 ואילך. אפשר גם להשתמש בדגל --release-channel כדי לבחור ערוץ הפצה. לערוץ ההפצה צריכה להיות גרסת ברירת מחדל של 1.30 ואילך.

יצירת ServiceFunction פודים

כדי ליצור את שרשרת השירותים, פורסים את ה-Pod של סיום ה-VPN ואת ה-Pods הנדרשים של פונקציות השירות בתוך האשכול. ‫Pods מכילים את האפליקציות בקונטיינרים שמבצעות את הפונקציות של הרשת.

ה-Pod של סיום ה-VPN הוא בדרך כלל פונקציית השירות הראשונה בשרשרת, שמסיימת את התנועה שנכנסת לאשכול דרך ה-VPN. לאחר מכן, הוא מכוון את פונקציות השירות האחרות, כמו חומות אש ואיזון עומסים, לעיבוד נוסף לפני ההגעה ליעד הסופי.

בקובץ התצורה לדוגמה הבא מוגדרים שלושת הרכיבים הבאים, שחיוניים לניהול תנועת הרשת באשכול:

  • ‫VPN Pod: יוצר נקודת קצה (endpoint) של רשת וירטואלית פרטית (VPN) באשכול, שמאפשרת תקשורת מאובטחת ומוצפנת בין האשכול לבין רשתות חיצוניות.
  • פריסת חומת אש: פריסה של כמה עותקים של Pod של חומת אש, שמספקים אבטחה ואיזון עומסים.
  • ‫Proxy DaemonSet: פורס Pod של שרת proxy בכל צומת באשכול, כדי לוודא שהתנועה ברשת תעובד באופן מקומי לפני שהיא תועבר לשירותים אחרים כמו חומת אש.

שומרים את קובץ המניפסט לדוגמה הבא בשם service_function.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: vpn
  namespace: vpn
  labels:
    app: vpn
spec:
  containers:
  - name: vpn
    image: openvpn
    ports:
    - containerPort: 51820
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: firewall
  namespace: firewall
spec:
  replicas: 3
  selector:
    matchLabels:
      app: firewall
  template:
    metadata:
      labels:
        app: firewall
    spec:
      containers:
      - name: firewall
        image: firewall
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: proxy
  namespace: proxy
spec:
  selector:
    matchLabels:
      app: proxy
  template:
    metadata:
      labels:
        app: proxy
    spec:
      containers:
      - name: proxy
        image: proxy

החלת המניפסט:

kubectl apply -f service_function.yaml

יצירת ServiceFunctionChains

כדי להגדיר רצף של פונקציות רשת לתעבורת נתונים, יוצרים צינור שבו כל פונקציה, כמו חומת אש, שרת proxy ומאזן עומסים, מבצעת את המשימה הספציפית שלה לפני העברת תעבורת הנתונים לפונקציה הבאה.

שומרים את קובץ המניפסט לדוגמה הבא בשם ServiceFunctionChain.yaml:

apiVersion: networking.gke.io/v1
kind: ServiceFunctionChain
metadata:
  name: firewall
spec:
  sessionAffinity:
    clientIPNoDestination:
      timeoutSeconds: 3600
  serviceFunctions:
  - name: firewall
    namespace: firewall
    podSelector:
      matchLabels:
        app: firewall
---
apiVersion: networking.gke.io/v1
kind: ServiceFunctionChain
metadata:
  name: proxy
spec:
  sessionAffinity:
    clientIPNoDestination: {}
  serviceFunctions:
  - name: proxy
    namespace: proxy
    podSelector:
      matchLabels:
        app: proxy

מחילים את המניפסט:

kubectl apply -f ServiceFunctionChain.yaml

פונקציות השירות מוגדרות בשורה בתוך ServiceFunctionChain באמצעות השדה serviceFunctions. פונקציית שירות היא בורר נקודות קצה (endpoint).

יצירת משאב TrafficSelector

כדי להגדיר איפה ואיזו תנועה נבחרת לניתוב תנועה לשירות, יוצרים את המשאב TrafficSelector שמפנה אל ServiceFunctionChains כדי להחיל אותו על התנועה שנבחרה.

שומרים את קובץ המניפסט לדוגמה הבא בשם TrafficSelector.yaml:

apiVersion: networking.gke.io/v1
kind: TrafficSelector
metadata:
  name: vpn-to-firewall
spec:
  serviceFunctionChain: firewall
  subject:
    pods:
      namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: vpn
      podSelector:
        matchLabels:
          app: vpn
  egress:
    to:
      ipBlock:
        cidr: 0.0.0.0/0
    ports:
    - allPorts:
        protocol: UDP
    - allPorts:
        protocol: TCP
---
apiVersion: networking.gke.io/v1
kind: TrafficSelector
metadata:
  name: firewall-to-proxy
spec:
  serviceFunctionChain: proxy
  subject:
    pods:
      namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: firewall
      podSelector:
        matchLabels:
          app: firewall
  egress:
    to:
      ipBlock:
        cidr: 0.0.0.0/0
    ports:
    - allPorts:
        protocol: UDP
    - allPorts:
        protocol: TCP

מחילים את המניפסט:

kubectl apply -f TrafficSelector.yaml

פתרון בעיות בהפניית שירותים

בקטע הזה מוסבר איך לפתור בעיות שקשורות ל-GKE Service Steering.

אין תנועה ברשת

כדי לנפות את הבעיה, אפשר לבצע את הפעולות הבאות:

שלב 1: מוודאים שההגדרה servicePathId מוגדרת ב-ServiceFunctionChain

מוודאים שהאפשרות servicePathId מוגדרת ב-ServiceFunctionChain. לכל אובייקט ServiceFunctionChain מוקצה מזהה servicePathId ייחודי, כמו שמוצג בדוגמה הבאה:

apiVersion: networking.gke.io/v1
kind: ServiceFunctionChain
metadata:
  name: firewall
spec:
  serviceFunctions:
  - name: firewall
    namespace: firewall
    podSelector:
      matchLabels:
        app: firewal
status:
  servicePathId: 1

שלב 2: מוודאים שנוצר שירות Kubernetes לכל פונקציית שירות

שירות ClusterIP נוצר באופן אוטומטי לכל פונקציית שירות. אפשר לראות את רשימת השירותים באמצעות kubectl:

kubectl get svc -A -l networking.gke.io/managed-by=service-steering-controller.gke.io

שלב 3: מוודאים שלכל פונקציית שירות נוצרת רשומה במפת ה-BPF בכל צומת כדי לאחסן את כתובת ה-IP של השירות

לכל פונקציית שירות, נוצרת רשומת מפת bpf בכל צומת כדי לאחסן את כתובת ה-IP של השירות.

אחזור השם של anetd Pod:

kubectl get pods -n kube-system -o wide -l k8s-app=cilium

רושמים את שם ה-Pod, בדומה ל-anetd.

מריצים את הפקודה הבאה:

kubectl -n kube-system exec -it ANETD-POD-NAME -- cilium bpf sfcpath list

מחליפים את ANETD-POD-NAME בשם של ה-Pod של anetd.

הפלט אמור להיראות כך:

PATH     SERVICE FUNCTION ADDRESS
(1, 1)   10.4.10.124

שלב 4: מוודאים שרשומות המפה של bpf נוצרו במפה sfcselect

בצומת, אם יש Pods שנבחרו על ידי TrafficSelector, רשומות של מפת bpf נוצרות במפה sfcselect. בדוגמה הבאה אפשר לראות שתעבורת TCP/UDP מכל יציאה של נקודת הקצה (Pod) 3783 לכתובת ה-IP של היעד 10.0.2.12 מנותבת אל ServiceFunctionChain.

מריצים את הפקודה הבאה:

kubectl -n kube-system exec -it ANETD-POD-NAME -- cilium bpf sfcselect list

מחליפים את ANETD-POD-NAME בשם בפועל של ה-Pod של anetd באשכול.

הפלט אמור להיראות כך:

SELECTOR                            PATH
3783, egress, 0/TCP, 10.0.2.12/32   /32 (1, 1)
3783, egress, 0/UDP, 10.0.2.12/32   /32 (1, 1)

שלב 5: שימוש ב-tcpdump ביציאה 7081 כדי ללכוד ולנתח את תעבורת הרשת

התכונה 'ניתוב שירותים' מבצעת הצפנה של Geneve ביציאת UDP‏ 7081. אפשר להשתמש ב-tcpdump בצמתים הרלוונטיים כדי לנתח את זרימת התנועה ולזהות את המקום שבו הבעיה מתרחשת.

המאמרים הבאים