אחסון מתמיד והקצאה ב-GKE

בדף הזה מוסבר על Persistent Volumes‏ (PV), על Persistent Volume Claims‏ (PVC) ועל Storage Classes ב-Google Kubernetes Engine‏ (GKE). הוא מתמקד באחסון שמגובה על ידי דיסקים לאחסון מתמיד של Compute Engine.

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

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

PersistentVolumes

משאבי PersistentVolume משמשים לניהול אחסון עמיד באשכול. ב-GKE, בדרך כלל PersistentVolume מגובה על ידי Persistent Disk.

במקום זאת, אתם יכולים להשתמש בפתרונות אחסון אחרים כמו NFS. ‫Filestore הוא פתרון NFS ב-Google Cloud. במאמר גישה למכונות Filestore באמצעות מנהל ההתקן של Filestore CSI שבמסמכי התיעוד של Filestore, מוסבר איך להגדיר מכונת Filestore כפתרון NFS PV לאשכולות GKE.

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

משאבי PersistentVolume הם משאבי אשכול שקיימים בנפרד מ-Pods. המשמעות היא שהדיסק והנתונים שמיוצגים על ידי PersistentVolume ממשיכים להתקיים כשהאשכול משתנה וכשפודים נמחקים ונוצרים מחדש. אפשר להקצות משאבי PersistentVolume באופן דינמי באמצעות PersistentVolumeClaims, או ליצור אותם באופן מפורש על ידי מנהל אשכול.

מידע נוסף על משאבי PersistentVolume זמין במאמרי העזרה של Kubernetes Persistent Volumes ובמאמרי העזרה של Persistent Volumes API.

PersistentVolumeClaims

PersistentVolumeClaim הוא בקשה ודרישה למשאב PersistentVolume. אובייקטים מסוג PersistentVolumeClaim מבקשים גודל ספציפי, מצב גישה וStorageClass עבור PersistentVolume. אם קיים PersistentVolume שעומד בדרישות הבקשה או שאפשר להקצות אותו, ה-PersistentVolumeClaim מקושר ל-PersistentVolume הזה.

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

יתרון נוסף של השימוש ב-PersistentVolumes וב-PersistentVolumeClaims הוא הניידות. אפשר להשתמש באותו מפרט Pod באשכולות ובסביבות שונות, כי PersistentVolume הוא ממשק לאחסון הגיבוי בפועל.

StorageClasses

הטמעות של נפחי אחסון, כמו מנהל ההתקן של ממשק אחסון קונטיינרים (CSI) לדיסק לאחסון מתמיד של Compute Engine, מוגדרות באמצעות משאבי StorageClass.

‫GKE יוצר בשבילכם StorageClass שמוגדר כברירת מחדל, שמשתמש בסוג הדיסק של אחסון מתמיד מאוזן (ext4). ערך ברירת המחדל של StorageClass משמש כשלא מצוין StorageClassName ב-PersistentVolumeClaim. אתם יכולים להחליף את ברירת המחדל של StorageClass בערך משלכם. הוראות מפורטות מופיעות במאמר שינוי ברירת המחדל של StorageClass.

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

אם אתם משתמשים באשכול עם מאגרי צמתים של Windows, אתם צריכים ליצור StorageClass ולציין StorageClassName ב-PersistentVolumeClaim, כי סוג ה-fstype שמוגדר כברירת מחדל (ext4) לא נתמך ב-Windows. אם אתם משתמשים ב-Persistent Disk של Compute Engine, עליכם להשתמש ב-NTFS כסוג האחסון של הקבצים.

כשמגדירים StorageClass, צריך לציין מנהל הקצאות. ב-GKE, מומלץ להשתמש באחד מהכלים הבאים להקצאת משאבים:

הקצאה דינמית של נפחי אחסון מתמיד

ברוב המקרים, אין צורך להגדיר ישירות אובייקטים של PersistentVolume או ליצור דיסקים קבועים של Compute Engine. במקום זאת, אתם יכולים ליצור PersistentVolumeClaim ו-Kubernetes יקצה לכם באופן אוטומטי דיסק אחסון מתמיד (persistent disk).

המניפסט הבא מתאר בקשה לדיסק עם נפח אחסון של ‎30 GiB, שמצב הגישה שלו מאפשר לטעון אותו לקריאה ולכתיבה על ידי צומת יחיד. הוא גם יוצר Pod שמשתמש ב-PersistentVolumeClaim כנפח אחסון.

# pvc-pod-demo.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-demo
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 30Gi
  storageClassName: standard-rwo
---
kind: Pod
apiVersion: v1
metadata:
  name: pod-demo
spec:
  volumes:
    - name: pvc-demo-vol
      persistentVolumeClaim:
       claimName: pvc-demo
  containers:
    - name: pod-demo
      image: nginx
      resources:
        limits:
          cpu: 10m
          memory: 80Mi
        requests:
          cpu: 10m
          memory: 80Mi
      ports:
        - containerPort: 80
          name: "http-server"
      volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: pvc-demo-vol

כשיוצרים את האובייקט PersistentVolumeClaim באמצעות kubectl apply -f pvc-pod-demo.yaml, ‏Kubernetes יוצר באופן דינמי אובייקט PersistentVolume תואם.

מכיוון שמחלקת האחסון standard-rwo משתמשת במצב קישור של נפח WaitForFirstConsumer, ה-PersistentVolume לא ייווצר עד ש-Pod יתוזמן לצרוך את הנפח.

בדוגמה הבאה מוצג ה-PersistentVolume שנוצר.

apiVersion: v1
kind: PersistentVolume
metadata:
  annotations:
    pv.kubernetes.io/provisioned-by: pd.csi.storage.gke.io
  finalizers:
  - kubernetes.io/pv-protection
  - external-attacher/pd-csi-storage-gke-io
  name: pvc-c9a44c07-cffa-4cd8-b92b-15bac9a9b984
  uid: d52af557-edf5-4f96-8e89-42a3008209e6
spec:
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 30Gi
  claimRef:
    apiVersion: v1
    kind: PersistentVolumeClaim
    name: pvc-demo
    namespace: default
    uid: c9a44c07-cffa-4cd8-b92b-15bac9a9b984
  csi:
    driver: pd.csi.storage.gke.io
    csi.storage.k8s.io/fstype: ext4
    volumeAttributes:
      storage.kubernetes.io/csiProvisionerIdentity: 1660085000920-8081-pd.csi.storage.gke.io
    volumeHandle: projects/xxx/zones/us-central1-c/disks/pvc-c9a44c07-cffa-4cd8-b92b-15bac9a9b984
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: topology.gke.io/zone
          operator: In
          values:
          - us-central1-c
  persistentVolumeReclaimPolicy: Delete
  storageClassName: standard-rwo
  volumeMode: Filesystem
status:
  phase: Bound

בהנחה שלא החלפתם את סוג האחסון standard-rwo, ה-PersistentVolume הזה מגובה בדיסק אחסון מתמיד (persistent disk) חדש וריק של Compute Engine.

מחיקת האחסון המתמיד

במהלך הקצאה דינמית, אתם יכולים לקבוע אם האחסון המתמיד יימחק כש-PersistentVolumeClaim (PVC) נמחק. כדי לעשות את זה, מגדירים את השדה reclaimPolicy בהגדרות של StorageClass לערך Delete או Retain.

  • ‫reclaimPolicy: Delete: זוהי מדיניות ברירת המחדל. כשמוחקים את ה-PVC, האובייקט PersistentVolume (PV) ודיסק האחסון המתמיד (persistent disk) הבסיסי (כמו Persistent Disk של Compute Engine) נמחקים באופן אוטומטי. כדאי להשתמש במדיניות הזו לעומסי עבודה זמניים או לדרישות תאימות שבהן צריך לנקות את הנתונים אחרי שהמשימה מסתיימת.

  • ‫reclaimPolicy: Retain: אם מגדירים את המדיניות הזו, ה-PV והדיסק הבסיסי לא נמחקים כשמוחקים את ה-PVC. הסטטוס של ה-PV משתנה ל-Released. הנתונים נשמרים, אבל אי אפשר להשתמש בנפח האחסון ב-PVC חדש עד שאדמין יתבע אותו מחדש באופן ידני.

ניהול ידני של אמצעי אחסון שנשמרו

כשהסטטוס של PersistentVolume הוא Released, צריך להחליט באופן ידני מה לעשות עם הנתונים והדיסק הבסיסיים.

  • כדי למחוק את האחסון באופן סופי: צריך למחוק גם את המשאב של Kubernetes PersistentVolume וגם את הדיסק הבסיסי של Compute Engine.

    1. מוחקים את PersistentVolume: bash kubectl delete pv <your-pv-name>
    2. כדי למצוא את שם הדיסק הבסיסי, מתארים את קובץ התצורה של YAML בשביל ה-PV ומחפשים את השדה volumeHandle או שדה דומה.
    3. מוחקים את הדיסק לאחסון מתמיד של Compute Engine: bash gcloud compute disks delete <your-disk-name> --zone=<your-zone>
  • כדי להשתמש שוב באמצעי האחסון: החזרת אמצעי אחסון עם סטטוס Released לשימוש על ידי PVC אחר היא משימה מתקדמת שכוללת ניקוי ידני של נתוני הדיסק ועריכת האובייקט PersistentVolume כדי להפוך אותו שוב ל-Available. למידע נוסף על ההליך המפורט, קראו את המאמר החזרת PersistentVolume במסמכי התיעוד הרשמיים של Kubernetes.

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

פתרון בעיות שקשורות למחיקה לא צפויה של דיסקים

אם אתם מבחינים שהדיסקים הקבועים הבסיסיים נמחקים כשאתם לא מצפים לכך, הסיבה הנפוצה ביותר היא שדה reclaimPolicy שמוגדר כ-Delete בהגדרות של StorageClass שמשמשות את PersistentVolumeClaim.

כדי לאבחן את הבעיה, מבצעים את השלבים הבאים:

  1. בודקים את ה-StorageClass של ה-PVC: bash kubectl get pvc <your-pvc-name> -o jsonpath='{.spec.storageClassName}'

  2. בודקים את השדה reclaimPolicy: bash kubectl get sc <your-storageclass-name> -o yaml

  3. התאמת המדיניות למטרה שלכם:

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

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

ניהול האחסון המתמיד במהלך מחיקת האשכול

כשמוחקים אשכול GKE, ‏GKE שומר את המשאבים של PersistentVolume עם מדיניות ההחזרה Delete או Retain.

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

אחרי מחיקת האשכול, אפשר לראות את המשאבים הנותרים של PersistentVolume במסוף Google Cloud .

כדי לראות משאבים שלא נמצאים בשימוש, כמו משאבי PersistentVolume, אתם יכולים לראות המלצות למשאבים לא פעילים.

מצבי גישה

משאבי PersistentVolume תומכים במצבי הגישה הבאים:

  • ‫ReadWriteOnce: אפשר לטעון את אמצעי האחסון לקריאה ולכתיבה על ידי צומת אחד.
  • ‫ReadOnlyMany: אפשר לטעון את אמצעי האחסון לקריאה בלבד על ידי צמתים רבים.
  • ‫ReadWriteMany: אפשר לטעון את אמצעי האחסון לקריאה ולכתיבה על ידי צמתים רבים. משאבי PersistentVolume שמגובים בדיסקים קבועים של Compute Engine לא תומכים במצב הגישה הזה.
  • ‫ReadWriteOncePod: אפשר לטעון את אמצעי האחסון לקריאה ולכתיבה רק באמצעות Pod אחד.

שימוש בדיסקים לאחסון מתמיד של Compute Engine כ-ReadOnlyMany

ReadWriteOnce הוא תרחיש השימוש הנפוץ ביותר לדיסקים קבועים, והוא פועל כמצב הגישה שמוגדר כברירת מחדל לרוב האפליקציות. דיסקים אחסון מתמידים (persistent disk) של Compute Engine תומכים גם במצב ReadOnlyMany, כך שהרבה אפליקציות או הרבה רפליקות של אותה אפליקציה יכולות להשתמש באותו דיסק בו-זמנית. תרחיש לדוגמה הוא הצגת תוכן סטטי בכמה רפליקות.

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

שימוש בדיסקים קיימים של אחסון מתמיד (persistent disks) כ-PersistentVolumes

משאבי PersistentVolume שמוקצים באופן דינמי ריקים כשהם נוצרים. אם יש לכם דיסק קיים מסוג Persistent Disk ב-Compute Engine שמכיל נתונים, אתם יכולים להוסיף אותו לאשכול על ידי יצירה ידנית של משאב PersistentVolume תואם. הדיסק הקבוע צריך להיות באותו אזור שבו נמצאים צומתי האשכול.

דוגמה ליצירת Persistent Volume שמגובה בדיסק אחסון מתמיד קיים.

פריסות לעומת StatefulSets

אפשר להשתמש בתבניות PersistentVolumeClaim או VolumeClaim בבקרים ברמה גבוהה יותר, כמו Deployments או StatefulSets בהתאמה.

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

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

StatefulSets היא השיטה המומלצת לפריסת אפליקציות עם שמירת מצב שדורשות נפח ייחודי לכל רפליקה. באמצעות StatefulSets עם תבניות PersistentVolumeClaim, אפשר להשתמש באפליקציות שיכולות להתרחב באופן אוטומטי עם PersistentVolumesClaims ייחודיים שמשויכים לכל פוד רפליקה.

דיסקים לאחסון מתמיד אזורי

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

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

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

אזורים בדיסקים לאחסון מתמיד (persistent disks)

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

מצב קישור אמצעי אחסון WaitForFirstConsumer

אם אתם מקצים באופן דינמי דיסק אחסון מתמיד (persistent disk) באשכול, מומלץ להגדיר את WaitForFirstConsumer volume binding mode בהגדרה של StorageClass. ההגדרה הזו מורה ל-Kubernetes להקצות דיסק אחסון מתמיד (persistent disk) באותו תחום (zone) שבו מתוזמן ה-Pod. הוא מכבד את אילוצי התזמון של Pod, כמו anti-affinity ובוררי צמתים. הגדרת anti-affinity באזורים מאפשרת לפזר פודים של StatefulSet באזורים שונים, יחד עם הדיסקים התואמים. בנוסף, ההגדרה הזו עוזרת לצמצם בעיות כשבאזור מסוים אין מספיק קיבולת מחשוב, על ידי דחיית הקצאת האחסון עד שניתן יהיה לאבטח צומת תקין וזמין. ההקצאה המאוחרת הזו עוזרת למנוע מצב שבו האפליקציה עם שמירת המצב ננעלת באופן קבוע באזור פגום, שבו היא תהיה תקועה במצב Pending.

הדוגמה הבאה היא הגדרה של StorageClass להקצאת דיסקים לאחסון מתמיד של תחום, שמגדירה את WaitForFirstConsumer:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: pd.csi.storage.gke.io
parameters:
  type: pd-balanced
  csi.storage.k8s.io/fstype: ext4
volumeBindingMode: WaitForFirstConsumer

דוגמה לשימוש בדיסקים לאחסון מתמיד אזורי מופיעה במאמר הקצאת דיסקים לאחסון מתמיד אזורי.

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