נפחי אחסון מתמיד (persistent volumes) והקצאת משאבים ב-GKE

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

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

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

PersistentVolumes

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

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

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

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

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

PersistentVolumeClaims

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

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

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

StorageClasses

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

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

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

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

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

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

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

במניפסט הבא מתוארת בקשה לדיסק עם נפח אחסון של 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 מגובה בדיסק אחסון מתמיד חדש וריק של Compute Engine.

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

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

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

כדי למנוע אובדן נתונים או לצמצם אותו, מומלץ גם להפעיל את הגיבוי ל-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 כשמוחקים אשכול, אפשר למחוק ידנית את מרחב השמות של משאבי PersistentVolume, וכך להפעיל מחיקה של אובייקטים של PersistentVolume עם מדיניות Delete.PersistentVolumeClaim אפשר גם למחוק משאבים ספציפיים של PersistentVolumeClaim. עם זאת, GKE לא ממתין עד שהפעולות האלה של המחיקה יושלמו לפני שהוא מתחיל למחוק את האשכול. לכן, אם מוחקים מרחב שמות ואז מוחקים מיד את האשכול, יכול להיות שמשאבי PersistentVolume עם מדיניות Delete לא יימחקו.

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

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

מצבי גישה

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

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

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

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

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

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

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

אפשר לעיין בדוגמה הזו ליצירת נפח אחסון מתמיד (Persistent Volume) שמגובה על ידי דיסק אחסון מתמיד (Persistent Disk) קיים.

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

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

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

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

‫StatefulSets היא השיטה המומלצת לפריסת אפליקציות עם מצב (stateful) שנדרש להן נפח אחסון ייחודי לכל עותק. באמצעות StatefulSets עם תבניות PersistentVolumeClaim, אפשר להגדיל את מספר העותקים של האפליקציות באופן אוטומטי עם PersistentVolumeClaims ייחודיים שמשויכים לכל עותק של Pod.

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

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

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

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

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

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

מצב של צירוף נפח אחסון WaitForFirstConsumer

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

בדוגמה הבאה מוצג 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

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

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