Docker Compose: Paano Mag-merge ng Maraming File
Alamin kung paano kusang nilo-load ang compose.override.yaml, paano nagme-merge ang file order, bakit nananatiling bukas ang port, at paano gamitin ang include.
Ano ang ginagawa ng Compose kapag higit sa isang file
Maaaring bumuo ang Docker Compose ng isang project mula sa ilang file. Binabasa nito ang mga file ayon sa pagkakasunod-sunod ng pagtanggap nito at pinagsasama ang mga ito sa iisang model. Dahil dito, nangingibabaw ang halaga sa mas huling file kapag may conflict. May dalawang mekanismo para rito mula sa command line: isang override file na awtomatikong nilo-load ng Compose, at ang -f flag na mano-mano mong ipinapasa. May ikatlong mekanismo sa loob mismo ng file, ang include element, at iba ang paraan ng paggana nito kumpara sa dalawa.
Hindi simpleng overwrite ang merge. Pinagsasama ang mappings batay sa bawat key, idinadagdag ang mga sequence, at pinapalitan nang buo ang maliit na pangkat ng mga field. Dito nagmumula ang mga hindi inaasahang resulta, at ang ports list ang madalas magdulot ng problema sa halos lahat.
Ipinapalagay ng lahat ng sumusunod na halimbawa na Compose v2 ang ginagamit, partikular ang docker compose plugin at hindi ang lumang docker-compose script. Patakbuhin ang docker compose version para mag-check. Kung wala ka pang naisusulat na Compose file, magsimula sa basic guide sa Docker Compose at bumalik dito.
Ang override file na awtomatikong nilo-load ng Compose
Patakbuhin ang docker compose up nang walang flag na -f. Maghahanap ang Compose sa working directory at pagkatapos ay sa mga parent directory nito ng compose.yaml o docker-compose.yaml. Kung may override file sa tabi ng base file, awtomatikong nilo-load ito ng Compose bilang pangalawang file.
ls compose.yaml compose.override.yaml
docker compose up -dKapag parehong available ang mga file, katumbas ito ng manu-manong paglalagay sa mga ito.
docker compose -f compose.yaml -f compose.override.yaml up -dAng mga pangalang kinikilala ng Compose ay compose.override.yaml, compose.override.yml, at ang mas lumang docker-compose.override.yml at docker-compose.override.yaml. Ang ibang pangalan, gaya ng compose.dev.yaml, ay nilo-load lamang kapag tinukoy mo ito gamit ang -f.
Sa sandaling magpasa ka ng isang -f, hihinto ang awtomatikong paglo-load. Binabasa ng docker compose -f compose.yaml up ang eksaktong file na iyon at binabalewala ang override. Ito ang property na ginagamit ng dev at prod pattern sa mga susunod na bahagi ng gabay na ito.
May dalawang epekto ito sa isang server. Ang override file na naiwan sa deploy directory ay nilo-load ng bawat bare na docker compose command na pinapatakbo mula sa directory na iyon, kasama ang command na pinapatakbo ng cron job. Dahil dito, maaaring mag-bind-mount ang production stack ng source directory na hindi naman dapat isama sa deployment. Patakbuhin ang docker compose config pagkatapos ng bawat deploy at basahin ang output nito. Kapag unattended ang deploy, magiging kapaki-pakinabang lamang ang check kung may mag-aabiso kapag may nangyaring mali. Ito ang tungkulin ng isang push channel gaya ng self-hosted ntfy server na maaaring padalhan ng post ng cron job o systemd OnFailure unit.
Pagkakasunod-sunod gamit ang -f, at kung saan nireresolba ang mga relative path
Binubuo ng Compose ang configuration ayon sa pagkakasunod ng mga file na ibinibigay mo. Ina-override at dinaragdagan ng mga kasunod na file ang mga nauna. Mula kaliwa pakanan, ang huli ang mananaig.
docker compose -f compose.yaml -f compose.prod.yaml config
docker compose -f compose.yaml -f compose.prod.yaml up -dParehong file list ang kailangan ng bawat command sa project na iyon. Kung patatakbuhin mo ang up gamit ang dalawang file at ang logs gamit ang isa, ibang merged model ang ginagamit mo. Mabilis itong humantong sa service na sinasabing wala sa Compose. Mas kritikal ito sa stack na ang mga upgrade ay tumatakbo bilang one-off command, gaya ng database migration step sa self-hosted na Chatwoot support desk, kung saan ang docker compose run na pinatakbo gamit ang maling file list ay tahimik na tumatarget sa ibang model kaysa sa ginagamit na ng iyong mga service. Sa halip, itakda nang isang beses ang listahan gamit ang COMPOSE_FILE environment variable.
export COMPOSE_FILE=compose.yaml:compose.prod.yaml
docker compose config
docker compose up -dAng separator ay : sa Linux, at binabago ito ng COMPOSE_PATH_SEPARATOR. Maaari ring ilagay ang COMPOSE_FILE sa .env file ng project, kaya bahagi ito ng checkout at hindi ng shell history mo. Ang anumang tahasang itinakda sa command line ay mananaig sa environment variable.
Narito ang panuntunang nakaaapekto sa bind mount. Kapag gumagamit ka ng maraming file kasama ang -f, ang lahat ng relative path sa lahat ng file ay nireresolba batay sa directory ng unang file, hindi sa file na naglalaman ng mga ito. Isulat ang ./data:/var/lib/postgresql/data sa loob ng deploy/prod/compose.prod.yaml at hahanapin pa rin ng Compose ang ./data sa tabi ng base file. Pagkatapos, gagawa ang Docker ng walang lamang directory sa maling path na iyon, at magsisimula ang container nang walang laman dito. Mukha itong data loss, pero hindi ito ganoon. Ipasa ang --project-directory upang ikaw ang magtakda ng base path, o gamitin ang include, na nireresolba ang bawat file batay sa sarili nitong directory.
Galing sa parehong base directory ang project name, kaya maaaring mapalitan ang pangalan ng project kapag binago mo kung aling file ang nauuna. Ang pinalitang project name ay nangangahulugan ng mga bagong container name at bagong volume name. Nananatili pa rin sa disk ang lumang volume gamit ang lumang pangalan. I-pin ito gamit ang top-level na name: sa base file.
name: myappAling field ang pinagsasama, at alin ang pinapalitan
Nagsasagawa ang Compose ng merge batay sa uri ng value, hindi sa pangalan ng field.
- Pinapalitan ang mga field na iisang value lang ang tinatanggap.
image,command,entrypointatmem_limitay direktang gumagamit ng value mula sa huling file. Hindi ka makakapagdagdag ng argument sa isangcommand, dahil nire-rewrite ng override ang buong linya. - Pinagsasama ang mga mapping ayon sa bawat key.
environment,labels,volumesatdevicesay nagpapanatili ng lahat ng key mula sa dalawang file, at ang huling file ang ginagamit para sa anumang key na nasa parehong file. Para saenvironmentatlabels, ang key ay pangalan ng variable o label. Para savolumesatdevices, ang key ay container path. - Idinadagdag ang mga sequence. Pinagdudugtong ang
dns,dns_search,expose,tmpfsatexternal_links. Ang base na mayexpose: ["3000"]kapag hinaluan ng override na may["4000", "5000"]ay nagbubunga ng["3000", "4000", "5000"].
May identity key ang apat na sequence, kaya nagme-merge ang mga entry na nagtutugma sa key na iyon sa halip na basta idagdag. Ang volumes, secrets at configs ay nagtutugma batay sa target. Ang ports ay nagtutugma batay sa kombinasyon ng ip, target, published at protocol.
Basahin nang dalawang beses ang patakarang ports, dahil dito karaniwang nagkakamali. Pareho lang ang dalawang port entry kapag nagtutugma ang lahat ng apat na bahaging iyon. Kapag binago ang alinman sa mga ito, itinuturing ng Compose na pangalawa at hiwalay na port ang entry, kaya pinananatili nito ang dalawa.
Bakit naka-publish pa rin ang port pagkatapos ng override
Isang base file na nagpa-publish ng service sa lahat ng interface:
services:
web:
image: nginx:1.27
ports:
- "8080:80"Isang override na ginawa upang i-bind lamang ito sa localhost dahil may reverse proxy na ilalagay sa harap nito:
services:
web:
ports:
- "127.0.0.1:8080:80"Suriin ang resulta bago ipagpalagay na gumana ito.
docker compose -f compose.yaml -f compose.prod.yaml configParehong entry ang nasa output. Magkaiba ang bahagi na ip, 0.0.0.0 laban sa 127.0.0.1, kaya itinuturing silang dalawang magkaibang port ng merge at nananatili sa model ang public binding na sinubukan mong alisin. Mas mahalaga ito sa Docker kaysa sa ibang environment dahil isinusulat ang published port sa iptables bago ang iyong firewall rules. Ipinaliwanag ang mekanismong ito sa kung bakit nilalampasan ng published Docker ports ang ufw.
May dalawang paraan para ayusin ito. Ang tahasang paraan ay ang !override tag, na pumapalit sa buong attribute at hindi ginagamit ang merge rules:
services:
web:
ports: !override
- "127.0.0.1:8080:80"Kailangan ng !override ang Compose v2.24.4 o mas bago. Walang tag na kailangan sa portable na paraan: huwag isama ang ports sa base file at ideklara lamang ito sa mga file para sa partikular na environment. Kapag walang ima-merge, walang mailalabas. Ito ang pattern na ginamit sa kumpletong halimbawa sa ibaba.
Pagtanggal ng value na itinakda ng base file
Inaalis ng !reset ang isang attribute at ibinabalik ito sa default value nito o ginagawa itong null. Tumatanggap ito ng value at hindi ito ginagamit, kaya sumulat ng valid at walang laman na value.
services:
web:
ports: !reset []
environment:
DEBUG: !reset nullKailangan ng !reset ang Compose v2.24 o mas bago. Gamitin ito kapag hindi ikaw ang nag-e-edit ng base file, gaya ng vendor fragment na kinukuha mo. Eksaktong ganitong sitwasyon ang isang inilabas na upstream stack: ang Compose file sa likod ng self-hosted na AFFiNE workspace ay nagdedeklara ng apat na container na hindi mo isinulat, at hinahayaan ka ng !reset na i-clear ang isang attribute sa isa sa mga ito nang hindi bina-fork ang file at inaako ang trabaho ng pagsubaybay sa mga pagbabago rito.
include, para sa mga stack na binuo mula sa magkakahiwalay na bahagi
include kumukuha ng isa pang Compose application papasok sa iyong model. Top-level element ito, hindi flag.
include:
- path: ../commons/compose.yamlAng bawat path sa include ay nilo-load bilang sariling Compose application model, kasama ang sarili nitong project directory. Kaya ang mga relative path sa file na iyon ay nire-resolve batay sa sarili nitong directory. Ito ang tunay na kaibahan nito sa -f, at ito ang dahilan kung bakit include ang tamang tool kapag nasa ibang folder o repository ang fragment. Karaniwan itong setup ng vendor stack na hindi mo ikaw ang sumulat: maaaring nasa sarili nitong directory ang multi-service Compose file sa likod ng self-hosted na Authentik SSO install, kasama ang mga relative path nito, habang ang file mo ay nakatuon sa sarili mong mga serbisyo.
May mga sub-option ang long form.
include:
- path:
- ../monitoring/compose.yaml
- ../monitoring/compose.vps.yaml
project_directory: ../monitoring
env_file: ../monitoring/.envTumatanggap ang path ng listahan, at mina-merge ang mga file na iyon gamit ang karaniwang rules bago idagdag ang resulta sa iyong model. Itinatakda ng project_directory ang base path na ginagamit para i-resolve ang mga relative path sa included file. Binibigyan ng env_file ang included file ng sarili nitong variables para sa interpolation. Pinipigilan nito ang shared fragment na tahimik na magbasa ng .env ng iyong project. Nangangailangan ang include ng Compose v2.20.0 o mas bago. Angkop din ang parehong mga option para sa single-container add-on sa stack na pinapatakbo mo na, gaya ng Halcyon, na muling nagdi-disensyo sa Jellyfin library bilang 90s rental store: pinananatili ng file nito ang sarili nitong image tag at env_file, kaya ang pag-upgrade rito ay hindi nangangahulugang kailangang baguhin ang file ng media stack mo.
Itinuturing na error ang magkakaparehong resource name sa iyong file at sa included file, sa halip na tahimik na i-merge ang mga ito. Sinasadya ito. Para baguhin ang idinedeklara ng included file, ilagay ang pagbabago sa compose.override.yaml. Inia-apply ang override sa assembled model, kaya maaari nitong baguhin ang mga resource na kasama nang hindi nagkakaroon ng conflict sa mga ito. Pinakamalaking pakinabang ng ganitong gawain ang makukuha sa stack na nire-rewrite ng upstream file sa bawat release, gaya ng mga multi-container photo server na inihahambing sa PhotoPrism laban sa Immich, kung saan dapat nasa override ang localhost binding o karagdagang volume, hindi sa file na papalitan ng susunod na upgrade.
Sa maikling salita: binubuo ng include ang magkakahiwalay na application, habang naglalagay ang -f ng configuration layer sa isang application.
Paghihiwalay ng dev at prod sa iisang VPS
Narito ang buong pattern sa tatlong file. Idinedeklara ng base file kung ano ang totoo sa lahat ng environment, at wala itong inilalathalang 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:Ang depends_on condition ang naghihintay sa app ng database na aktuwal na sumasagot, sa halip na container na umiiral lamang. Ipinaliwanag ito sa healthchecks at mga condition ng depends_on. Ini-interpolate ang POSTGRES_PASSWORD mula sa file na .env ng project, at hindi kailanman dapat mapabilang ang file na ito sa git. Tingnan ang mga env file at Compose secrets para sa mas ligtas na mga variant.
Sunod ang compose.override.yaml, na awtomatikong nilo-load ng Compose. Ito ang file ng developer.
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"Sa laptop, pinagsasama ng isang walang dagdag na docker compose up ang dalawang file na ito. Pinapalitan ng command ang default na image dahil iisa lang ang value nito. Pinapalitan ng LOG_LEVEL ang info dahil nagsasama ang environment batay sa key. Purong dagdag ang bind mount at ang dalawang published port. Naka-bind sa localhost ang database port, kaya hindi iniaalok ng laptop ang PostgreSQL sa ibang device sa shared network.
Panghuli, compose.prod.yaml. Hindi ito kabilang sa mga file na hinahanap ng Compose, kaya hindi ito aksidenteng nilo-load.
services:
app:
ports:
- "127.0.0.1:8000:3000"
deploy:
resources:
limits:
memory: 512MSa VPS, parehong file ang pangalanan mo. Ang mismong pagbibigay ng mga pangalang ito ang eksaktong nagbubukod sa 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 psDapat ilista ng ps ang parehong service bilang tumatakbo, at dapat ipakita ng db ang (healthy). Dahil ipinasa mo ang -f, hindi binasa ang compose.override.yaml. Kaya hindi makararating sa production ang dev command, source bind mount, at public port 3000 kahit nasa parehong directory ang file. Nasa localhost lamang ang port 8000 at handa ito para sa proxy. Tingnan ang pagpapatakbo ng maraming app sa likod ng Traefik kapag idinagdag mo ang pangalawang service.
Itakda ang COMPOSE_FILE=compose.yaml:compose.prod.yaml sa .env ng server, at magiging simpleng docker compose logs -f app muli ang iba mong command.
Pareho rin ang ganitong ayos para sa single-service stack. Kailangang sumagot ang self-hosted na openGym workout tracker sa pamamagitan ng TLS sa likod ng proxy bago mo i-enrol ang unang passkey. Ang base file na walang ports ang pumipigil sa isang hindi sinasadyang public binding na mauna sa proxy sa pagtanggap ng traffic.
Basahin ang pinagsamang model bago mag-deploy
Ang docker compose config ay nagpi-print ng ganap na pinagsama at ganap na na-interpolate na model. Hindi ito preview. Ito ang eksaktong input na gagamitin ng Compose, kaya kapag hindi tugma ang output sa inaasahan mo, ang output ang tama.
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 --servicesHindi ine-expand ng --no-interpolate ang ${VAR}. Gamitin ito bago i-paste ang output kahit saan, dahil ipinapakita ng plain config ang bawat na-resolve na secret bilang clear text. Ang --services ay naglilista lamang ng mga pangalan ng service. Mabilis itong paraan para makumpirma na may hinatak na include ayon sa inaasahan mo.
Mga failure mode at kung ano ang makikita mo
no configuration file provided: not found. Walang nahanap na babasahin ang Compose. Nasa labas ka ng project directory, o tumutukoy ang COMPOSE_FILE sa path na hindi umiiral. Naghahanap ang Compose sa mga parent directory ng default base file, pero hindi ito naghahanap saanman para sa file na ikaw mismo ang nagngalan.
WARN[0000] The "POSTGRES_PASSWORD" variable is not set. Defaulting to a blank string. Niresolba ang interpolation batay sa project na .env file at sa shell environment. Ang project directory dito ay ang directory ng unang -f file. Kung magde-deploy ka mula sa ibang directory kaysa sa directory na naglalaman ng .env, lalabas ang warning na ito at magkakaroon ka ng database na tumatanggi sa bawat connection.
Hindi lumalabas ang ginawa mong override edit sa docker compose config. Maaaring ipinasa mo ang -f, na nag-o-off ng awtomatikong pag-load ng override, o nakita ng Compose ang compose.yaml sa isang parent directory at wala sa tabi nito ang override file mo. Ipinapakita ng pagpapatakbo ng docker compose config nang walang ibang argumento kung anong model ang aktuwal na binubuo ng Compose.
Walang laman ang bind mount at gumawa ang Docker ng directory na hindi mo hiniling. Na-resolve ang relative path batay sa directory ng unang file. Ayusin ang path, ipasa ang --project-directory, o ilagay ang fragment sa likod ng include.
Bumalik ang mga container na may bagong pangalan at mukhang walang laman ang isang volume. Nagbago ang project name dahil nakabatay ito sa directory ng unang file. Magdagdag ng top-level na name: sa base file upang hindi na magbago ang mga pangalan. Nandoon pa rin ang lumang volume sa ilalim ng lumang prefix, at ipapakita ito ng docker volume ls.
Bukas pa rin ang port na inalis mo sa override. Idinagdag ng ports merge ang entry sa halip na palitan ito. Kumpirmahin gamit ang docker compose config, pagkatapos ay gamitin ang !override o alisin ang ports sa base file.
FAQ
Awtomatiko bang nilo-load ng Compose ang compose.override.yaml?
Oo, kapag pinatakbo mo ang docker compose nang walang -f flag. Hinahanap ng Compose sa working directory at sa mga parent directory nito ang compose.yaml o docker-compose.yaml. Kung may override file sa tabi nito, nilo-load ang file na iyon bilang pangalawa. Ang mga kinikilalang pangalan ay compose.override.yaml, compose.override.yml, docker-compose.override.yml, at docker-compose.override.yaml. Kapag nagpasa ka ng anumang -f, hindi ito ginagawa. Kaya isang file lamang ang binabasa ng docker compose -f compose.yaml up.
Sa anong pagkakasunod-sunod pinagsasama ang maraming -f file?
Mula kaliwa pakanan. Binubuo ng Compose ang configuration ayon sa pagkakasunod-sunod ng mga file na ibinigay mo. Ino-override at dinaragdagan ng bawat file ang mga naunang file. Kaya ang huling file sa command line ang nananalo kapag may conflict. Dapat gamitin ang parehong listahan sa bawat command para sa project na iyon. Ito ang gamit ng COMPOSE_FILE=compose.yaml:compose.prod.yaml.
Bakit naka-publish pa rin ang port ko matapos ko itong i-override?
Dahil tinutukoy ang mga entry na ports batay sa buong set ng ip, target, published, at protocol. Kapag ang 127.0.0.1:8080:80 sa override ay ikinumpara sa 8080:80 sa base, magkaiba ang bahaging ip. Kaya itinuturing ito ng Compose bilang ikalawang port at pinananatili ang dalawa. Patakbuhin ang docker compose config upang makita ang dalawang entry. Gamitin ang ports: !override sa Compose v2.24.4 o mas bago. Maaari mo ring alisin ang ports sa base file para walang mapagbatayan sa merge.
Ano ang pagkakaiba ng include at -f?
Pinagsasama ng -f ang ilang file para sa isang application. Niresolba ang bawat relative path sa bawat file batay sa directory ng unang file. Isinasama naman ng include ang isang hiwalay na Compose application. Pinananatili ng bawat included path ang sarili nitong project directory, kaya nire-resolve ang mga relative path nito batay sa sariling directory. Gamitin ang -f para sa mga environment layer ng sarili mong stack. Gamitin ang include para sa fragment na pinapanatili sa ibang lugar. Kailangan ng include ang Compose v2.20.0 o mas bago.
Paano ko aalisin ang value na itinakda ng base file?
Gamitin ang !reset tag sa Compose v2.24 o mas bago. Isulat ang ports: !reset [] o MY_VAR: !reset null sa overriding file. Ibabalik nito ang attribute sa default value nito o sa null. Kinakailangan ang value na ibinibigay mo sa tag, pero hindi ito ginagamit. Kung gusto mong palitan ang isang attribute sa halip na i-clear ito, iyon ang ginagawa ng !override. Kailangan nito ang v2.24.4 o mas bago.