Docker Compose: Bind Mount vs Named Volume
Ketahui perbezaan antara bind mount dan named volume dalam Docker Compose. Panduan ini menjelaskan cara mengurus fail konfigurasi, data aplikasi, serta isu keizinan fail.
Bind mount atau named volume: jawapan ringkas
Docker Compose mempunyai dua jenis volume, dan pemilihannya bergantung kepada siapa yang memiliki fail tersebut. Gunakan bind mount untuk fail yang anda tulis dan baca sendiri, seperti konfigurasi, templat, dan laman web statik. Gunakan named volume untuk data yang dimiliki oleh aplikasi, seperti fail pangkalan data, indeks carian, dan media yang dimuat naik. Bind mount merujuk kepada laluan pada hos yang boleh anda buka dalam editor. Named volume pula ialah storan yang dicipta dan dijejak oleh Docker untuk anda, dan anda mengaksesnya melalui Docker.
Kedua-duanya muncul di bawah kunci volumes: yang sama di dalam servis, itulah sebabnya ia sering tertukar. Perbezaannya terletak pada bahagian kiri tanda titik bertindih. Bahagian kiri yang bermula dengan . atau / ialah laluan hos, maka ia adalah bind mount. Sebarang input lain ialah nama, maka ia adalah named volume, dan nama tersebut mesti diisytiharkan dalam blok volumes: peringkat atas.
Dua sintaks dalam fail 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 ialah volum bernama. ./nginx.conf:/etc/nginx/nginx.conf ialah bind mount, dan :ro melekapkannya sebagai baca sahaja, yang merupakan tetapan lalai yang betul untuk konfigurasi yang tidak sepatutnya diubah suai oleh kontena. Abaikan entri volumes: di peringkat atas dan Compose akan berhenti dengan service "db" refers to undefined volume pgdata.
Jalankan servis tersebut dan senaraikan apa yang telah dicipta oleh Docker:
docker compose up -d
docker volume lsVolum tersebut tidak dinamakan pgdata. Ia dinamakan <project>_pgdata, di mana nama projek secara lalai mengambil nama direktori yang menyimpan fail compose tersebut. Direktori bernama myapp akan menghasilkan myapp_pgdata. Perkara ini penting kerana menamakan semula direktori akan memberikan anda volum baharu yang kosong, dan aplikasi akan kelihatan seolah-olah kehilangan datanya. Ia tidak hilang: volum lama masih disenaraikan oleh docker volume ls. Tetapkan nama secara khusus dengan name: dalam fail compose, atau tetapkan COMPOSE_PROJECT_NAME, jika direktori tersebut mungkin dipindahkan. Tetapan seperti itu sepatutnya diletakkan bersama fail persekitaran dan rahsia Compose anda yang lain.
Mengapa ralat keizinan hanya menjejaskan bind mount
Ini merupakan perbezaan praktikal yang paling ketara, dan ia berpunca daripada satu peraturan: named volume yang kosong pada penggunaan pertama akan diisi (seeded) daripada imej, manakala bind mount tidak akan berbuat demikian.
Apabila Docker melekapkan named volume yang kosong di atas direktori yang sudah mempunyai kandungan dalam imej, ia menyalin kandungan tersebut ke dalam volume, dengan pemilikan dan mod yang ditetapkan oleh imej. Imej rasmi Postgres dihantar dengan /var/lib/postgresql/data yang dimiliki oleh pengguna postgres miliknya sendiri, jadi volume tersebut akan dimiliki oleh id angka yang sama, dan pangkalan data akan bermula.
Bind mount melakukan perkara sebaliknya. Apa sahaja yang ada pada hos adalah apa yang dilihat oleh kontena, termasuk pemilikan, dan kandungan imej pada laluan tersebut akan disembunyikan. Jika direktori hos tidak wujud, daemon Docker akan menciptanya, dan daemon tersebut berjalan sebagai root, jadi anda akan mendapat direktori yang dimiliki oleh root:root. Proses kontena yang berjalan sebagai pengguna bukan root kemudiannya tidak dapat menulis ke dalamnya:
PermissionError: [Errno 13] Permission denied: '/data/app.db'Penyelesaiannya adalah dengan menyelaraskan angka tersebut. Pemilikan merentasi bind mount dibandingkan mengikut id pengguna angka, bukan mengikut nama, kerana kontena mempunyai /etc/passwd sendiri. Pengguna bernama app di dalam kontena tidak membawa sebarang makna pada hos. Uid 1000 bermaksud uid 1000 pada kedua-dua belah pihak.
id -u
mkdir -p ./data
docker compose exec web id
sudo chown -R 1000:1000 ./datadocker compose exec web id mencetak uid yang digunakan oleh proses kontena semasa ia berjalan. Padankan direktori hos dengan nombor tersebut, atau tetapkan kontena kepada nombor anda dengan user: "1000:1000" dalam servis. Menetapkan user: adalah lebih kemas untuk aplikasi yang anda tulis sendiri. Melakukan chown pada direktori hos adalah lebih selamat untuk imej yang tidak anda tulis, kerana sesetengah imej memulakan entrypoint sebagai root, menggugurkan keistimewaan, dan menjangkakan pemilikan khusus di bawahnya.
Dua lagi perangkap yang perlu diketahui. Pada Fedora, RHEL dan sistem lain dengan SELinux (security-enhanced Linux) yang dikuatkuasakan, bind mount akan dinafikan sehingga ia dilabel semula, jadi tambahkan :z untuk laluan yang dikongsi antara kontena atau :Z untuk laluan yang hanya boleh digunakan oleh satu kontena, ditulis sebagai - ./data:/data:Z. Selain itu, bind mount bagi satu fail tunggal, bukannya direktori, akan tergendala apabila editor menggantikan fail tersebut dan bukannya menulis di tempat asal, kerana mount tersebut mengikut inode asal. Kontena akan terus melihat kandungan lama sehingga anda memulakannya semula. Lekapkan direktori induk apabila fail tersebut sering disunting.
Prestasi: di mana jurang tersebut nyata
Pada pelayan Linux, kedua-dua jenis storan melalui laluan kernel yang sama, jadi perbezaan daya pemprosesan (throughput) adalah cukup kecil sehingga anda tidak perlu membuat pilihan berdasarkan faktor tersebut. Named volumes yang menggunakan pemacu local lalai berada pada sistem fail yang sama dengan bahagian Docker yang lain, di bawah /var/lib/docker/volumes/, manakala bind mount pula berada di mana-mana lokasi yang anda tetapkan.
Jurang prestasi muncul pada Docker Desktop untuk macOS dan Windows, di mana kontena berjalan di dalam mesin maya. Bind mount di sana merentas dari sistem fail hos ke dalam mesin maya tersebut melalui lapisan perkongsian fail, dan beban kerja yang melibatkan banyak operasi fail kecil, seperti pokok dependensi Node.js atau cache rangka kerja PHP, akan menjadi perlahan dengan ketara. Named volumes kekal di dalam mesin maya dan tidak menanggung kos tersebut. Inilah sebabnya banyak fail compose pembangunan melakukan bind-mount pada direktori sumber tetapi mengisytiharkan named volume pada node_modules.
Perbezaan sebenar yang lain ialah di mana bait data disimpan. Bind mount ke /mnt/backup meletakkan data pada cakera tersebut. Named volume pula akan berada pada mana-mana sistem fail yang menempatkan /var/lib/docker, yang pada VPS biasanya merupakan cakera root. Pangkalan data yang membesar di dalam named volume akan memenuhi cakera yang sama dengan tempat log sistem anda disimpan. Periksa perkara ini sebelum ia menjadi insiden:
docker system df -v
df -h /var/lib/dockerdocker system df -v menyenaraikan setiap volume beserta saiznya, dan menandakan volume yang tidak lagi dirujuk oleh mana-mana kontena.
Memeriksa named volume
Named volume bukanlah kotak hitam. Tanya Docker di mana lokasinya:
docker volume inspect myapp_pgdataMedan Mountpoint memberikan laluan hos sebenar, biasanya /var/lib/docker/volumes/myapp_pgdata/_data. Anda boleh membacanya dengan sudo ls, dan ini berguna untuk pemeriksaan pantas. Jangan anggap ia sebagai tempat untuk menyunting fail. Menulis di sana sebagai root akan mewujudkan semula masalah pemilikan yang diterangkan di atas, dan laluan tersebut merupakan perincian pemacu local yang tidak dikongsi oleh pemacu volume lain.
Cara selamat untuk melihat bahagian dalam adalah dengan menggunakan container sementara yang melekapkan (mount) volume tersebut:
docker run --rm -v myapp_pgdata:/vol alpine ls -la /volCara ini berkesan untuk mana-mana pemacu, melihat keizinan yang sama seperti yang dilihat oleh container sebenar, dan tidak meninggalkan sebarang kesan kerana --rm.
Sandaran bagi setiap jenis
Bind mount merupakan direktori biasa, jadi mana-mana alat sandaran peringkat fail boleh mengendalikannya. Halakan sandaran tersebut ke laluan hos dan proses selesai. Named volume memerlukan satu langkah tambahan kerana alat tersebut perlu mengakses bahagian dalamannya. Mount volume dan direktori hos ke dalam bekas (container) jangka pendek yang sama, kemudian tulis arkib:
docker run --rm \
-v myapp_pgdata:/data:ro \
-v "$PWD":/backup \
alpine tar czf /backup/pgdata.tar.gz -C /data .Lakukan pemulihan dengan menterbalikkan proses tersebut ke dalam volume baharu:
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 mengekalkan pemilikan numerik apabila ia dijalankan sebagai root di dalam bekas, yang memastikan volume yang dipulihkan boleh digunakan oleh aplikasi.
Satu amaran terpakai untuk kedua-dua jenis tersebut. Menyalin fail pangkalan data semasa pangkalan data sedang berjalan akan menghasilkan arkib data yang berubah-ubah, dan ia boleh menyebabkan keadaan rosak semasa pemulihan. Hentikan servis terlebih dahulu, atau lakukan dump melalui alat pangkalan data itu sendiri, seperti dalam docker compose exec -T db pg_dump -U postgres appdb > appdb.sql. Tindakan itu menghasilkan fail biasa yang kemudiannya boleh anda sertakan dalam rutin sandaran restic yang disulitkan bersama-sama fail compose anda.
Memindahkan bind mount ke named volume
Proses ini merupakan penyalinan, bukan penamaan semula, dan mengambil masa kira-kira satu minit.
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 mengekalkan pemilikan, mod dan cap masa, supaya pengguna kontena yang boleh membaca direktori lama masih boleh membaca volum baharu tersebut. Kemudian, ubah servis untuk menggunakan pgdata:/var/lib/postgresql/data, tambahkan pgdata pada blok volumes: peringkat atas, jalankan docker compose up -d, dan baca log aplikasi sebelum anda memadam direktori lama. Proses sebaliknya menggunakan arahan yang sama dengan /from dan /to ditukar ganti.
Ingat satu perkara semasa anda membuat ujian. docker compose down tidak menjejaskan named volume, tetapi docker compose down -v memadamkan setiap named volume yang diisytiharkan oleh projek, dan tindakan ini tidak boleh dibatalkan. Bind mount terselamat daripada kedua-duanya, kerana Docker tidak pernah memiliki direktori tersebut. Jika arahan kitaran hayat masih baharu bagi anda, panduan asas Docker Compose untuk VPS menerangkan perkara ini dengan lebih lanjut.
Memilih, servis demi servis
Tentukan siapa yang menulis fail tersebut. Konfigurasi yang anda sunting dalam penyunting teks dan lakukan komit ke git adalah milik bind mount, yang dipasang pada :ro, kerana anda mahu ia kelihatan dan mempunyai versi. Keadaan aplikasi yang tidak pernah anda buka secara manual adalah milik named volume, kerana Docker menetapkan kebenaran (permissions) dengan betul dan data tersebut tidak bergantung pada laluan hos.
Kes campuran adalah media. Pustaka foto ditulis oleh aplikasi tetapi juga diuruskan oleh anda, dan ia sering kali cukup besar sehingga memerlukan cakera khusus. Lakukan bind mount pada laluan di cakera tersebut, dan tetapkan pemilikan secara sengaja sekali sahaja. Itulah corak yang menjadi pilihan kebanyakan timbunan (stacks) yang dihoskan sendiri: named volume untuk pangkalan data dan cache, bind mount untuk konfigurasi dan direktori besar yang anda pentingkan. Meja sokongan seperti Chatwoot yang berjalan pada VPS berakhir tepat di situ, dengan Postgres dalam named volume dan lampiran yang dimuat naik pada laluan yang boleh anda halakan sandaran (backup) kepadanya.
FAQ
Apakah perbezaan antara bind mount dan named volume?
Bind mount memetakan laluan pada hos ke dalam kontena, supaya kedua-dua pihak melihat direktori yang sama dan anda boleh menyuntingnya dengan alatan biasa. Named volume ialah storan yang dicipta dan diuruskan oleh Docker, dirujuk melalui nama dan diisytiharkan dalam blok volumes: peringkat atas. Perbezaan praktikalnya ialah pemilikan: bind mount untuk konfigurasi yang anda selenggara, named volume untuk data yang diselenggara oleh aplikasi.
Mengapa saya mendapat "permission denied" dengan bind mount tetapi tidak dengan named volume?
Named volume yang kosong disuapkan daripada imej, jadi ia mewarisi pemilikan yang ditetapkan oleh imej dan pengguna kontena boleh menulis kepadanya. Bind mount menunjukkan direktori hos tepat seperti sedia ada, dan jika Docker terpaksa mencipta direktori tersebut, ia menjadikannya dimiliki oleh root. Jalankan docker compose exec <service> id untuk melihat id berangka yang digunakan oleh kontena, kemudian sudo chown -R <uid>:<gid> direktori hos tersebut, atau tetapkan user: "1000:1000" pada servis.
Di manakah Docker menyimpan named volume pada cakera?
Dengan pemacu local lalai, ia berada di bawah /var/lib/docker/volumes/<volume>/_data, dan docker volume inspect <volume> mencetak Mountpoint yang tepat. Baca fail tersebut jika anda perlu menyemak sesuatu, tetapi tulis kepadanya hanya melalui kontena, kerana menyunting sebagai root pada hos akan mengubah pemilikan dengan cara yang tidak dijangka oleh kontena.
Bagaimanakah cara saya membuat sandaran named volume?
Jalankan kontena jangka pendek dengan volume dan direktori hos kedua-duanya dipasang (mounted), kemudian arkibkan daripada satu ke yang lain dengan docker run --rm -v myvol:/data:ro -v "$PWD":/backup alpine tar czf /backup/myvol.tar.gz -C /data .. Untuk pangkalan data, lakukan dump dengan alat pangkalan data itu sendiri dan bukannya menyalin fail secara langsung, kerana salinan fail yang diambil semasa proses penulisan sedang berjalan boleh menyebabkan pemulihan kepada keadaan yang rosak.
Adakah docker compose down memadamkan volume saya?
docker compose down membuang kontena dan rangkaian serta membiarkan named volume di tempatnya. docker compose down -v juga memadamkan setiap named volume yang diisytiharkan oleh projek tersebut secara kekal. Bind mount tidak akan dibuang oleh mana-mana arahan, kerana direktori tersebut adalah milik hos dan bukan milik Docker.