מידע על kube-dns ל-GKE

אם אתם מפעילים אפליקציות באשכולות רגילים, kube-dns הוא ספק ה-DNS שמוגדר כברירת מחדל ועוזר לכם להפעיל גילוי שירותים ותקשורת. במאמר הזה מוסבר איך לנהל DNS באמצעות kube-dns, כולל הארכיטקטורה, ההגדרה ושיטות מומלצות לאופטימיזציה של פעולות ה-DNS בסביבת GKE.

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

לפני שמתחילים, חשוב להכיר את השירותים של Kubernetes ואת המושגים הכלליים של DNS.

הסבר על ארכיטקטורת kube-dns

kube-dns פועל בתוך אשכול GKE כדי לאפשר פענוח DNS בין Pods לבין Services.

בתרשים הבא אפשר לראות איך ה-Pods שלכם מקיימים אינטראקציה עם שירות kube-dns:

איור 1: דיאגרמה שמראה איך רכיבי Pod שולחים שאילתות DNS לשירות kube-dns, שמגובה על ידי רכיבי Pod של kube-dns. הפודים של kube-dns מטפלים בפענוח DNS פנימי ומעבירים שאילתות חיצוניות לשרתי DNS במעלה הזרם.

רכיבים מרכזיים

kube-dns כולל את הרכיבים העיקריים הבאים:

  • kube-dns Pods: these Pods run the kube-dns server software. כמה עותקים של ה-Pods האלה פועלים במרחב השמות kube-system, והם מספקים זמינות גבוהה ויתירות.
  • kube-dns שירות: בטבלה הבאה מוצגת השוואה בין מגבלות ההגדרה והמדרגיות של הגרסאות הקודמות של kube-dns ושל הגרסאות שמבוססות על CoreDNS:
    תכונה גרסה קודמת (kube-dns 1.35 ואילך) ‫kube-dns ב-CoreDNS (מגרסה 1.36 ואילך)
    הגברת המוּדעוּת לנקודות קצה יודע על עד 1,000 נקודות קצה לכל שירות. אם לשירות יש יותר מ-1,000 פודים, kube-dns לא מודע לנקודות הקצה הנוספות. מודע לכל נקודות הקצה. הגרסה הזו משתמשת ב-EndpointSlices כדי להבטיח דיוק ולשפר את היעילות של שירותים גדולים.
    שרתי שמות Upstream מוגבל ל-3 תמיכה בעד 15
    חיבורי TCP יוצאים בו-זמנית עד 200 תמיכה בעד 1,500
  • kube-dns-autoscaler: ה-Pod הזה משנה את מספר העותקים המשוכפלים של kube-dns בהתאם לגודל האשכול, שכולל את מספר הצמתים וליבות ה-CPU. הגישה הזו עוזרת להבטיח ש-kube-dns יוכל להתמודד עם עומסים משתנים של שאילתות DNS.

פענוח DNS פנימי

כש-Pod צריך לפתור שם DNS בדומיין של האשכול, כמו myservice.my-namespace.svc.cluster.local, מתרחש התהליך הבא:

  1. הגדרת ה-DNS של ה-Pod:kubelet בכל צומת מגדיר את קובץ /etc/resolv.conf של ה-Pod. בקובץ הזה נעשה שימוש בkube-dns של שירות ClusterIP כשרת השמות.
  2. שאילתת DNS: ה-Pod שולח שאילתת DNS לשירות kube-dns.
  3. תרגום שם (Name resolution):

    • GKE גרסה 1.36 ואילך: ההטמעה שמבוססת על CoreDNS משתמשת ב-EndpointSlices כדי ש-kube-dns יכיר את כל ה-Pods בשירות. כך אפשר לשפר את הדיוק והיעילות של שירותים בקנה מידה גדול.
    • GKE גרסה 1.35 ואילך: kube-dns פותרת שמות על סמך Cloud Endpoints API מהדור הקודם, שמוגבל ל-1,000 נקודות קצה. אם לשירות יש יותר מ-1,000 פודים תומכים, kube-dns לא מודע לנקודות הקצה הנוספות.
  4. תקשורת: ה-Pod משתמש בכתובת ה-IP שזוהתה כדי לתקשר עם שירות היעד.

פענוח DNS חיצוני

כשצריך לפתור שם DNS חיצוני או שם שנמצא מחוץ לדומיין של האשכול, kube-dns פועל כפתרון רקורסיבי. הוא מעביר את השאילתה לשרתי DNS במעלה הזרם שהוגדרו בקובץ ConfigMap. אפשר גם להגדיר פותרים בהתאמה אישית לדומיינים ספציפיים, שנקראים גם דומיינים מסוג stub. ההגדרה הזו מכוונת את kube-dns להעביר בקשות עבור הדומיינים האלה לשרתי DNS ספציפיים במעלה הזרם.

הגדרת DNS של Pod

ב-GKE, סוכן kubelet בכל צומת מגדיר את הגדרות ה-DNS של ה-Pods שפועלים בצומת הזה.

הגדרת הקובץ /etc/resolv.conf

כש-GKE יוצר Pod, סוכן kubelet משנה את הקובץ /etc/resolv.conf של ה-Pod. הקובץ הזה מגדיר את שרת ה-DNS לפתרון שמות ומציין דומיינים לחיפוש. כברירת מחדל, kubelet מגדיר את ה-Pod להשתמש בשירות ה-DNS הפנימי של האשכול, kube-dns, כשרת השמות שלו. הוא גם מאכלס את דומייני החיפוש בקובץ. דומייני החיפוש האלה מאפשרים לכם להשתמש בשמות לא מלאים בשאילתות DNS. לדוגמה, אם הפוד שולח שאילתה ל-myservice,‏ Kubernetes מנסה קודם לפתור את myservice.default.svc.cluster.local, אחר כך את myservice.svc.cluster.local ואז את שאר הדומיינים מהרשימה search.

בדוגמה הבאה מוצגת הגדרת ברירת מחדל של /etc/resolv.conf:

nameserver 10.0.0.10
search default.svc.cluster.local svc.cluster.local cluster.local c.my-project-id.internal google.internal
options ndots:5

הקובץ הזה כולל את הערכים הבאים:

  • nameserver: מגדיר את ClusterIP של שירות kube-dns.
  • search: מגדיר את דומייני החיפוש שמצורפים לשמות לא מלאים במהלך חיפושי DNS.
  • options ndots:5: הגדרת ערך הסף שלפיו GKE קובע אם שם הוא מוגדר במלואו. שם נחשב למוגדר במלואו אם יש בו חמש נקודות או יותר.

‫Pods שמוגדרים עם hostNetwork: true מקבלים בירושה את הגדרת ה-DNS מkube-dns ולא מבצעים שאילתה ישירות ל-kube-dns, אלא אם הם משתמשים ב-ClusterFirstWithHostNet dnsPolicy.

התאם אישית את kube-dns

kube-dns מספק פענוח DNS חזק כברירת מחדל. אתם יכולים להתאים את ההתנהגות שלו לצרכים ספציפיים, כמו שיפור היעילות של הרזולוציה או שימוש בשרתי DNS מועדפים. הגדרת דומיינים מסוג stub ושרתי שמות במעלה הזרם מתבצעת על ידי שינוי kube-dns ConfigMap במרחב השמות kube-system.

שינוי של kube-dns ConfigMap

כדי לשנות את kube-dns ConfigMap, מבצעים את הפעולות הבאות:

  1. פותחים את ConfigMap לעריכה:

    kubectl edit configmap kube-dns -n kube-system
    
  2. בקטע data, מוסיפים את השדות stubDomains ו-upstreamNameservers באופן הבא:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      labels:
        addonmanager.kubernetes.io/mode: EnsureExists
      name: kube-dns
      namespace: kube-system
    data:
      stubDomains: |
        {
          "example.com": [
            "8.8.8.8",
            "8.8.4.4"
          ],
          "internal": [ # Required if your upstream nameservers can't resolve GKE internal domains
            "169.254.169.254" # IP of the metadata server
          ]
        }
      upstreamNameservers: |
        [
          "8.8.8.8", # Google Public DNS
          "8.8.4.4" # Google Public DNS Backup
        ]
    
  3. שומרים את ה-ConfigMap. kube-dns טוען מחדש את ההגדרה באופן אוטומטי.

דומיינים מסוג Stub

דומיינים מסוג Stub מאפשרים להגדיר פותרים מותאמים אישית של DNS לדומיינים ספציפיים. כש-Pod שולח שאילתה לשם בדומיין stub, ‏ kube-dns מעביר את השאילתה לפתרון שצוין במקום להשתמש במנגנון ברירת המחדל שלו לפתרון.

אתם כוללים קטע stubDomains בkube-dns ConfigMap.

בקטע הזה מצוינים הדומיין ושרתי השמות המתאימים במעלה הזרם. ‫kube-dns ואז מעביר שאילתות לשמות בדומיין הזה לשרתים המיועדים. לדוגמה, אפשר להפנות את כל שאילתות ה-DNS של internal.mycompany.com אל 192.168.0.10, ולהוסיף את "internal.mycompany.com": ["192.168.0.10"] אל stubDomains.

כשמגדירים מפענח DNS מותאם אישית לדומיין stub, כמו example.com, kube-dns מעביר את כל בקשות תרגום השמות של הדומיין הזה, כולל תת-דומיינים כמו *.example.com, לשרתים שצוינו.

שרתי שמות במעלה הזרם

אפשר להגדיר את kube-dns כך שישתמש בשרתי שמות בהתאמה אישית במעלה הזרם כדי לפתור שמות של דומיינים חיצוניים. ההגדרה הזו מורה ל-kube-dns להעביר את כל בקשות ה-DNS, מלבד הבקשות לדומיין הפנימי של האשכול (*.cluster.local), לשרתי ה-upstream המיועדים. יכול להיות ששרתי upstream מותאמים אישית לא יוכלו לפענח דומיינים פנימיים כמו metadata.internal ו-*.google.internal. אם מפעילים את איחוד הזהויות של עומסי עבודה ל-GKE או שיש עומסי עבודה שתלויים בדומיינים האלה, צריך להוסיף דומיין stub ל-internal ב-ConfigMap. משתמשים ב-169.254.169.254, כתובת ה-IP של שרת המטא-נתונים, כמפענח ה-DNS של דומיין ה-stub הזה.

ניהול פריסה מותאמת אישית של kube-dns

באשכול רגיל, ‏kube-dns פועל כפריסה. פריסה kube-dns בהתאמה אישית מאפשרת לכם, כאדמינים של האשכול, לשלוט בפריסה ולהתאים אותה לצרכים שלכם, במקום להשתמש בפריסה שמוגדרת כברירת מחדל ב-GKE.

סיבות לפריסה בהתאמה אישית

כדאי לשקול פריסה מותאמת אישית של kube-dns מהסיבות הבאות:

  • הקצאת משאבים: כוונון עדין של משאבי המעבד והזיכרון עבור kube-dns Pods כדי לשפר את הביצועים באשכולות עם נפח תנועה גבוה של DNS.
  • גרסת התמונה: אפשר להשתמש בגרסה ספציפית של התמונה kube-dns או לעבור לספק DNS חלופי כמו CoreDNS.
  • הגדרה מתקדמת: אפשר להתאים אישית את רמות הרישום ביומן, מדיניות האבטחה והתנהגות של שמירת DNS במטמון.

התאמה אוטומטית של נפח האחסון (autoscaling) לפריסות מותאמות אישית

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

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

הוראות מפורטות להגדרה ולניהול של פריסה משלכם של kube-dns kube-dns זמינות במאמר הגדרת פריסה מותאמת אישית של kube-dns.

פתרון בעיות

למידע על פתרון בעיות ב-kube-dns, אפשר לעיין בדפים הבאים:

אופטימיזציה של פענוח DNS

בקטע הזה מתוארות בעיות נפוצות ושיטות מומלצות לניהול DNS ב-GKE.

המגבלה של דומיינים לחיפוש ב-Pod‏ dnsConfig

ב-Kubernetes, מספר הדומיינים של חיפוש DNS מוגבל ל-32. אם מנסים להגדיר יותר מ-32 דומיינים לחיפוש ב-dnsConfig של Pod, ה-kube-apiserver לא ייצור את ה-Pod, ותופיע שגיאה דומה לזו:

The Pod "dns-example" is invalid: spec.dnsConfig.searches: Invalid value: []string{"ns1.svc.cluster-domain.example", "my.dns.search.suffix1", "ns2.svc.cluster-domain.example", "my.dns.search.suffix2", "ns3.svc.cluster-domain.example", "my.dns.search.suffix3", "ns4.svc.cluster-domain.example", "my.dns.search.suffix4", "ns5.svc.cluster-domain.example", "my.dns.search.suffix5", "ns6.svc.cluster-domain.example", "my.dns.search.suffix6", "ns7.svc.cluster-domain.example", "my.dns.search.suffix7", "ns8.svc.cluster-domain.example", "my.dns.search.suffix8", "ns9.svc.cluster-domain.example", "my.dns.search.suffix9", "ns10.svc.cluster-domain.example", "my.dns.search.suffix10", "ns11.svc.cluster-domain.example", "my.dns.search.suffix11", "ns12.svc.cluster-domain.example", "my.dns.search.suffix12", "ns13.svc.cluster-domain.example", "my.dns.search.suffix13", "ns14.svc.cluster-domain.example", "my.dns.search.suffix14", "ns15.svc.cluster-domain.example", "my.dns.search.suffix15", "ns16.svc.cluster-domain.example", "my.dns.search.suffix16", "my.dns.search.suffix17"}: must not have more than 32 search paths.

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

מגבלת nameservers בשרת במעלה הזרם עבור kube-dns

בגרסאות קודמות של kube-dns (גרסה 1.35 ומטה) מספר upstreamNameservers מוגבל לשלושה. אם מגדירים יותר משלושה, Cloud Logging מציג שגיאה שדומה לזו:

Invalid configuration: upstreamNameserver cannot have more than three entries (value was &TypeMeta{Kind:,APIVersion:,}), ignoring update

בתרחיש הזה, kube-dns מתעלם מההגדרה upstreamNameservers וממשיך להשתמש בהגדרה הקודמת התקינה. כדי לפתור את הבעיה, צריך להסיר את upstreamNameservers הנוסף מ-kube-dns ConfigMap.

הגדלת הקיבולת kube-dns

ב-Standard clusters, אפשר להשתמש בערך נמוך יותר עבור nodesPerReplica כדי ליצור יותר kube-dns Pods כשצמתים של אשכולות עוברים שינוי גודל. מומלץ מאוד להגדיר ערך מפורש לשדה max כדי לוודא שהמכונה הווירטואלית (VM) של מישור הבקרה של GKE לא תהיה עמוסה מדי בגלל המספר הגדול של רכיבי Pod‏ kube-dns שעוקבים אחרי Kubernetes API.

אפשר להגדיר את הערך של השדה max למספר הצמתים באשכול. אם באשכול יש יותר מ-500 צמתים, צריך להגדיר את הערך של השדה max ל-500.

אפשר לשנות את מספר העותקים המשוכפלים של kube-dns על ידי עריכת kube-dns-autoscaler ConfigMap.

kubectl edit configmap kube-dns-autoscaler --namespace=kube-system

הפלט אמור להיראות כך:

linear: '{"coresPerReplica":256, "nodesPerReplica":16,"preventSinglePointFailure":true}'

מספר העותקים המשוכפלים של kube-dns מחושב באמצעות הנוסחה הבאה:

replicas = max( ceil( cores * 1/coresPerReplica ) , ceil( nodes * 1/nodesPerReplica ) )

כדי להגדיל את מספר המכונות, משנים את הערך של השדה nodesPerReplica לערך קטן יותר, ומוסיפים ערך לשדה max.

linear: '{"coresPerReplica":256, "nodesPerReplica":8,"max": 15,"preventSinglePointFailure":true}'

ההגדרה הזו יוצרת Pod אחד לכל שמונה צמתים באשכול.kube-dns ב-cluster עם 24 צמתים יש 3 עותקים וב-cluster עם 40 צמתים יש 5 עותקים. אם גודל האשכול גדל מעבר ל-120 צמתים, מספר העותקים לא גדל מעבר ל-15, שהוא הערך של השדה max.kube-dns

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

הפלט של kube-dns-autoscaler ConfigMap עם השדה min שמוגדר אמור להיראות כך:

linear: '{"coresPerReplica":256, "nodesPerReplica":8,"max": 15,"min": 5,"preventSinglePointFailure":true}'

שיפור הזמנים של חיפושי DNS

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

  • חיפושי DNS תכופים בעומס העבודה.
  • צפיפות גבוהה של Pods לכל צומת.
  • הפעלת kube-dns במכונות וירטואליות מסוג Spot או במכונות וירטואליות שניתנות להפסקת פעולה, מה שעלול להוביל למחיקות לא צפויות של צמתים.
  • מגבלות חיבור: בגרסאות קודמות של kube-dns (GKE גרסה 1.35 ואילך) יש מגבלה של 200 חיבורי TCP בו-זמנית. ‫kube-dns ב-CoreDNS (GKE גרסה 1.36 ואילך) מסיר את המגבלות הקבועות האלה לחיבורים נכנסים ומספק קיבולת גבוהה משמעותית לחיבורים יוצאים.

כדי לשפר את זמני החיפוש ב-DNS:

  • מומלץ להימנע מהרצת רכיבי מערכת קריטיים כמו kube-dns במכונות וירטואליות מסוג Spot או במכונות וירטואליות שניתן לקטוע את הפעילות שלהן. יוצרים לפחות מאגר צמתים אחד עם מכונות וירטואליות רגילות, ללא מכונות וירטואליות זמניות או מכונות וירטואליות שניתנות להפסקת פעולה. כדי לוודא שעומסי עבודה קריטיים מתוזמנים בצמתים האמינים האלה, אפשר להשתמש ב-taints וב-tolerations.
  • מפעילים את NodeLocal DNSCache. ‫NodeLocalDNSCache שומר במטמון את תשובות ה-DNS ישירות בכל צומת, וכך מקצר את זמן האחזור ומפחית את העומס על שירות kube-dns. אם מפעילים את NodeLocal DNSCache ומשתמשים בכללי מדיניות של רשת עם כללי ברירת מחדל של דחייה, צריך להוסיף מדיניות שתאפשר לעומסי עבודה לשלוח שאילתות DNS לקבוצות ה-Pod של node-local-dns.
  • הרחבת המינוי kube-dns.
  • צריך לוודא שהאפליקציה משתמשת בפונקציות שמבוססות על dns.resolve* ולא בפונקציות שמבוססות על dns.lookup, כי dns.lookup היא סינכרונית.
  • משתמשים בשמות דומיין שמוגדרים במלואם (FQDN), לדוגמה, https://google.com./ במקום https://google.com/.

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

מוודאים שאפשר לגלות את השירות

kube-dns יוצר רק רשומות DNS לשירותים שיש להם נקודות קצה. אם לשירות אין נקודות קצה, kube-dns לא יוצר רשומות DNS עבור השירות הזה.

ניהול פערים ב-TTL של DNS

אם kube-dns מקבל תשובה ממפענח DNS במעלה הזרם עם TTL גדול או אינסופי, הוא שומר את ערך ה-TTL הזה. ההתנהגות הזו עלולה ליצור חוסר התאמה בין הרשומה שנשמרה במטמון לבין כתובת ה-IP בפועל.

ב-GKE, הבעיה הזו נפתרת בגרסאות ספציפיות של מישור הבקרה, כמו 1.21.14-gke.9100 ואילך או 1.22.15-gke.2100 ואילך. בגרסאות האלה מוגדר ערך TTL מקסימלי של 30 שניות לכל תגובת DNS עם ערך TTL גבוה יותר. ההתנהגות הזו דומה ל-NodeLocal DNSCache.

הצגת המדדים של kube-dns

אפשר לאחזר מדדים לגבי שאילתות DNS ישירות מ-kube-dns Pods. אופן האחזור של המדדים האלה תלוי בגרסת GKE.

GKE מגרסה 1.36 ואילך

אם האשכול שלכם מריץ GKE מגרסה 1.36 ואילך (kube-dns ב-CoreDNS), אתם יכולים לעקוב אחרי ביצועי ה-DNS באמצעות לוחות בקרה מוגדרים מראש ב-Cloud Monitoring, או לאחזר מדדים באופן ידני מה-Pods.

הצגת מדדים במסוף Google Cloud

  1. נכנסים לדף Dashboards במסוף Google Cloud .
  2. בוחרים את מרכז הבקרה GKE DNS Observability - Cluster View.

לחלופין, אפשר לשלוח שאילתה לגבי המדדים האלה ישירות במסוף Google Cloud . כדי לעשות זאת, עוברים אל Monitoring > Metrics explorer ומחפשים את השמות הספציפיים של המדדים.

אחזור מדדים באופן ידני

כדי לאחזר מדדים מה-Pod באופן ידני:

  1. מוצאים את kube-dns Pods.

    kubectl get pods -n kube-system --selector=k8s-app=kube-dns
    
  2. מעבירים את יציאה 9153 לאחד מה-Pods.

    kubectl port-forward pod/POD_NAME -n kube-system 9153:9153
    

    מחליפים את POD_NAME בשם של אחד מ-kube-dns Pods מהפלט הקודם.

  3. גישה למדדים.

    curl http://127.0.0.1:9153/metrics
    

GKE מגרסה 1.35 ומגרסאות קודמות

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

  1. מחפשים את kube-dns Pods במרחב השמות kube-system.

    kubectl get pods -n kube-system --selector=k8s-app=kube-dns
    
  2. הפניית יציאות (port-forward) ליציאות 10055 (למאגר kube-dns) ו-10054 (למאגר dnsmasq):

    #For the kube-dns container
    kubectl port-forward pod/POD_NAME -n kube-system 10055:10055
    #For the dnsmasq container
    kubectl port-forward pod/POD_NAME -n kube-system 10054:10054
    

    מחליפים את POD_NAME בשם של אחד מ-kube-dns Pods מהפלט הקודם. מריצים את הפקודות האלה להעברת פורטים בסשנים נפרדים של מסוף.

  3. גישה למדדים.

    #Metrics from the kube-dns container
    curl http://127.0.0.1:10055/metrics
    
    #Metrics from the dnsmasq container
    curl http://127.0.0.1:10054/metrics
    

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