Docker Compose: Bind Mount o Named Volume?
Alamin kung kailan gagamit ng bind mount para sa config at named volume para sa data, pati file permission traps, pag-inspect, backup, at migration.
Bind mount o named volume: maikling sagot
May dalawang uri ng volume sa Docker Compose, at nakabatay ang pagpili sa kung sino ang nagmamay-ari ng mga file. Gumamit ng bind mount para sa mga file na ikaw mismo ang sumusulat at nagbabasa, gaya ng config, template, at static site. Gumamit ng named volume para sa data na application ang nagmamay-ari, gaya ng mga database file, search index, at na-upload na media. Tumuturo ang bind mount sa path sa host na maaari mong buksan sa editor. Ang named volume ay storage na ginagawa at sinusubaybayan ng Docker para sa iyo, at ina-access mo ito sa pamamagitan ng Docker.
Pareho silang lumalabas sa ilalim ng volumes: key sa loob ng isang service, kaya napagkakamalan silang pareho. Ang pinagkaiba ay ang kaliwang bahagi ng colon. Kapag nagsisimula sa . o / ang kaliwang bahagi, host path iyon kaya bind mount ito. Ang anupamang halaga ay pangalan, kaya named volume ito, at dapat ding ideklara ang pangalang iyon sa top-level na volumes: block.
Ang dalawang syntax sa 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:Ang pgdata:/var/lib/postgresql/data ay isang named volume. Ang ./nginx.conf:/etc/nginx/nginx.conf ay isang bind mount, at mino-mount ito ng :ro bilang read only. Ito ang tamang default para sa config na hindi dapat kailanman i-rewrite ng container. Kapag nakalimutan ang top-level na entry na volumes:, hihinto ang Compose at magpapakita ng service "db" refers to undefined volume pgdata.
I-start ito at ilista ang mga ginawa ng Docker:
docker compose up -d
docker volume lsHindi pgdata ang pangalan ng volume. Ang pangalan nito ay <project>_pgdata, kung saan ang project name ay nagde-default sa pangalan ng directory na naglalaman ng compose file. Kung ang directory ay tinatawag na myapp, magiging myapp_pgdata ito. Mahalaga ito dahil kapag pinalitan ang pangalan ng directory, magkakaroon ka ng bagong empty volume, at magmumukhang nawala ang data ng application. Hindi ito nawala: nakalista pa rin ang lumang volume gamit ang docker volume ls. I-pin ang pangalan gamit ang name: sa compose file, o i-set ang COMPOSE_PROJECT_NAME, kung maaaring ilipat ang directory. Ang mga setting na tulad nito ay dapat ilagay kasama ng iba mo pang Compose environment files at secrets.
Bakit ang mga error sa permission ay nangyayari lamang sa bind mounts
Ito ang pinakamalaking praktikal na pagkakaiba. Nagmumula ito sa isang rule: ang named volume na walang laman sa unang paggamit ay sine-seed mula sa image, samantalang ang bind mount ay hindi.
Kapag nag-mount ang Docker ng walang-lamang named volume sa ibabaw ng directory na may laman na sa image, kinokopya nito ang laman sa volume, kasama ang ownership at modes na itinakda ng image. Ang official Postgres image ay nagre-release ng /var/lib/postgresql/data na pagmamay-ari ng sarili nitong postgres user. Kaya ang volume ay nagkakaroon ng parehong numeric id bilang owner, at nagsisimula ang database.
Kabaligtaran ang ginagawa ng bind mount. Anuman ang nasa host ang nakikita ng container, kasama ang ownership, at natatago ang laman ng image sa path na iyon. Kung hindi umiiral ang host directory, ginagawa ito ng Docker daemon. Dahil tumatakbo ang daemon bilang root, nagkakaroon ka ng directory na pagmamay-ari ng root:root. Hindi ito maisusulatan ng container process na tumatakbo bilang non-root user:
PermissionError: [Errno 13] Permission denied: '/data/app.db'Ang solusyon ay pagtugmain ang mga numero. Ang ownership sa bind mount ay ikinukumpara gamit ang numeric user id, hindi ang pangalan, dahil may sarili itong /etc/passwd ang container. Walang kahulugan sa host ang user na tinatawag na app sa loob ng container. Ang uid 1000 ay uid 1000 sa magkabilang panig.
id -u
mkdir -p ./data
docker compose exec web id
sudo chown -R 1000:1000 ./dataIpinapakita ng docker compose exec web id ang uid kung saan aktuwal na tumatakbo ang container process. Itugma ang host directory sa numerong iyon, o itakda ang container na gamitin ang iyong numero sa pamamagitan ng user: "1000:1000" sa service. Mas maayos ang pagtatakda ng user: para sa application na ikaw mismo ang sumulat. Mas ligtas namang baguhin ang owner ng host directory para sa image na hindi mo isinulat, dahil may ilang image na nagsisimula ng entrypoint bilang root, nagbabawas ng privilege, at umaasa sa partikular na ownership sa loob.
Mahalaga ring malaman ang dalawa pang karaniwang problema. Sa Fedora, RHEL at iba pang system na naka-enforce ang SELinux (security-enhanced Linux), dini-deny ang bind mount hanggang sa ma-relabel ito. Kaya idagdag ang :z para sa path na ibinabahagi ng mga container, o :Z para sa path na isang container lamang ang dapat gumamit, na isinusulat bilang - ./data:/data:Z. At nabibigo ang bind mount ng iisang file, sa halip na directory, kapag pinapalitan ng editor ang file sa halip na sumulat dito nang hindi pinapalitan ang inode, dahil sinusundan ng mount ang orihinal na inode. Patuloy na nakikita ng container ang lumang content hanggang sa i-restart mo ito. I-mount ang parent directory kapag madalas ine-edit ang file.
Pagganap: kung saan tunay ang pagkakaiba
Sa Linux server, parehong dumadaan ang dalawang uri sa parehong kernel path. Maliit ang pagkakaiba sa throughput, kaya huwag pumili batay dito. Ang mga named volume na gumagamit ng default na local driver ay nasa parehong filesystem ng iba pang bahagi ng Docker, sa ilalim ng /var/lib/docker/volumes/. Ang bind mount naman ay nasa lokasyong itinuro mo.
Lumilitaw ang pagkakaiba sa Docker Desktop para sa macOS at Windows, kung saan tumatakbo ang mga container sa loob ng virtual machine. Doon, tumatawid ang bind mount mula sa host filesystem papunta sa virtual machine sa pamamagitan ng file-sharing layer. Kapansin-pansing bumabagal ang mga workload na maraming maliit na file operation, gaya ng dependency tree ng Node.js o cache ng PHP framework. Nananatili ang mga named volume sa loob ng virtual machine at hindi nagkakaroon ng ganitong dagdag na gastos. Ito ang dahilan kung bakit maraming development compose file ang nagbi-bind-mount ng source directory ngunit nagdedeklara ng named volume sa node_modules.
Ang isa pang tunay na pagkakaiba ay kung saan napupunta ang mga byte. Kapag nag-bind mount ka sa /mnt/backup, sa disk na iyon mapupunta ang data. Ang named volume ay mapupunta sa filesystem na naglalaman ng /var/lib/docker, na karaniwang root disk sa isang VPS. Kapag lumalaki ang database sa loob ng named volume, mapupuno nito ang parehong disk na ginagamit ng iyong system log. Suriin ito bago ito maging insidente:
docker system df -v
df -h /var/lib/dockerInililista ng docker system df -v ang lahat ng volume kasama ang laki ng mga ito at minamarkahan ang mga volume na wala nang container na gumagamit sa mga ito.
Pagsusuri sa pinangalanang volume
Ang pinangalanang volume ay hindi black box. Itanong sa Docker kung nasaan ito:
docker volume inspect myapp_pgdataIbinibigay ng field na Mountpoint ang aktuwal na path sa host, karaniwang /var/lib/docker/volumes/myapp_pgdata/_data. Mababasa mo ito gamit ang sudo ls, at kapaki-pakinabang ito para sa mabilisang pagsusuri. Huwag itong ituring na lugar para mag-edit ng mga file. Kapag sumulat ka roon bilang root, mauulit ang problema sa ownership na inilarawan sa itaas. Ang path ay detalye ng local driver, at hindi ito pareho sa ibang volume driver.
Ang ligtas na paraan para tingnan ang laman nito ay gumamit ng pansamantalang container na nagmo-mount sa volume:
docker run --rm -v myapp_pgdata:/vol alpine ls -la /volGumagana ito sa anumang driver, nakikita nito ang parehong permissions na nakikita ng aktuwal na container, at walang naiiwan dahil sa --rm.
Bina-backup sa bawat uri
Ang bind mount ay isang ordinaryong directory, kaya awtomatiko itong nahahawakan ng anumang file-level backup tool. Ituro ang backup sa host path, at tapos na. Kailangan ng isang karagdagang hakbang ang named volume dahil kailangang makapasok dito ang tool. I-mount ang volume at isang host directory sa parehong panandaliang container, pagkatapos ay gumawa ng archive:
docker run --rm \
-v myapp_pgdata:/data:ro \
-v "$PWD":/backup \
alpine tar czf /backup/pgdata.tar.gz -C /data .I-restore ito sa pamamagitan ng pagbaliktad ng proseso papunta sa bagong 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 /dataPinapanatili ng tar ang numeric ownership kapag tumatakbo ito bilang root sa loob ng container. Ito ang nagpapanatiling magagamit ng application ang na-restore na volume.
May isang babalang naaangkop sa parehong uri. Kapag kinopya ang mga file ng database habang tumatakbo ang database, makakakuha ka ng archive mula sa data na patuloy na nagbabago. Maaari itong ma-restore sa corrupt na estado. Ihinto muna ang service, o mag-dump gamit ang sariling tool ng database, gaya ng nasa docker compose exec -T db pg_dump -U postgres appdb > appdb.sql. Gumagawa ito ng plain file na maaari mong isama sa isang karaniwang encrypted na restic backup routine kasama ng iyong compose files.
Paglilipat ng bind mount sa isang named volume
Kopya ang paglilipat, hindi pagpapalit ng pangalan, at tumatagal ito nang humigit-kumulang isang minuto.
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/'Pinapanatili ng cp -a ang ownership, modes, at timestamps. Dahil dito, mababasa pa rin ng container user na nakabasa sa lumang directory ang bagong volume. Pagkatapos, baguhin ang service para gamitin ang pgdata:/var/lib/postgresql/data, idagdag ang pgdata sa top-level na volumes: block, patakbuhin ang docker compose up -d, at basahin ang logs ng application bago tanggalin ang lumang directory. Para sa kabaligtarang operasyon, gamitin ang parehong command at pagpalitin ang /from at /to.
Tandaan ito habang nagsasagawa ka ng mga test. Hindi ginalaw ng docker compose down ang mga named volume, ngunit dine-delete ng docker compose down -v ang bawat named volume na idineklara ng project, at hindi na ito maa-undo. Nananatili ang bind mount sa parehong operasyon dahil hindi kailanman pagmamay-ari ng Docker ang directory na iyon. Kung bago pa sa iyo ang lifecycle commands, ipinapaliwanag ng gabay sa mga pangunahing kaalaman sa Docker Compose para sa VPS ang mga ito.
Pagpili, bawat serbisyo
Tanungin kung sino ang sumusulat sa file. Ang config na ine-edit mo sa isang text editor at kino-commit sa git ay dapat nasa bind mount, na naka-mount sa :ro, dahil kailangan itong makita at ma-version. Ang application state na hindi mo kailanman binubuksan nang manu-mano ay dapat nasa named volume, dahil wastong itinatakda ng Docker ang mga permission at hindi nakadepende ang data sa path ng host.
Ang mixed case ay media. Isinusulat ng application ang photo library, ngunit pinamamahalaan mo rin ito, at kadalasan ay sapat itong kalaki para mangailangan ng partikular na disk. I-bind mount ito sa isang path sa disk na iyon, at sadyang itakda ang ownership nang isang beses. Ito ang pattern na karaniwang ginagamit ng mga self-hosted stack: named volume para sa mga database at cache, at bind mount para sa config at sa malaking directory na mahalaga sa iyo.
FAQ
Ano ang pagkakaiba ng bind mount at named volume?
Ang bind mount ay nagmamapa ng path sa host papunta sa container, kaya pareho nilang nakikita ang parehong directory at maaari mo itong i-edit gamit ang mga karaniwang tool. Ang named volume ay storage na ginagawa at pinamamahalaan ng Docker, tinutukoy ayon sa pangalan at idinedeklara sa top-level na volumes: block. Ang praktikal na pagkakaiba ay pamamahala: gamitin ang bind mounts para sa configuration na ikaw ang nagpapanatili, at ang named volumes para sa data na pinamamahalaan ng application.
Bakit nakakakuha ako ng "permission denied" sa bind mount pero hindi sa named volume?
Ang isang walang-laman na named volume ay kinokopyahan ng laman mula sa image, kaya minamana nito ang ownership na itinakda ng image at maaaring sulatan ito ng container user. Ipinapakita ng bind mount ang host directory kung ano talaga ito, at kung kinailangang gawin ng Docker ang directory na iyon, ginawa niya itong pagmamay-ari ng root. Patakbuhin ang docker compose exec <service> id para makita ang numeric id na ginagamit ng container, pagkatapos ay ang sudo chown -R <uid>:<gid> sa host directory, o itakda ang user: "1000:1000" sa service.
Saan iniimbak ng Docker sa disk ang named volumes?
Gamit ang default na local driver, nasa /var/lib/docker/volumes/<volume>/_data ang mga ito, at ipinapakita ng docker volume inspect <volume> ang eksaktong Mountpoint. Basahin ito kung kailangan mong magsuri, ngunit dito lamang ito sulatan sa pamamagitan ng isang container, dahil kapag ini-edit ito bilang root sa host, nagbabago ang ownership sa paraang hindi inaasahan ng container.
Paano ako magba-back up ng named volume?
Magpatakbo ng panandaliang container na parehong may naka-mount na volume at host directory, pagkatapos ay i-archive ang data mula sa isa papunta sa isa gamit ang docker run --rm -v myvol:/data:ro -v "$PWD":/backup alpine tar czf /backup/myvol.tar.gz -C /data .. Para sa database, gamitin ang sariling tool ng database para gumawa ng dump sa halip na kopyahin ang mga live file, dahil ang file copy na ginawa habang may isinasagawang writes ay maaaring ma-restore sa corrupt na estado.
Tinatanggal ba ng docker compose down ang mga volume ko?
Tinatanggal ng docker compose down ang mga container at network at iniiwan ang named volumes. Tinatanggal din ng docker compose down -v ang bawat named volume na idinedeklara ng project, nang permanente. Hindi inaalis ng alinman sa mga command ang bind mounts, dahil pagmamay-ari ng host ang directory na iyon at hindi ng Docker.