Docker Compose मध्ये command आणि entrypoint मधील फरक
ENTRYPOINT कोणता program चालवते आणि command त्याचे arguments देते. Compose मधील चार override संयोजन पाहा, तसेच entrypoint सेट केल्यावर CMD का हटते ते समजा.
Docker Compose मधील command आणि entrypoint: एक नियम
Docker Compose मध्ये entrypoint: चालणारा प्रोग्राम ठरवते आणि command: त्या प्रोग्रामला दिले जाणारे arguments ठरवते. कंटेनरची process म्हणजे entrypoint list आणि तिच्या शेवटी जोडलेली command list होय. या पानावरील इतर सर्व वर्तन या एका वाक्यातून स्पष्ट होते.
या दोन keys Dockerfile मधील दोन instructions शी संबंधित आहेत. entrypoint: image मधील ENTRYPOINT बदलते. command: image मधील CMD बदलते. हे दोन्ही स्वतंत्र नाहीत. याच ठिकाणी अनेकांना अडचण येते: entrypoint: सेट केल्यावर image मधील CMD देखील काढून टाकले जाते. Compose specification मध्ये हे स्पष्टपणे नमूद केले आहे. entrypoint null नसल्यास Compose image मधील default command दुर्लक्षित करते.
आधी image मध्ये घोषित केलेली माहिती वाचा
काहीही override करण्यापूर्वी image मध्ये आधीपासून काय समाविष्ट आहे ते पाहा.
docker image inspect --format '{{json .Config.Entrypoint}}' postgres:16
docker image inspect --format '{{json .Config.Cmd}}' postgres:16तुम्हाला ["docker-entrypoint.sh"] आणि ["postgres"] मिळतात. त्यामुळे container docker-entrypoint.sh postgres चालवतो. ही script पहिल्या boot वेळी data directory तयार करते, POSTGRES_* variables वाचते, privileges postgres user कडे कमी करते आणि शेवटी तिला दिलेले arguments exec करते. तुम्हाला यातील कोणता भाग बदलायचा आहे हे ठरवणे महत्त्वाचे आहे. Database ला flag देण्यासाठी command: बदला. entrypoint: बदलल्यास यापैकी कोणतीही setup प्रक्रिया चालणार नाही.
The four combinations, shown on a tiny image
Build an image whose only job is to print the argument list it was started with.
FROM alpine:3.20
ENTRYPOINT ["/bin/echo", "ep"]
CMD ["cmd"]docker build -t argdemo .services:
demo:
image: argdemoRun docker compose up after each edit and read the single line it logs.
- Neither key set. The process is
/bin/echo ep cmdand the log showsep cmd. command: ["cmd2"]only. The process is/bin/echo ep cmd2. The entrypoint is untouched and only the arguments changed.entrypoint: ["/bin/echo", "ep2"]only. The process is/bin/echo ep2and the log showsep2. Thecmdfrom the image is gone, and nothing warns you.- Both keys set. The process is
/bin/echo ep2 cmd2. This is the only case where you control the whole argument list.
entrypoint सेट केल्यावर image मधील CMD का साफ होते
Image मधील CMD ही त्या image च्या ENTRYPOINT साठी default argument list म्हणून लिहिलेली असते. entrypoint बदलल्यावर हे arguments आता चालू नसलेल्या program साठी लागू होतात. त्यामुळे Compose ते काढून टाकते. Image च्या लेखकाने अभिप्रेत न केलेली command line तयार करण्याऐवजी Compose हे करते. docker run --entrypoint देखील याच प्रकारे वागते. त्यामुळे ही Compose ची विशेष बाब नसून Docker चे वर्तन आहे.
याचा परिणाम स्पष्टपणे दिसतो. nginx:1.27 मध्ये ENTRYPOINT ["/docker-entrypoint.sh"] आणि CMD ["nginx", "-g", "daemon off;"] घोषित केलेले आहेत. entrypoint: /custom-init.sh सेट केल्यावर तुमची script रिकाम्या argument list सह सुरू होते. नेहमीच्या exec "$@" ने संपणाऱ्या script कडे exec करण्यासाठी काहीही उरत नाही. त्यामुळे exec काहीही करत नाही. Script तिच्या शेवटच्या ओळीपर्यंत पोहोचते आणि container code 0 सह, कोणताही error message न देता बंद होतो. Arguments स्वतः पुन्हा द्या:
services:
web:
image: nginx:1.27
entrypoint: /custom-init.sh
command: ["nginx", "-g", "daemon off;"]लक्षात ठेवण्याचा नियम: तुम्ही entrypoint: सेट करता त्या प्रत्येक वेळी, त्याच बदलात command: काय असावे हे ठरवा.
Exec form आणि shell form, तसेच Compose यापेक्षा कसे वेगळे आहे
Dockerfile दोन syntax स्वीकारते. CMD ["nginx", "-g", "daemon off;"] हा exec form आहे: binary थेट चालतो आणि कोणताही shell वापरला जात नाही. CMD nginx -g "daemon off;" हा shell form आहे: Docker त्याचे /bin/sh -c 'nginx -g "daemon off;"' मध्ये रूपांतर करते. त्यामुळे shell आधी चालतो आणि तुमचा प्रोग्राम त्याचा child process बनतो.
Compose हा नियम तसाच वापरत नाही. त्यामुळे अनेकांना आश्चर्य वाटते. command: मधील string arguments मध्ये विभाजित केली जाते आणि थेट execute केली जाते; कोणतेही /bin/sh -c wrapper वापरले जात नाही. Compose reference मध्ये हे स्पष्टपणे नमूद केले आहे: command field image मध्ये परिभाषित केलेल्या SHELL context मध्ये चालत नाही. त्यामुळे shell features आवश्यक असल्यास shell स्वतः invoke करावा लागतो.
म्हणून command: echo "hello $$HOSTNAME" literal text hello $HOSTNAME print करते. ही string कोणत्याही shell ने पाहिलेली नसल्यामुळे तिचे expansion झाले नाही. Shell हवा असल्यास तो स्पष्टपणे मागवा:
services:
demo:
image: alpine:3.20
command: /bin/sh -c 'echo "hello $$HOSTNAME"'Signals, PID 1 आणि स्वच्छ docker compose down
docker compose stop आणि docker compose down प्रत्येक container मधील PID 1 कडे SIGTERM पाठवतात, stop_grace_period ची प्रतीक्षा करतात आणि त्यानंतर SIGKILL पाठवतात. Default grace period 10 seconds असतो.
Linux मध्ये PID 1 विशेष असतो. Kernel सिग्नलची default action PID 1 वर लागू करत नाही. त्यामुळे SIGTERM handler स्थापित न करणारी process PID 1 म्हणून चालत असताना SIGTERM दुर्लक्षित करते. ती पूर्ण grace period पर्यंत चालू राहते आणि नंतर थेट kill केली जाते. त्यामुळे उघडे connection किंवा commit न केलेला transaction मध्येच बंद होऊ शकतो.
तुमच्या program च्या पुढे shell असल्यास ही शक्यता वाढते, कारण shell हा PID 1 असतो आणि बहुतेक shells child कडे signals forward करत नाहीत. काही shells -c string मधील अंतिम command साठी स्वतःची process replace करतात. त्यामुळे काही वेळा तुमचा program PID 1 पर्यंत पोहोचतो. हे shell आणि अचूक string वर अवलंबून असते. त्यामुळे अंदाज लावू नका. ते तपासा:
docker compose exec -T web cat /proc/1/cmdline | tr '\0' ' '; echoतुमच्या program ऐवजी PID 1 म्हणून /bin/sh -c ... दिसत असल्यास, दोन उपाय आहेत. Image मध्ये exec form वापरा किंवा shell ठेवून exec ने process कडे नियंत्रण सोपवा:
services:
web:
image: myapp:1.4
command: /bin/sh -c 'exec myapp --config /etc/myapp.toml'exec child fork करण्याऐवजी shell process तुमच्या program ने replace करते. त्यामुळे तुमच्या program ला PID 1 मिळतो आणि त्याला signal प्राप्त होतो.
काही programs child processes सुरू करतात, पण त्यांना कधीही reap करत नाहीत. त्यामुळे zombie processes निर्माण होतात, कारण PID 1 हा reaper देखील असतो. Compose मध्ये यासाठी switch आहे:
services:
web:
image: myapp:1.4
init: true
stop_grace_period: 30sinit: true एक लहान init process PID 1 म्हणून चालवते. ती तुमच्या process कडे signals forward करते आणि child processes reap करते. stop_grace_period खरोखर संथ shutdown साठी अधिक वेळ देते. तुमचा program वेगळ्या signal ची अपेक्षा करत असल्यास, stop_signal: SIGQUIT Compose ने पाठवला जाणारा signal बदलते. Image आधीच काय मागते ते docker image inspect --format '{{.Config.StopSignal}}' nginx:1.27 ने तपासा.
एखाद्या stack मधील docker compose down प्रत्येक service साठी नेहमी ten seconds घेत असेल, तर SIGTERM हाताळणारे काहीही नाही, असे त्यावरून दिसते. Tooling ला दोष देण्यापूर्वी हे दुरुस्त करा. प्रत्येक subcommand काय remove करते हे समजून घेण्यासाठी docker compose down आणि stop मधील फरक पहा.
हेच exec-versus-shell विभाजन आणखी एका ठिकाणी दिसते. test: ["CMD", "curl", "-f", "http://localhost/"] म्हणून लिहिलेला healthcheck binary थेट चालवतो, तर test: ["CMD-SHELL", "curl -f http://localhost/ || exit 1"] shell मार्फत चालतो; त्यामुळे || ला अर्थ प्राप्त होतो. त्या field विषयी उर्वरित माहिती प्रामाणिकपणे अपयशी ठरणारे Compose healthchecks लिहिणे येथे दिली आहे.
अधिकृत image मध्ये flag जोडणे
बहुतेक वाचक यासाठीच आले आहेत. तुम्हाला postgres साठी आणखी एक flag हवा आहे, पण initialisation script मध्ये कोणताही बदल करायचा नाही.
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
command: postgres -c max_connections=200 -c shared_buffers=256MB
volumes:
pgdata:फक्त command: बदलले आहे. त्यामुळे docker-entrypoint.sh अजूनही चालते आणि तुम्ही दिलेली command अजूनही exec करते. गृहीत धरण्याऐवजी परिणाम तपासा:
docker compose up -d db
docker compose exec -T db psql -U postgres -c 'show max_connections;'Output मध्ये 200 दिसले पाहिजे. अजूनही 100 दिसत असल्यास, docker compose config चालवा आणि अपेक्षित command merged output मध्ये आहे याची खात्री करा. Compose override files एकत्र करताना command पूर्णपणे बदलते; त्यात भर घालत नाही. त्यामुळे command: सेट करणारी दुसरी file असल्यास तिची value कोणताही इशारा न देता लागू होते.
वरील ${POSTGRES_PASSWORD} Compose तुमच्या .env file मधून host वर expand करते, container तयार होण्यापूर्वी. ही value सुरक्षितपणे कुठे ठेवता येईल, यासाठी Compose मधील Env files आणि secrets पहा.
docker compose run वापरून एकदाच migration चालवणे
docker compose run त्याच service definition वरून नवीन container तयार करते आणि service name नंतर तुम्ही टाइप केलेल्या command ने मूळ command बदलते. Image चा entrypoint तरीही चालतो. त्यामुळे container हा दीर्घकाळ चालणाऱ्या container प्रमाणेच तयार होतो.
docker compose run --rm app python manage.py migrate--rmcommand पूर्ण झाल्यावर container हटवते. हे न वापरल्यास प्रत्येक run नंतर एक stopped container मागे राहतो. तोdocker compose ps -aमध्ये दिसतो.- Ports publish केले जात नाहीत.
runcontainer service च्याports:कडे दुर्लक्ष करते, जोपर्यंत तुम्ही--service-portsजोडत नाही. त्यामुळे आधीपासून चालू असलेल्या service शी port conflict होत नाही. - Dependencies आधी सुरू होतात.
depends_onमधील प्रत्येक गोष्ट तुमच्या command आधी सुरू होते.--no-depsहे वर्तन वगळते. - Container ला
myproject-app-run-9f2c1aसारखे generated name मिळते. त्यामुळे ते service container शी कधीही conflict करत नाही.
Entrypoint देखील बदलायचा असल्यास त्यासाठी एक flag उपलब्ध आहे:
docker compose run --rm --entrypoint /bin/sh app -c 'python manage.py migrate'तयार होणारी argument list /bin/sh -c 'python manage.py migrate' असते, कारण service name नंतरचे शब्द अजूनही command म्हणूनच वापरले जातात. docker compose exec हे दुसरे tool आहे आणि ते वेगळ्या पद्धतीने कार्य करते: ते आधीपासून चालू असलेल्या container मध्ये process चालवते आणि entrypoint: तसेच command: या दोन्हींकडे पूर्णपणे दुर्लक्ष करते. नवीन container आवश्यक असलेल्या task साठी run वापरा. चालू container मध्ये पाहण्यासाठी exec वापरा. Compose command cheat sheet मध्ये उर्वरित subcommands शेजारी-शेजारी दिले आहेत.
माझा container लगेच exit का होतो?
Exit code पासून सुरुवात करा. त्यामुळे कारण लवकर स्पष्ट होते.
docker compose ps -a
docker compose logs appExit code 0 आणि कोणतेही output नाही. Command चालून पूर्ण झाली आहे. सर्वात सामान्य कारण म्हणजे entrypoint: override मुळे image चे CMD देखील बदलले गेले. त्यामुळे entrypoint रिकाम्या argument list सह चालला आणि पुढे hand off करण्यासाठी त्याच्याकडे काहीही नव्हते.
permission denied ने समाप्त होणारी error. Image मध्ये script ला executable bit नाही. Repository मधील file वर हा bit कधीच सेट केला नसल्यामुळे असे सहसा घडते. Build वेळी COPY --chmod=0755 entrypoint.sh /entrypoint.sh वापरून तो सेट करा.
Image मध्ये file स्पष्टपणे दिसत असूनही no such file or directory ने समाप्त होणारी error. Script मध्ये Windows line endings आहेत. त्यामुळे तिची पहिली ओळ #!/bin/sh आणि carriage return byte अशी वाचली जाते. परिणामी kernel त्या byte चा समावेश असलेला interpreter शोधतो आणि तो सापडत नाही. dos2unix entrypoint.sh चालवा. त्यानंतर * text eol=lf मध्ये .gitattributes जोडा, जेणेकरून ही समस्या पुन्हा उद्भवणार नाही.
executable file not found in $PATH. command: मध्ये नमूद केलेला binary image मध्ये नाही. किंवा जिथे प्रत्यक्ष program आवश्यक आहे तिथे तुम्ही cd सारखा shell built-in लिहिला आहे.
entrypoint अयशस्वी झालेल्या image मध्ये shell मिळवणे
काहीही तपासण्यापूर्वी entrypoint बंद होत असेल, तर तो बदलून चालवा:
docker compose run --rm --entrypoint /bin/sh appयातून executable file not found in $PATH मिळाल्यास, image मध्ये कोणताही shell नाही. Distroless आणि scratch आधारित images मध्ये अनेकदा shell दिलेला नसतो. entrypoint सुरू न करता बाहेरून filesystem वाचता येते:
docker create --name probe myapp:1.4
docker export probe | tar -tv | head -40
docker rm probecontainer वारंवार त्याला attach करता यावे म्हणून सुरू ठेवायचा असल्यास, तो कधीही बंद न होणाऱ्या process वर ठेवा. हे commit न करायच्या override file मध्ये लिहा:
services:
app:
entrypoint: ["tail", "-f", "/dev/null"]
command: []command: [] आवश्यक नाही, कारण entrypoint: सेट केल्याने image मधील CMD आधीच काढले गेले आहे. तरीही ते लिहिल्याने file पुढे वाचणाऱ्या व्यक्तीस उद्देश स्पष्ट होतो. तो सुरू करा आणि आत जा:
docker compose -f compose.yaml -f compose.debug.yaml up -d app
docker compose exec app /bin/shआता खरा entrypoint हाताने चालवा आणि तो कुठे थांबतो ते monitor करा. त्यामुळे अर्ध्या सेकंदापूर्वी बंद झालेल्या container मध्ये संदेश राहण्याऐवजी error message तुमच्या terminal वर दिसेल. तुम्ही अजून पहिला stack तयार करत असाल, तर VPS वर पहिला Compose stack वरील सर्व मांडणी गृहीत धरतो.
FAQ
माझे container docker compose up नंतर लगेच का बंद होते?
Exit code पाहण्यासाठी docker compose ps -a तपासा. कोणतेही output नसताना Exit 0 मिळत असेल, तर सेवेसाठी entrypoint: सेट केले असण्याची शक्यता आहे. त्यामुळे image मधील CMD देखील काढले गेले आणि entrypoint रिकाम्या argument list सह चालून पूर्ण झाले. command: वापरून arguments पुन्हा जोडा. permission denied ने समाप्त होणारी त्रुटी म्हणजे entrypoint script वर executable bit सेट नाही. अस्तित्वात असलेल्या file साठी no such file or directory ने समाप्त होणारी त्रुटी म्हणजे script मध्ये Windows line endings आहेत. त्यामुळे तिच्या shebang line मध्ये अस्तित्वात नसलेल्या interpreter चे नाव आहे.
Compose मध्ये entrypoint सेट केल्यावर image मधील CMD काढले जाते का?
होय. entrypoint null नसल्यास Compose image ने घोषित केलेली default command दुर्लक्षित करते. हे documented behaviour आहे आणि docker run --entrypoint शी सुसंगत आहे. Image मधील CMD हे त्या image च्या ENTRYPOINT साठी arguments म्हणून लिहिलेले असते. त्यामुळे entrypoint बदलल्यानंतर जुने arguments कोणत्याही घटकाशी संबंधित राहत नाहीत. नवीन entrypoint ला अजूनही arguments आवश्यक असल्यास त्याच service मध्ये command: सेट करा.
Compose मधील string स्वरूपातील command shell मधून चालवली जाते का?
नाही. Dockerfile मधील CMD च्या उलट, Compose मधील string स्वरूपातील command: arguments मध्ये विभाजित करून थेट execute केली जाते. त्यासाठी कोणताही /bin/sh -c wrapper वापरला जात नाही. त्यामुळे container मधील shell कडून $VARIABLE कधीही expand होत नाही. Shell आवश्यक असल्यास command: /bin/sh -c 'echo "hello $$HOSTNAME"' प्रमाणे तो स्वतः call करा. दुहेरी $$ dollar sign ला escape करते. त्यामुळे Compose ते host वर expand न करता container कडे पाठवते.
एका container साठी docker compose down ला दहा सेकंद का लागतात?
Compose PID 1 कडे SIGTERM पाठवते आणि stop_grace_period पर्यंत प्रतीक्षा करते. हे default ने 10 seconds असते. त्यानंतर ते SIGKILL पाठवते. Kernel PID 1 साठी default signal actions लागू करत नाही. त्यामुळे SIGTERM handler नसलेला program signal दुर्लक्षित करतो आणि पूर्ण प्रतीक्षा कालावधी संपेपर्यंत थांबतो. PID 1 प्रत्यक्षात काय आहे हे docker compose exec -T app cat /proc/1/cmdline | tr '\0' ' ' वापरून तपासा. ते shell असल्यास image ला exec form मध्ये बदला किंवा shell string मध्ये exec लिहा. Process ने निर्माण केलेल्या child processes चे reap कधीच केले जात नसल्यास service वर init: true सेट करा.