Docker Compose में Bind mount बनाम Named volume का चुनाव
Docker Compose में Bind mount और Named volume के बीच सही विकल्प कैसे चुनें। कॉन्फ़िगरेशन, डेटा स्टोरेज, फाइल परमिशन की समस्याओं और बैकअप लेने के तरीकों के बारे में विस्तार से जानें।
Bind mount या named volume: संक्षिप्त उत्तर
Docker Compose volumes दो प्रकार के होते हैं, और इनका चयन इस आधार पर किया जाता है कि फाइलों का स्वामी कौन है। जिन फाइलों को आप स्वयं लिखते और पढ़ते हैं, उनके लिए bind mount का उपयोग करें, जैसे कि config, templates, और static sites। जिन डेटा का स्वामी application स्वयं है, उनके लिए named volume का उपयोग करें, जैसे कि database files, search indexes, और uploaded media। Bind mount host पर एक ऐसे path की ओर संकेत करता है जिसे आप किसी editor में खोल सकते हैं। Named volume वह storage है जिसे Docker आपके लिए बनाता और ट्रैक करता है, और आप इसे Docker के माध्यम से एक्सेस करते हैं।
दोनों ही एक service के भीतर समान volumes: key के अंतर्गत दिखाई देते हैं, यही कारण है कि इनमें भ्रम होता है। अंतर colon के बाईं ओर है। यदि बाईं ओर . या / से शुरू होता है, तो यह एक host path है, इसलिए यह एक bind mount है। इसके अलावा कुछ भी एक नाम है, इसलिए यह एक named volume है, और उस नाम को top-level volumes: block में घोषित किया जाना अनिवार्य है।
Compose file में दो सिंटैक्स
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 एक named volume है। ./nginx.conf:/etc/nginx/nginx.conf एक bind mount है, और :ro इसे read-only मोड में माउंट करता है, जो उन कॉन्फ़िगरेशन के लिए सही डिफ़ॉल्ट है जिन्हें कंटेनर द्वारा कभी भी ओवरराइट नहीं किया जाना चाहिए। यदि आप टॉप-लेवल volumes: एंट्री को हटा देते हैं, तो Compose service "db" refers to undefined volume pgdata एरर के साथ रुक जाएगा।
इसे शुरू करें और देखें कि Docker ने क्या बनाया है:
docker compose up -d
docker volume lsवॉल्यूम का नाम pgdata नहीं है। इसका नाम <project>_pgdata है, जहाँ प्रोजेक्ट का नाम डिफ़ॉल्ट रूप से उस डायरेक्टरी के नाम पर आधारित होता है जिसमें compose file मौजूद है। myapp नामक डायरेक्टरी का परिणाम myapp_pgdata होता है। यह महत्वपूर्ण है क्योंकि डायरेक्टरी का नाम बदलने पर आपको एक नया खाली वॉल्यूम मिल जाएगा, और ऐसा लगेगा कि एप्लिकेशन का डेटा खो गया है। डेटा खोया नहीं है: पुराना वॉल्यूम अभी भी docker volume ls द्वारा लिस्ट किया जाता है। यदि डायरेक्टरी के स्थान बदलने की संभावना हो, तो compose file में name: के साथ नाम को पिन करें, या COMPOSE_PROJECT_NAME सेट करें। इस तरह की सेटिंग्स आपकी अन्य Compose environment files and secrets के साथ होनी चाहिए।
Bind mounts में permission errors क्यों आते हैं
यह सबसे बड़ा व्यावहारिक अंतर है, और यह एक नियम से आता है: एक named volume जो पहली बार उपयोग करने पर खाली होता है, उसे image से seed किया जाता है, जबकि bind mount कभी ऐसा नहीं करता।
जब Docker किसी ऐसी directory पर खाली named volume mount करता है जिसमें image के अंदर पहले से content मौजूद हो, तो वह उस content को volume में copy कर देता है, और वही ownership और modes लागू करता है जो image में सेट थे। आधिकारिक Postgres image /var/lib/postgresql/data के साथ आती है जो इसके अपने postgres user के स्वामित्व में होती है, इसलिए volume भी उसी numeric id के स्वामित्व में आ जाती है और database start हो जाता है।
Bind mount इसके विपरीत काम करता है। host पर जो कुछ भी है, container वही देखता है, जिसमें ownership भी शामिल है, और उस path पर मौजूद image का content छिप जाता है। यदि host directory मौजूद नहीं है, तो Docker daemon उसे create कर देता है, और daemon root के रूप में चलता है, इसलिए आपको root:root के स्वामित्व वाली directory मिलती है। एक non-root user के रूप में चलने वाला container process फिर उसमें write नहीं कर पाता:
PermissionError: [Errno 13] Permission denied: '/data/app.db'इसका समाधान यह है कि numbers को समान किया जाए। bind mount के पार ownership की तुलना numeric user id द्वारा की जाती है, नाम से नहीं, क्योंकि container का अपना /etc/passwd होता है। container के अंदर app नाम के user का host पर कोई अर्थ नहीं है। Uid 1000 का मतलब दोनों तरफ uid 1000 ही होता है।
id -u
mkdir -p ./data
docker compose exec web id
sudo chown -R 1000:1000 ./datadocker compose exec web id उस uid को print करता है जिस पर container process वास्तव में चलता है। host directory को उस number से match करें, या service में user: "1000:1000" के साथ container को अपने number पर pin करें। आपके द्वारा लिखे गए application के लिए user: को pin करना अधिक साफ-सुथरा तरीका है। ऐसी image के लिए जिसे आपने नहीं लिखा है, host directory को chown करना अधिक सुरक्षित है, क्योंकि कुछ images entrypoint को root के रूप में start करती हैं, privileges छोड़ती हैं, और नीचे विशिष्ट ownership की अपेक्षा करती हैं।
दो और traps के बारे में जानना उपयोगी है। Fedora, RHEL और SELinux (security-enhanced Linux) लागू करने वाले अन्य systems पर, bind mount को तब तक deny कर दिया जाता है जब तक कि उसे relabel न किया जाए, इसलिए containers के बीच साझा किए गए path के लिए :z जोड़ें, या केवल एक container द्वारा उपयोग किए जाने वाले path के लिए :Z जोड़ें, जिसे - ./data:/data:Z के रूप में लिखा जाता है। और directory के बजाय एक single file का bind mount तब टूट जाता है जब कोई editor file को place में write करने के बजाय replace कर देता है, क्योंकि mount original inode का अनुसरण करता है। जब तक आप container को restart नहीं करते, वह पुराना content ही देखता रहता है। यदि file को अक्सर edit किया जाता है, तो parent directory को mount करें।
प्रदर्शन: जहाँ अंतर वास्तविक है
Linux सर्वर पर दोनों प्रकार के वॉल्यूम एक ही kernel path से होकर गुजरते हैं, इसलिए throughput में अंतर इतना कम होता है कि आपको इस आधार पर चुनाव नहीं करना चाहिए। डिफ़ॉल्ट local ड्राइवर का उपयोग करने वाले named volumes Docker के बाकी हिस्सों की तरह ही /var/lib/docker/volumes/ के अंतर्गत उसी filesystem पर रहते हैं, और bind mount वहीं रहता है जहाँ आपने उसे point किया है।
यह अंतर macOS और Windows के लिए Docker Desktop पर दिखाई देता है, जहाँ containers एक virtual machine के अंदर चलते हैं। वहाँ एक bind mount host filesystem से उस virtual machine में एक file sharing layer के माध्यम से जाता है, और बहुत सारी छोटी file operations वाले workloads, जैसे कि Node.js dependency tree या PHP framework cache, काफी धीमे हो जाते हैं। Named volumes virtual machine के अंदर ही रहते हैं और उन्हें यह लागत नहीं चुकानी पड़ती। यही कारण है कि बहुत सारी development compose files source directory को bind-mount करती हैं लेकिन node_modules पर एक named volume घोषित करती हैं।
दूसरा वास्तविक अंतर यह है कि bytes कहाँ स्टोर होते हैं। /mnt/backup पर एक bind mount डेटा को उस डिस्क पर रखता है। एक named volume उस filesystem पर जाता है जो /var/lib/docker को होल्ड करता है, जो VPS पर आमतौर पर root disk होती है। एक named volume के अंदर बढ़ती हुई database उसी डिस्क को भर देती है जिस पर आपके system logs होते हैं। इसे किसी incident के बनने से पहले जाँच लें:
docker system df -v
df -h /var/lib/dockerdocker system df -v हर वॉल्यूम को उसके आकार के साथ सूचीबद्ध करता है, और उन वॉल्यूम को चिह्नित करता है जिन्हें अब कोई container refer नहीं करता है।
Named volume का निरीक्षण करना
Named volume कोई black box नहीं है। Docker से पूछें कि यह कहाँ स्थित है:
docker volume inspect myapp_pgdataMountpoint field एक वास्तविक host path प्रदान करता है, जो सामान्यतः /var/lib/docker/volumes/myapp_pgdata/_data होता है। आप इसे sudo ls के साथ पढ़ सकते हैं, और यह त्वरित जाँच के लिए उपयोगी है। इसे फाइलें संपादित करने के स्थान के रूप में न देखें। वहाँ root के रूप में लिखने से ऊपर वर्णित ownership की समस्या फिर से उत्पन्न हो जाती है, और यह path local driver का एक विवरण है जिसे अन्य volume drivers साझा नहीं करते हैं।
इसके अंदर देखने का सुरक्षित तरीका एक throwaway container का उपयोग करना है जो उस volume को mount करता है:
docker run --rm -v myapp_pgdata:/vol alpine ls -la /volयह किसी भी driver के लिए काम करता है, वही permissions देखता है जो वास्तविक container देखता है, और --rm के कारण पीछे कुछ भी नहीं छोड़ता है।
प्रत्येक प्रकार का बैकअप लेना
Bind mount एक सामान्य directory होती है, इसलिए कोई भी file-level backup tool इसे आसानी से संभाल लेता है। बैकअप को host path पर निर्देशित करें और आपका काम हो गया। Named volume के लिए एक अतिरिक्त चरण की आवश्यकता होती है, क्योंकि tool को इसके अंदर जाना पड़ता है। Volume और एक host directory को एक अल्पकालिक container में mount करें, फिर एक archive लिखें:
docker run --rm \
-v myapp_pgdata:/data:ro \
-v "$PWD":/backup \
alpine tar czf /backup/pgdata.tar.gz -C /data .इसे एक नए volume में reverse करके restore करें:
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 /datatar container के अंदर root के रूप में चलने पर numeric ownership को सुरक्षित रखता है, जिससे restore किया गया volume application द्वारा उपयोग करने योग्य बना रहता है।
एक चेतावनी दोनों प्रकारों पर लागू होती है। Database के चलते समय उसकी files को copy करने से आपको एक moving target का archive मिलता है, और यह restore होने पर corrupt स्थिति में हो सकता है। पहले service को रोकें, या docker compose exec -T db pg_dump -U postgres appdb > appdb.sql के अनुसार database के अपने tool के माध्यम से dump करें। इससे एक plain file बनती है जिसे आप अपनी compose files के साथ encrypted restic backup routine में शामिल कर सकते हैं।
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 ओनरशिप, मोड और टाइमस्टैम्प को सुरक्षित रखता है, इसलिए जिस कंटेनर यूजर के पास पुरानी डायरेक्टरी को पढ़ने की अनुमति थी, वह नई वॉल्यूम को भी पढ़ पाएगा। इसके बाद सर्विस को pgdata:/var/lib/postgresql/data का उपयोग करने के लिए बदलें, टॉप-लेवल volumes: ब्लॉक में pgdata जोड़ें, docker compose up -d चलाएं, और पुरानी डायरेक्टरी को डिलीट करने से पहले एप्लिकेशन के लॉग्स की जाँच करें। विपरीत दिशा में जाने के लिए समान कमांड का उपयोग करें, जिसमें /from और /to को आपस में बदल दिया जाता है।
टेस्टिंग के दौरान एक बात का ध्यान रखें। docker compose down named volumes को प्रभावित नहीं करता है, लेकिन docker compose down -v प्रोजेक्ट द्वारा घोषित हर named volume को डिलीट कर देता है, और इसे वापस ठीक करने का कोई विकल्प नहीं है। Bind mount दोनों स्थितियों में सुरक्षित रहता है, क्योंकि Docker उस डायरेक्टरी का स्वामी नहीं होता है। यदि आप लाइफसाइकिल कमांड्स के लिए नए हैं, तो VPS के लिए Docker Compose बेसिक्स गाइड में इनका विस्तार से वर्णन किया गया है।
सेवा-दर-सेवा चयन
यह तय करें कि फाइल कौन लिखता है। जिस कॉन्फ़िगरेशन को आप टेक्स्ट एडिटर में एडिट करते हैं और git में कमिट करते हैं, उसे bind mount में रखें और :ro पर माउंट करें, क्योंकि आप चाहते हैं कि वह दिखाई दे और उसका वर्ज़न बना रहे। जिस एप्लिकेशन स्टेट को आप कभी हाथ से नहीं खोलते, उसे named volume में रखें, क्योंकि Docker अनुमतियों (permissions) को सही ढंग से सेट करता है और डेटा किसी होस्ट पाथ पर निर्भर नहीं होता।
मिश्रित स्थिति मीडिया की है। फोटो लाइब्रेरी को एप्लिकेशन द्वारा लिखा जाता है, लेकिन आप उसे मैनेज भी करते हैं, और यह अक्सर इतनी बड़ी होती है कि इसके लिए एक विशिष्ट डिस्क की आवश्यकता होती है। इसे उस डिस्क के एक पाथ पर bind mount करें और एक बार जानबूझकर स्वामित्व (ownership) सेट करें। अधिकांश self-hosted स्टैक इसी पैटर्न का पालन करते हैं: डेटाबेस और कैश के लिए named volumes, और कॉन्फ़िगरेशन तथा आपके द्वारा उपयोग की जाने वाली बड़ी डायरेक्टरी के लिए bind mounts। VPS पर चल रहा Chatwoot जैसा सपोर्ट डेस्क बिल्कुल इसी स्थिति में आता है, जहाँ Postgres एक named volume में होता है और अपलोड की गई अटैचमेंट एक ऐसे पाथ पर होती हैं जिसे आप बैकअप के लिए चुन सकते हैं।
FAQ
Bind mount और named volume में क्या अंतर है?
Bind mount होस्ट पर मौजूद एक पाथ को कंटेनर के अंदर मैप करता है, जिससे दोनों तरफ एक ही डायरेक्टरी दिखाई देती है और आप उसे सामान्य टूल्स से एडिट कर सकते हैं। Named volume वह स्टोरेज है जिसे Docker बनाता और मैनेज करता है, जिसे नाम से पहचाना जाता है और टॉप-लेवल volumes: ब्लॉक में घोषित किया जाता है। व्यावहारिक अंतर ओनरशिप का है: आपके द्वारा मेंटेन की जाने वाली कॉन्फ़िगरेशन के लिए bind mounts का उपयोग करें, और एप्लिकेशन द्वारा मेंटेन किए जाने वाले डेटा के लिए named volumes का।
Bind mount के साथ "permission denied" क्यों मिलता है, जबकि named volume के साथ नहीं?
एक खाली named volume को इमेज से सीड (seed) किया जाता है, इसलिए यह इमेज द्वारा सेट की गई ओनरशिप को इनहेरिट करता है और कंटेनर यूजर इसमें लिख सकता है। Bind mount होस्ट डायरेक्टरी को बिल्कुल वैसा ही दिखाता है जैसी वह है, और यदि Docker को वह डायरेक्टरी बनानी पड़ी, तो उसने उसे root के स्वामित्व में बनाया होगा। कंटेनर द्वारा उपयोग की जाने वाली न्यूमेरिक आईडी देखने के लिए docker compose exec <service> id चलाएं, फिर होस्ट डायरेक्टरी पर sudo chown -R <uid>:<gid> करें, या सर्विस पर user: "1000:1000" सेट करें।
Docker डिस्क पर named volumes को कहाँ स्टोर करता है?
डिफ़ॉल्ट local ड्राइवर के साथ वे /var/lib/docker/volumes/<volume>/_data के अंतर्गत रहते हैं, और docker volume inspect <volume> सटीक Mountpoint को प्रिंट करता है। यदि आपको कुछ चेक करना है तो इसे पढ़ें, लेकिन इसमें केवल कंटेनर के माध्यम से ही लिखें, क्योंकि होस्ट पर root के रूप में एडिट करने से ओनरशिप ऐसी बदल जाती है जिसकी कंटेनर अपेक्षा नहीं करता।
मैं named volume का बैकअप कैसे लूँ?
वॉल्यूम और एक होस्ट डायरेक्टरी दोनों को माउंट करके एक अल्पकालिक (short-lived) कंटेनर चलाएं, फिर docker run --rm -v myvol:/data:ro -v "$PWD":/backup alpine tar czf /backup/myvol.tar.gz -C /data . के साथ एक से दूसरे में आर्काइव करें। डेटाबेस के लिए, लाइव फाइलों को कॉपी करने के बजाय डेटाबेस के अपने टूल से डंप लें, क्योंकि राइट्स के दौरान ली गई फाइल कॉपी रिस्टोर होने पर करप्ट हो सकती है।
क्या docker compose down मेरे वॉल्यूम्स को डिलीट कर देता है?
docker compose down कंटेनरों और नेटवर्क्स को हटा देता है और named volumes को यथावत छोड़ देता है। docker compose down -v प्रोजेक्ट द्वारा घोषित प्रत्येक named volume को भी स्थायी रूप से डिलीट कर देता है। Bind mounts को किसी भी कमांड द्वारा नहीं हटाया जाता, क्योंकि वह डायरेक्टरी होस्ट की होती है, न कि Docker की।