Docker Compose में bind mount या named volume?
Docker Compose में config और database data के लिए सही volume चुनें। permission traps समझें और inspect, backup व migrate करने के व्यावहारिक तरीके जानें।
Bind mount या named volume: संक्षिप्त उत्तर
Docker Compose volumes दो प्रकार के होते हैं। चुनाव इस बात पर निर्भर करता है कि files का स्वामित्व किसके पास है। जिन files को आप स्वयं लिखते और पढ़ते हैं, उनके लिए bind mount का उपयोग करें। उदाहरण के लिए config, templates और static sites। जिस data का स्वामित्व application के पास है, उसके लिए named volume का उपयोग करें। उदाहरण के लिए database files, search indexes और uploaded media। Bind mount host के उस path को निर्दिष्ट करता है जिसे आप editor में खोल सकते हैं। Named volume वह storage है जिसे Docker आपके लिए बनाता और track करता है। आप इसे Docker के माध्यम से access करते हैं।
दोनों किसी service के अंदर एक ही volumes: key के अंतर्गत दिखाई देते हैं। इसी कारण अक्सर इनके बीच भ्रम होता है। अंतर colon के बाईं ओर के भाग में है। . या / से शुरू होने वाला बायाँ भाग host path होता है। इसलिए वह bind mount है। अन्य कोई भी भाग name होता है। इसलिए वह named volume है। इस name को top-level volumes: block में भी declare करना आवश्यक है।
compose file में दो syntax
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 रूप में mount करता है। यह उस config के लिए सही default है जिसे container को कभी rewrite नहीं करना चाहिए। Top-level volumes: entry भूलने पर Compose service "db" refers to undefined volume pgdata के साथ रुक जाता है।
इसे शुरू करें और देखें कि Docker ने क्या बनाया:
docker compose up -d
docker volume lsVolume का नाम pgdata नहीं है। इसका नाम <project>_pgdata है, क्योंकि project name default रूप से उस directory के नाम पर आधारित होता है जिसमें compose file रखी है। myapp नाम की directory से myapp_pgdata मिलता है। यह महत्वपूर्ण है, क्योंकि directory का नाम बदलने पर नया empty volume बनता है और application ऐसा दिखता है जैसे उसका data खो गया हो। ऐसा नहीं हुआ है: पुराना volume अभी भी docker volume ls में सूचीबद्ध है। compose file में name: के साथ नाम निश्चित करें, या COMPOSE_PROJECT_NAME सेट करें, यदि directory बदल सकती है। ऐसी settings आपकी अन्य Compose environment files और secrets के साथ रखी जानी चाहिए।
अनुमति संबंधी त्रुटियाँ केवल bind mounts में क्यों आती हैं
यह सबसे बड़ा व्यावहारिक अंतर है। इसका कारण एक नियम है: पहली बार उपयोग किए जाने पर खाली named volume को image से प्रारंभिक सामग्री मिलती है, लेकिन bind mount को नहीं।
जब Docker किसी ऐसी directory पर खाली named volume mount करता है जिसमें image में पहले से सामग्री मौजूद है, तो वह उस सामग्री को volume में कॉपी कर देता है। इसमें image द्वारा निर्धारित ownership और modes भी शामिल होते हैं। आधिकारिक Postgres image में /var/lib/postgresql/data उसके अपने postgres user के स्वामित्व में होता है। इसलिए volume भी उसी numeric id के स्वामित्व में बनता है और database शुरू हो जाता है।
bind mount इसके विपरीत काम करता है। Host पर जो कुछ मौजूद है, container वही देखता है, ownership सहित। उस path पर image की सामग्री छिप जाती है। यदि host directory मौजूद नहीं है, तो Docker daemon उसे बनाता है। daemon root के रूप में चलता है, इसलिए आपको root:root के स्वामित्व वाली directory मिलती है। इसके बाद non-root user के रूप में चलने वाली container process उसमें लिख नहीं सकती:
PermissionError: [Errno 13] Permission denied: '/data/app.db'समाधान यह है कि दोनों जगह numeric values समान हों। bind mount में ownership की तुलना numeric user id से की जाती है, name से नहीं, क्योंकि 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 दिखाता है जिसके रूप में container process वास्तव में चलती है। Host directory को उस number के स्वामित्व में करें, या service में user: "1000:1000" के साथ container को अपने number पर चलाएँ। अपने द्वारा लिखे गए application के लिए user: को निर्धारित करना अधिक साफ़ तरीका है। जिस image को आपने नहीं लिखा है, उसके लिए host directory का Chown करना अधिक सुरक्षित है, क्योंकि कुछ images root के रूप में entrypoint शुरू करती हैं, फिर privileges कम करती हैं और अंदर specific ownership की अपेक्षा करती हैं।
दो अन्य समस्याओं की जानकारी भी रखें। Fedora, RHEL और SELinux (security-enhanced Linux) enforcing वाले अन्य systems में bind mount को relabel किए बिना access से इनकार कर दिया जाता है। इसलिए containers के बीच साझा किए जाने वाले path के लिए :z या केवल एक container द्वारा उपयोग किए जाने वाले path के लिए :Z जोड़ें, और इसे - ./data:/data:Z के रूप में लिखें। किसी directory के बजाय एक single file का bind mount भी तब विफल हो जाता है जब editor file को उसी स्थान पर लिखने के बजाय उसे बदल देता है, क्योंकि mount original inode का अनुसरण करता है। Container पुरानी सामग्री देखता रहता है, जब तक कि आप उसे restart न करें। जिस file को अक्सर edit किया जाता है, उसके लिए parent directory को mount करें।
प्रदर्शन: अंतर वास्तव में कहाँ है
Linux server पर दोनों प्रकार एक ही kernel path से गुजरते हैं। इसलिए throughput का अंतर इतना कम होता है कि आपको इस आधार पर चुनाव नहीं करना चाहिए। Default local driver का उपयोग करने वाले named volumes, Docker के बाकी डेटा की तरह, /var/lib/docker/volumes/ के अंतर्गत उसी filesystem पर रहते हैं। Bind mount उस स्थान पर रहता है जिसे आपने निर्दिष्ट किया है।
यह अंतर macOS और Windows के Docker Desktop पर दिखाई देता है, जहाँ containers एक virtual machine के अंदर चलते हैं। वहाँ bind mount, file sharing layer के माध्यम से host filesystem से उस virtual machine में जाता है। Node.js dependency tree या PHP framework cache जैसे बहुत-सी छोटी file operations वाले workloads स्पष्ट रूप से धीमे हो जाते हैं। Named volumes virtual machine के अंदर ही रहते हैं और यह लागत नहीं उठाते। इसी कारण development compose files अक्सर source directory को bind-mount करती हैं, लेकिन node_modules पर named volume घोषित करती हैं।
दूसरा वास्तविक अंतर यह है कि bytes कहाँ लिखे जाते हैं। /mnt/backup पर bind mount करने से डेटा उसी disk पर रखा जाता है। Named volume उस filesystem पर रहता है जिसमें /var/lib/docker स्थित है। VPS पर यह आमतौर पर root disk होता है। Named volume में बढ़ता हुआ database उसी disk को भरता है जिस पर आपके system logs हैं। Incident बनने से पहले इसकी जाँच करें:
docker system df -v
df -h /var/lib/dockerdocker system df -v हर volume को उसके size के साथ सूचीबद्ध करता है और उन volumes को चिह्नित करता है जिन्हें अब कोई container refer नहीं करता।
नामित वॉल्यूम का निरीक्षण
नामित वॉल्यूम कोई रहस्यमय संरचना नहीं है। Docker से उसका स्थान पूछें:
docker volume inspect myapp_pgdataMountpoint फ़ील्ड वास्तविक होस्ट पथ देता है, जो सामान्यतः /var/lib/docker/volumes/myapp_pgdata/_data होता है। आप इसे sudo ls से पढ़ सकते हैं। यह त्वरित जाँच के लिए उपयोगी है। इसे फ़ाइलों में बदलाव करने की जगह न मानें। वहाँ root के रूप में लिखने पर ऊपर बताई गई स्वामित्व संबंधी समस्या फिर उत्पन्न हो जाती है। यह पथ local driver का एक विवरण है, जिसे अन्य volume drivers साझा नहीं करते।
अंदर देखने का सुरक्षित तरीका यह है कि ऐसा अस्थायी 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 इसे पहले से संभालता है। Backup को 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 .Fresh volume में प्रक्रिया उलटकर 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 /dataजब यह container के अंदर root के रूप में चलता है, तो tar numeric ownership को सुरक्षित रखता है। इसी कारण restored volume application के लिए उपयोग योग्य रहता है।
दोनों प्रकारों पर एक चेतावनी लागू होती है। Database के चलने के दौरान उसकी files कॉपी करने पर आपको बदलती स्थिति का archive मिलता है। इसे restore करने पर corrupt state बन सकती है। पहले service को stop करें, या database के अपने tool से dump लें, जैसा कि docker compose exec -T db pg_dump -U postgres appdb > appdb.sql में है। इससे एक plain file बनती है। फिर आप इसे अपनी compose files के साथ सामान्य encrypted restic backup प्रक्रिया में शामिल कर सकते हैं।
bind mount को named volume में माइग्रेट करना
यह स्थानांतरण कॉपी होता है, नाम बदलना नहीं। इसमें लगभग 1 मिनट लगता है।
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 ownership, modes और timestamps बनाए रखता है। इसलिए जो container user पुरानी directory को पढ़ सकता था, वह नई volume को भी पढ़ सकता है। इसके बाद service को pgdata:/var/lib/postgresql/data का उपयोग करने के लिए बदलें, शीर्ष-स्तरीय volumes: block में pgdata जोड़ें, docker compose up -d चलाएँ, और पुरानी directory हटाने से पहले application के logs पढ़ें। दूसरी दिशा में जाने के लिए उसी command का उपयोग करें, जिसमें /from और /to की अदला-बदली की गई हो।
परीक्षण के दौरान एक बात ध्यान में रखें। docker compose down named volumes को नहीं हटाता, लेकिन docker compose down -v project द्वारा घोषित हर named volume को हटा देता है और इसे पूर्ववत नहीं किया जा सकता। Bind mount दोनों स्थितियों में बना रहता है, क्योंकि Docker उस directory का स्वामी कभी नहीं था। यदि lifecycle commands अभी भी आपके लिए नई हैं, तो VPS के लिए Docker Compose की मूल बातें गाइड में इनके बारे में विस्तार से बताया गया है।
सेवा के अनुसार चयन
पूछें कि फ़ाइल कौन लिखता है। जिस कॉन्फ़िगरेशन को आप टेक्स्ट एडिटर में संपादित करके git में commit करते हैं, उसे bind mount में रखें और :ro पर mount करें, क्योंकि आप उसे दिखाई देने योग्य और versioned रखना चाहते हैं। Application state, जिसे आप कभी हाथ से नहीं खोलते, named volume में रखें, क्योंकि Docker permissions को सही तरीके से सेट करता है और डेटा host path पर निर्भर नहीं करता।
मिश्रित स्थिति media की है। Photo library application द्वारा लिखी जाती है, लेकिन उसका प्रबंधन आप भी करते हैं, और वह अक्सर इतनी बड़ी होती है कि उसके लिए किसी विशिष्ट disk की आवश्यकता होती है। उसे उस disk के किसी path पर bind mount करें और एक बार ownership को जानबूझकर सेट करें। अधिकांश self-hosted stacks इसी तरीके पर टिकते हैं: databases और caches के लिए named volumes, config के लिए bind mounts, और उस बड़े directory के लिए भी bind mount जिसकी आपको आवश्यकता है।
FAQ
बाइंड माउंट और नामित वॉल्यूम में क्या अंतर है?
बाइंड माउंट होस्ट के किसी path को container में map करता है। इसलिए दोनों ओर एक ही directory दिखाई देती है और आप उसे सामान्य tools से edit कर सकते हैं। नामित वॉल्यूम वह storage है जिसे Docker बनाता और manage करता है। इसे नाम से refer किया जाता है और top-level volumes: block में declare किया जाता है। व्यावहारिक अंतर ownership का है। आपके द्वारा maintain किए जाने वाले config के लिए bind mounts और application द्वारा maintain किए जाने वाले data के लिए named volumes उपयोग करें।
Bind mount के साथ "permission denied" क्यों मिलता है, लेकिन named volume के साथ नहीं?
Empty named volume को image से seed किया जाता है। इसलिए उसे image द्वारा निर्धारित ownership मिलती है और container user उसमें लिख सकता है। Bind mount host directory को ठीक उसी स्थिति में दिखाता है जैसी वह host पर है। यदि Docker को वह directory बनानी पड़ी, तो उसने उसका owner root निर्धारित किया। Container द्वारा उपयोग किए जाने वाले numeric id को देखने के लिए docker compose exec <service> id चलाएँ। फिर host directory पर sudo chown -R <uid>:<gid> चलाएँ, या service पर user: "1000:1000" सेट करें।
Docker named volumes को disk पर कहाँ store करता है?
Default local driver के साथ वे /var/lib/docker/volumes/<volume>/_data के अंतर्गत रहते हैं। docker volume inspect <volume> सटीक Mountpoint प्रदर्शित करता है। यदि आपको किसी चीज़ की जाँच करनी हो तो इसे पढ़ें। लेकिन इसमें केवल container के माध्यम से लिखें, क्योंकि host पर root के रूप में edit करने से ownership इस तरह बदल सकती है जिसकी container अपेक्षा नहीं करता।
Named volume का backup कैसे लें?
एक short-lived container चलाएँ और उसमें volume तथा host directory दोनों mount करें। फिर docker run --rm -v myvol:/data:ro -v "$PWD":/backup alpine tar czf /backup/myvol.tar.gz -C /data . से एक स्थान से दूसरे स्थान पर archive बनाएँ। Database के लिए live files copy करने के बजाय database के अपने tool से dump लें। इसका कारण यह है कि writes चलने के दौरान लिया गया file copy corrupt state में restore हो सकता है।
क्या docker compose down मेरे volumes को delete करता है?
docker compose down containers और networks को हटाता है और named volumes को यथास्थान छोड़ देता है। docker compose down -v project द्वारा declared हर named volume को भी permanently delete करता है। इनमें से कोई भी command bind mounts को नहीं हटाता, क्योंकि वह directory host की होती है, Docker की नहीं।