Docker Compose: Bind Mount o Named Volume?
Alamin kung bind mount o named volume ang para sa config at data, pati file permission traps at mga hakbang sa pag-inspect, backup, at migration.
Bind mount o named volume: ang maikling sagot
May dalawang uri ng volume sa Docker Compose, at nakabatay ang pagpili sa kung sino ang may kontrol sa 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 pagmamay-ari ng application, gaya ng mga database file, search index, at uploaded media. Tumutukoy ang bind mount sa isang path sa host na maaari mong buksan sa isang 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 iisang volumes: key sa loob ng isang service, kaya madalas silang napagkakamalan. Ang pinagkaiba ay ang nasa kaliwang bahagi ng colon. Kapag nagsisimula sa . o / ang kaliwang bahagi, host path ito, kaya bind mount ito. Anumang iba pa ay isang 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 named volume. Ang ./nginx.conf:/etc/nginx/nginx.conf ay bind mount, at ang :ro ay nagmo-mount nito bilang read-only. Ito ang tamang default para sa configuration na hindi dapat kailanman baguhin ng container. Kapag nakalimutan ang top-level na volumes: entry, hihinto ang Compose at magpapakita ng service "db" refers to undefined volume pgdata.
I-start ito at ilista ang ginawa ng Docker:
docker compose up -d
docker volume lsHindi pgdata ang pangalan ng volume. <project>_pgdata ang pangalan nito, kung saan ang project name ay nagde-default sa pangalan ng directory na naglalaman ng compose file. Ang directory na may pangalang myapp ay magbibigay ng myapp_pgdata. Mahalaga ito dahil kapag pinalitan ang pangalan ng directory, magkakaroon ka ng bagong bakanteng volume, at magmumukhang nawala ang data ng application. Hindi ito nawala: nakalista pa rin ang lumang volume sa docker volume ls. Itakda ang pangalan gamit ang name: sa compose file, o itakda ang COMPOSE_PROJECT_NAME kung maaaring ilipat ang directory. Ang mga setting na gaya nito ay dapat ilagay kasama ng iba mo pang Compose environment files at secrets.
Bakit kadalasang bind mount ang nagkakaroon ng mga error sa permission
Ito ang pinakamalaking praktikal na pagkakaiba, at pangunahing nagmumula ito sa isang rule: ang named volume na walang laman sa unang paggamit ay pinupunuan mula sa image, samantalang ang bind mount ay hindi.
Kapag nag-mount ang Docker ng walang-lamang named volume sa directory na mayroon nang content sa image, kinokopya nito ang content na iyon sa volume, kasama ang ownership at modes na itinakda ng image. Ang official Postgres image ay naglalabas ng /var/lib/postgresql/data na pagmamay-ari ng sarili nitong postgres user, kaya ang volume ay magkakaroon ng parehong numeric id bilang owner, at magsisimula ang database.
Ang pagse-seed na ito lamang ang head start ng isang named volume. Pinamamahalaan ng Docker kung saan nakalagay ang volume, pero nagbabasa at nagsusulat ang mga container dito gamit ang ordinaryong filesystem operations. Kaya ipinapatupad ang ownership at modes dito gaya rin sa bind mount. Ang named volume na naka-mount sa path na wala sa image, o ang volume na unang pinunuan ng ibang image o user, ay walang ownership na minana mula sa image. Maaari ring ma-deny dito ang non-root process. Gumagana ang id check sa parehong uri.
Kabaligtaran ang ginagawa ng bind mount. Anuman ang nasa host, iyon ang nakikita ng container, kasama ang ownership, at natatago ang image content sa path na iyon. Kung hindi umiiral ang host directory, ginagawa ito ng Docker daemon, at tumatakbo ang daemon bilang root. Kaya makakakuha 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 pag-isahin ang mga numero. Sa bind mount, ikinukumpara ang ownership gamit ang numeric user id, hindi ang pangalan, dahil may sarili itong /etc/passwd ang container. Walang kahulugan sa host ang user na may pangalang app sa loob ng container. Ang uid 1000 ay uid 1000 sa magkabilang panig, maliban kung tumatakbo ang daemon gamit ang user-namespace remapping (userns-remap, na sine-set sa /etc/docker/daemon.json o sa command line ng dockerd). Inililipat ng remapping ang bawat container id sa subordinate range sa host. Sa halimbawang range ng Docker na nagsisimula sa 231072, ang container uid 0 ay host uid 231072. Kaya ang container uid 1000 ay host uid 232072.
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. Kung walang remapping, itugma ang host directory sa numerong iyon, o i-pin ang container sa iyong numero gamit ang user: "1000:1000" sa service. Kapag naka-enable ang remapping, hindi maipi-pin ng user: ang container sa host number mo, dahil inililipat din ang id na sine-set mo: ang user: "1000:1000" ay mapupunta pa rin sa host uid 232072 sa halimbawang range na iyon. Sa halip, i-chown ang host directory sa mapped host id. Nasa /etc/subuid at /etc/subgid ang sarili mong range. Tanging --userns=host sa docker container create, run o exec ang tinutukoy ng dokumentasyon ng Docker bilang paraan upang i-off ang remapping para sa isang container. Mas malinis gamitin ang user: para sa application na ikaw mismo ang sumulat. Mas ligtas i-chown ang host directory para sa image na hindi ikaw ang sumulat, dahil may ilang image na nagsisimula ng entrypoint bilang root, nagda-drop ng privileges, at umaasa sa partikular na ownership sa loob nito.
May dalawa pang trap na dapat malaman. Sa Fedora, RHEL at iba pang system na naka-enforce ang SELinux (security-enhanced Linux), madi-deny ang bind mount hanggang sa ma-relabel ito. Kaya idagdag ang :z para sa path na pinaghahatian ng mga container, o :Z para sa path na isang container lamang ang dapat gumamit, na isinusulat bilang - ./data:/data:Z. At nagkakaproblema ang bind mount ng iisang file, sa halip na directory, kapag pinapalitan ng editor ang file sa halip na magsulat dito nang in place, dahil sinusundan ng mount ang orihinal na inode. Patuloy na makikita ng container ang lumang content hanggang sa i-restart mo ito. I-mount ang parent directory kapag madalas ine-edit ang file.
Performance: kung saan talaga may pagkakaiba
Sa Linux server, dumadaan ang parehong uri sa iisang kernel path. Maliit lang ang pagkakaiba sa throughput. Huwag itong gawing batayan sa pagpili. Ang named volumes 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. Sa ganitong setup, dumadaan ang bind mount mula sa host filesystem papunta sa virtual machine sa pamamagitan ng file sharing layer. Dahil dito, kapansin-pansing bumabagal ang mga workload na maraming maliliit na file operation, gaya ng dependency tree ng Node.js o cache ng PHP framework. Nananatili sa loob ng virtual machine ang named volumes at hindi nila binabayaran ang ganitong overhead. Ito ang dahilan kung bakit maraming development compose file ang nagbi-bind-mount ng source directory ngunit nagde-declare ng named volume sa ibabaw ng node_modules.
Ang isa pang tunay na pagkakaiba ay kung saan napupunta ang bytes. 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 nasa root disk sa isang VPS. Kapag lumaki ang database sa loob ng named volume, mapupuno nito ang disk na ginagamit din ng system logs. 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 hindi na ginagamit ng anumang container.
Pagsusuri sa named volume
Ang named volume ay hindi black box. Itanong sa Docker kung saan ito matatagpuan:
docker volume inspect myapp_pgdataIbinibigay ng Mountpoint field ang aktuwal na host path, 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 kung saan dapat 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 ibinabahagi ng ibang volume driver.
Ang ligtas na paraan upang tingnan ang laman nito ay gumamit ng throwaway 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.
Pagba-backup ng bawat uri
Ordinaryong directory ang bind mount, kaya awtomatiko itong nahahawakan ng anumang file-level backup tool. Ituro ang backup sa host path at tapos na. May isang dagdag na hakbang para sa named volume dahil kailangang makapasok dito ang tool. I-mount ang volume at isang host directory sa parehong short-lived container, saka 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 pagbabaligtad 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 dahilan kung bakit magagamit ng application ang ni-restore na volume.
May isang babala para sa parehong uri. Kapag kinopya ang mga file ng database habang tumatakbo ito, archive ito ng patuloy na nagbabagong data at maaaring ma-restore sa corrupt na estado. Ihinto muna ang service, o gumamit ng dump sa pamamagitan ng 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 naka-encrypt na restic backup routine kasama ng iyong compose files.
Paglipat ng bind mount sa named volume
Ang paglipat ay pagkopya, 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 dating directory ang bagong volume. Pagkatapos, baguhin ang service upang 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 mo burahin ang dating directory. Sa kabilang direksiyon, gamitin ang parehong command at pagpalitin ang /from at /to.
Tandaan ang isang bagay habang nagsusuri ka. Hindi ginalaw ng docker compose down ang named volumes, pero binubura ng docker compose down -v ang lahat ng named volume na dineklara 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 paggamit ng mga ito.
Pagpili ayon sa bawat serbisyo
Tukuyin kung sino ang sumusulat sa file. Ang configuration na ine-edit mo sa text editor at kino-commit sa git ay dapat ilagay sa bind mount, na naka-mount sa :ro, dahil kailangan mo itong makita at ma-version. Ang application state na hindi mo manu-manong binubuksan ay dapat ilagay sa named volume, dahil itinatakda ng Docker ang permissions batay sa image at hindi nakadepende ang data sa path sa host.
Ang mixed case ay media. Ang photo library ay sinusulatan ng application pero mina-manage mo rin, at kadalasan ay sapat ang laki nito para mangailangan ng partikular na disk. I-bind mount ito sa isang path sa disk na iyon, at maingat na itakda ang ownership nang isang beses. Ito ang pattern na karaniwang ginagamit ng mga self-hosted stack: named volumes para sa databases at caches, at bind mounts para sa configuration at sa malaking directory na mahalaga sa iyo. Ganito mismo ang setup ng isang support desk gaya ng Chatwoot na tumatakbo sa isang VPS, kung saan nasa named volume ang Postgres at nasa path na maaaring ituro sa backup ang mga uploaded attachment.
FAQ
Ano ang pagkakaiba ng bind mount at named volume?
Imamapa ng bind mount ang isang path sa host papunta sa container, kaya parehong nakikita ng magkabilang panig ang iisang directory at maaari mo itong i-edit gamit ang mga karaniwang tool. Ang named volume ay storage na ginagawa at pinamamahalaan ng Docker, tinutukoy gamit ang pangalan, at dine-declare sa top-level volumes: block. Ang praktikal na pagkakaiba ay ownership: gamitin ang bind mounts para sa config na ikaw ang nagpapanatili, at named volumes para sa data na pinapanatili ng application.
Bakit nakukuha ko ang "permission denied" sa bind mount pero hindi sa named volume?
Ang walang-lamang named volume ay sine-seed mula sa image, kaya karaniwan nitong minamana ang ownership na itinakda ng image at maaaring sulatan ito ng container user. Ipinapatupad pa rin nito ang permissions, kaya maaari rin itong mabigo kung walang laman ang image sa path na iyon. Eksaktong ipinapakita ng bind mount ang host directory. Kung kinailangang gawin ng Docker ang directory na iyon, ginawa ito na pagmamay-ari ng root. Gamitin ang docker compose exec <service> id upang makita ang numeric id na ginagamit ng container, saka sudo chown -R <uid>:<gid> ang host directory. Kung hindi nagre-remap ng user namespaces ang daemon, i-set ang user: "1000:1000" sa service. Kung gumagamit ang daemon ng user-namespace remapping, i-chown ito sa mapped host id, dahil inililipat ang mga id ng container sa host at hindi ito binabago ng user:.
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, pero dito lamang magsulat sa pamamagitan ng container, dahil kapag nag-edit ka bilang root sa host, maaaring mabago ang ownership sa paraang hindi aasahan ng container.
Paano ako magba-back up ng named volume?
Magpatakbo ng panandaliang container na parehong naka-mount ang volume at host directory, saka 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 dump tool ng database sa halip na kopyahin ang live files, dahil maaaring ma-restore ang kopyang ginawa habang may kasabay na writes sa corrupt na state.
Binubura ba ng docker compose down ang mga volume ko?
Tinatanggal ng docker compose down ang mga container at network, pero iniiwan nito ang named volumes. Tinatanggal din ng docker compose down -v ang bawat named volume na dine-declare ng project, nang permanente. Hindi kailanman tinatanggal ng alinmang command ang bind mounts, dahil pagmamay-ari ng host ang directory na iyon at hindi ng Docker.