Cara Guna Docker Compose Dengan Banyak Fail
Ketahui cara fail compose.override.yaml berfungsi, urutan penggabungan fail, perangkap port yang sering berlaku, serta penggunaan include untuk konfigurasi dev dan prod.
Tindakan Compose dengan lebih daripada satu fail
Docker Compose boleh membina satu projek daripada beberapa fail. Ia membaca fail tersebut mengikut urutan ia diterima dan menggabungkannya menjadi satu model tunggal, jadi fail yang kemudian akan mengatasi sebarang nilai yang bercanggah. Dua mekanisme melakukan ini daripada baris perintah: fail override yang dimuatkan oleh Compose secara automatik, dan flag -f yang anda berikan secara manual. Mekanisme ketiga wujud di dalam fail itu sendiri, iaitu elemen include, dan ia berfungsi secara berbeza daripada kedua-duanya.
Penggabungan ini bukanlah penggantian biasa. Pemetaan (mappings) digabungkan mengikut kunci, jujukan (sequences) ditambah, dan sekumpulan kecil medan diganti sepenuhnya. Perbezaan itulah yang sering menimbulkan kejutan, dan senarai ports adalah perkara yang hampir memerangkap semua orang.
Segala maklumat di bawah mengandaikan penggunaan Compose v2, iaitu pemalam docker compose dan bukannya skrip docker-compose yang lama. Jalankan docker compose version untuk menyemak. Jika anda belum menulis fail Compose, mulakan dengan panduan asas Docker Compose dan kembali semula ke sini.
Fail override yang dimuatkan oleh Compose secara automatik
Jalankan docker compose up tanpa sebarang flag -f dan Compose akan mencari direktori kerja, kemudian direktori induknya, untuk compose.yaml atau docker-compose.yaml. Jika fail override berada di sebelah fail asas, Compose akan memuatkan fail tersebut secara automatik.
ls compose.yaml compose.override.yaml
docker compose up -dApabila kedua-dua fail hadir, ia adalah sama seperti menaipnya secara manual.
docker compose -f compose.yaml -f compose.override.yaml up -dNama yang dikenali oleh Compose ialah compose.override.yaml, compose.override.yml, serta docker-compose.override.yml dan docker-compose.override.yaml yang lebih lama. Sebarang nama lain, contohnya compose.dev.yaml, hanya akan dimuatkan apabila anda menamakannya dengan -f.
Sebaik sahaja anda memasukkan satu -f, pemuatan automatik akan terhenti. docker compose -f compose.yaml up hanya membaca fail tersebut dan mengabaikan override; ini merupakan sifat yang menjadi asas kepada corak pembangunan (dev) dan pengeluaran (prod) dalam panduan ini.
Ini memberi kesan dua hala pada pelayan. Fail override yang ditinggalkan dalam direktori penempatan (deploy) akan dimuatkan oleh setiap arahan docker compose yang dijalankan dari direktori tersebut, termasuk arahan yang dijalankan oleh cron job anda. Inilah punca stack pengeluaran akhirnya melakukan bind-mount pada direktori sumber yang tidak sepatutnya disertakan. Jalankan docker compose config selepas sebarang penempatan dan baca outputnya. Apabila penempatan tersebut tidak dipantau, semakan ini hanya membantu jika terdapat sistem yang memaklumkan anda tentang kegagalan, iaitu tugas saluran pemberitahuan seperti pelayan ntfy yang dihoskan sendiri yang boleh menerima hantaran daripada cron job atau unit OnFailure systemd.
Penyusunan dengan -f, dan lokasi penyelesaian laluan relatif
Compose membina konfigurasi mengikut urutan fail yang anda berikan, dan fail seterusnya akan mengatasi serta menambah pada fail sebelumnya. Dari kiri ke kanan, fail terakhir akan diguna pakai.
docker compose -f compose.yaml -f compose.prod.yaml config
docker compose -f compose.yaml -f compose.prod.yaml up -dSetiap arahan dalam projek tersebut memerlukan senarai fail yang sama. Jalankan up dengan dua fail dan logs dengan satu fail, maka anda berinteraksi dengan model gabungan yang berbeza. Ini merupakan cara pantas untuk mendapatkan servis yang didakwa oleh Compose sebagai tidak wujud. Risiko meningkat bagi stack yang naik tarafnya dijalankan sebagai arahan sekali jalan (one-off), seperti langkah migrasi pangkalan data dalam meja bantuan Chatwoot yang dihoskan sendiri, di mana docker compose run yang dikeluarkan dengan senarai fail yang salah akan menyasarkan model yang berbeza secara senyap daripada model yang sedang digunakan oleh servis anda. Sebaliknya, tetapkan senarai tersebut sekali sahaja dengan pemboleh ubah persekitaran COMPOSE_FILE.
export COMPOSE_FILE=compose.yaml:compose.prod.yaml
docker compose config
docker compose up -dPemisahnya ialah : pada Linux, dan COMPOSE_PATH_SEPARATOR mengubahnya. COMPOSE_FILE juga boleh diletakkan dalam fail .env projek, yang menjadikannya sebahagian daripada checkout dan bukannya sebahagian daripada sejarah shell anda. Apa-apa yang ditetapkan secara eksplisit pada baris arahan akan mengatasi pemboleh ubah persekitaran.
Kini, peraturan yang menjejaskan bind mount. Apabila anda menggunakan berbilang fail dengan -f, semua laluan relatif dalam semua fail tersebut diselesaikan berdasarkan direktori fail pertama, bukan berdasarkan fail yang mengandungi laluan tersebut. Tulis ./data:/var/lib/postgresql/data di dalam deploy/prod/compose.prod.yaml dan Compose masih mencari ./data di sebelah fail asas. Docker kemudian mencipta direktori kosong pada laluan yang salah itu dan kontena bermula tanpa sebarang kandungan di dalamnya, yang kelihatan seperti kehilangan data sedangkan sebenarnya tidak. Berikan --project-directory untuk menetapkan laluan asas sendiri, atau gunakan include, yang menyelesaikan setiap fail berdasarkan direktorinya sendiri.
Nama projek datang daripada direktori asas yang sama, jadi menukar fail mana yang berada di kedudukan pertama boleh menamakan semula projek tersebut. Projek yang dinamakan semula bermakna nama kontena dan nama volum baharu, manakala volum lama masih berada pada cakera di bawah nama lama. Tetapkan ia dengan name: peringkat atas dalam fail asas.
name: myappMedan yang digabungkan dan medan yang diganti
Compose melakukan penggabungan berdasarkan jenis nilai, bukan berdasarkan nama medan.
- Medan bernilai tunggal akan diganti.
image,command,entrypointdanmem_limitakan mengambil nilai terkemudian secara terus. Anda tidak boleh menambah satu argumen padacommand, kerana penggantian (override) akan menulis semula keseluruhan baris tersebut. - Pemetaan (mappings) digabungkan mengikut kunci.
environment,labels,volumesdandevicesmengekalkan setiap kunci daripada kedua-dua fail, dan fail terkemudian akan mengatasi mana-mana kunci yang wujud dalam kedua-duanya. Bagienvironmentdanlabels, kunci tersebut ialah nama pemboleh ubah atau label. Bagivolumesdandevices, kunci tersebut ialah laluan kontena. - Jujukan (sequences) ditambah (append).
dns,dns_search,expose,tmpfsdanexternal_linksakan dicantumkan. Fail asas yang mengandungiexpose: ["3000"]apabila digabungkan dengan penggantian yang mengandungi["4000", "5000"]akan menghasilkan["3000", "4000", "5000"].
Empat jujukan membawa kunci identiti, jadi entri yang sepadan pada kunci tersebut akan digabungkan dan bukannya ditambah. volumes, secrets dan configs dipadankan berdasarkan target. ports dipadankan berdasarkan gabungan ip, target, published dan protocol.
Baca peraturan ports itu dua kali, kerana ia merupakan perangkap. Dua entri port dianggap sebagai entri yang sama hanya apabila keempat-empat bahagian tersebut bersetuju. Ubah mana-mana satu daripadanya dan Compose akan melihatnya sebagai port kedua yang tidak berkaitan, jadi ia akan mengekalkan kedua-duanya.
Mengapa port anda masih diterbitkan selepas penggantian (override)
Fail asas yang menerbitkan servis pada setiap antara muka:
services:
web:
image: nginx:1.27
ports:
- "8080:80"Satu penggantian yang ditulis untuk mengikatnya kepada localhost sahaja, kerana reverse proxy akan diletakkan di hadapannya:
services:
web:
ports:
- "127.0.0.1:8080:80"Semak hasilnya sebelum menganggap ia telah berjaya.
docker compose -f compose.yaml -f compose.prod.yaml configKedua-dua entri berada dalam output. Bahagian ip berbeza, 0.0.0.0 berbanding 127.0.0.1, jadi ia dianggap sebagai dua port yang berbeza dari segi penggabungan (merge), dan ikatan awam yang anda cuba alih keluar masih berada dalam model. Perkara ini lebih penting pada Docker berbanding tempat lain, kerana port yang diterbitkan ditulis ke dalam iptables sebelum peraturan firewall anda. Mekanisme ini diterangkan dalam mengapa port Docker yang diterbitkan memintas ufw.
Terdapat dua cara penyelesaian. Cara yang eksplisit ialah tag !override, yang menggantikan keseluruhan atribut dan melangkau peraturan penggabungan:
services:
web:
ports: !override
- "127.0.0.1:8080:80"!override memerlukan Compose v2.24.4 atau lebih baharu. Penyelesaian mudah alih tidak memerlukan sebarang tag: pastikan ports tiada langsung dalam fail asas dan isytiharkannya hanya dalam fail khusus persekitaran. Tiada apa yang perlu digabungkan bermakna tiada apa yang akan terdedah. Itulah corak yang digunakan dalam contoh kerja di bawah.
Memadamkan nilai daripada set fail asas
!reset memadamkan atribut dan mengembalikannya kepada nilai lalai atau null. Perintah ini memerlukan nilai sebagai input tetapi akan mengabaikannya, jadi masukkan nilai yang sah dan kosong.
services:
web:
ports: !reset []
environment:
DEBUG: !reset null!reset memerlukan Compose v2.24 atau lebih baharu. Gunakan fungsi ini apabila fail asas bukan milik anda untuk disunting, contohnya fragmen vendor yang anda ambil. Stack yang diterbitkan oleh pembangun asal adalah contoh tepat bagi situasi ini: fail Compose di sebalik ruang kerja AFFiNE yang dihoskan sendiri mengisytiharkan empat kontena yang bukan anda tulis, dan !reset membolehkan anda mengosongkan satu atribut pada salah satu kontena tersebut tanpa perlu melakukan fork pada fail itu dan menanggung beban untuk menyelenggaranya.
include, untuk tindanan yang dipasang daripada bahagian
include menarik aplikasi Compose lain ke dalam model anda. Ia merupakan elemen peringkat atas, bukan flag.
include:
- path: ../commons/compose.yamlSetiap laluan dalam include dimuatkan sebagai model aplikasi Compose yang tersendiri, dengan direktori projeknya sendiri, jadi laluan relatif di dalam fail tersebut diselesaikan berdasarkan direktori fail itu sendiri. Itulah perbezaan sebenar daripada -f, dan sebab mengapa include ialah alat yang tepat apabila fragmen tersebut berada dalam folder lain atau repositori lain. Itulah bentuk biasa bagi tindanan vendor yang bukan anda tulis: fail Compose berbilang servis di sebalik pemasangan Authentik SSO yang dihoskan sendiri boleh berada dalam direktorinya sendiri dengan laluan relatifnya kekal utuh, sementara fail anda kekal tertumpu pada servis anda sendiri.
Bentuk panjang menerima sub-pilihan.
include:
- path:
- ../monitoring/compose.yaml
- ../monitoring/compose.vps.yaml
project_directory: ../monitoring
env_file: ../monitoring/.envpath menerima senarai, dan fail-fail tersebut digabungkan bersama mengikut peraturan biasa sebelum hasilnya menyertai model anda. project_directory menetapkan laluan asas yang digunakan untuk menyelesaikan laluan relatif dalam fail yang disertakan. env_file memberikan fail yang disertakan pemboleh ubah sendiri untuk interpolasi, yang menghalang fragmen kongsi daripada membaca .env projek anda secara senyap. include memerlukan Compose v2.20.0 atau lebih baharu. Pilihan yang sama sesuai untuk alat tambah satu kontena kepada tindanan yang sudah anda jalankan, contohnya Halcyon, yang menukar rupa pustaka Jellyfin menjadi kedai sewa tahun 90-an: failnya mengekalkan tag imej sendiri dan env_file sendiri, jadi menaik tarafnya tidak bermakna anda perlu menyentuh fail tempat tindanan media anda berada.
Nama sumber yang bertindih antara fail anda dan fail yang disertakan akan dilaporkan sebagai ralat dan bukannya digabungkan secara senyap, dan itu adalah disengajakan. Untuk menukar sesuatu yang diisytiharkan oleh fail yang disertakan, letakkan perubahan tersebut dalam compose.override.yaml: penggantian (override) digunakan pada model yang telah dipasang, jadi ia boleh menyentuh sumber yang disertakan tanpa bertembung dengannya. Tabiat itu paling berbaloi dengan tindanan yang fail hulunya ditulis semula pada setiap keluaran, seperti pelayan foto berbilang kontena yang dipertimbangkan dalam PhotoPrism berbanding Immich, di mana pengikatan localhost atau volum tambahan sepatutnya berada dalam penggantian anda dan bukannya dalam fail yang akan digantikan oleh naik taraf seterusnya.
Versi ringkas: include menggabungkan aplikasi yang berasingan, -f melapiskan konfigurasi ke atas satu aplikasi.
Pengasingan persekitaran pembangunan dan pengeluaran pada satu VPS
Berikut adalah corak lengkap dalam tiga fail. Fail asas mengisytiharkan perkara yang terpakai di mana-mana, dan ia tidak menerbitkan sebarang port.
name: myapp
services:
app:
image: ghcr.io/example/app:1.4.2
environment:
DATABASE_URL: postgres://app:${POSTGRES_PASSWORD}@db:5432/app
LOG_LEVEL: info
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:16
environment:
POSTGRES_USER: app
POSTGRES_DB: app
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
volumes:
db_data:Keadaan depends_on adalah perkara yang menyebabkan aplikasi menunggu pangkalan data yang benar-benar memberi respons, bukannya sekadar bekas yang wujud, seperti yang dijelaskan dalam healthchecks dan syarat depends_on. POSTGRES_PASSWORD diinterpolasi daripada fail .env projek, yang tidak sepatutnya dimasukkan ke dalam git. Lihat fail env dan rahsia Compose untuk varian yang lebih selamat.
Seterusnya, compose.override.yaml, yang dimuatkan oleh Compose secara automatik. Ini adalah fail untuk pembangun.
services:
app:
build: .
command: npm run dev
environment:
LOG_LEVEL: debug
ports:
- "3000:3000"
volumes:
- ./src:/app/src
db:
ports:
- "127.0.0.1:5432:5432"Pada komputer riba, arahan docker compose up biasa akan menggabungkan kedua-dua fail tersebut. command menggantikan nilai lalai imej kerana ia adalah nilai tunggal. LOG_LEVEL menggantikan info kerana environment digabungkan mengikut kunci. Bind mount dan dua port yang diterbitkan adalah penambahan tulen, dan port pangkalan data diikat pada localhost supaya komputer riba dalam rangkaian kongsi tidak mendedahkan PostgreSQL kepada persekitaran luar.
Akhir sekali, compose.prod.yaml. Namanya bukan nama yang dicari oleh Compose, jadi ia tidak akan dimuatkan secara tidak sengaja.
services:
app:
ports:
- "127.0.0.1:8000:3000"
deploy:
resources:
limits:
memory: 512MPada VPS, anda perlu menamakan kedua-dua fail tersebut, dan tindakan menamakan fail itulah yang mengecualikan fail override.
docker compose -f compose.yaml -f compose.prod.yaml config
docker compose -f compose.yaml -f compose.prod.yaml up -d
docker compose -f compose.yaml -f compose.prod.yaml psps sepatutnya menyenaraikan kedua-dua servis sebagai sedang berjalan, dengan db menunjukkan (healthy). Kerana anda menghantar -f, compose.override.yaml tidak dibaca, jadi arahan pembangunan, bind mount kod sumber, dan port awam 3000 tidak boleh mencapai persekitaran pengeluaran walaupun fail tersebut berada dalam direktori yang sama. Port 8000 hanya berada pada localhost, sedia untuk proksi: lihat menjalankan beberapa aplikasi di belakang Traefik apabila anda menambah servis kedua.
Tetapkan COMPOSE_FILE=compose.yaml:compose.prod.yaml dalam .env pelayan dan baki arahan anda akan kembali menjadi docker compose logs -f app biasa.
Stack satu servis juga menggunakan bentuk yang sama, kerana penjejak senaman openGym yang dihoskan sendiri perlu memberi respons melalui TLS di belakang proksi sebelum anda mendaftarkan passkey pertama, dan fail asas tanpa ports di dalamnya adalah perkara yang menghalang binding awam yang tidak diingini daripada mendahului proksi dalam menjalankan tugasnya.
Baca model yang telah digabungkan sebelum anda melakukan deployment
docker compose config mencetak model yang telah digabungkan dan diinterpolasi sepenuhnya. Ini bukan pratonton. Ini adalah input tepat yang akan digunakan oleh Compose, jadi apabila output tidak sepadan dengan jangkaan anda, output tersebut adalah yang betul.
docker compose -f compose.yaml -f compose.prod.yaml config
docker compose -f compose.yaml -f compose.prod.yaml config --no-interpolate
docker compose -f compose.yaml -f compose.prod.yaml config --services--no-interpolate membiarkan ${VAR} tidak dikembangkan. Gunakannya sebelum menampal output ke mana-mana, kerana config biasa mencetak setiap secret yang telah diselesaikan dalam teks jelas. --services hanya menyenaraikan nama servis, yang merupakan cara pantas untuk mengesahkan bahawa include telah menarik apa yang anda jangkakan.
Mod kegagalan dan perkara yang akan anda lihat
no configuration file provided: not found. Compose tidak menemui apa-apa untuk dibaca. Anda berada di luar direktori projek, atau COMPOSE_FILE menamakan laluan yang tidak wujud. Compose mencari direktori induk untuk fail asas lalai, tetapi ia tidak mencari di mana-mana untuk fail yang anda namakan sendiri.
WARN[0000] The "POSTGRES_PASSWORD" variable is not set. Defaulting to a blank string. Interpolasi diselesaikan berdasarkan fail .env projek dan persekitaran shell, dan direktori projek di sini ialah direktori bagi fail -f yang pertama. Melakukan deployment dari direktori yang berbeza daripada direktori yang memegang .env akan memberikan amaran ini dan kemudian pangkalan data yang menolak setiap sambungan.
Suntingan override anda tidak muncul dalam docker compose config. Sama ada anda telah melepasi -f, yang mematikan pemuatan override automatik, atau Compose menemui compose.yaml dalam direktori induk dan fail override anda tidak berada di sebelahnya. Menjalankan docker compose config tanpa argumen lain akan memberitahu anda model mana yang sebenarnya sedang dibina oleh Compose.
Bind mount kosong dan Docker mencipta direktori yang anda tidak minta. Laluan relatif diselesaikan berdasarkan direktori fail pertama. Betulkan laluan tersebut, lepasi --project-directory, atau alihkan fragmen di belakang include.
Kontena kembali dengan nama baharu dan volum kelihatan kosong. Nama projek telah berubah, kerana nama projek mengikut direktori fail pertama. Tambahkan name: peringkat atas ke fail asas dan penamaan tersebut akan berhenti berubah. Volum lama masih ada di bawah awalan lama, dan docker volume ls akan menunjukkannya.
Port yang anda alih keluar dalam override masih terbuka. Gabungan ports telah menambah (append) dan bukannya menggantikan. Sahkan dengan docker compose config, kemudian sama ada gunakan !override atau alihkan ports keluar daripada fail asas.
FAQ
Adakah Compose memuatkan compose.override.yaml secara automatik?
Ya, apabila anda menjalankan docker compose tanpa sebarang flag -f. Compose akan mencari direktori kerja dan direktori induknya untuk compose.yaml atau docker-compose.yaml, dan jika fail override wujud di sebelahnya, fail tersebut akan dimuatkan sebagai yang kedua. Nama yang diiktiraf ialah compose.override.yaml, compose.override.yml, docker-compose.override.yml dan docker-compose.override.yaml. Memberikan sebarang -f akan melumpuhkan fungsi ini, jadi docker compose -f compose.yaml up hanya membaca satu fail sahaja.
Dalam urutan apakah berbilang fail -f digabungkan?
Dari kiri ke kanan. Compose membina konfigurasi mengikut urutan fail yang anda berikan, dan setiap fail akan mengatasi serta menambah kepada fail sebelumnya, jadi fail terakhir dalam baris tersebut akan mengatasi sebarang konflik. Senarai yang sama mesti digunakan untuk setiap arahan dalam projek tersebut, itulah tujuan COMPOSE_FILE=compose.yaml:compose.prod.yaml.
Mengapa port saya masih diterbitkan selepas saya membuat override?
Kerana entri ports dikenal pasti oleh keseluruhan set ip, target, published dan protocol. Override 127.0.0.1:8080:80 terhadap asas 8080:80 berbeza pada bahagian ip, jadi Compose menganggapnya sebagai port kedua dan mengekalkan kedua-duanya. Jalankan docker compose config dan anda akan melihat kedua-dua entri tersebut. Gunakan ports: !override pada Compose v2.24.4 atau lebih baharu, atau pastikan ports tiada dalam fail asas supaya tiada apa-apa untuk digabungkan.
Apakah perbezaan antara include dan -f?
-f melapiskan beberapa fail ke atas satu aplikasi, dan setiap laluan relatif dalam setiap fail diselesaikan berdasarkan direktori fail pertama. include menarik masuk aplikasi Compose yang berasingan, dan setiap laluan yang disertakan mengekalkan direktori projeknya sendiri, jadi laluan relatifnya diselesaikan berdasarkan dirinya sendiri. Gunakan -f untuk lapisan persekitaran bagi stack anda sendiri, dan include untuk fragmen yang diselenggara di tempat lain. include memerlukan Compose v2.20.0 atau lebih baharu.
Bagaimanakah cara saya membuang nilai yang ditetapkan oleh fail asas?
Gunakan tag !reset pada Compose v2.24 atau lebih baharu. Tulis ports: !reset [] atau MY_VAR: !reset null dalam fail override dan atribut tersebut akan kembali kepada lalai atau kepada null. Nilai yang anda berikan kepada tag tersebut diperlukan tetapi akan diabaikan. Jika anda ingin menggantikan atribut dan bukannya mengosongkannya, !override melakukan perkara tersebut, dan ia memerlukan v2.24.4 atau lebih baharu.