פתרון בעיות בהפעלת עומסי עבודה ב-Cloud Service Mesh

במסמך הזה מוסבר על בעיות נפוצות ב-Cloud Service Mesh ואיך לפתור אותן. לקבלת עזרה נוספת, אפשר לעיין במאמר בנושא קבלת תמיכה.

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

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

  Error adding/updating listener(s) 0.0.0.0_80: cannot bind '0.0.0.0:80': Permission denied

הדוגמה הבאה היא קובץ ה-YAML של פריסת שער ליציאה:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: istio-egressgateway
spec:
  selector:
    matchLabels:
      app: istio-egressgateway
      istio: egressgateway
  template:
    metadata:
      annotations:
        # This is required to tell Anthos Service Mesh to inject the gateway with the
        # required configuration.
        inject.istio.io/templates: gateway
      labels:
        app: istio-egressgateway
        istio: egressgateway
    spec:
      containers:
      - name: istio-proxy
        image: auto # The image will automatically update each time the pod starts.
        resources:
          limits:
            cpu: 2000m
            memory: 1024Mi
          requests:
            cpu: 100m
            memory: 128Mi
      # Allow binding to all ports (such as 80 and 443)
      securityContext:
        sysctls:
        - name: net.ipv4.ip_unprivileged_port_start
          value: "0"
      serviceAccountName: istio-egressgateway 

החיבור נדחה כשמגיעים לנקודת קצה של Cloud Service Mesh

יכול להיות שתיתקלו מדי פעם בשגיאות של דחיית חיבור (ECONNREFUSED) בתקשורת מהאשכולות לנקודות הקצה, למשל Memorystore Redis,‏ Cloud SQL או כל שירות חיצוני אחר שעומס העבודה של האפליקציה צריך לגשת אליו.

המצב הזה יכול לקרות אם עומס העבודה של האפליקציה מתחיל מהר יותר מהקונטיינר istio-proxy ‏ (Envoy) ומנסה להגיע לנקודת קצה חיצונית. בשלב הזה, הפקודה istio-init ‏ (initContainer) כבר הופעלה, ולכן יש כללי iptables שמפנים את כל התנועה היוצאת אל Envoy. מכיוון ש-istio-proxy עדיין לא מוכן, כללי ה-iptables יפנו את התנועה אל sidecar proxy שעדיין לא הופעל, ולכן האפליקציה מקבלת את השגיאה ECONNREFUSED.

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

  1. בדוק את יומני ה-stackdriver באמצעות המסנן הבא כדי לזהות באילו פודים הייתה הבעיה.

    בדוגמה הבאה מוצגת הודעת שגיאה אופיינית:

    Error: failed to create connection to feature-store redis, err=dial tcp   192.168.9.16:19209: connect: connection refused
    [ioredis] Unhandled error event: Error: connect ECONNREFUSED
    
  2. חפש אחר מופע של הבעיה. אם אתם משתמשים בגרסה הקודמת של Stackdriver, אז משתמשים ב-resource.type="container".

    resource.type="k8s_container"
    textPayload:"$ERROR_MESSAGE$"
    
  3. מרחיבים את המופע האחרון כדי לקבל את שם הפוד, ורושמים את pod_name בקטע resource.labels.

  4. מאתרים את המופע הראשון של הבעיה בפוד:

    resource.type="k8s_container"
    resource.labels.pod_name="$POD_NAME$"
    

    פלט לדוגמה:

    E 2020-03-31T10:41:15.552128897Z
    post-feature-service post-feature-service-v1-67d56cdd-g7fvb failed to create
    connection to feature-store redis, err=dial tcp 192.168.9.16:19209: connect:
    connection refused post-feature-service post-feature-service-v1-67d56cdd-g7fvb
    
  5. רושמים את חותמת הזמן של השגיאה הראשונה בפוד הזה.

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

    resource.type="k8s_container"
    resource.labels.pod_name="$POD_NAME$"
    

    פלט לדוגמה:

    I 2020-03-31T10:41:15Z spec.containers{istio-proxy} Container image "docker.io/istio/proxyv2:1.3.3" already present on machine  spec.containers{istio-proxy}
    I 2020-03-31T10:41:15Z spec.containers{istio-proxy} Created container  spec.containers{istio-proxy}
    I 2020-03-31T10:41:15Z spec.containers{istio-proxy} Started container  spec.containers{istio-proxy}
    I 2020-03-31T10:41:15Z spec.containers{APP-CONTAINER-NAME} Created container  spec.containers{APP-CONTAINER-NAME}
    W 2020-03-31T10:41:17Z spec.containers{istio-proxy} Readiness probe failed: HTTP probe failed with statuscode: 503  spec.containers{istio-proxy}
    W 2020-03-31T10:41:26Z spec.containers{istio-proxy} Readiness probe failed: HTTP probe failed with statuscode: 503  spec.containers{istio-proxy}
    W 2020-03-31T10:41:28Z spec.containers{istio-proxy} Readiness probe failed: HTTP probe failed with statuscode: 503  spec.containers{istio-proxy}
    W 2020-03-31T10:41:31Z spec.containers{istio-proxy} Readiness probe failed: HTTP probe failed with statuscode: 503  spec.containers{istio-proxy}
    W 2020-03-31T10:41:58Z spec.containers{istio-proxy} Readiness probe failed: HTTP probe failed with statuscode: 503  spec.containers{istio-proxy}
    
  7. משתמשים בחותמות הזמן של השגיאות ושל אירועי ההפעלה של istio-proxy כדי לוודא שהשגיאות מתרחשות כש-Envoy לא מוכן.

    אם השגיאות מתרחשות בזמן שהקונטיינר istio-proxy עדיין לא מוכן, זה נורמלי לקבל שגיאות של דחיית חיבור. בדוגמה הקודמת, הפוד ניסה להתחבר ל-Redis ברגע ש-2020-03-31T10:41:15.552128897Z אבל עדיין נכשל בבדיקות המוכנות של 2020-03-31T10:41:58Z istio-proxy.

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

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

  8. הוסף הערות לתצורה ברמת הפוד. האפשרות הזו זמינה רק ברמת הפוד ולא ברמה הגלובלית.

    annotations:
    proxy.istio.io/config: '{ "holdApplicationUntilProxyStarts": true }'
    
  9. משנים את קוד האפליקציה כך שיבדוק אם Envoy מוכן לפני שינסה לשלוח בקשות אחרות לשירותים חיצוניים. לדוגמה, כשמפעילים את האפליקציה, מתחיל לולאה ששולחת בקשות לנקודת הקצה של הבריאות של istio-proxy, והיא ממשיכה רק אחרי שמתקבל סטטוס 200. נקודת הקצה של הבדיקה istio-proxy health היא:

    http://localhost:15020/healthz/ready
    

מרוץ תהליכים במהלך הזרקת קובץ עזר בין Vault לבין Cloud Service Mesh

כשמשתמשים ב-vault לניהול סודות, לפעמים vault מוסיף sidecar לפני istio, וכתוצאה מכך הפודים נתקעים בסטטוס Init. במקרה כזה, הפודים שנוצרו נתקעים בסטטוס Init אחרי הפעלה מחדש של פריסה או פריסה של פוד חדש. לדוגמה:

E 2020-03-31T10:41:15.552128897Z
post-feature-service post-feature-service-v1-67d56cdd-g7fvb failed to create
connection to feature-store redis, err=dial tcp 192.168.9.16:19209: connect:
connection refused post-feature-service post-feature-service-v1-67d56cdd-g7fvb

הבעיה הזו נגרמת בגלל מצב מירוץ. גם Istio וגם vault מזריקים את ה-sidecar, ו-Istio צריך להיות האחרון שעושה את זה. ה-proxy של istio לא פועל במהלך אתחול הקונטיינרים. הקונטיינר istio init מגדיר כללי iptables כדי להפנות את כל התנועה לשרת ה-proxy. מכיוון שהיא עדיין לא פועלת, הכללים האלה מפנים לשום מקום וחוסמים את כל התנועה. לכן קובץ ה-init container צריך להיות האחרון, כדי שהפרוקסי יפעל מיד אחרי הגדרת כללי ה-iptables. לצערנו, הסדר לא דטרמיניסטי, ולכן אם Istio מוזרק קודם, הוא נשבר.

כדי לפתור את הבעיה הזו, צריך לאפשר את כתובת ה-IP של vault כדי שהתעבורה שמועברת לכתובת ה-IP של Vault לא תופנה מחדש ל-Envoy Proxy, שלא מוכן עדיין ולכן חוסם את התקשורת. כדי לעשות את זה, צריך להוסיף הערה חדשה בשם excludeOutboundIPRanges.

ב-Cloud Service Mesh מנוהל, אפשר לעשות זאת רק ברמת הפריסה או ה-Pod בקטע spec.template.metadata.annotations, לדוגמה:

apiVersion: apps/v1
kind: Deployment
...
...
...
spec:
  template:
    metadata:
      annotations:
        traffic.sidecar.istio.io/excludeOutboundIPRanges:

ב-Cloud Service Mesh בתוך האשכול, יש אפשרות להגדיר אותו כגלובלי באמצעות IstioOperator בקטע spec.values.global.proxy.excludeIPRanges, לדוגמה:

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  values:
    global:
      proxy:
        excludeIPRanges: ""

אחרי שמוסיפים את ההערה, מפעילים מחדש את עומסי העבודה.

זיהוי סוג תמונת ה-proxy שנעשה בה שימוש באשכול

כדי לזהות את סוג תמונת ה-proxy (default או distroless) שמשמשת בפוד ספציפי, אפשר לבדוק את מפרט הפוד.

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

kubectl get pod POD_NAME -n NAMESPACE -o jsonpath='{.spec.containers[?(@.name=="istio-proxy")].image}'
  • אם נתיב התמונה לא מכיל את התווים -distroless בתג או בסיומת, נעשה שימוש בתמונה default.
  • אם נתיב התמונה מכיל-distroless בתג או בסיומת, הוא משתמש ב-distroless תְמוּנָה.

אימות כוונת ההגדרה

אפשר להגדיר את סוג התמונה בשתי דרכים (ההערה קודמת ל-MeshConfig):

  1. הערה לגבי Pod: בודקים אם ל-Pod יש את ההערה sidecar.istio.io/proxyImageType.

    kubectl get pod POD_NAME -n NAMESPACE -o jsonpath='{.metadata.annotations["sidecar.istio.io/proxyImageType"]}'
    
  2. ‫MeshConfig: בודקים את ההגדרה defaultConfig.image.imageType ב-ConfigMap‏ istio-RELEASE_CHANNEL במרחב השמות istio-system.

הערה: באשכולות עם מישור הבקרה המנוהל TRAFFIC_DIRECTOR:

  • עבור אשכולות שהוקצו ישירות עם מישור בקרה מנוהל TRAFFIC_DIRECTOR, יש תמיכה רק בתמונות distroless. המערכת מתעלמת משינויים בסוגים אחרים של תמונות.
  • במקרים של אשכולות שהועברו לTRAFFIC_DIRECTOR מישור הבקרה (כמו אשכולות שהועברו מ-CSM-ISTIOD), סוג התמונה הוא default כברירת מחדל, אבל אפשר להפעיל את האפשרות distroless באמצעות MeshConfig או הערת פוד. אין תמיכה בשינויים בסוגי תמונות אחרים (כמו debug).