פתרון בעיות בניראות של רשת GKE

במסמך הזה מוסבר איך לאבחן בעיות ברשת באשכולות של Google Kubernetes Engine‏ (GKE) באמצעות יכולת הצפייה של GKE Dataplane V2,‏ Hubble ו-Cloud Monitoring.

לסקירה כללית של הארכיטקטורה ושיטות מומלצות, קראו את המאמר שיטות מומלצות לתצפית על הרשת.

עץ החלטות לפתרון בעיות ומושגי ליבה

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

שאלה לאבחון או תיאור של הבעיה רמת החשד ההליך המומלץ
ה-Pods לא מצליחים לפתור דומיינים חיצוניים או שירותי Kubernetes פנימיים (פסקי זמן קצובים של DNS,‏ NXDOMAIN,‏ SERVFAIL). רמה 1: Pod ושירות (DNS) אבחון כשלים בפענוח DNS
השירותים לא יכולים לתקשר, יש זמן קצוב לתפוגה של החיבור או שחבילות נתונים נמחקות בין Pods. רמה 1: Pod ושירות (מדיניות או אובדן מנות) אבחון של אובדן חבילות וחסימות של NetworkPolicy
עומס התנועה ברפליקות של עומס העבודה לא אחיד, או שיש שיעור גבוה של איפוסים של TCP (RST) ביומנים של Pods. רמה 1: Pod ושירות (איזון עומסים או העברה) אבחון של חוסר איזון בתנועה ואיפוסים של TCP
זמן אחזור כללי, פסקי זמן לסירוגין או הפעלות מחדש של CNI בצמתים. רמה 2: צומת ו-CNI (ליבה) מיון לפי זמן אחזור ברמת הצומת וצווארי בקבוק של CNI
פודים לא יכולים להגיע למשאבים מחוץ ל-GKE (כמו Cloud SQL, ממשקי API חיצוניים או רשתות VPC אחרות). רמה 3: VPC וניתוב בידוד בעיות ב-GKE לעומת בעיות ב-VPC או בקישוריות חיצונית
צריך הדמיית נתיב אוטומטית כדי לוודא אם כללי חומת האש, הנתיבים או NetworkPolicies חוסמים את התנועה. רמה 3: VPC וניתוב (סימולציה) אבחון הקישוריות באמצעות בדיקות קישוריות
צריך לזהות אילו עומסי עבודה שולחים תנועה לאינטרנט ועוברים NAT. רמה 4: שער חיצוני ועלות זיהוי תנועת NAT (תעבורת נתונים יוצאת לאינטרנט)
עלויות גבוהות של העברת נתונים בין אזורים, או צורך להציג באופן חזותי את המשתמשים שיוצרים הכי הרבה תעבורת נתונים בלי לכתוב שאילתות SQL. רמה 4: שער חיצוני ועלות ניתוח העלויות והביצועים של תנועת הנתונים באשכול באמצעות Flow Analyzer

מושגי ליבה בנושא רשתות

אם אתם חדשים ב-Kubernetes או ברשתות של Google Cloud , חשוב לזכור את מושגי הליבה הבאים:

  • ‫eBPF (מסנן חבילות מורחב של ברקלי): טכנולוגיה של מערכת הפעלה שמאפשרת להריץ תוכניות מאובטחות של ניטור לצורכי אבטחה וניתוב ישירות בתוך ליבת Linux. ‫GKE Dataplane V2 משתמש ב-eBPF כדי לנתב חבילות ולאכוף את NetworkPolicies עם תקורה מינימלית של ביצועים.
  • ‫הסתרת IP‏ (SNAT): התהליך של כתיבה מחדש של כתובת ה-IP של המקור של חבילת נתונים. כש-Pod של GKE (שיש לו כתובת IP פרטית) מתקשר עם האינטרנט או עם משאבי VPC חיצוניים, GKE מסווה (משכתב) את כתובת ה-IP של ה-Pod לכתובת ה-IP של הצומת, כדי שמערכות חיצוניות יידעו איך לנתב את התשובה.
  • מעקב אחרי חיבורים (Conntrack): תכונה של הליבה שעוקבת אחרי כל החיבורים הפעילים לרשת. באשכולות של GKE Dataplane V2, המעקב הזה מפוצל בין שתי טבלאות: טבלת conntrack הרגילה של ליבת Linux (שמשמשת את ip-masq-agent) וטבלת conntrack שמנוהלת על ידי Cilium ו-GKE Dataplane V2, שמאוחסנת במפת eBPF. אם צומת מטפל ביותר מדי חיבורים בו-זמנית, אחת מטבלאות המעקב האלה יכולה להתמלא (מיצוי conntrack), מה שגורם לצומת להשמיט חבילות חדשות בלי להודיע על כך.
  • ‫Hubble: מנוע התצפיתיות של GKE Dataplane V2. הוא פועל על גבי eBPF ומספק תצוגה בזמן אמת של זרימת התנועה, חבילות שנמחקו והערכה של NetworkPolicy.

רמה 1: ניטור Pod ו-Service (אפליקציה)

שכבת ה-Pod והשירות כוללת תקשורת רשת בין Pods, שירותים ו-DNS של אשכולות. בעיות בשכבה הזו בדרך כלל מתבטאות בפסקי זמן קצובים של חיבור לאפליקציה, כשלים בתרגום שם (name resolution) או חלוקת עומסים לא אחידה.

אבחון כשלים בפענוח DNS

לפני שפותרים בעיות ב-DNS, צריך לבדוק את היקף האבחון ואת הדרישות המוקדמות:

  • תחום המיקוד: תקשורת בין Pods לבין CoreDNS או NodeLocal DNSCache, זמן אחזור של DNS, זמן קצוב לתפוגה של פענוח DNS במעלה הזרם ואימות של FQDN NetworkPolicy.
  • דרישות מוקדמות: הפעלת המדדים של GKE Dataplane V2; גישה ל-kubectl.
  • תאימות ל-CNI: ‏GKE Dataplane V2 (נתיב נתונים מתקדם) ו-GKE CNI רגיל.
  • תיאור הבעיה: ביומני ה-Pod מופיעות השגיאות dial tcp: lookup <domain>: i/o timeout,‏ NXDOMAIN או זמן אחזור לסירוגין בקריאות ל-API יוצאות.
  • המטרה: לבדוק אם מקור הכשל ב-DNS הוא בתוך האשכול (kube-dns או רוויה של NodeLocal DNSCache), חסימה של יציאת UDP או TCP‏ 53 על ידי NetworkPolicy, או פגיעה ברשת במעלה הזרם.

שלב 1: בדיקת נגישות בסיסית

לפני שפותרים בעיות בשכבות DNS, צריך לוודא שאפשר להגיע ליעד ישירות באמצעות כתובת ה-IP שלו מתוך ה-Pod המושפע:

# 1. Test raw IP reachability (bypasses DNS entirely)
kubectl exec -it my-pod -n default -- curl -v --connect-timeout 5 http://10.240.0.10:8080

# 2. Test domain resolution
kubectl exec -it my-pod -n default -- curl -v --connect-timeout 5 http://my-service.default.svc.cluster.local:8080
  • אם קישור דרך IP מצליח אבל החיבור לדומיין נכשל: הבעיה מבודדת לשכבת פענוח ה-DNS. עוברים לשלב 2.
  • אם שתי הבדיקות נכשלות: הבעיה היא בניתוב או באכיפת המדיניות ברמת הרשת. עוברים לקטע אבחון של אובדן חבילות וחסימות של NetworkPolicy.
בדיקה אם יש אכלוס מראש של DNS ב-NetworkPolicy FQDN

אם באשכול שלכם נעשה שימוש ב-NetworkPolicies שמבוססות על FQDN ‏(FQDNNetworkPolicy), ודאו ששם הדומיין מותר באופן מפורש. אם Pod שולח שאילתה לדומיין חיצוני שלא מאוכלס מראש במטמון של שרת ה-proxy ל-DNS של GKE Dataplane V2, או שלא מותר לפי המדיניות, GKE Dataplane V2 חוסם את תעבורת הנתונים היוצאת לכתובת ה-IP שנפתרה:

# Verify whether UDP or TCP port 53 egress is permitted in the Pod's namespace
kubectl get networkpolicy -n default -o yaml | grep -A 5 -B 2 "port: 53"

שלב 2: בדיקת מדדי DNS ב-Cloud Monitoring

ב-GKE, מדדי ה-DNS המובנים מוצגים ב-Cloud Monitoring עם הקידומת kubernetes.io/networking/dns/.

כדי לבדוק את מדדי ה-DNS ב-Cloud Monitoring:

  1. במסוף Google Cloud , נכנסים לדף Cloud Monitoring > Dashboards.
  2. בוחרים את מרכז הבקרה המוגדר מראש GKE DNS Observability - Cluster View (או עוברים אל Metrics Explorer ומסננים לפי kubernetes.io/networking/dns/).
  3. בודקים את האותות העיקריים הבאים (ב-NodeLocal DNSCache, מחליפים את kubedns ב-node_local_dns בנתיב המדד):
שם המדד סף האזהרה שורש הבעיה
kubernetes.io/networking/dns/kubedns/max_concurrent_rejected_request_count > 0 הגעת למגבלת השאילתות בו-זמנית. ‫kube-dns או NodeLocal DNSCache מבטלים שאילתות.
kubernetes.io/networking/dns/kubedns/dns_request_latencies p99 > 100ms זמן אחזור ארוך מקצה לקצה של פענוח DNS ב-kube-dns או ב-NodeLocal DNSCache.
kubernetes.io/networking/dns/kubedns/forwarding_request_latencies p99 > 100ms זמן אחזור או רוויה של שרת DNS במעלה הזרם.
רצף טריאז' של ביצועי DNS וזמן קצוב לתפוגה

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

  1. בדיקת שיעור המציאות במטמון (cache hit): שאילתה ל-kubernetes.io/networking/dns/kubedns/dns_cache_request_count (או ל-node_local_dns/dns_cache_request_count) מקובצת לפי התווית cache_status. אם הערך של cache_status="hit" נמוך והערך של cache_status="miss" גבוה, יכול להיות שהאפליקציות שולחות שאילתות שהן לא FQDN (למשל my-service במקום my-service.default.svc.cluster.local), מה שגורם למעבר בנתיב החיפוש בכל הרשומות ב-/etc/resolv.conf.
  2. בדיקת זמן האחזור במעלה הזרם: ערך גבוה של forwarding_request_latencies מצביע על בעיות בשרת ה-DNS במעלה הזרם (לדוגמה, DNS מקומי של הארגון שאליו מגיעים דרך Cloud Interconnect או Cloud VPN, או מגבלות של Cloud DNS).
  3. ביקורת על שינויים בהתאמה אישית של DNS: בודקים את ה-ConfigMaps של kube-dns בהתאמה אישית כדי לאתר הגדרות שגויות של סטאבים או העברות במעלה הזרם:

    kubectl get configmap kube-dns -n kube-system -o yaml
    

שלב 3: סטרימינג של תנועת DNS בזמן אמת באמצעות Hubble CLI

משתמשים בשם הסודי של כלי העזר של Hubble CLI כדי לבדוק בקשות ותשובות של DNS בשידור חי, שמועברות בסטרימינג מהקרנל של הצומת:

# 1. Ensure the helper alias is set in your terminal session
alias gke-hubble="kubectl exec -it -n gke-managed-dpv2-observability deployment/hubble-relay -c hubble-cli -- hubble"

# 2. Observe live DNS (Port 53) traffic for a specific Pod
gke-hubble observe --pod default/my-pod --port 53

שלב 4: מאמתים את התיקון

אם היו דחיות של שאילתות בו-זמניות, צריך להחיל את NodeLocal DNSCache כדי לספוג חיפושי DNS בתדירות גבוהה ישירות בצומת, בלי להגיע למגבלות של kube-dns ברמת האשכול:

# Verify NodeLocal DNSCache DaemonSet is running
kubectl get daemonset node-local-dns -n kube-system

אבחון של אובדן חבילות וחסימות של NetworkPolicy

לפני שבודקים את דחיות המדיניות והחבילות שנמחקו, צריך לבדוק את היקף האבחון ואת הדרישות המוקדמות:

  • תחום התמקדות: ירידה בתנועה בין אובייקטים של Pod או בין אובייקטים של Pod ו-Service, אכיפת NetworkPolicy, סיבות לירידה ב-eBPF של הליבה.
  • דרישות מוקדמות: הפעלה של GKE Dataplane V2 Flow Observability; הגדרה של רישום ביומן של NetworkPolicy.
  • תאימות ל-CNI: רק GKE Dataplane V2.
  • התופעה: ניסיונות החיבור של האפליקציה נכשלים עם השגיאה Connection timed out או Connection reset by peer.
  • המטרה: לאתר את הסיבה המדויקת להשמטת החבילות ב-NetworkPolicy או ב-eBPF, בלי לבצע שינויים במדיניות האבטחה בשיטת ניסוי וטעייה.

שלב 1: מעקב אחר מדדי הירידה של Hubble

כש-GKE Dataplane V2 משמיט חבילה, הוא פולט את המדד hubble_drop_total עם תיוג של סיבת ההשמטה והמטא-נתונים של המקור והיעד. כדי לעקוב אחרי מדדי הנפילה של Hubble, מבצעים את הפעולות הבאות:

  1. אם עדיין לא עשיתם את זה, פורסים משאב של השירות המנוהל של Google Cloud ל-Prometheus PodMonitoring כדי לאסוף מדדים של Hubble:

    apiVersion: monitoring.googleapis.com/v1
    kind: PodMonitoring
    metadata:
      name: hubble-metrics
      namespace: gke-managed-dpv2-observability
    spec:
      selector:
        matchLabels:
          k8s-app: cilium
      endpoints:
      - port: hubble-metrics
        interval: 30s
    
  2. כדי לראות את הירידות לפי סיבה, מריצים את השאילתה הבאה ב-Cloud Monitoring > Metrics Explorer:

    sum by (reason) (rate(hubble_drop_total[5m])) > 0
    
  3. הסבר על קודי reason הנפוצים:

    • ‫Policy denied: מדיניות NetworkPolicy של Kubernetes חוסמת את החיבור באופן מפורש או משתמע.
    • CT: Map insertion failed: טבלת מעקב החיבורים (conntrack) מלאה.
    • ‫Unsupported L3 protocol: חבילת נתונים שהיא לא IPv4 או IPv6, או כותרת פגומה.

שלב 2: בדיקת היומנים של NetworkPolicy ב-Cloud Logging

רישום ביומן של NetworkPolicy מייצא יומני JSON מובְנים לכל ההחלטות לגבי המדיניות. כדי לשלוח שאילתה ליומני NetworkPolicy ב-Cloud Logging:

  1. במסוף Google Cloud , נכנסים לדף Cloud Logging > Logs Explorer.
  2. מריצים את השאילתה הבאה:

    resource.type="k8s_node"
    log_name:"projects/PROJECT_ID/logs/events"
    jsonPayload.connection.verdict="DENY"
    jsonPayload.src.pod_name="my-source-pod"
    
  3. בודקים את המטען הייעודי (payload) של ה-JSON:

    • ‫jsonPayload.drop_reason: הסיבה שבגללה החבילה נמחקה.
    • ‫jsonPayload.policies: רשימה של כללי NetworkPolicy שנבדקו. אם מוחזרת רשימה ריקה עם פסיקת DENY, מרחב השמות פועל במצב דחייה כברירת מחדל, ואף מדיניות לא מאפשרת את התנועה.

אם לא מופיעים יומני NetworkPolicy, צריך לוודא שהרישום ביומן מופעל במשאב המותאם אישית NetworkLogging של האשכול. לדוגמה, בודקים שהשדה spec.cluster.deny.log מוגדר ל-true:

kubectl get networklogging default -o yaml

שלב 3: מעקב אחרי חבילות שנזרקו בזמן אמת באמצעות Hubble CLI

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

gke-hubble observe --verdict DROPPED --namespace default --follow

פלט לדוגמה:

TIMESTAMP             SOURCE               DESTINATION          TYPE     VERDICT
10:14:22.102          default/frontend     default/backend:80   to-stack DROPPED (Policy denied by NetworkPolicy: backend-deny-all)

הפלט מציין את ה-NetworkPolicy המדויק שחוסם את התנועה (backend-deny-all).

שלב 4: בודקים אם יש ניצול יתר של conntrack

אם הסיבה להסרה היא CT: Map insertion failed:

  1. בודקים את יומן הפעילות של הסוכן של GKE Dataplane V2 כדי לראות אם טבלת conntrack בניצול מלא:

    kubectl logs -n kube-system daemonset/anetd -c cilium-agent --tail=100 | grep "Conntrack table full"
    
  2. בודקים את הגודל המקסימלי של טבלת ה-conntrack בצומת המושפע:

    kubectl exec -it -n kube-system daemonset/anetd -c cilium-agent -- cilium status --all-controllers | grep -i conntrack
    

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


אבחון חוסר איזון בתנועה ואיפוסים של TCP

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

  • תחום המיקוד: חוסר איזון בעומסים, כשלים בלחיצת היד של TCP, ניתוק פתאומי של החיבור.
  • דרישות מוקדמות: הפעלת המדדים של GKE Dataplane V2.
  • תאימות ל-CNI: ‏GKE Dataplane V2.
  • התופעה: רפליקות מסוימות של Pod מקבלות תנועה מוגזמת, ואחרות נשארות במצב חוסר פעילות. אפליקציות הלקוח מתעדות ביומן את השגיאות connection reset by peer או broken pipe.
  • המטרה: להבין אם חוסר האיזון בתנועה נגרם בגלל חיבורים דביקים בשכבת התעבורה (שכבה 4 של OSI, ‏TCP) או בשכבת האפליקציה (שכבה 7 של OSI, ‏HTTP/2 או gRPC), ולזהות את המקור של חבילות ה-TCP RST.

שלב 1: השוואה בין זרימת התנועה ברמת ה-Pod

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

  1. ב-Cloud Monitoring, שולחים שאילתה לגבי מספר תזרימי התעבורה הנכנסת בכל ה-Pods ב-Deployment:

    sum by (pod) (rate(pod_flow_ingress_flows_count{destination_workload="my-service"}[5m]))
    
  2. הערכת חלוקת התנועה בין ה-Pods. אם Pod אחד מקבל את רוב התנועה, צריך לבדוק אם יש שימוש חוזר בחיבור או סשנים דביקים:

    • ‫gRPC או HTTP/2: חיבורי TCP לטווח ארוך גורמים לכל הבקשות לעבור ב-TCP stream אחד ל-Pod אחד בקצה העורפי. ניתוב של שירות Kubernetes בשכבת התעבורה (שכבה 4 של OSI, ‏ TCP) לא יכול לאזן בקשות בתוך חיבור HTTP/2 קיים.
    • ‫ClientIP Session Affinity: בודקים אם השירות מוגדר עם sessionAffinity: ClientIP.
    • שירותים ללא ממשק משתמש: יכול להיות שהלקוחות יפתרו את ה-DNS פעם אחת וישמרו במטמון את כתובת ה-IP היחידה באופן קבוע.

שלב 2: ניתוח מדדי איפוס TCP

איפוסים של TCP‏ (RST) מסיימים את החיבורים באופן מיידי. הם נשלחים על ידי ליבת מערכת ההפעלה כשנקודת קצה מקבלת חבילה ליציאה לא ידועה, או כשאפליקציה סוגרת חיבור עם נתונים שלא נקראו במאגר הנתונים הזמני.

מריצים את שאילתת ה-MQL הבאה ב-Cloud Monitoring:

fetch prometheus_target
| metric 'prometheus.googleapis.com/hubble_tcp_flags_total/counter'
| filter (metric.flag == 'RST')
| align rate(1m)
| every 1m
| group_by [metric.source, metric.destination, metric.traffic_direction], sum(val())

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

  • ‫Outgoing RST (traffic_direction=egress): ה-Pod המקומי יוצר את האיפוס. בודקים אם אפליקציית ה-Pod קורסת, מגיעה למגבלת החיבורים או דוחה את החיבור באופן פעיל.
  • ‫Incoming RST (traffic_direction=ingress): המחשב המרוחק (מסד נתונים חיצוני, API או Pod מרוחק) שלח את האיפוס. בודקים את התקינות של שרת היעד ואת המצבים של חומת האש.

שלב 3: שידור חי של איפוסים של TCP

משתמשים ב-Hubble CLI כדי לתעד את לחיצת היד של האיפוס בזמן אמת:

gke-hubble observe --type trace --verdict FORWARDED --tcp-flags RST --namespace default

‫שלב 4: תיקון ופעולות לביצוע

כדי לפתור את הבעיה, בצעו את השלבים הבאים בהתאם לסיבה לחוסר האיזון בתנועה או לאיפוסים של ה-TCP:

  • לשמירת החיבור בשכבת האפליקציה (שכבה 7 של OSI) או ב-gRPC:
    • פורסים את Cloud Service Mesh כדי להפעיל איזון עומסים ברמת הבקשה בשכבת האפליקציה (שכבה 7 של OSI).
    • מגדירים מגבלות חיבור בצד הלקוח או זמן קצוב לתפוגה של keep-alive (לדוגמה, MAX_CONNECTION_AGE ו-MAX_CONNECTION_AGE_GRACE של gRPC) כדי לאלץ חידוש תקופתי של החיבור.
  • לשיוך כתובת IP של שירות: מסירים את service.spec.sessionAffinity, אלא אם מצב האפליקציה מחייב זאת.
  • לשמירת DNS במטמון של שירות ללא ממשק משתמש: ודאו שסביבות זמן הריצה של האפליקציה (כמו JVM networkaddress.cache.ttl) לא שומרות במטמון תוצאות DNS ללא הגבלת זמן.
  • במקרה של גלישת תור של בקשות ממתינות באפליקציה: כשתור ההאזנה של האפליקציה מלא, ליבת Linux משמיטה חבילות SYN נכנסות או שולחת TCP RST. להרחיב אופקית את הרפליקות של Pod או להגדיל את ה-backlog של האזנה לאפליקציה (somaxconn).

רמה 2: ניראות של צומת ו-CNI (ליבה)

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

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

‫GKE Dataplane V2 פועל כ-DaemonSet מנוהל (anetd) במרחב השמות kube-system. ב-Cloud Logging, אתם יכולים לשלוח שאילתות ליומני הביקורת של Kubernetes כדי לזהות אם משתמשים לא מורשים או סקריפטים אוטומטיים תיקנו או הפעילו מחדש את anetd:

protoPayload.methodName="io.k8s.core.v1.daemonsets.patch" OR
protoPayload.methodName="io.k8s.core.v1.daemonsets.update"
protoPayload.resourceName="namespaces/kube-system/daemonsets/anetd"

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


מיון בעיות שקשורות לזמן אחזור ברמת הצומת ולצווארי בקבוק ב-CNI

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

  • תחום המיקוד: זמן האחזור של ליבת המארח, חבילות שנזרקות בממשק של המכונה הווירטואלית, רוויה של סוכן eBPF ב-GKE Dataplane V2, מיצוי של conntrack.
  • דרישות מוקדמות: הפעלת מדדי VM של Compute Engine, גישה ל-kubectl.
  • תאימות ל-CNI: ‏GKE Dataplane V2 ו-GKE CNI רגיל.
  • התופעה: בתעבורת הנתונים בין צמתים יש עליות חדות בזמן האחזור או ירידות אקראיות, אבל תעבורת הנתונים בתוך הצומת תקינה.
  • המטרה: להבחין בין הגבלת רוחב פס של רשת ה-VM המארחת, השמטות של ליבת Linux וצווארי בקבוק ברמת ה-CNI.

שלב 1: הבחנה בין בעיות מחוץ ל-GKE לבין בעיות בתוך GKE

מריצים את בדיקת הבסיס של המכונה הווירטואלית ב-Compute Engine: פורסים מכונה וירטואלית נפרדת ב-Compute Engine באותה רשת משנה של VPC כמו הצמתים באשכול GKE. בודקים את הקישוריות מה-VM ליעד.

  • אם במכונה הווירטואלית העצמאית יש את אותן בעיות של אובדן חבילות או זמן אחזור: הבעיה נמצאת מחוץ ל-GKE (חומות אש של VPC, ‏Cloud NAT, ‏Cloud Interconnect או השרת החיצוני). עוברים לקטע בידוד בעיות ב-GKE לעומת בעיות ב-VPC או בקישוריות חיצונית.
  • אם המכונה הווירטואלית העצמאית מתקשרת כרגיל, אבל ה-Pods של GKE נכשלים: הבעיה היא בתוך GKE (ברמת הצומת eBPF,‏ conntrack או CNI). עוברים לשלב 2.

שלב 2: הבחנה בין בעיות באפליקציה לבין בעיות בשכבת הצומת ובשכבת ה-CNI

כדי לבדוק אם מקור זמן האחזור הוא באפליקציה או בצומת ובשכבת ה-CNI, מבצעים את הפעולות הבאות:

  1. בדיקת רוויית המשאבים של האפליקציה: מוודאים שהצומת לא חווה ויסות של המעבד (CPU) או לחץ על הזיכרון, שמעכבים את עיבוד החבילות במרחב המשתמש:

    kubectl top nodes
    kubectl top pods -n default
    
  2. בדיקת מספר ה-conntrack של ליבת Linux: בודקים את מספר ה-conntrack הפעילים במארח:

    # On a node where you have debugging access
    cat /proc/sys/net/netfilter/nf_conntrack_count
    cat /proc/sys/net/netfilter/nf_conntrack_max
    

    אם nf_conntrack_count מתקרב ל-nf_conntrack_max, ליבת המארח משמיטה חבילות TCP SYN חדשות.

שלב 3: אימות של אובדן חבילות ברמת המכונה הווירטואלית באמצעות נתוני הטלמטריה של Compute Engine

Google Cloud ‫Compute Engine מייצא ל-Cloud Monitoring מדדים של ממשק הרשת ברמת המכונה הווירטואלית.

ב-Cloud Monitoring > Metrics Explorer, שולחים את השאילתה:

sum by (drop_reason) (rate(compute_googleapis_com:instance_network_dropped_packets_count[5m]))
  • FQ_CODEL_DROP: חבילת הנתונים נמחקה בגלל רוויה בתור של Fair Queuing או CoDel (חריגה ממגבלת רוחב הפס של תעבורת הנתונים היוצאת של המכונה הווירטואלית).
  • ‫FIREWALL_RULE_DROP: החבילה נמחקה על ידי כלל חומת אש של VPC.
  • ‫RATE_LIMIT_DROP: החבילה נמחקה כי המכונה הווירטואלית חרגה ממכסת החבילות לשנייה (PPS) של ממשק הרשת.

שלב 4: חקירת חבילות שנזרקו ברמת הליבה באמצעות eBPF

אם המדדים ברמת ה-VM לא מראים חבילות שנמחקו, אבל עדיין יש מחיקה של חבילות בפודים של GKE, צריך לבדוק את הסוכן של GKE Dataplane V2‏ (anetd) כדי לראות אם יש מחיקות במפת ה-eBPF:

kubectl logs -n kube-system daemonset/anetd -c cilium-agent --tail=200 | grep -i "drop"

מחפשים את ct-map-insertion-failed או את fib-lookup-failed.


רמה 3: ניראות של VPC וניתוב (רשת בענן)

שכבת הניתוב של VPC והענן מחברת בין צומתי GKE לבין שירותי Google Cloud אחרים, רשתות מקומיות והאינטרנט. כשלים בשלב הזה נובעים בדרך כלל מכללי חומת האש של ה-VPC, מנתיבים בהתאמה אישית או מהגדרות של שערים.

בידוד בעיות ב-GKE לעומת בעיות ב-VPC או בקישוריות חיצונית

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

  • תחום התמקדות: בידוד הגבולות בין הניתוב הפנימי של Kubernetes לבין הניתוב בענן של VPC.
  • דרישות מוקדמות: ה-CLI של gcloud, הרשאות ליצירת בדיקות קישוריות.
  • תאימות ל-CNI: כל האשכולות.
  • הבעיה: Pods לא מצליחים להתחבר למשאב חיצוני (לדוגמה, Cloud SQL,‏ API מקומי או נקודת קצה של צד שלישי).
  • המטרה: לזהות במהירות אם אובדן החבילות מתרחש בצומת GKE או בתוך ה-VPC או הרשת החיצונית. Google Cloud

שלב 1: בדיקת בסיס של מכונה וירטואלית ב-Compute Engine

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

  1. פורסים מכונה וירטואלית זמנית של Compute Engine באותו תחום ובאותה תת-רשת של VPC כמו מאגר הצמתים של GKE:

    gcloud compute instances create gke-baseline-tester \
        --zone=us-central1-a \
        --subnet=gke-subnet \
        --machine-type=e2-micro
    
  2. מתחברים למכונה באמצעות SSH ובודקים את הקישוריות ליעד:

    curl -v --connect-timeout 5 https://api.example.com
    
  3. הערכת התוצאה:

    • אם המכונה הווירטואלית ב-Compute Engine לא מצליחה להתחבר: הבעיה היא ב-VPC או ברשת החיצונית (כללי חומת אש, טבלאות ניתוב, מיצוי כתובות IP ב-Cloud NAT או הוספת כתובות IP חיצוניות לרשימת ההיתרים).
    • אם המכונה הווירטואלית של Compute Engine מתחברת בהצלחה: הבעיה היא ב-GKE (חסימת תעבורת נתונים יוצאת על ידי NetworkPolicy,‏ Pod CIDR לא מוסווה או DNS ברמת הקונטיינר).

שלב 2: הרצת בדיקת קישוריות לפי דרישה

מריצים בדיקה של Google Cloud Connectivity Tests מה-VM של הצומת ליעד:

gcloud network-management connectivity-tests create test-node-to-dest \
    --source-instance=projects/PROJECT_ID/zones/us-central1-a/instances/gke-baseline-tester \
    --destination-ip-address=203.0.113.10 \
    --destination-port=443 \
    --protocol=TCP \
    --round-trip

בודקים את התוצאה במסוף Google Cloud כדי לזהות אם כלל של חומת האש ב-VPC או נתיב גורמים לירידה בנפח התנועה.


אבחון הקישוריות באמצעות בדיקות קישוריות

בדיקות קישוריות מדמות נתיבי חבילות במשאבים של GKE ו-VPC בלי לשלוח תנועה בזמן אמת. הסימולציה הזו בודקת את הרזולוציה של Service ClusterIP-to-Pod DNAT, את כללי הכניסה והיציאה של GKE Dataplane V2 NetworkPolicy ואת הסתרת ה-IP של הצומת (SNAT). לפני שמריצים סימולציות אוטומטיות של נתיבים, חשוב לבדוק את היקף האבחון ואת הדרישות המוקדמות:

  • תחום התמקדות: סימולציה אוטומטית של נתיב סטטי לפודים, לשירותים, ל-NetworkPolicies ולמסלולי VPC ב-GKE.
  • דרישות מוקדמות: הפעלה של Network Intelligence Center והפעלה של Network Management API.
  • תאימות ל-CNI: ‏GKE Dataplane V2 (ניתוח משופר).
  • תיאור הבעיה: ניתוקי קישוריות לא מוסברים, כשכל ההגדרות נראות תקינות בבדיקה ידנית.
  • המטרה: לדמות ולעקוב באופן סטטי אחרי כל נתיב החבילה מהמקור של Pod ליעד, ולזהות את שורת המדיניות המדויקת שגורמת לניתוק.

תרחיש א': בדיקה אם GKE NetworkPolicy חוסמת את איסוף המדדים

כשמגרדים מדדים מפודים במרחב שמות מאובטח, יכול להיות שהמדיניות NetworkPolicy שמוגדרת כברירת מחדל לדחייה תחסום את האספנים של השירות המנוהל של Google Cloud ל-Prometheus:

gcloud network-management connectivity-tests create test-gmp-to-pod \
    --source-ip-address=10.0.0.15 \
    --destination-gke-pod=projects/PROJECT_ID/locations/LOCATION/clusters/CLUSTER_NAME/k8s/namespaces/prod/pods/backend-pod \
    --destination-port=8080 \
    --protocol=TCP \
    --round-trip

בודקים את המעקב לבדיקה במסוף Google Cloud . אם הבדיקה מסתיימת בשלב GKE Network Policy evaluation עם DROP, צריך להוסיף כלל כניסה כדי לאפשר תנועה מה-Pods של ה-collector.

תרחיש ב': בדיקת הנגישות לשירות GKE

הדמיה של יכולת הגעה ממכונה וירטואלית של לקוח לשירות Kubernetes פנימי:

gcloud network-management connectivity-tests create test-vm-to-service \
    --source-instance=projects/PROJECT_ID/zones/us-central1-a/instances/client-vm \
    --destination-ip-address=10.96.0.100 \
    --destination-port=80 \
    --protocol=TCP \
    --round-trip

המעקב המדומה מציג:

  1. התאמה של מסלול VPC.
  2. אישור תעבורת נתונים נכנסת ויוצאת בחומת האש של VPC.
  3. הגעה לצומת GKE.
  4. DNAT של שירות לכתובת ה-IP של Pod לקצה העורפי.
  5. הערכה של Ingress NetworkPolicy ב-Pod לקצה העורפי.

תרחיש ג': אבחון בעיות בתעבורת נתונים יוצאת מה-Pod לאינטרנט

כשה-Pods לא יכולים להגיע ל-API חיצוני באינטרנט:

  1. יוצרים בדיקה מה-Pod לכתובת ה-IP הציבורית החיצונית (למשל 8.8.8.8):

    gcloud network-management connectivity-tests create test-pod-to-internet \
        --source-gke-pod=projects/PROJECT_ID/locations/LOCATION/clusters/CLUSTER_NAME/k8s/namespaces/prod/pods/app-pod \
        --destination-ip-address=8.8.8.8 \
        --destination-port=53 \
        --protocol=UDP \
        --round-trip
    
  2. בודקים את נקודת ההורדה:

    • ‫Dropped at NetworkPolicy: ל-Pod חסרה מדיניות NetworkPolicy של תעבורה יוצאת שמאפשרת תעבורת נתונים אל 0.0.0.0/0.
    • ‫Dropped at VPC Firewall: כלל של חומת אש ב-VPC דוחה תעבורת נתונים יוצאת מתת-הרשת של הצומת.
    • Dropped at Cloud NAT or Route: לתת-הרשת אין נתיב ברירת מחדל לשער האינטרנט, או ש-Cloud NAT לא מוגדר לתת-הרשת.

רמה 4: ניראות של שער חיצוני ועלות (אינטרנט ו-NAT)

שכבת השער החיצוני מנהלת את תעבורת הנתונים היוצאת ליעדים באינטרנט הציבורי דרך Cloud NAT או שערים חיצוניים. התצפיתיות ברמה הזו עוזרת לזהות עומסי עבודה עם תעבורת נתונים יוצאת גבוהה ולשלוט בעלויות של העברת הנתונים.

זיהוי תנועת NAT (תעבורה יוצאת לאינטרנט)

לפני שמנתחים את תעבורת הנתונים היוצאת באינטרנט וב-NAT, צריך לבדוק את היקף האבחון ואת הדרישות המוקדמות:

  • תחום התמקדות: תעבורת נתונים יוצאת, ניצול יציאות של Cloud NAT, עלויות של העברת נתונים חיצוניים.
  • דרישות מוקדמות: הפעלת GKE Dataplane V2 Flow Observability.
  • תאימות ל-CNI: ‏GKE Dataplane V2.
  • תיאור הבעיה: שגיאות של מיצוי יציאות ב-Cloud NAT או עלויות גבוהות באופן בלתי צפוי של תעבורת נתונים לאינטרנט.
  • המטרה: לזהות אילו אובייקטים ספציפיים של Pod ו-Service ב-GKE מעבירים תנועה לנקודות קצה חיצוניות באינטרנט.

שלב 1: הבנת המושגים "to-stack" ו-"world" ב-GKE Dataplane V2

ב-GKE Dataplane V2:

  • ‫world: מייצג כל יעד מחוץ לאשכול GKE ומחוץ ל-VPC (האינטרנט הציבורי).
  • ‫to-stack: מייצג חבילות שעוברות מממשק veth של מאגר eBPF למקבץ הרשת (network stack) של המארח ב-Linux, כדי לעבור הסתרת IP ‏(SNAT) לפני שהן מגיעות ל-Cloud NAT.

שלב 2: סטרימינג וסינון של תנועת NAT באמצעות Hubble CLI

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

# Stream egress flows heading to external internet ("world")
gke-hubble observe \
    --traffic-direction egress \
    --verdict FORWARDED \
    --to-identity world \
    --follow

פלט לדוגמה:

TIMESTAMP             SOURCE               DESTINATION          TYPE     VERDICT
10:25:01.120          default/worker-pod   142.250.190.46:443   to-stack FORWARDED

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

timeout 60s gke-hubble observe \
    --traffic-direction egress \
    --verdict FORWARDED \
    --to-identity world \
    -o json | jq -r '.flow.source.namespace + "/" + .flow.source.pod_name' | sort | uniq -c | sort -nr | head -n 10

שלב 3: מעקב אחרי תנועה חיצונית באמצעות מדדים

ב-Cloud Monitoring, עוקבים אחרי נפח התנועה החיצונית:

fetch prometheus_target
| metric 'prometheus.googleapis.com/pod_flow_egress_flows_count/counter'
| filter (metric.destination_identity == 'world')
| align rate(1m)
| every 1m
| group_by [metric.source_workload, metric.source_namespace], sum(val())

ניתוח העלויות והביצועים של תנועת הנתונים באשכול באמצעות Flow Analyzer

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

  • תחום המיקוד: עלויות של העברת נתונים בין אזורים, המשתמשים העיקריים, חשיפה לתנועה בין צמתים בלי שאילתות SQL.
  • דרישות מוקדמות: יומני זרימה של VPC מופעלים עם INCLUDE_ALL_METADATA, הרשאות גישה בתוך הצומת מופעלות, Observability Analytics מופעל בקטגוריית היומנים.
  • תאימות ל-CNI: כל האשכולות.
  • התופעה: חיובים גבוהים על העברת נתונים בין אזורים בחשבונות החודשיים שלGoogle Cloud .
  • המטרה: לזהות באופן חזותי אילו עומסי עבודה ב-GKE יוצרים תנועה בין אזורים, ולבצע אופטימיזציה של המיקום בלי לשלוח שאילתות ליומנים גולמיים.

דרישות מוקדמות

כדי להשתמש ב-Flow Analyzer ב-GKE:

  1. יומני זרימה של VPC: צריך להפעיל אותם ברשת המשנה של האשכול באמצעות metadata="INCLUDE_ALL_METADATA".
  2. Intranode Visibility: צריך להפעיל את התכונה הזו באשכול כדי שתעבורת הנתונים בין pod ל-pod תיחשף לצינור עיבוד הנתונים של רישום ביומן של תעבורת נתונים ב-VPC.
  3. ניתוח נתוני יומן: כדי להשתמש ב-Observability Analytics, צריך לשדרג את הקטגוריה _Default ב-Cloud Logging.

שלב 1: זיהוי ה-GKE top talkers ב-Flow Analyzer

כדי להציג עומסי עבודה בנפח גבוה ב-Flow Analyzer:

  1. נכנסים לדף Flow Analyzer במסוף Google Cloud .
  2. לוחצים על Source bucket ובוחרים את קטגוריית היומנים שבה נמצאים יומני התנועה. אם לא ניתבתם אותם למקום אחר, זהו הקטגוריה _Default.
  3. בקטע Traffic aggregation, בוחרים באפשרות Source - Destination.
  4. מגדירים את טווח התאריכים של חלון הניתוח.
  5. בקטע Organize flows by, בוחרים את השדות של Pod או עומס העבודה ב-GKE.
  6. לוחצים על הפעלת שאילתה חדשה. בתרשים Highest data flows מוצגים עומסי העבודה שבהם מועברים הכי הרבה נתונים.

שלב 2: ניתוח עלויות התנועה בין אזורים

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

  1. בקטע Organize flows by, בוחרים את השדות של אזור המקור ואזור היעד.
  2. לוחצים על Run new query וקוראים את הטבלה All data flows. שורות שבהן אזור המקור ואזור היעד שונים מייצגות את התנועה בין אזורים. המסננים של Flow Analyzer תואמים לערכים, כך שאי אפשר לסנן לפי "לא שווה". במקום זאת, צריך להשוות בין זוגות האזורים בתוצאות.
  3. מרחיבים את זוג האזורים עם נפח התעבורה הגבוה כדי לראות את עומסי העבודה ב-GKE במקור וביעד.

תיקון:

  • להטמיע את topologySpreadConstraints או את podAffinity ב-Kubernetes כדי למקם שירותים שמתקשרים ביניהם באותו אזור זמינות.
  • מפעילים את ניתוב מבוסס-טופולוגיה (service.kubernetes.io/topology-mode: Auto) בשירות כדי לשמור את התנועה באזור המקור.

שלב 3: מעבר ל-Observability Analytics (לשאילתות SQL מתקדמות)

ב-Flow Analyzer, לוחצים על View in Log Analytics כדי להריץ שאילתות SQL על נתוני התנועה.

שאילתת ה-SQL הבאה מחשבת את הפודים המובילים שמתקשרים בין אזורים:

SELECT
  JSON_VALUE(json_payload.src_gke_details.pod.workload.workload_name) AS src_workload,
  JSON_VALUE(json_payload.dest_gke_details.pod.workload.workload_name) AS dest_workload,
  JSON_VALUE(json_payload.src_instance.zone) AS src_zone,
  JSON_VALUE(json_payload.dest_instance.zone) AS dest_zone,
  SUM(CAST(JSON_VALUE(json_payload.bytes_sent) AS INT64)) / 1024 / 1024 / 1024 AS total_gb_sent
FROM
  `PROJECT_ID.global._Default._AllLogs`
WHERE
  log_name LIKE '%vpc_flows%'
  AND timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
  AND JSON_VALUE(json_payload.src_instance.zone) != JSON_VALUE(json_payload.dest_instance.zone)
GROUP BY
  1, 2, 3, 4
ORDER BY
  total_gb_sent DESC
LIMIT 20;

תקציר בנושא שאילתות והפניות ל-CLI של Hubble

‫Hubble CLI מעביר בסטרימינג ומסנן נתונים של זרימת רשת בזמן אמת ישירות ממאגר הנתונים הזמני (ring buffer) של ליבת GKE Dataplane V2.

הגדרה: יצירת כתובת אימייל חלופית לעזרה

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

alias gke-hubble="kubectl exec -it -n gke-managed-dpv2-observability deployment/hubble-relay -c hubble-cli -- hubble"

מתכוני סינון נפוצים

פתרון בעיות שקשורות למטרות עסקיות פקודת Hubble CLI
צפייה בכל החבילות שנמחקו בזמן אמת בכל האשכול gke-hubble observe --verdict DROPPED --follow
העברת כל התנועה של Pod ספציפי בכל מרחב שמות gke-hubble observe --pod default/my-pod --follow
סינון תנועה בין שני מרחבי שמות ספציפיים gke-hubble observe --from-namespace frontend --to-namespace backend
בידוד התנועה ביציאה ספציפית (כמו יציאה 80) gke-hubble observe --port 80
בדיקת שאילתות ותשובות של DNS בזמן אמת gke-hubble observe --port 53
צפייה באיפוסים של TCP בזמן אמת (חבילות RST) gke-hubble observe --type trace --verdict FORWARDED --tcp-flags RST
העברת כל תנועת הנתונים היוצאת שמיועדת לאינטרנט הציבורי gke-hubble observe --traffic-direction egress --to-identity world
בדיקת תנועה בשכבת האפליקציה של HTTP (שכבה 7 של OSI) gke-hubble observe --protocol http

סינון מתקדם עם שלילה (--not)

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

# Observe drops, but exclude internal kube-system health probes and DNS
gke-hubble observe \
    --verdict DROPPED \
    --not --namespace kube-system \
    --not --port 53

עיצוב הפלט ושילוב jq

כדי לעבד רשומות של תהליכים באופן פרוגרמטי, צריך להפיק פלט בפורמט JSON:

# Extract only source, destination, and drop reason from the last 100 flows
gke-hubble observe --verdict DROPPED -o json --last 100 | \
    jq -r '[.time, .flow.source.pod_name, .flow.destination.pod_name, .flow.drop_reason_desc] | @tsv'

מגבלות

כשמשתמשים ב-Hubble CLI לפתרון בעיות בזמן אמת, חשוב לזכור את המגבלות הטכניות הבאות:

  • מאגר נתונים זמני מקומי בצומת: תזרימי Hubble מאוחסנים במאגר נתונים בזיכרון בכל צומת בנפרד. באירועים עם נפח תעבורת נתונים גבוה, יומני תנועה ישנים יותר מוחלפים תוך שניות. כדי לבצע ניתוח היסטורי, הסתמכו על רישום ביומן של NetworkPolicy ועל יומני זרימה של VPC ב-Cloud Logging.
  • אין OR לוגי מובנה: דגלי Hubble CLI מעריכים כמה ארגומנטים באמצעות AND לוגי. כדי לחפש כמה תנאים (כמו יציאה 80 או יציאה 443), מריצים פקודות נפרדות או מסננים את פלט ה-JSON באמצעות jq.

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