Docker Compose मध्ये एकापेक्षा अधिक फाइल्स कशा वापराव्यात
compose.override.yaml स्वतंत्रपणे कशी लोड होते, फाइल्सचा क्रम कसा merge होतो, ports मुळे पोर्ट उघडे का राहते आणि dev-prod विभाजनासाठी include कसे वापरावे ते जाणून घ्या.
एकापेक्षा अधिक फाइल्ससह Compose काय करते
Docker Compose अनेक फाइल्समधून एक प्रकल्प तयार करू शकते. ते फाइल्स ज्या क्रमाने मिळतात त्या क्रमाने वाचते आणि त्यांचे एकाच मॉडेलमध्ये विलीनीकरण करते. त्यामुळे परस्परविरोधी मूल्य असल्यास नंतरची फाइल प्राधान्य घेते. कमांड लाइनवरून हे करण्यासाठी दोन यंत्रणा आहेत: Compose स्वतःहून लोड करते ती override फाइल आणि तुम्ही स्वतः देत असलेला -f flag. तिसरी यंत्रणा फाइलमध्येच असते: include element. तिचे कार्य या दोन्हींपेक्षा वेगळे आहे.
विलीनीकरण म्हणजे साधे overwrite नाही. Mappings चे key नुसार विलीनीकरण होते, sequences शेवटी जोडल्या जातात आणि काही मोजकी fields पूर्णपणे बदलली जातात. याच फरकामुळे अनपेक्षित परिणाम दिसतात. ports list ही जवळजवळ सर्वांनाच अडचणीत आणते.
खालील सर्व माहिती Compose v2 गृहीत धरते. येथे जुन्या docker-compose script ऐवजी docker compose plugin वापरले आहे. तपासण्यासाठी docker compose version चालवा. तुम्ही अद्याप Compose file लिहिली नसेल, तर Docker Compose च्या मूलभूत गोष्टींच्या मार्गदर्शकापासून सुरुवात करा आणि नंतर येथे परत या.
Compose स्वतः शोधून लोड करते ती override फाइल
-f flag न देता docker compose up चालवल्यास Compose प्रथम कार्यरत directory मध्ये आणि नंतर तिच्या parent directories मध्ये compose.yaml किंवा docker-compose.yaml शोधते. Base file च्या शेजारी override file असल्यास Compose ती दुसरी फाइल आपोआप लोड करते.
ls compose.yaml compose.override.yaml
docker compose up -dदोन्ही files उपलब्ध असतील, तर हे त्यांना स्वतः टाइप करून देण्यासारखेच आहे.
docker compose -f compose.yaml -f compose.override.yaml up -dCompose ओळखत असलेली नावे compose.override.yaml, compose.override.yml आणि जुनी docker-compose.override.yml व docker-compose.override.yaml ही आहेत. compose.dev.yaml सारखे इतर कोणतेही नाव -f द्वारे स्पष्टपणे दिल्यावरच लोड केले जाते.
तुम्ही एकही -f दिल्याक्षणी स्वयंचलित loading थांबते. docker compose -f compose.yaml up नेमकी ती एकच file वाचते आणि override file कडे दुर्लक्ष करते. या guide मधील dev आणि prod pattern याच गुणधर्मावर आधारित आहे.
Server वर याचे दोन्ही परिणाम होऊ शकतात. Deploy directory मध्ये ठेवलेली override file त्या directory मधून चालवलेल्या प्रत्येक रिकाम्या docker compose command सोबत लोड होते. त्यात तुमची cron job चालवणारी command देखील येते. त्यामुळे production stack मध्ये अनवधानाने source directory bind-mount होऊ शकते. प्रत्येक deploy नंतर docker compose config चालवा आणि त्यातून निर्माण झालेला output तपासा.
-f सह क्रम, आणि सापेक्ष मार्ग कुठे निश्चित होतात
Compose कॉन्फिगरेशन तुम्ही फाइल ज्या क्रमाने देता त्या क्रमाने तयार करते. त्यानंतरच्या फाइल्स आधीच्या फाइल्समधील मूल्ये अधिलिखित करतात आणि नवीन मूल्ये जोडतात. डावीकडून उजवीकडे, शेवटची फाइल लागू होते.
docker compose -f compose.yaml -f compose.prod.yaml config
docker compose -f compose.yaml -f compose.prod.yaml up -dत्या प्रोजेक्टमधील प्रत्येक कमांडसाठी हीच फाइल यादी आवश्यक आहे. up दोन फाइल्ससह चालवले आणि logs एकाच फाइलसह चालवले, तर तुम्ही वेगळ्या एकत्रित मॉडेलशी संवाद साधत असता. त्यामुळे Compose अस्तित्वात नसल्याचे सांगणारी सेवा तयार होण्याची शक्यता वाढते. त्याऐवजी COMPOSE_FILE पर्यावरण चल वापरून ही यादी एकदाच सेट करा.
export COMPOSE_FILE=compose.yaml:compose.prod.yaml
docker compose config
docker compose up -dLinux वर विभाजक : आहे आणि COMPOSE_PATH_SEPARATOR तो बदलतो. COMPOSE_FILE प्रोजेक्टच्या .env फाइलमध्येही ठेवता येतो. त्यामुळे तो shell इतिहासाचा भाग न राहता checkout चा भाग बनतो. कमांड लाइनवर स्पष्टपणे सेट केलेले कोणतेही मूल्य पर्यावरण चलापेक्षा प्राधान्याने लागू होते.
आता bind mount मध्ये समस्या निर्माण करणारा नियम पाहू. -f सह अनेक फाइल्स वापरल्यास, त्या सर्व फाइल्समधील सापेक्ष मार्ग त्यांना समाविष्ट करणाऱ्या फाइलच्या आधारे नव्हे, तर पहिल्या फाइलच्या निर्देशिकेच्या आधारे निश्चित होतात. deploy/prod/compose.prod.yaml मध्ये ./data:/var/lib/postgresql/data लिहिले तरी Compose ./data मूळ फाइलच्या शेजारी शोधते. त्यानंतर Docker त्या चुकीच्या मार्गावर रिकामी निर्देशिका तयार करते आणि कंटेनरमध्ये काहीही नसताना तो सुरू होतो. हे डेटा नष्ट झाल्यासारखे दिसते, पण डेटा नष्ट झालेला नसतो. मूळ मार्ग स्वतः सेट करण्यासाठी --project-directory द्या, किंवा include वापरा. त्यात प्रत्येक फाइलचा मार्ग तिच्या स्वतःच्या निर्देशिकेच्या आधारे निश्चित होतो.
प्रोजेक्टचे नाव त्याच मूळ निर्देशिकेतून घेतले जाते. त्यामुळे कोणती फाइल पहिली आहे हे बदलल्यास प्रोजेक्टचे नाव बदलू शकते. प्रोजेक्टचे नाव बदलल्यावर नवीन कंटेनर नावे आणि नवीन volume नावे तयार होतात. जुना volume जुन्या नावाखाली डिस्कवरच राहतो. त्याऐवजी मूळ फाइलमध्ये उच्च-स्तरीय name: देऊन नाव निश्चित करा.
name: myappकोणती फील्ड एकत्र विलीन होतात आणि कोणती बदलली जातात
Compose फील्डच्या नावानुसार नव्हे, तर मूल्याच्या प्रकारानुसार विलीन करते.
- एकच मूल्य असलेल्या फील्ड बदलल्या जातात.
image,command,entrypointआणिmem_limitनंतरचे मूल्य थेट स्वीकारतात.commandमध्ये एक आर्ग्युमेंट जोडता येत नाही, कारण override संपूर्ण ओळ पुन्हा लिहिते. - Mappings कीनुसार विलीन होतात.
environment,labels,volumesआणिdevicesदोन्ही फाइलमधील प्रत्येक की ठेवतात. दोन्ही फाइलमध्ये समान की असल्यास नंतरच्या फाइलमधील मूल्य लागू होते.environmentआणिlabelsसाठी की म्हणजे variable किंवा label चे नाव असते.volumesआणिdevicesसाठी की म्हणजे container path असतो. - Sequences मध्ये मूल्ये जोडली जातात.
dns,dns_search,expose,tmpfsआणिexternal_linksएकत्र जोडले जातात.expose: ["3000"]असलेला base आणि["4000", "5000"]असलेला override विलीन केल्यास["3000", "4000", "5000"]तयार होते.
चार sequences मध्ये identity key असते. त्यामुळे त्या entries जोडण्याऐवजी त्या key वरून विलीन होतात. volumes, secrets आणि configs यांचा मेळ target वर होतो. ports यांचा मेळ ip, target, published आणि protocol यांच्या संयोगावर होतो.
ही ports नियमावली दोनदा वाचा, कारण इथेच चूक होण्याची शक्यता असते. दोन port entries एकच entry तेव्हाच मानल्या जातात, जेव्हा त्या चारही भागांमध्ये समानता असते. त्यापैकी कोणताही एक भाग बदलल्यास Compose त्याला दुसरा, स्वतंत्र port मानते आणि दोन्ही entries ठेवते.
override केल्यानंतरही तुमचा port प्रकाशित का राहतो
प्रत्येक interface वर service प्रकाशित करणारी base file:
services:
web:
image: nginx:1.27
ports:
- "8080:80"reverse proxy तिच्यासमोर ठेवला जाणार असल्यामुळे, service फक्त localhost ला bind करण्यासाठी लिहिलेली override:
services:
web:
ports:
- "127.0.0.1:8080:80"ते कार्य केले असे गृहीत धरण्यापूर्वी परिणाम तपासा.
docker compose -f compose.yaml -f compose.prod.yaml configआउटपुटमध्ये दोन्ही entries आहेत. ip भाग वेगळा आहे; 0.0.0.0 विरुद्ध 127.0.0.1. त्यामुळे merge च्या दृष्टीने ते दोन वेगळे ports आहेत आणि तुम्ही काढण्याचा प्रयत्न केलेला public binding अजूनही model मध्ये आहे. Docker मध्ये याचा परिणाम इतर ठिकाणांपेक्षा अधिक महत्त्वाचा असतो, कारण published port तुमच्या firewall rules च्या आधी iptables मध्ये लिहिला जातो. ही यंत्रणा published Docker ports ufw ला कसे bypass करतात येथे स्पष्ट केली आहे.
यासाठी दोन उपाय आहेत. स्पष्ट उपाय म्हणजे !override tag. तो संपूर्ण attribute बदलतो आणि merge rules लागू करत नाही:
services:
web:
ports: !override
- "127.0.0.1:8080:80"!override साठी Compose v2.24.4 किंवा त्यानंतरची आवृत्ती आवश्यक आहे. Portable उपायासाठी कोणत्याही tag ची गरज नाही: ports base file मधून पूर्णपणे वगळा आणि ते फक्त environment-specific files मध्ये declare करा. Merge करण्यासाठी काहीच नसल्यास leak होण्यासाठीही काहीच राहत नाही. खालील worked example मध्ये हाच pattern वापरला आहे.
बेस फाइलमध्ये सेट केलेले मूल्य हटवणे
!reset एखादा attribute काढून टाकते आणि तो त्याच्या default मूल्यावर किंवा null वर परत ठेवते. ही कमांड दिलेले मूल्य स्वीकारते पण त्याकडे दुर्लक्ष करते. त्यामुळे वैध आणि रिक्त मूल्य लिहा.
services:
web:
ports: !reset []
environment:
DEBUG: !reset null!reset साठी Compose v2.24 किंवा त्यानंतरची आवृत्ती आवश्यक आहे. बेस फाइलमध्ये बदल करण्याची परवानगी नसताना, उदाहरणार्थ समाविष्ट केलेला vendor fragment असल्यास, ही कमांड वापरा.
भागांपासून एकत्र केलेल्या स्टॅकसाठी include
include दुसरे Compose application तुमच्या model मध्ये आणते. हे top-level element आहे, flag नाही.
include:
- path: ../commons/compose.yamlinclude मधील प्रत्येक path स्वतंत्र Compose application model म्हणून load केला जातो. त्याची स्वतःची project directory असते. त्यामुळे त्या file मधील relative paths त्या file च्या स्वतःच्या directory च्या संदर्भात resolve होतात. -f पेक्षा हा मुख्य फरक आहे. Fragment दुसऱ्या folder मध्ये किंवा दुसऱ्या repository मध्ये असल्यास include हे योग्य साधन आहे.
Long form मध्ये sub-options असतात.
include:
- path:
- ../monitoring/compose.yaml
- ../monitoring/compose.vps.yaml
project_directory: ../monitoring
env_file: ../monitoring/.envpath एक list स्वीकारते. त्या files सामान्य नियमांनुसार एकत्र merge केल्या जातात. त्यानंतर परिणाम तुमच्या model मध्ये जोडला जातो. project_directory included file मधील relative paths resolve करण्यासाठी वापरला जाणारा base path सेट करते. env_file interpolation साठी included file ला स्वतःचे variables देते. त्यामुळे shared fragment तुमच्या project मधील .env शांतपणे वाचत नाही. include साठी Compose v2.20.0 किंवा त्यानंतरची आवृत्ती आवश्यक आहे.
तुमच्या file आणि included file मध्ये resource names समान असल्यास ते शांतपणे merge करण्याऐवजी error म्हणून नोंदवले जातात. हे जाणीवपूर्वक केलेले आहे. Included file मध्ये घोषित केलेली एखादी गोष्ट बदलायची असल्यास तो बदल compose.override.yaml मध्ये लिहा. Override assembled model वर लागू केला जातो. त्यामुळे तो included resources शी संघर्ष न करता त्यांना बदलू शकतो.
थोडक्यात: include स्वतंत्र applications एकत्र compose करते, तर -f एका application वर configuration चे स्तर चढवते.
एका VPS वर dev आणि prod विभाजन
संपूर्ण नमुना तीन फाइलमध्ये आहे. Base फाइलमध्ये सर्वत्र लागू असलेल्या बाबी घोषित केल्या आहेत. ती कोणतेही ports प्रकाशित करत नाही.
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:depends_on condition मुळे app केवळ अस्तित्वात असलेल्या container ची वाट पाहत नाही, तर प्रतिसाद देणाऱ्या database ची वाट पाहते. याचे स्पष्टीकरण healthchecks आणि depends_on conditions मध्ये दिले आहे. POSTGRES_PASSWORD हे project च्या .env फाइलमधून interpolated केले जाते. ही फाइल कधीही git मध्ये ठेवू नका. अधिक सुरक्षित पर्यायांसाठी env files आणि Compose secrets पहा.
यानंतर compose.override.yaml येते. Compose ही फाइल स्वतः load करते. ही 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"Laptop वर साधे docker compose up केल्यास या दोन फाइल merge होतात. command हे image default बदलते, कारण त्याची एकच value असते. LOG_LEVEL हे info बदलते, कारण environment key नुसार merge करते. Bind mount आणि प्रकाशित केलेले दोन ports हे थेट जोडले जातात. Database port localhost ला bind केलेला आहे. त्यामुळे shared network वरील laptop PostgreSQL इतरांना उपलब्ध करून देत नाही.
शेवटी compose.prod.yaml. Compose ज्या नावाच्या फाइल्स शोधते त्यापैकी हे नाव नाही. त्यामुळे ती चुकून कधीही load होत नाही.
services:
app:
ports:
- "127.0.0.1:8000:3000"
deploy:
resources:
limits:
memory: 512MVPS वर तुम्ही दोन्ही फाइलची नावे देता. ही नावे देण्याची कृतीच 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 ने दोन्ही services running असल्याचे दाखवले पाहिजे आणि db मध्ये (healthy) दिसले पाहिजे. तुम्ही -f दिल्यामुळे compose.override.yaml read झाली नाही. त्यामुळे dev command, source bind mount आणि public port 3000 production पर्यंत पोहोचू शकत नाहीत, जरी ही फाइल त्याच directory मध्ये असली तरी. Port 8000 केवळ localhost वर उपलब्ध आहे आणि proxy साठी तयार आहे. दुसरी service जोडल्यावर Traefik मागे अनेक apps चालवणे पहा.
Server च्या .env मध्ये COMPOSE_FILE=compose.yaml:compose.prod.yaml सेट करा. त्यानंतर तुमच्या उर्वरित commands पुन्हा साध्या docker compose logs -f app राहतील.
उपयोजित करण्यापूर्वी विलीन केलेले मॉडेल वाचा
docker compose config पूर्णपणे विलीन केलेले आणि पूर्णपणे इंटरपोलेट केलेले मॉडेल दाखवते. हे पूर्वावलोकन नाही. Compose ज्या अचूक इनपुटवर कार्य करेल तेच हे आहे. त्यामुळे आउटपुट तुमच्या अपेक्षेशी जुळत नसेल, तर आउटपुटच योग्य आहे.
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 ${VAR} चे विस्तार करत नाही. आउटपुट इतरत्र पेस्ट करण्यापूर्वी याचा वापर करा, कारण साधे config प्रत्येक निराकरण केलेले secret स्पष्ट मजकुरात दाखवते. --services फक्त service ची नावे दाखवते. अपेक्षित गोष्टी include ने समाविष्ट केल्या आहेत की नाही, हे त्वरेने तपासण्याचा हा एक मार्ग आहे.
अपयशाच्या स्थिती आणि तुम्हाला दिसणारे परिणाम
no configuration file provided: not found. Compose ला वाचण्यासाठी काहीही सापडले नाही. तुम्ही project directory च्या बाहेर आहात किंवा COMPOSE_FILE मध्ये अस्तित्वात नसलेल्या path चे नाव आहे. Compose default base file शोधण्यासाठी parent directories मध्ये शोध घेते; परंतु तुम्ही स्वतः निर्दिष्ट केलेल्या file साठी ती कुठेही शोध घेत नाही.
WARN[0000] The "POSTGRES_PASSWORD" variable is not set. Defaulting to a blank string. Interpolation चे निराकरण project .env file आणि shell environment यांच्या आधारे होते. येथे project directory म्हणजे पहिल्या -f file ची directory आहे. .env असलेल्या directory पेक्षा वेगळ्या directory मधून deploy केल्यास ही warning दिसते आणि त्यानंतर database कोणतेही connection स्वीकारत नाही.
तुमच्या override मधील बदल docker compose config मध्ये दिसत नाही. तुम्ही -f pass केले असण्याची शक्यता आहे. यामुळे automatic override loading बंद होते. किंवा Compose ला parent directory मध्ये compose.yaml सापडले आणि तुमची override file त्याच्या शेजारी नाही. इतर कोणतेही arguments न देता docker compose config चालवल्यास Compose प्रत्यक्षात कोणता model तयार करत आहे हे कळते.
Bind mount रिकामा आहे आणि Docker ने तुम्ही न मागितलेली directory तयार केली आहे. Relative path चे निराकरण पहिल्या file च्या directory च्या आधारे झाले. Path दुरुस्त करा, --project-directory pass करा किंवा fragment include च्या मागे हलवा.
Containers नवीन नावांसह पुन्हा सुरू होतात आणि volume रिकामा दिसतो. Project name बदलले आहे, कारण project name पहिल्या file च्या directory नुसार ठरते. Base file मध्ये सर्वात वरच्या स्तरावर name: जोडा. यामुळे naming बदलत राहणार नाही. जुना volume जुन्या prefix अंतर्गत अद्याप उपलब्ध आहे आणि docker volume ls तो दाखवेल.
Override मधून काढलेला port अद्याप open आहे. ports merge ने बदलण्याऐवजी घटक जोडला. docker compose config वापरून खात्री करा. त्यानंतर !override वापरा किंवा ports ला base file मधून बाहेर हलवा.
FAQ
Compose docker compose आपोआप compose.override.yaml लोड करते का?
होय, जेव्हा तुम्ही -f flag शिवाय docker compose चालवता. Compose कार्यरत निर्देशिका आणि तिच्या पालक निर्देशिकांमध्ये compose.yaml किंवा docker-compose.yaml शोधते. त्याच ठिकाणी override file असल्यास ती file दुसरी लोड केली जाते. मान्य नावे compose.override.yaml, compose.override.yml, docker-compose.override.yml आणि docker-compose.override.yaml आहेत. कोणतेही -f दिल्यास ही प्रक्रिया बंद होते. त्यामुळे docker compose -f compose.yaml up फक्त एक file वाचते.
अनेक -f files कोणत्या क्रमाने merge होतात?
डावीकडून उजवीकडे. तुम्ही files ज्या क्रमाने देता, त्या क्रमाने Compose configuration तयार करते. प्रत्येक file आधीच्या files मधील मूल्ये override करते आणि त्यात नवीन मूल्ये जोडते. त्यामुळे ओळीतील शेवटची file कोणताही conflict असल्यास अंतिम ठरते. त्या project मधील प्रत्येक command साठी हीच यादी वापरावी लागते. यासाठी COMPOSE_FILE=compose.yaml:compose.prod.yaml वापरले जाते.
Override केल्यानंतरही माझा port published का राहतो?
कारण ports entries ची ओळख ip, target, published आणि protocol यांच्या संपूर्ण संचावर आधारित असते. 8080:80 base च्या तुलनेत 127.0.0.1:8080:80 चा override केल्यास ip भाग वेगळा असतो. त्यामुळे Compose त्याला दुसरा port मानते आणि दोन्ही ठेवते. docker compose config चालवल्यावर तुम्हाला दोन्ही entries दिसतील. Compose v2.24.4 किंवा त्यानंतरच्या आवृत्तीत ports: !override वापरा. किंवा base file मधून ports काढून टाका, म्हणजे merge करण्यासाठी तेथे काहीच उरणार नाही.
include आणि -f यांच्यात काय फरक आहे?
-f अनेक files एका application वर स्तरांप्रमाणे लागू करते. प्रत्येक file मधील प्रत्येक relative path पहिल्या file च्या directory च्या संदर्भात resolve होतो. include वेगळे Compose application समाविष्ट करते. प्रत्येक included path स्वतःची project directory ठेवतो. त्यामुळे त्याचे relative paths त्याच्याच संदर्भात resolve होतात. स्वतःच्या stack साठी environment layers वापरताना -f वापरा. अन्यत्र देखरेख केली जाणारी fragment समाविष्ट करण्यासाठी include वापरा. include साठी Compose v2.20.0 किंवा त्यानंतरची आवृत्ती आवश्यक आहे.
Base file मध्ये सेट केलेले मूल्य कसे काढायचे?
Compose v2.24 किंवा त्यानंतरच्या आवृत्तीत !reset tag वापरा. Override करणाऱ्या file मध्ये ports: !reset [] किंवा MY_VAR: !reset null लिहा. त्यामुळे attribute त्याच्या default मूल्यावर किंवा null वर परत जाते. Tag ला दिलेले मूल्य आवश्यक असते, पण त्याकडे दुर्लक्ष केले जाते. Attribute clear करण्याऐवजी ते replace करायचे असल्यास !override वापरा. त्यासाठी v2.24.4 किंवा त्यानंतरची आवृत्ती आवश्यक आहे.