פריסת שער מרובה אשכולות לאיזון עומסים לפי קיבולת

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

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

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

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

כדי לפרוס שערים מרובי-אשכולות, צריך לבצע הכנה מסוימת של הסביבה. לפני שממשיכים, פועלים לפי השלבים שמפורטים במאמר הכנת הסביבה לשימוש בשערי גישה מרובי-אשכולות:

  1. פריסה של אשכולות GKE.

  2. רושמים את האשכולות ב-Fleet (אם הם עדיין לא רשומים).

  3. מפעילים את בקרי השירות מרובי האשכולות והשער מרובה האשכולות.

לבסוף, מומלץ לעיין במגבלות ובבעיות הידועות של GKE Gateway Controller לפני שמשתמשים בבקר בסביבה שלכם.

פריסת איזון עומסים מבוסס-קיבולת

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

התרשים הבא מציג את הטופולוגיה שתפרסו ואת האופן שבו תעבורת נתונים גולשת בין אשכולות ואזורים כשתעבורת הנתונים חורגת מהקיבולת של השירות:

תנועה שגולשת מקלאסטר אחד לקלאסטר אחר

הכנת הסביבה

  1. כדי להכין את הסביבה, פועלים לפי ההוראות שבמאמר הכנת הסביבה לשימוש בשערי גישה מרובי-אשכולות.

  2. מוודאים שמשאבי GatewayClass מותקנים באשכול ההגדרות:

    kubectl get gatewayclasses --context=gke-west-1
    

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

    NAME                                  CONTROLLER                  ACCEPTED   AGE
    gke-l7-global-external-managed        networking.gke.io/gateway   True       16h
    gke-l7-global-external-managed-mc     networking.gke.io/gateway   True       14h
    gke-l7-gxlb                           networking.gke.io/gateway   True       16h
    gke-l7-gxlb-mc                        networking.gke.io/gateway   True       14h
    gke-l7-regional-external-managed      networking.gke.io/gateway   True       16h
    gke-l7-regional-external-managed-mc   networking.gke.io/gateway   True       14h
    gke-l7-rilb                           networking.gke.io/gateway   True       16h
    gke-l7-rilb-mc                        networking.gke.io/gateway   True       14h
    

פריסת אפליקציה

פורסים את שרת אפליקציית האינטרנט לדוגמה בשני האשכולות:

kubectl apply --context gke-west-1 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/master/gateway/docs/store-traffic-deploy.yaml
kubectl apply --context gke-east-1 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/master/gateway/docs/store-traffic-deploy.yaml

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

namespace/store created
deployment.apps/store created

פריסת שירות, שער ו-HTTPRoute

  1. מחילים את מניפסט Service הבא על אשכולות gke-west-1 ו-gke-east-1:

    cat << EOF | kubectl apply --context gke-west-1 -f -
    apiVersion: v1
    kind: Service
    metadata:
      name: store
      namespace: traffic-test
      annotations:
        networking.gke.io/max-rate-per-endpoint: "10"
    spec:
      ports:
      - port: 8080
        targetPort: 8080
        name: http
      selector:
        app: store
      type: ClusterIP
    ---
    kind: ServiceExport
    apiVersion: net.gke.io/v1
    metadata:
      name: store
      namespace: traffic-test
    EOF
    
    cat << EOF | kubectl apply --context gke-east-1 -f -
    apiVersion: v1
    kind: Service
    metadata:
      name: store
      namespace: traffic-test
      annotations:
        networking.gke.io/max-rate-per-endpoint: "10"
    spec:
      ports:
      - port: 8080
        targetPort: 8080
        name: http
      selector:
        app: store
      type: ClusterIP
    ---
    kind: ServiceExport
    apiVersion: net.gke.io/v1
    metadata:
      name: store
      namespace: traffic-test
    EOF
    

    השירות מסומן ב-max-rate-per-endpoint עם הערך 10 בקשות לשנייה. עם 2 רפליקות לכל אשכול, לכל שירות יש קיבולת של 20 RPS לכל אשכול.

    מידע נוסף על בחירת רמת הקיבולת של השירות זמין במאמר קביעת הקיבולת של השירות.

  2. מחילים את Gatewayהמניפסטgke-west-1 הבא על אשכול ההגדרות, gke-west-1בדוגמה הזו:

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: Gateway
    apiVersion: gateway.networking.k8s.io/v1
    metadata:
      name: store
      namespace: traffic-test
    spec:
      gatewayClassName: gke-l7-global-external-managed-mc
      listeners:
      - name: http
        protocol: HTTP
        port: 80
        allowedRoutes:
          kinds:
          - kind: HTTPRoute
    EOF
    

    קובץ המניפסט מתאר שער חיצוני גלובלי מרובה-אשכולות שמפעיל מאזן עומסים של אפליקציות (ALB) חיצוני עם כתובת IP שנגישה לכולם.

  3. מחילים את HTTPRouteהמניפסטgke-west-1 הבא על אשכול ההגדרות, gke-west-1בדוגמה הזו:

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1
    metadata:
      name: store
      namespace: traffic-test
      labels:
        gateway: store
    spec:
      parentRefs:
      - kind: Gateway
        namespace: traffic-test
        name: store
      rules:
      - backendRefs:
        - name: store
          group: net.gke.io
          kind: ServiceImport
          port: 8080
    EOF
    

    קובץ המניפסט מתאר HTTPRoute שמגדיר את ה-Gateway עם כלל ניתוב שמפנה את כל התנועה אל ServiceImport של החנות. הקבוצה store ServiceImport מקבצת את store Service Pods בשני האשכולות ומאפשרת למאזן העומסים להתייחס אליהם כשירות יחיד.

    אפשר לבדוק את האירועים של השער אחרי כמה דקות כדי לראות אם הפריסה הסתיימה:

    kubectl describe gateway store -n traffic-test --context gke-west-1
    

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

    ...
    Status:
      Addresses:
        Type:   IPAddress
        Value:  34.102.159.147
      Conditions:
        Last Transition Time:  2023-10-12T21:40:59Z
        Message:               The OSS Gateway API has deprecated this condition, do not depend on it.
        Observed Generation:   1
        Reason:                Scheduled
        Status:                True
        Type:                  Scheduled
        Last Transition Time:  2023-10-12T21:40:59Z
        Message:
        Observed Generation:   1
        Reason:                Accepted
        Status:                True
        Type:                  Accepted
        Last Transition Time:  2023-10-12T21:40:59Z
        Message:
        Observed Generation:   1
        Reason:                Programmed
        Status:                True
        Type:                  Programmed
        Last Transition Time:  2023-10-12T21:40:59Z
        Message:               The OSS Gateway API has altered the "Ready" condition semantics and reservedit for future use.  GKE Gateway will stop emitting it in a future update, use "Programmed" instead.
        Observed Generation:   1
        Reason:                Ready
        Status:                True
        Type:                  Ready
      Listeners:
        Attached Routes:  1
        Conditions:
          Last Transition Time:  2023-10-12T21:40:59Z
          Message:
          Observed Generation:   1
          Reason:                Programmed
          Status:                True
          Type:                  Programmed
          Last Transition Time:  2023-10-12T21:40:59Z
          Message:               The OSS Gateway API has altered the "Ready" condition semantics and reservedit for future use.  GKE Gateway will stop emitting it in a future update, use "Programmed" instead.
          Observed Generation:   1
          Reason:                Ready
          Status:                True
          Type:                  Ready
        Name:                    http
        Supported Kinds:
          Group:  gateway.networking.k8s.io
          Kind:   HTTPRoute
    Events:
      Type    Reason  Age                  From                   Message
      ----    ------  ----                 ----                   -------
      Normal  ADD     12m                  mc-gateway-controller  traffic-test/store
      Normal  SYNC    6m43s                mc-gateway-controller  traffic-test/store
      Normal  UPDATE  5m40s (x4 over 12m)  mc-gateway-controller  traffic-test/store
      Normal  SYNC    118s (x6 over 10m)   mc-gateway-controller  SYNC on traffic-test/store was a success
    

    הפלט הזה מראה שהפריסה של שער הגישה בוצעה בהצלחה. יכול להיות שיחלפו כמה דקות עד שהתנועה תתחיל לעבור אחרי פריסת שער. שימו לב לכתובת ה-IP בפלט הזה, כי תצטרכו אותה בשלב הבא.

אישור התנועה

כדי לוודא שהתנועה עוברת לאפליקציה, בודקים את כתובת ה-IP של השער באמצעות פקודת curl:

curl GATEWAY_IP_ADDRESS

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

{
  "cluster_name": "gke-west-1",
  "host_header": "34.117.182.69",
  "pod_name": "store-54785664b5-mxstv",
  "pod_name_emoji": "👳🏿",
  "project_id": "project",
  "timestamp": "2021-11-01T14:06:38",
  "zone": "us-west1-a"
}

בפלט הזה מוצגים המטא-נתונים של ה-Pod, שמציינים את האזור שממנו הבקשה נשלחה.

אימות התנועה באמצעות בדיקות עומס

כדי לוודא שמאזן העומסים פועל, אפשר לפרוס מחולל תנועה באשכול gke-west-1. מחולל התנועה יוצר תנועה ברמות עומס שונות כדי להדגים את הקיבולת ואת יכולות הגלישה של מאזן העומסים. השלבים הבאים מראים שלוש רמות של עומס:

  • ‫10 בקשות לשנייה, שזה מתחת לקיבולת של שירות החנות ב-gke-west-1.
  • ‫30 בקשות לשנייה, שזו חריגה מהקיבולת של שירות החנות gke-west-1 וגורמת לעומס תנועה בשירות gke-east-1.
  • ‫60 RPS, שזה מעל הקיבולת של השירותים בשני האשכולות.

הגדרת לוח הבקרה

  1. מקבלים את השם של מיפוי כתובות ה-URL הבסיסי בשער:

    kubectl get gateway store -n traffic-test --context=gke-west-1 -o=jsonpath="{.metadata.annotations.networking\.gke\.io/url-maps}"
    

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

    /projects/PROJECT_NUMBER/global/urlMaps/gkemcg1-traffic-test-store-armvfyupay1t
    
  2. נכנסים לדף Metrics explorer במסוף Google Cloud .

    כניסה לדף Metrics Explorer

  3. בקטע Select a metric (בחירת מדד), לוחצים על CODE: MQL (קוד: MQL).

  4. מזינים את השאילתה הבאה כדי לראות את מדדי התנועה של שירות החנות בשני האשכולות:

    fetch https_lb_rule
    | metric 'loadbalancing.googleapis.com/https/backend_request_count'
    | filter (resource.url_map_name == 'GATEWAY_URL_MAP')
    | align rate(1m)
    | every 1m
    | group_by [resource.backend_scope],
        [value_backend_request_count_aggregate:
            aggregate(value.backend_request_count)]
    

    מחליפים את GATEWAY_URL_MAP בשם של מיפוי ה-URL מהשלב הקודם.

  5. לוחצים על Run query. אחרי שמפעילים את מחולל העומסים שמתואר בקטע הבא, צריך להמתין לפחות 5 דקות עד שהמדדים יופיעו בתרשים.

בדיקה עם 10 RPS

  1. פריסת Pod באשכול gke-west-1:

    kubectl run --context gke-west-1 -i --tty --rm loadgen  \
        --image=cyrilbkr/httperf  \
        --restart=Never  \
        -- /bin/sh -c 'httperf  \
        --server=GATEWAY_IP_ADDRESS  \
        --hog --uri="/zone" --port 80  --wsess=100000,1,1 --rate 10'
    

    מחליפים את GATEWAY_IP_ADDRESS בכתובת ה-IP של השער מהשלב הקודם.

    הפלט אמור להיראות כך, ולציין שמחולל התנועה שולח תנועה:

    If you don't see a command prompt, try pressing enter.
    

    מחולל העומסים שולח באופן רציף 10 בקשות לשנייה (RPS) אל שער הכניסה. למרות שתעבורת הנתונים מגיעה מאזור Google Cloud , מאזן העומסים מתייחס אליה כתעבורת נתונים של לקוחות שמגיעה מהחוף המערבי של ארה"ב. כדי לדמות מגוון לקוחות ריאליסטי, מחולל העומסים שולח כל בקשת HTTP כחיבור TCP חדש, מה שאומר שהתנועה מתחלקת בין ה-Pods של ה-Backend בצורה שווה יותר.

    תהליך יצירת התנועה למרכז הבקרה יימשך עד 5 דקות.

  2. צפייה בלוח הבקרה של Metrics Explorer. מופיעות שתי שורות שמציינות כמה תנועה מאוזנת בעומס לכל אחד מהאשכולות:

    תרשים שמציג את עומס התנועה שמאוזן באשכולות

    אפשר לראות ש-us-west1-a מקבלת תנועה של כ-10 בקשות לשנייה, ואילו us-east1-b לא מקבלת תנועה. מכיוון שגנרטור התנועה פועל ב-us-west1, כל התנועה נשלחת לשירות באשכול gke-west-1.

  3. מפסיקים את מחולל העומסים באמצעות Ctrl+C, ואז מוחקים את ה-pod:

    kubectl delete pod loadgen --context gke-west-1
    

בדיקה עם 30 בקשות לשנייה

  1. מפעילים מחדש את מחולל העומסים, אבל מגדירים אותו לשליחת 30 בקשות לשנייה:

    kubectl run --context gke-west-1 -i --tty --rm loadgen  \
        --image=cyrilbkr/httperf  \
        --restart=Never  \
        -- /bin/sh -c 'httperf  \
        --server=GATEWAY_IP_ADDRESS  \
        --hog --uri="/zone" --port 80  --wsess=100000,1,1 --rate 30'
    

    תהליך יצירת התנועה למרכז הבקרה יימשך עד 5 דקות.

  2. צופים במרכז הבקרה של Cloud Ops.

    תרשים שבו מוצגת תנועה שגולשת ל-gke-east-1

    אמור להופיע ש-20 RPS נשלחים אל us-west1-a ו-10 RPS אל us-east1-b. המשמעות היא שהשירות ב-gke-west-1 נמצא בשימוש מלא, ו-10 בקשות לשנייה של תעבורת נתונים מועברות לשירות ב-gke-east-1.

  3. מפסיקים את מחולל העומסים באמצעות Ctrl+C, ואז מוחקים את ה-Pod:

    kubectl delete pod loadgen --context gke-west-1
    

בדיקה עם 60 בקשות לשנייה

  1. פורסים את מחולל העומסים שהוגדר לשליחת 60 בקשות לשנייה:

    kubectl run --context gke-west-1 -i --tty --rm loadgen  \
        --image=cyrilbkr/httperf  \
        --restart=Never  \
        -- /bin/sh -c 'httperf  \
        --server=GATEWAY_IP_ADDRESS  \
        --hog --uri="/zone" --port 80  --wsess=100000,1,1 --rate 60'
    
  2. ממתינים 5 דקות ומציגים את לוח הבקרה של Cloud Ops. עכשיו אמור להופיע ששני האשכולות מקבלים בערך 30 בקשות לשנייה. מכיוון שכל השירותים נמצאים בשימוש יתר ברחבי העולם, אין תנועה שגולשת מעבר לקיבולת והשירותים סופגים את כל התנועה שהם יכולים.

    תרשים שבו מוצג שימוש יתר בשירותים

  3. מפסיקים את מחולל העומסים באמצעות Ctrl+C, ואז מוחקים את ה-Pod:

    kubectl delete pod loadgen --context gke-west-1
    

הסרת המשאבים

אחרי שתסיימו את התרגילים במסמך הזה, תצטרכו לפעול לפי השלבים הבאים כדי להסיר משאבים ולמנוע חיובים לא רצויים בחשבון:

  1. מחיקת האשכולות.

  2. ביטול הרישום של האשכולות ב-Fleet אם אין צורך לרשום אותם למטרה אחרת.

  3. השבתת התכונה multiclusterservicediscovery:

    gcloud container fleet multi-cluster-services disable
    
  4. השבתת Multi Cluster Ingress:

    gcloud container fleet ingress disable
    
  5. משביתים את ממשקי ה-API:

    gcloud services disable \
        multiclusterservicediscovery.googleapis.com \
        multiclusteringress.googleapis.com \
        trafficdirector.googleapis.com \
        --project=PROJECT_ID
    

פתרון בעיות

אין מקור תקין

התסמין:

יכולה להתרחש הבעיה הבאה כשיוצרים Gateway אבל אין גישה לשירותי ה-Backend (קוד תגובה 503):

no healthy upstream

הסיבה:

הודעת השגיאה הזו מציינת שכלי הבדיקה של בדיקת תקינות לא יכול למצוא שירותי קצה עורפיים תקינים. יכול להיות שהשירותים שלכם ב-Backend תקינים, אבל תצטרכו להתאים אישית את בדיקות התקינות.

פתרון עקיף:

כדי לפתור את הבעיה, מתאימים אישית את בדיקת התקינות בהתאם לדרישות של האפליקציה (לדוגמה, /health) באמצעות HealthCheckPolicy.

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