פתרון בעיות בחיבור לאשכול

בעיות בקישוריות לרשת של אשכול Google Kubernetes Engine ‏ (GKE) יכולות לגרום לזמן קצוב לתפוגה של החיבור, לחיבורים שנדחו, לפודים שלא ניתן לגשת אליהם, לשיבושים בשירות או לכשלים בתקשורת בין פודים.

במסמך הזה מוסבר איך לפתור בעיות בקישוריות של אשכולות על ידי לכידת מנות רשת (באמצעות toolbox ו-tcpdump) ואבחון בעיות ברשת של Pod.

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

בעיות קישוריות שקשורות לתפיסת חבילות רשת ב-GKE

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

בעיות בקישוריות ברשת של האשכול נכנסות בדרך כלל לקטגוריות הבאות:

  • לא ניתן להגיע ל-Pods: יכול להיות שלא ניתן לגשת ל-Pod מתוך האשכול או מחוצה לו בגלל הגדרות שגויות של הרשת.
  • שיבושים בשירות: יכול להיות שיהיו שיבושים או עיכובים בשירות.
  • בעיות בתקשורת בין Pods: יכול להיות ש-Pods לא יוכלו לתקשר ביניהם בצורה יעילה.

בעיות בקישוריות באשכול GKE יכולות לנבוע מסיבות שונות, כולל:

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

בקטע הבא מוסבר איך לפתור את הבעיה בצמתים או בתרמילים הבעייתיים.

  1. מזהים את הצומת שבו פועל ה-Pod הבעייתי באמצעות הפקודה הבאה:

    kubectl get pods POD_NAME -o=wide -n NAMESPACE
    

    מחליפים את מה שכתוב בשדות הבאים:

    • ‫POD_NAME בשם ה-Pod.
    • ‫NAMESPACE עם מרחב השמות של Kubernetes.
  2. מתחברים לצומת:

    gcloud compute ssh NODE_NAME \
        --zone=ZONE
    

    מחליפים את מה שכתוב בשדות הבאים:

    • ‫NODE_NAME: שם הצומת.
    • ‫ZONE: שם האזור שבו פועל הצומת.
  3. כדי לנפות באגים ב-Pod ספציפי, מזהים את הממשק veth שמשויך ל-Pod:

    ip route | grep POD_IP
    

    מחליפים את POD_IP בכתובת ה-IP של Pod.

  4. מריצים את הפקודות של כלי הניהול.

פקודות toolbox

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

  1. בזמן שהמכשיר מחובר לצומת, מפעילים את הכלי toolbox:

    toolbox
    

    הקבצים שיאפשרו את השימוש בכלי השירות toolbox יורדו.

  2. בפרומפט toolbox root, מתקינים את tcpdump:

    • עבור אשכולות עם כתובות IP חיצוניות או Cloud NAT:

      apt update -y && apt install -y tcpdump
      
    • באשכולות פרטיים בלי Cloud NAT:

      אם יש לכם אשכול פרטי בלי Cloud NAT, אתם לא יכולים להתקין את tcpdump באמצעות apt. במקום זאת, מורידים את קובצי הגרסה libpcap ו-tcpdump מהמאגר הרשמי ומעתיקים את הקבצים ל-VM באמצעות gcloud compute scp או gcloud storage cp. לאחר מכן, מתקינים את הספריות באופן ידני באמצעות השלבים הבאים:

      # Copy the downloaded packages to a staging area
      cp /media/root/home/USER_NAME/tcpdump-VERSION.tar.gz  /usr/sbin/
      cp /media/root/home/USER_NAME/libpcap-VERSION.tar.gz  /usr/sbin/
      cd /usr/sbin/
      
      # Extract the archives
      tar -xvzf tcpdump-VERSION.tar.gz
      tar -xvzf libpcap-VERSION.tar.gz
      
      # Build and install libpcap (a dependency for tcpdump)
      cd libpcap-VERSION
      ./configure ; make ; make install
      
      # Build and install tcpdump
      cd ../tcpdump-VERSION
      ./configure ; make ; make install
      
      # Verify tcpdump installation
      tcpdump --version
      

      מחליפים את מה שכתוב בשדות הבאים:

      • ‫USER_NAME: שם המשתמש שלכם במערכת שבה נמצאים הקבצים.
      • ‫VERSION: מספר הגרסה הספציפי של חבילות tcpdump ו-libpcap.
  3. מתחילים את תיעוד החבילות:

    tcpdump -i eth0 -s 100 "port PORT" \
    -w /media/root/mnt/stateful_partition/CAPTURE_FILE_NAME
    

    מחליפים את מה שכתוב בשדות הבאים:

    • PORT: שם מספר היציאה.
    • ‫CAPTURE_FILE_NAME: שם קובץ התיעוד.
  4. מפסיקים את לכידת המנות ומפריעים ל-tcpdump.

  5. כדי לצאת מארגז הכלים, מקלידים exit.

  6. מציגים את קובץ לכידת המנות ובודקים את הגודל שלו:

    ls -ltr /mnt/stateful_partition/CAPTURE_FILE_NAME
    
  7. מעתיקים את לכידת החבילות מהצומת לספריית העבודה הנוכחית במחשב:

    gcloud compute scp NODE_NAME:/mnt/stateful_partition/CAPTURE_FILE_NAME \
        --zone=ZONE
    

    מחליפים את מה שכתוב בשדות הבאים:

    • ‫NODE_NAME: שם הצומת.
    • ‫CAPTURE_FILE_NAME: השם של קובץ הלכידה.
    • ‫ZONE: שם האזור.

פקודות חלופיות

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

בעיות בקישוריות לרשת של Pod

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

ל-Pods בצמתים נבחרים אין זמינות

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

ip address show cbr0

אם הגשר של Linux מושבת, מפעילים אותו:

sudo ip link set cbr0 up

מוודאים שהצומת לומד את כתובות ה-MAC של ה-Pod שמצורפות ל-cbr0:

arp -an

ל-Pods בצמתים נבחרים יש קישוריות מינימלית

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

sudo toolbox bash

אם עדיין לא עשיתם את זה, מתקינים את tcpdump בארגז הכלים:

apt install -y tcpdump

מריצים את הפקודה tcpdump מול cbr0:

tcpdump -ni cbr0 host HOSTNAME and port PORT_NUMBER and [TCP|UDP|ICMP]

אם נראה שחבילות גדולות נמחקות במורד הזרם מהגשר (לדוגמה, לחיצת היד של TCP מסתיימת, אבל לא מתקבלות הודעות שלום של SSL), צריך לוודא שה-MTU של כל ממשק Pod של Linux מוגדר בצורה נכונה ל-MTU של רשת ה-VPC של האשכול.

ip address show cbr0

כשמשתמשים בשכבות-על (לדוגמה, Weave או Flannel), צריך להקטין עוד יותר את ה-MTU כדי להתאים את התקורה של האנקפסולציה בשכבת-העל.

GKE MTU

ה-MTU שנבחר לממשק Pod תלוי בממשק הרשת של הקונטיינר (CNI) שבו משתמשים צומתי האשכול, ובהגדרת ה-MTU של ה-VPC הבסיסי. מידע נוסף זמין במאמר בנושא Pods.

ערך ה-MTU של ממשק ה-Pod הוא 1460 או שהוא עובר בירושה מהממשק הראשי של הצומת.

CNI MTU GKE Standard
kubenet 1460 ברירת מחדל
kubenet
(גרסה 1.26.1 של GKE ואילך)
עבר בירושה ברירת מחדל
Calico 1460

הופעל באמצעות --enable-network-policy.

לפרטים, ראו שליטה בתקשורת בין Pods לבין שירותים באמצעות מדיניות רשת ב-Kubernetes.

netd עבר בירושה ההגדרה מופעלת באמצעות אחת מהאפשרויות הבאות:
GKE Dataplane V2 עבר בירושה

הופעל באמצעות --enable-dataplane-v2.

פרטים נוספים זמינים במאמר שימוש ב-GKE Dataplane V2.

חיבורים שנכשלים לסירוגין

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

nf_conntrack: table full, dropping packet

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

new_ct_max=$(awk '$1 == "MemTotal:" { printf "%d\n", $2/32; exit; }' /proc/meminfo)
sysctl -w net.netfilter.nf_conntrack_max="${new_ct_max:?}" \
  && echo "net.netfilter.nf_conntrack_max=${new_ct_max:?}" >> /etc/sysctl.conf

אפשר גם להשתמש ב-NodeLocal DNSCache כדי לצמצם את מספר הרשומות של מעקב אחרי חיבורים.

הודעת השגיאה "bind: Address already in use " דווחה לגבי מאגר

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

resource.type="container"
textPayload:"bind: Address already in use"
resource.labels.container_name="redis"

2018-10-16 07:06:47.000 CEST 16 Oct 05:06:47.533 # Creating Server TCP listening socket *:60250: bind: Address already in use
2018-10-16 07:07:35.000 CEST 16 Oct 05:07:35.753 # Creating Server TCP listening socket *:60250: bind: Address already in use

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

כדי לאבחן את הבעיה:

  1. צריך להזין את המזהה הייחודי האוניברסלי (UUID) של ה-Pod בשדה .metadata.uuid:

    kubectl get pod -o custom-columns="name:.metadata.name,UUID:.metadata.uid" ubuntu-6948dd5657-4gsgg
    
    name                      UUID
    ubuntu-6948dd5657-4gsgg   db9ed086-edba-11e8-bdd6-42010a800164
    
  2. מריצים את הפקודות הבאות בצומת כדי לקבל את הפלט:

    docker ps -a
    ps -eo pid,ppid,stat,wchan:20,netns,comm,args:50,cgroup --cumulative -H | grep [Pod UUID]
    
  3. בודקים את התהליכים שפועלים מה-Pod הזה. מכיוון שה-UUID של מרחבי השמות של cgroup מכיל את ה-UUID של ה-Pod, אפשר לחפש את ה-UUID של ה-Pod בפלט של ps באמצעות grep. כדאי להשתמש ב-grep גם בשורה הקודמת, כדי לקבל את התהליכים של docker-containerd-shim עם מזהה מאגר התגים בארגומנט. חותכים את שאר העמודה cgroup כדי לקבל פלט פשוט יותר:

    # ps -eo pid,ppid,stat,wchan:20,netns,comm,args:50,cgroup --cumulative -H | grep -B 1 db9ed086-edba-11e8-bdd6-42010a800164 | sed s/'blkio:.*'/''/
    1283089     959 Sl   futex_wait_queue_me  4026531993       docker-co       docker-containerd-shim 276e173b0846e24b704d4 12:
    1283107 1283089 Ss   sys_pause            4026532393         pause           /pause                                     12:
    1283150     959 Sl   futex_wait_queue_me  4026531993       docker-co       docker-containerd-shim ab4c7762f5abf40951770 12:
    1283169 1283150 Ss   do_wait              4026532393         sh              /bin/sh -c echo hello && sleep 6000000     12:
    1283185 1283169 S    hrtimer_nanosleep    4026532393           sleep           sleep 6000000                            12:
    1283244     959 Sl   futex_wait_queue_me  4026531993       docker-co       docker-containerd-shim 44e76e50e5ef4156fd5d3 12:
    1283263 1283244 Ss   sigsuspend           4026532393         nginx           nginx: master process nginx -g daemon off; 12:
    1283282 1283263 S    ep_poll              4026532393           nginx           nginx: worker process
    
  4. ברשימה הזו אפשר לראות את מזהי מאגרי התגים, שצריכים להיות גלויים גם ב-docker ps.

    במקרה הזה:

    • docker-containerd-shim 276e173b0846e24b704d4 להשהיה
    • docker-containerd-shim ab4c7762f5abf40951770 for sh with sleep (sleep-ctr)
    • ‫docker-containerd-shim 44e76e50e5ef4156fd5d3 ל-nginx‏ (echoserver-ctr)
  5. בודקים את הפלט docker ps:

    # docker ps --no-trunc | egrep '276e173b0846e24b704d4|ab4c7762f5abf40951770|44e76e50e5ef4156fd5d3'
    44e76e50e5ef4156fd5d383744fa6a5f14460582d0b16855177cbed89a3cbd1f   registry.k8s.io/echoserver@sha256:3e7b182372b398d97b747bbe6cb7595e5ffaaae9a62506c725656966d36643cc                   "nginx -g 'daemon off;'"                                                                                                                                                                                                                                                                                                                                                                     14 hours ago        Up 14 hours                             k8s_echoserver-cnt_ubuntu-6948dd5657-4gsgg_default_db9ed086-edba-11e8-bdd6-42010a800164_0
    ab4c7762f5abf40951770d3e247fa2559a2d1f8c8834e5412bdcec7df37f8475   ubuntu@sha256:acd85db6e4b18aafa7fcde5480872909bd8e6d5fbd4e5e790ecc09acc06a8b78                                                "/bin/sh -c 'echo hello && sleep 6000000'"                                                                                                                                                                                                                                                                                                                                                   14 hours ago        Up 14 hours                             k8s_sleep-cnt_ubuntu-6948dd5657-4gsgg_default_db9ed086-edba-11e8-bdd6-42010a800164_0
    276e173b0846e24b704d41cf4fbb950bfa5d0f59c304827349f4cf5091be3327   registry.k8s.io/pause-amd64:3.1
    

    במקרים רגילים, כל מזהי מאגרי התגים מ-ps מופיעים ב-docker ps. אם יש קונטיינר שלא מופיע, זה קונטיינר לא פעיל, וכנראה תראו תהליך צאצא של docker-containerd-shim process שמאזין ליציאת ה-TCP שמדווחת שכבר נמצאת בשימוש.

    כדי לוודא זאת, מריצים את הפקודה netstat במרחב השמות של הרשת של הקונטיינר. משיגים את מזהה התהליך (PID) של כל תהליך בקונטיינר (כלומר, לא docker-containerd-shim) של ה-Pod.

    מהדוגמה הקודמת:

    • ‫1283107 - pause
    • 1283169 - sh
    • ‫1283185 - sleep
    • ‪1283263 - nginx master
    • ‫1283282 - nginx worker
    # nsenter -t 1283107 --net netstat -anp
    Active Internet connections (servers and established)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
    tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      1283263/nginx: mast
    Active UNIX domain sockets (servers and established)
    Proto RefCnt Flags       Type       State         I-Node   PID/Program name     Path
    unix  3      [ ]         STREAM     CONNECTED     3097406  1283263/nginx: mast
    unix  3      [ ]         STREAM     CONNECTED     3097405  1283263/nginx: mast
    
    gke-zonal-110-default-pool-fe00befa-n2hx ~ # nsenter -t 1283169 --net netstat -anp
    Active Internet connections (servers and established)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
    tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      1283263/nginx: mast
    Active UNIX domain sockets (servers and established)
    Proto RefCnt Flags       Type       State         I-Node   PID/Program name     Path
    unix  3      [ ]         STREAM     CONNECTED     3097406  1283263/nginx: mast
    unix  3      [ ]         STREAM     CONNECTED     3097405  1283263/nginx: mast
    

    אפשר גם להריץ את הפקודה netstat באמצעות ip netns, אבל צריך לקשר את מרחב השמות של הרשת של התהליך באופן ידני, כי Docker לא מבצע את הקישור:

    # ln -s /proc/1283169/ns/net /var/run/netns/1283169
    gke-zonal-110-default-pool-fe00befa-n2hx ~ # ip netns list
    1283169 (id: 2)
    gke-zonal-110-default-pool-fe00befa-n2hx ~ # ip netns exec 1283169 netstat -anp
    Active Internet connections (servers and established)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
    tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      1283263/nginx: mast
    Active UNIX domain sockets (servers and established)
    Proto RefCnt Flags       Type       State         I-Node   PID/Program name     Path
    unix  3      [ ]         STREAM     CONNECTED     3097406  1283263/nginx: mast
    unix  3      [ ]         STREAM     CONNECTED     3097405  1283263/nginx: mast
    gke-zonal-110-default-pool-fe00befa-n2hx ~ # rm /var/run/netns/1283169
    

השבתה זמנית של אותות אכיפה:

הפתרון הזמני הוא לזהות תהליכים לא פעילים באמצעות השיטה שמתוארת למעלה, ולסיים את התהליכים באמצעות הפקודה kill [PID].

כדי לפתור את הבעיה לטווח הארוך, צריך לזהות למה Docker קורס ולתקן את זה. סיבות אפשריות:

  • תהליכי זומבי מצטברים, ולכן נגמרים מרחבי השמות של PID
  • באג ב-Docker
  • עומס על המשאבים / OOM

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