פתרון בעיות ב-NFS וב-DataPlane v2 ב-Google Distributed Cloud

במאמר הזה מפורט הליך ידני ל-Google Distributed Cloud אם נתקלתם בבעיות בחיבורי NFS עם נפח או Pod תקועים, ויצרתם את האשכול עם DataPlane v2 מופעל.

הבעיה הזו נפתרה בגרסאות הבאות:

  • לגרסה משנית 1.16, גרסה 1.16.4-gke.37 ומעלה.
  • בגרסה משנית 1.28, גרסה 1.28.200-gke.111 ואילך.

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

אם אתם משתמשים בגרסה שבה הבעיה הזו לא נפתרה, יכול להיות שתיתקלו בבעיות אם יש לכם עומסי עבודה שמשתמשים בכרכים של ReadWriteMany שמבוססים על מנהלי התקנים של אחסון שרגישים לבעיה הזו, כמו (אבל לא רק):

  • Robin.io
  • ‫Portworx (sharedv4 כרכים של שירות)
  • csi-nfs

יכול להיות שחיבורי NFS בארכיטקטורות אחסון מסוימות ייתקעו כשהם מחוברים לנקודת קצה באמצעות Kubernetes Service ‏ (ClusterIP) ו-DataPlane v2. ההתנהגות הזו נובעת מהמגבלות באופן שבו קוד שקע של ליבת Linux מקיים אינטראקציה עם תוכנית eBPF של Cillium. יכול להיות שהקונטיינרים ייחסמו ב-I/O או אפילו לא יהיה אפשר להרוג אותם, כי אי אפשר לבטל את הטעינה של NFS.

יכול להיות שתיתקלו בבעיה הזו אם אתם משתמשים באחסון RWX שמתארח בשרתי NFS שפועלים בצומת Kubernetes, כולל פתרונות אחסון מוגדרים בתוכנה או היפר-קונברגנטיים כמו Ondat,‏ Robin.io או Portworx.

בדיקת ההגדרות הקיימות של האשכול

מקבלים כמה ערכי הגדרה קיימים מהאשכול. משתמשים בערכים מהשלבים האלה כדי ליצור kube-proxy קובץ מניפסט בסעיף הבא.

  1. קחו את ClusterCIDR מ-cm/cilium-config:

    kubectl get cm -n kube-system cilium-config -o yaml | grep native-routing-cidr
    

    בדוגמה הבאה של הפלט אפשר לראות שצריך להשתמש ב-192.168.0.0/16 בתור ClusterCIDR:

    ipv4-native-routing-cidr: 192.168.0.0/16
    native-routing-cidr: 192.168.0.0/16
    
  2. מקבלים את APIServerAdvertiseAddress ואת APIServerPort מ-DaemonSet‏ anetd:

    kubectl get ds -n kube-system  anetd -o yaml | grep KUBERNETES -A 1
    

    בדוגמה הבאה של הפלט אפשר לראות שצריך להשתמש ב-21.1.4.119 בתור APIServerAdvertiseAddress וב-443 בתור APIServerPort:

    - name: KUBERNETES_SERVICE_HOST
      value: 21.1.4.119
    - name: KUBERNETES_SERVICE_PORT
      value: "443"
    
  3. קבלת RegistryCredentialsSecretName מ-DaemonSet‏ anetd:

    kubectl get ds -n kube-system  anetd -o yaml | grep imagePullSecrets -A 1
    

    בדוגמת הפלט הבאה אפשר לראות שצריך להשתמש ב-private-registry-creds בתור RegistryCredentialsSecretName:

    imagePullSecrets:
      - name: private-registry-creds
    
  4. קחו את Registry מ-anetd DameonSet:

    kubectl get ds -n kube-system  anetd -o yaml | grep image
    

    בדוגמת הפלט הבאה אפשר לראות שצריך להשתמש ב-gcr.io/gke-on-prem-release בתור Registry:

    image: gcr.io/gke-on-prem-release/cilium/cilium:v1.12.6-anthos1.15-gke4.2.7
    
  5. מקבלים את KubernetesVersion מתג התמונה של kube-apiserver במרחב השמות של האשכול של אשכול האדמין:

    KUBECONFIG=ADMIN_KUBECONFIG
    kubectl get sts -n CLUSTER_NAME kube-apiserver -o yaml | grep image
    

    מחליפים את ADMIN_KUBECONFIG בקובץ kubeconfig של אשכול האדמין ואת CLUSTER_NAME בשם של אשכול המשתמשים.

    בדוגמה הבאה של הפלט אפשר לראות שצריך להשתמש ב-v1.26.2-gke.1001 בתור KubernetesVersion:

    image: gcr.io/gke-on-prem-release/kube-apiserver-amd64:v1.26.2-gke.1001
    imagePullPolicy: IfNotPresent
    

הכנת קובצי מניפסט kube-proxy

משתמשים בערכים שהתקבלו בקטע הקודם כדי ליצור וליישם מניפסט YAML שיפרוס את kube-proxy לאשכול.

  1. יוצרים קובץ מניפסט בשם kube-proxy.yaml בכלי העריכה הרצוי:

    nano kube-proxy.yaml
    
  2. מעתיקים ומדביקים את הגדרת ה-YAML הבאה:

    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      labels:
        k8s-app: kube-proxy
      name: kube-proxy
      namespace: kube-system
    spec:
      selector:
        matchLabels:
          k8s-app: kube-proxy
      template:
        metadata:
          annotations:
            scheduler.alpha.kubernetes.io/critical-pod: ""
          labels:
            k8s-app: kube-proxy
        spec:
          containers:
          - command:
            - kube-proxy
            - --v=2
            - --profiling=false
            - --iptables-min-sync-period=10s
            - --iptables-sync-period=1m
            - --oom-score-adj=-998
            - --ipvs-sync-period=1m
            - --ipvs-min-sync-period=10s
            - --cluster-cidr=ClusterCIDR
            env:
            - name: KUBERNETES_SERVICE_HOST
              value:APIServerAdvertiseAddress
            - name: KUBERNETES_SERVICE_PORT
              value: "APIServerPort"
            image: Registry/kube-proxy-amd64:KubernetesVersion
            imagePullPolicy: IfNotPresent
            name: kube-proxy
            resources:
              requests:
                cpu: 100m
                memory: 15Mi
            securityContext:
              privileged: true
            volumeMounts:
            - mountPath: /run/xtables.lock
              name: xtables-lock
            - mountPath: /lib/modules
              name: lib-modules
          imagePullSecrets:
          - name: RegistryCredentialsSecretName
          nodeSelector:
            kubernetes.io/os: linux
          hostNetwork: true
          priorityClassName: system-node-critical
          serviceAccount: kube-proxy
          serviceAccountName: kube-proxy
          tolerations:
          - effect: NoExecute
            operator: Exists
          - effect: NoSchedule
            operator: Exists
          volumes:
          - hostPath:
              path: /run/xtables.lock
              type: FileOrCreate
            name: xtables-lock
          - hostPath:
              path: /lib/modules
              type: DirectoryOrCreate
            name: lib-modules
      ---
      apiVersion: rbac.authorization.k8s.io/v1
      kind: ClusterRoleBinding
      metadata:
        name: system:kube-proxy
      roleRef:
        apiGroup: rbac.authorization.k8s.io
        kind: ClusterRole
        name: system:node-proxier
      subjects:
        - kind: ServiceAccount
          name: kube-proxy
          namespace: kube-system
      ---
      apiVersion: v1
      kind: ServiceAccount
      metadata:
        name: kube-proxy
        namespace: kube-system
    

    במניפסט ה-YAML הזה, מגדירים את הערכים הבאים:

    • APIServerAdvertiseAddress: הערך של KUBERNETES_SERVICE_HOST, למשל 21.1.4.119.
    • APIServerPort: הערך של KUBERNETES_SERVICE_PORT, למשל 443.
    • Registry: הקידומת של תמונת Cilium, למשל gcr.io/gke-on-prem-release.
    • RegistryCredentialsSecretName: השם של סוד משיכת התמונה, כמו private-registry-creds.
  3. שומרים את קובץ המניפסט וסוגרים אותו בעורך.

הכנת anetd תיקון

יצירה והכנה של עדכון ל-anetd:

  1. יוצרים קובץ מניפסט בשם cilium-config-patch.yaml בכלי העריכה הרצוי:

    nano cilium-config-patch.yaml
    
  2. מעתיקים ומדביקים את הגדרת ה-YAML הבאה:

    data:
      kube-proxy-replacement: "disabled"
      kube-proxy-replacement-healthz-bind-address: ""
      retry-kube-proxy-healthz-binding: "false"
      enable-host-reachable-services: "false"
    
  3. שומרים את קובץ המניפסט וסוגרים אותו בעורך.

פריסה של kube-proxy והגדרה מחדש של anetd

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

  1. מגבים את ההגדרות הנוכחיות של anetd ושל cilium-config:

    kubectl get ds -n kube-system anetd > anetd-original.yaml
    kubectl get cm -n kube-system cilium-config > cilium-config-original.yaml
    
  2. שימוש בסכום kube-proxy.yaml באמצעות kubectl:

    kubectl apply -f kube-proxy.yaml
    
  3. בודקים שה-Pods הם Running:

    kubectl get pods -n kube-system -o wide | grep kube-proxy
    

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

    kube-proxy-f8mp9    1/1    Running   1 (4m ago)    [...]
    kube-proxy-kndhv    1/1    Running   1 (5m ago)    [...]
    kube-proxy-sjnwl    1/1    Running   1 (4m ago)    [...]
    
  4. מבצעים תיקון (patch) ל-ConfigMap‏ cilium-config באמצעות kubectl:

    kubectl patch cm -n kube-system cilium-config --patch-file cilium-config-patch.yaml
    
  5. עריכת anetd באמצעות kubectl:

    kubectl edit ds -n kube-system anetd
    

    בעורך שנפתח, עורכים את המפרט של anetd. מוסיפים את הפריט הבא כפריט הראשון מתחת ל-initContainers:

    - name: check-kube-proxy-rules
      image: Image
      imagePullPolicy: IfNotPresent
      command:
      - sh
      - -ec
      - |
        if [ "$KUBE_PROXY_REPLACEMENT" != "strict" ]; then
          kube_proxy_forward() { iptables -L KUBE-FORWARD; }
          until kube_proxy_forward; do sleep 2; done
        fi;
      env:
      - name: KUBE_PROXY_REPLACEMENT
        valueFrom:
          configMapKeyRef:
            key: kube-proxy-replacement
            name: cilium-config
            optional: true
      securityContext:
        privileged: true
    

    מחליפים את Image באותה תמונה שמשמשת בקונטיינרים האחרים של Cilium ב-DaemonSet‏ anetd, כמו gcr.io/gke-on-prem-release/cilium/cilium:v1.12.6-anthos1.15-gke4.2.7.

  6. שומרים את קובץ המניפסט וסוגרים אותו בעורך.

  7. כדי להחיל את השינויים האלה, צריך להפעיל מחדש את כל הצמתים באשכול. כדי לצמצם את השיבושים, אפשר לנסות לרוקן כל צומת לפני האתחול מחדש. עם זאת, Pods שמשתמשים בנפחים מסוג RWX יכולים להיתקע במצב Terminating בגלל טעינות NFS פגומות שחוסמות את תהליך הניקוז.

    אפשר למחוק בכוח את ה-Pods החסומים ולאפשר ל-Node לנקז את הנתונים בצורה תקינה:

    kubectl delete pods -–force -–grace-period=0 --namespace POD_NAMESPACE POD_NAME
    

    מחליפים את POD_NAME ב-Pod שמנסים למחוק ואת POD_NAMESPACE במרחב השמות שלו.

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

לקבלת עזרה נוספת, אפשר לפנות אל Cloud Customer Care.

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