פתרון בעיות בהפעלת עומסי עבודה ב-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 יפנו את התנועה אל קובץ עזר חיצוני שעדיין לא הופעל, ולכן האפליקציה מקבלת את השגיאה 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 עדיין לא מוכן, זה נורמלי לקבל שגיאות של דחיית חיבור. בדוגמה הקודמת, ה-pod ניסה להתחבר ל-Redis ברגע ש-2020-03-31T10:41:15.552128897Z, אבל עדיין נכשלו בדיקות המוכנות של istio-proxy ב-2020-03-31T10:41:58Z.

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

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

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

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

    http://localhost:15020/healthz/ready
    

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

כשמשתמשים ב-vault לניהול סודות, לפעמים vault מוסיף sidecar לפני istio, וכתוצאה מכך ה-Pods נתקעים בסטטוס 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: ""

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