פתרון בעיות במתזמן של Kubernetes

בדף הזה מוסבר איך לפתור בעיות בתזמן של Kubernetes ‏(kube-scheduler) ב-Google Distributed Cloud.

מערכת Kubernetes תמיד מתזמנת את ה-Pods לאותה קבוצת צמתים

השגיאה הזו יכולה להופיע בכמה דרכים שונות:

  • ניצול לא מאוזן של האשכול. אפשר לבדוק את ניצול האשכול לכל צומת באמצעות הפקודה kubectl top nodes. בדוגמה הבאה, שהיא מוגזמת, אפשר לראות שימוש מוגבר בצמתים מסוימים:

    NAME                   CPU(cores)   CPU%      MEMORY(bytes)   MEMORY%
    XXX.gke.internal       222m         101%       3237Mi          61%
    YYY.gke.internal       91m          0%         2217Mi          0%
    ZZZ.gke.internal       512m         0%         8214Mi          0%
    
  • יותר מדי בקשות. אם מתזמנים הרבה Pods בבת אחת לאותו צומת, וה-Pods האלה שולחים בקשות HTTP, יכול להיות שהצומת יוגבל על ידי הגבלת קצב. השגיאה הנפוצה שהשרת מחזיר בתרחיש הזה היא 429 Too Many Requests.

  • השירות לא זמין. לדוגמה, שרת אינטרנט שמארח צומת תחת עומס גבוה עשוי להשיב לכל הבקשות עם שגיאות 503 Service Unavailable עד שהעומס עליו יפחת.

כדי לבדוק אם יש לכם Pods שתמיד מתוזמנים לאותם צמתים, מבצעים את השלבים הבאים:

  1. מריצים את פקודת kubectl הבאה כדי לראות את הסטטוס של ה-Pods:

    kubectl get pods -o wide -n default
    

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

    NAME                               READY  STATUS   RESTARTS  AGE  IP             NODE
    nginx-deployment-84c6674589-cxp55  1/1    Running  0         55s  10.20.152.138  10.128.224.44
    nginx-deployment-84c6674589-hzmnn  1/1    Running  0         55s  10.20.155.70   10.128.226.44
    nginx-deployment-84c6674589-vq4l2  1/1    Running  0         55s  10.20.225.7    10.128.226.44
    

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

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

  1. הגברת רמת הפירוט של הרישום ביומן:

    1. עורכים את הפריסה kube-scheduler:

      kubectl --kubeconfig ADMIN_CLUSTER_KUBECONFIG edit deployment kube-scheduler \
        -n USER_CLUSTER_NAMESPACE
      
    2. מוסיפים את הדגל --v=5 בקטע spec.containers.command:

      containers:
      - command:
      - kube-scheduler
      - --profiling=false
      - --kubeconfig=/etc/kubernetes/scheduler.conf
      - --leader-elect=true
      - --v=5
      
  2. אחרי שמסיימים לפתור את הבעיות, מאפסים את רמת הפירוט בחזרה לרמת ברירת המחדל:

    1. עורכים את הפריסה kube-scheduler:

      kubectl --kubeconfig ADMIN_CLUSTER_KUBECONFIG edit deployment kube-scheduler \
        -n USER_CLUSTER_NAMESPACE
      
    2. מגדירים את רמת הפירוט בחזרה לערך ברירת המחדל:

      containers:
      - command:
      - kube-scheduler
      - --profiling=false
      - --kubeconfig=/etc/kubernetes/scheduler.conf
      - --leader-elect=true
      

מגבלות על פיזור הטופולוגיה

אפשר להשתמש באילוצים של פיזור טופולוגי כדי לפזר את ה-Pods באופן שווה בין הצמתים בהתאם לzones,‏ regions,‏ node או לטופולוגיה אחרת שהוגדרה בהתאמה אישית.

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

apiVersion: apps/v1
kind: Deployment
metadata:
  name: topology-spread-deployment
  labels:
    app: myapp
spec:
  replicas: 30
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      topologySpreadConstraints:
      - maxSkew: 1 # Default. Spreads evenly. Maximum difference in scheduled Pods per Node.
        topologyKey: kubernetes.io/hostname
        whenUnsatisfiable: DoNotSchedule # Default. Alternatively can be ScheduleAnyway
        labelSelector:
          matchLabels:
            app: myapp
        matchLabelKeys: # beta in 1.27
        - pod-template-hash
      containers:
      # pause is a lightweight container that simply sleeps
      - name: pause
        image: registry.k8s.io/pause:3.2

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

  • labels.app: myapp של ה-Pod תואם לlabelSelector של האילוץ.
  • הערך topologyKey מציין kubernetes.io/hostname. התווית הזו מצורפת באופן אוטומטי לכל הצמתים ומאוכלסת בשם המארח של הצומת.
  • ההגדרה matchLabelKeys מונעת מפריסות של גרסאות חדשות להתחשב ב-Pods של גרסאות ישנות כשמחשבים איפה לתזמן Pod. התווית pod-template-hash מאוכלסת באופן אוטומטי על ידי פריסה.

אנטי-זיקה של Pod

Pod anti-affinity מאפשרת להגדיר אילוצים לגבי האפשרות למקם Pods באותו צומת.

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

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-affinity-deployment
  labels:
    app: myapp
spec:
  replicas: 30
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      name: with-pod-affinity
      labels:
        app: myapp
    spec:
      affinity:
        podAntiAffinity:
          # requiredDuringSchedulingIgnoredDuringExecution
          # prevents Pod from being scheduled on a Node if it
          # does not meet criteria.
          # Alternatively can use 'preferred' with a weight
          # rather than 'required'.
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - myapp
            # Your nodes might be configured with other keys
            # to use as `topologyKey`. `kubernetes.io/region`
            # and `kubernetes.io/zone` are common.
            topologyKey: kubernetes.io/hostname
      containers:
      # pause is a lightweight container that simply sleeps
      - name: pause
        image: registry.k8s.io/pause:3.2

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

כשמשתמשים בתכונה Pod anti-affinity, צריך להתחשב בשיקולים הבאים:

  • labels.app: myapp של ה-Pod תואם לlabelSelector של האילוץ.
  • הערך topologyKey מציין kubernetes.io/hostname. התווית הזו מצורפת באופן אוטומטי לכל הצמתים ומאוכלסת בשם המארח של הצומת. אתם יכולים להשתמש בתוויות אחרות אם האשכול תומך בהן, כמו region או zone.

משיכת תמונות של קונטיינרים מראש

אם אין אילוצים אחרים, כברירת מחדל kube-scheduler מעדיף לתזמן Pods על Nodes שכבר הורידו אליהם את קובץ אימג' של קונטיינר. ההתנהגות הזו עשויה להיות רלוונטית באשכולות קטנים יותר ללא הגדרות תזמון אחרות, שבהם אפשר להוריד את התמונות בכל צומת. עם זאת, מומלץ להסתמך על הרעיון הזה רק כמוצא אחרון. פתרון טוב יותר הוא להשתמש ב-nodeSelector, במגבלות של פיזור טופולוגי או בזיקה / אנטי-זיקה. מידע נוסף זמין במאמר בנושא הקצאת Pods לצמתים.

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

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: prepulled-images
spec:
  selector:
    matchLabels:
      name: prepulled-images
  template:
    metadata:
      labels:
        name: prepulled-images
    spec:
      initContainers:
        - name: prepulled-image
          image: IMAGE
          # Use a command the terminates immediately
          command: ["sh", "-c", "'true'"]
      containers:
      # pause is a lightweight container that simply sleeps
      - name: pause
        image: registry.k8s.io/pause:3.2

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

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

לקבלת עזרה נוספת, אפשר לפנות אל Cloud Customer Care.

אפשר גם לעיין במאמר קבלת תמיכה לקבלת מידע נוסף על מקורות מידע לתמיכה, כולל: