SSD Nodes Learn 8GB RAM — $66/שנה
מדריכים Matt Connorמאת Matt Connor · עודכן 2026-08-01

Docker Compose: מתי להשתמש ב-bind ומתי ב-volume

למדו לבחור בין bind mount ל-named volume עבור תצורה ונתונים, להימנע מבעיות הרשאות, ולבדוק, לגבות ולהעביר אמצעי אחסון ב-Docker Compose.

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

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

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

התחביר הכפול בקובץ compose

services:
  db:
    image: postgres:17
    environment:
      POSTGRES_PASSWORD: changeme
    volumes:
      - pgdata:/var/lib/postgresql/data
  web:
    image: nginx:1.27
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./site:/usr/share/nginx/html:ro

volumes:
  pgdata:

pgdata:/var/lib/postgresql/data הוא אמצעי אחסון בעל שם. ./nginx.conf:/etc/nginx/nginx.conf הוא עגינה מקושרת, ו-:ro מעגן אותו לקריאה בלבד. זוהי ברירת המחדל המתאימה עבור תצורה שמכולה לעולם אינה אמורה לשכתב. אם תשמיטו את הרשומה העליונה volumes:, Compose יפסיק עם service "db" refers to undefined volume pgdata.

הפעילו את השירותים והציגו את המשאבים ש-Docker יצר:

docker compose up -d
docker volume ls

שם אמצעי האחסון אינו pgdata. שמו הוא <project>_pgdata, כאשר שם הפרויקט מוגדר כברירת מחדל לפי שם התיקייה שמכילה את קובץ compose. תיקייה בשם myapp תיצור את myapp_pgdata. הדבר חשוב משום ששינוי שם התיקייה יוצר אמצעי אחסון ריק חדש, ואז נראה כאילו היישום איבד את הנתונים שלו. הנתונים לא אבדו: אמצעי האחסון הישן עדיין מופיע ברשימה של docker volume ls. הגדירו את השם במפורש באמצעות name: בקובץ compose, או הגדירו את COMPOSE_PROJECT_NAME אם ייתכן שהתיקייה תועתק למיקום אחר. הגדרות מסוג זה צריכות להופיע יחד עם קובצי הסביבה והסודות של Compose.

מדוע שגיאות הרשאה מתרחשות רק ב-bind mounts

זהו ההבדל המעשי העיקרי, והוא נובע מכלל אחד: volume בעל שם, שהוא ריק בעת השימוש הראשון, מאותחל מתוך ה-image, ואילו bind mount אינו מאותחל כך.

כאשר Docker טוען volume בעל שם וריק מעל תיקייה שכבר מכילה תוכן ב-image, הוא מעתיק את התוכן אל ה-volume, כולל הבעלות וההרשאות שהוגדרו ב-image. ה-image הרשמי של Postgres מספק את /var/lib/postgresql/data בבעלות המשתמש postgres שלו. לכן ה-volume נוצר בבעלות אותו מזהה מספרי, ומסד הנתונים מופעל.

bind mount פועל בכיוון ההפוך. כל מה שנמצא ב-host הוא מה שה-container רואה, כולל הבעלות, והתוכן של ה-image בנתיב זה מוסתר. אם התיקייה ב-host אינה קיימת, ה-Docker daemon יוצר אותה. ה-daemon פועל כ-root, ולכן מתקבלת תיקייה בבעלות root:root. תהליך ב-container שפועל כמשתמש שאינו root אינו יכול לכתוב אליה:

PermissionError: [Errno 13] Permission denied: '/data/app.db'

הפתרון הוא להתאים בין המספרים. בעלות דרך bind mount נבדקת לפי מזהה משתמש מספרי, ולא לפי שם, משום של-container יש /etc/passwd משלו. משתמש בשם app בתוך ה-container אינו אומר דבר ב-host. Uid 1000 פירושו uid 1000 בשני הצדדים.

id -u
mkdir -p ./data
docker compose exec web id
sudo chown -R 1000:1000 ./data

docker compose exec web id מציג את ה-uid שבו תהליך ה-container פועל בפועל. התאם את תיקיית ה-host למספר זה, או קבע שה-container יפעל עם המזהה שלך באמצעות user: "1000:1000" בשירות. קביעת user: נקייה יותר עבור יישום שכתבת בעצמך. שינוי הבעלות על תיקיית ה-host באמצעות chown בטוח יותר עבור image שלא כתבת, משום שחלק מה-images מפעילים entrypoint כ-root, מורידים הרשאות, ומצפים לבעלות מסוימת בנתיבים שמתחתיו.

כדאי להכיר שתי מלכודות נוספות. ב-Fedora, ב-RHEL ובמערכות אחרות שבהן SELinux (Linux משופר אבטחה) פועל במצב enforcing, גישה דרך bind mount נחסמת עד שמבצעים לה relabel. לכן יש להוסיף :z לנתיב המשותף בין containers, או :Z לנתיב שמיועד לשימוש של container אחד בלבד, בפורמט - ./data:/data:Z. בנוסף, bind mount של קובץ יחיד, במקום של תיקייה, עלול להישבר כאשר עורך מחליף את הקובץ במקום לכתוב לתוכו, משום שה-mount עוקב אחר ה-inode המקורי. ה-container ממשיך לראות את התוכן הישן עד שמפעילים אותו מחדש. כאשר עורכים את הקובץ לעיתים קרובות, יש לטעון את תיקיית האב.

ביצועים: היכן הפער ממשי

בשרת Linux, שני הסוגים עוברים באותו נתיב ליבה, ולכן ההבדל בתפוקה קטן מכדי לבחור ביניהם על בסיס זה. אמצעי אחסון בעלי שם המשתמשים במנהל ההתקן local המוגדר כברירת מחדל נמצאים באותה מערכת קבצים כמו שאר Docker, תחת /var/lib/docker/volumes/, ואילו עיגון bind נמצא בכל מקום שאליו הפניתם אותו.

הפער מופיע ב-Docker Desktop עבור macOS ו-Windows, שבהם הקונטיינרים פועלים בתוך מכונה וירטואלית. עיגון bind שם עובר ממערכת הקבצים של המארח אל המכונה הווירטואלית דרך שכבת שיתוף קבצים. עומסי עבודה הכוללים פעולות רבות על קבצים קטנים, כגון עץ תלויות של Node.js או מטמון של מסגרת PHP, נעשים איטיים יותר באופן ניכר. אמצעי אחסון בעלי שם נשארים בתוך המכונה הווירטואלית ואינם נושאים בעלות הזו. לכן קובצי compose רבים בסביבות פיתוח מעגנים את ספריית המקור באמצעות bind, אך מצהירים על אמצעי אחסון בעל שם מעל node_modules.

הבדל ממשי נוסף הוא המיקום שבו הבתים נשמרים. עיגון bind אל /mnt/backup שומר את הנתונים בדיסק הזה. אמצעי אחסון בעל שם נשמר במערכת הקבצים שמכילה את /var/lib/docker, שב-VPS היא בדרך כלל דיסק השורש. מסד נתונים שגדל בתוך אמצעי אחסון בעל שם ממלא את אותו דיסק שעליו נמצאים יומני המערכת. בדקו זאת לפני שהמצב הופך לתקרית:

docker system df -v
df -h /var/lib/docker

docker system df -v מציג את כל אמצעי האחסון עם הגודל שלהם ומסמן את אלה שאף קונטיינר אינו מפנה אליהם עוד.

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

אמצעי אחסון בעל שם אינו קופסה שחורה. בדקו ב-Docker היכן הוא נמצא:

docker volume inspect myapp_pgdata

השדה Mountpoint מציג נתיב אמיתי במארח, בדרך כלל /var/lib/docker/volumes/myapp_pgdata/_data. אפשר לקרוא אותו באמצעות sudo ls, וזה שימושי לבדיקה מהירה. אין להתייחס אליו כמקום לעריכת קבצים. כתיבה לשם באמצעות root יוצרת מחדש את בעיית הבעלות שתוארה לעיל, והנתיב הוא פרט של מנהל ההתקן local, שמנהלי התקנים אחרים של אמצעי אחסון אינם חולקים.

הדרך הבטוחה לעיין בתוכן היא להשתמש במכל זמני שמחבר את אמצעי האחסון:

docker run --rm -v myapp_pgdata:/vol alpine ls -la /vol

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

גיבוי של כל סוג

Bind mount הוא ספרייה רגילה, לכן כל כלי גיבוי ברמת הקבצים מטפל בו כברירת מחדל. הגדירו את נתיב המארח כיעד הגיבוי, וסיימתם. Volume בעל שם דורש שלב נוסף, מכיוון שעל הכלי להיכנס לתוכו. בצעו mount של ה-volume ושל ספרייה במארח לאותו container קצר-חיים, ולאחר מכן כתבו ארכיון:

docker run --rm \
  -v myapp_pgdata:/data:ro \
  -v "$PWD":/backup \
  alpine tar czf /backup/pgdata.tar.gz -C /data .

שחזרו באמצעות ביצוע הפעולה בכיוון ההפוך אל volume חדש:

docker volume create myapp_pgdata_restored
docker run --rm \
  -v myapp_pgdata_restored:/data \
  -v "$PWD":/backup \
  alpine tar xzf /backup/pgdata.tar.gz -C /data

tar משמר בעלות מספרית כאשר הוא רץ כ-root בתוך ה-container. כך ה-volume המשוחזר נשאר שמיש עבור היישום.

אזהרה זו חלה על שני הסוגים. העתקת קובצי מסד נתונים בזמן שמסד הנתונים פועל יוצרת ארכיון של נתונים המשתנים במהלך ההעתקה, והדבר עלול לגרום לשחזור למצב פגום. עצרו תחילה את השירות, או בצעו dump באמצעות הכלי של מסד הנתונים עצמו, כפי שמוצג ב-docker compose exec -T db pg_dump -U postgres appdb > appdb.sql. פעולה זו יוצרת קובץ רגיל, שאותו תוכלו לכלול לאחר מכן בשגרת גיבוי restic מוצפן רגילה לצד קובצי ה-compose שלכם.

העברת bind mount ל-named volume

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

docker compose down
docker volume create myapp_pgdata
docker run --rm \
  -v "$PWD/data":/from \
  -v myapp_pgdata:/to \
  alpine sh -c 'cp -a /from/. /to/'

cp -a שומר על הבעלות, ההרשאות וחותמות הזמן, ולכן משתמש ה-container שהיה יכול לקרוא את הספרייה הישנה עדיין יוכל לקרוא את ה-volume החדש. לאחר מכן שנו את השירות כך שישתמש ב-pgdata:/var/lib/postgresql/data, הוסיפו את pgdata לבלוק volumes: ברמה העליונה, הריצו את docker compose up -d, וקראו את יומני היישום לפני שתמחקו את הספרייה הישנה. בכיוון ההפוך משתמשים באותה פקודה, לאחר החלפה בין /from ל-/to.

זכרו נקודה אחת במהלך הבדיקות. docker compose down משאיר named volumes ללא שינוי, אך docker compose down -v מוחק כל named volume שהפרויקט מצהיר עליו, ואין אפשרות לבטל זאת. bind mount שורד את שתי הפעולות, משום ש-Docker מעולם לא היה הבעלים של הספרייה הזו. אם פקודות מחזור החיים עדיין חדשות לכם, המדריך ליסודות Docker Compose עבור VPS מסביר אותן שלב אחר שלב.

בחירה, שירות אחר שירות

שאלו מי כותב את הקובץ. תצורה שאתם עורכים בעורך טקסט ומבצעים לה commit אל git שייכת ל-bind mount, כשהיא מותקנת ב-:ro, מכיוון שחשוב שתהיה גלויה ושתנוהל באמצעות בקרת גרסאות. נתוני מצב של יישום שאינכם פותחים ידנית שייכים ל-named volume, מכיוון ש-Docker מגדיר את ההרשאות כראוי, והנתונים אינם תלויים בנתיב במארח.

המקרה המשולב הוא מדיה. היישום כותב לספריית תמונות, אך גם אתם מנהלים אותה, ולעיתים היא גדולה מספיק כדי להצדיק שימוש בדיסק מסוים. התקינו אותה באמצעות bind mount לנתיב בדיסק הזה, והגדירו את הבעלות במכוון פעם אחת. זהו הדפוס שרוב המחסניות באירוח עצמי מאמצות: named volumes עבור מסדי נתונים ומטמונים, ו-bind mounts עבור תצורה ועבור הספרייה הגדולה שחשובה לכם.

FAQ

מה ההבדל בין עיגון bind לבין volume בעל שם?

עיגון bind ממפה נתיב במארח אל תוך הקונטיינר, כך ששני הצדדים רואים את אותה ספרייה וניתן לערוך אותה בכלים רגילים. volume בעל שם הוא אחסון ש-Docker יוצר ומנהל, והוא מזוהה לפי שם ומוצהר בבלוק volumes: ברמה העליונה. ההבדל המעשי הוא הבעלות: משתמשים בעיגוני bind עבור תצורה שאתם מתחזקים, וב-volumes בעלי שם עבור נתונים שהיישום מתחזק.

מדוע מתקבלת השגיאה "permission denied" עם עיגון bind, אך לא עם volume בעל שם?

volume ריק בעל שם מאותחל מה-image, ולכן יורש את הבעלות שהוגדרה ב-image והמשתמש בקונטיינר יכול לכתוב אליו. עיגון bind מציג את ספריית המארח בדיוק כפי שהיא, ואם Docker נדרש ליצור את הספרייה, הוא יצר אותה בבעלות root. הריצו את docker compose exec <service> id כדי לראות את המזהה המספרי שבו הקונטיינר משתמש, ולאחר מכן את sudo chown -R <uid>:<gid> על ספריית המארח, או הגדירו user: "1000:1000" בשירות.

היכן Docker מאחסן volumes בעלי שם בדיסק?

עם מנהל ההתקנים המוגדר כברירת מחדל, local, הם נמצאים תחת /var/lib/docker/volumes/<volume>/_data, והפקודה docker volume inspect <volume> מדפיסה את Mountpoint המדויק. קראו אותו אם עליכם לבדוק דבר מה, אך כתבו אליו רק דרך קונטיינר, משום שעריכה כ-root במארח משנה את הבעלות בדרכים שהקונטיינר לא יצפה להן.

כיצד מגבים volume בעל שם?

הריצו קונטיינר קצר-חיים, כאשר גם ה-volume וגם ספריית המארח מעוגנים אליו, ולאחר מכן צרו ארכיון מאחד לשני באמצעות docker run --rm -v myvol:/data:ro -v "$PWD":/backup alpine tar czf /backup/myvol.tar.gz -C /data .. עבור מסד נתונים, בצעו dump באמצעות הכלי הייעודי של מסד הנתונים במקום להעתיק קבצים פעילים, משום שהעתקת קבצים בזמן שמתבצעות כתיבות עלולה לשחזר את מסד הנתונים למצב פגום.

האם docker compose down מוחק את ה-volumes שלי?

docker compose down מסיר קונטיינרים ורשתות ומשאיר את ה-volumes בעלי השם במקומם. docker compose down -v מוחק גם כל volume בעל שם שהפרויקט מצהיר עליו, לצמיתות. אף אחת מהפקודות אינה מסירה עיגוני bind, משום שהספרייה שייכת למארח ולא ל-Docker.