Docker Compose میں command اور entrypoint کا فرق
ENTRYPOINT پروگرام چلاتا ہے اور command اسے arguments دیتا ہے۔ Compose میں چاروں override صورتیں دیکھیں، اور جانیں کہ entrypoint set کرنے سے CMD کیوں صاف ہو جاتا ہے۔
Docker Compose میں command اور entrypoint کا فرق، ایک اصول میں
Docker Compose میں entrypoint: چلنے والے پروگرام کا تعین کرتا ہے، جبکہ command: اس پروگرام کو دی جانے والی arguments متعین کرتا ہے۔ Container کا process، entrypoint list اور اس کے آخر میں شامل command list پر مشتمل ہوتا ہے۔ اس صفحے کا ہر دوسرا رویہ اسی ایک جملے سے اخذ ہوتا ہے۔
یہ دونوں keys، Dockerfile کی دو instructions سے متعلق ہیں۔ entrypoint: image کے ENTRYPOINT کو replace کرتا ہے۔ command: image کے CMD کو replace کرتا ہے۔ یہ دونوں مستقل طور پر الگ نہیں ہیں، اور یہی وہ نکتہ ہے جہاں لوگ الجھتے ہیں: entrypoint: set کرنے سے image کا CMD بھی ختم ہو جاتا ہے۔ Compose specification میں یہ بات براہ راست بیان کی گئی ہے۔ اگر entrypoint non-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 کرتی ہے۔ آپ کو اس setup کے کس حصے کو تبدیل کرنا ہے، اصل فیصلہ یہی ہے۔ Database کو کوئی flag دینے کے لیے command: کو replace کریں۔ اگر آپ entrypoint: کو replace کرتے ہیں تو یہ پورا setup بالکل نہیں چلتا۔
ایک مختصر image میں دکھائے گئے چار امتزاج
ایسی image بنائیں جس کا واحد کام یہ ہو کہ اسے شروع کرتے وقت دی گئی argument list پرنٹ کرے۔
FROM alpine:3.20
ENTRYPOINT ["/bin/echo", "ep"]
CMD ["cmd"]docker build -t argdemo .services:
demo:
image: argdemoہر ترمیم کے بعد docker compose up چلائیں اور اس میں درج واحد سطر پڑھیں۔
- دونوں keys set نہیں ہیں۔ process
/bin/echo ep cmdہے اور log میںep cmdدکھتا ہے۔ - صرف
command: ["cmd2"]۔ process/bin/echo ep cmd2ہے۔ entrypoint میں کوئی تبدیلی نہیں ہوتی، صرف arguments تبدیل ہوتے ہیں۔ - صرف
entrypoint: ["/bin/echo", "ep2"]۔ process/bin/echo ep2ہے اور log میںep2دکھتا ہے۔ image سےcmdختم ہو جاتا ہے، اور کوئی warning نہیں دی جاتی۔ - دونوں keys set ہیں۔ process
/bin/echo ep2 cmd2ہے۔ یہی واحد صورت ہے جس میں آپ پوری argument list کو control کرتے ہیں۔
entrypoint مقرر کرنے سے image کا CMD کیوں ختم ہو جاتا ہے
کسی image کا CMD اسی image کے ENTRYPOINT کے لیے default argument list کے طور پر لکھا جاتا ہے۔ entrypoint تبدیل کرنے سے یہ arguments ایسے program سے متعلق رہتے ہیں جو اب چل نہیں رہا ہوتا۔ اس لیے Compose انہیں خارج کر دیتا ہے، بجائے اس کے کہ ایسی command line بنائے جس کا image author نے کبھی ارادہ نہیں کیا تھا۔ 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 دو syntaxes قبول کرتا ہے۔ 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 چلتا ہے اور آپ کا program اس کا child بن جاتا ہے۔
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 کرتا ہے۔ کسی shell نے یہ string دیکھی ہی نہیں، اس لیے اسے expand نہیں کیا گیا۔ جب 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 بھیجتے ہیں۔ پہلے سے مقرر grace period 10 سیکنڈ ہے۔
Linux میں PID 1 خصوصی حیثیت رکھتا ہے۔ kernel کسی signal کا default action PID 1 پر لاگو نہیں کرتا۔ اس لیے جو process کوئی SIGTERM handler install نہیں کرتا، وہ PID 1 کے طور پر چلتے وقت SIGTERM کو نظرانداز کر دیتا ہے۔ یہ پورے grace period تک چلتا رہتا ہے، پھر اسے فوراً ختم کر دیا جاتا ہے۔ اس سے کھلا ہوا connection یا commit نہ ہونے والا transaction منقطع ہو سکتا ہے۔
آپ کے program کے سامنے shell موجود ہو تو یہ مسئلہ زیادہ ممکن ہو جاتا ہے، کیونکہ shell PID 1 ہوتا ہے اور زیادہ تر shells child کو signals forward نہیں کرتے۔ کچھ shells -c string میں آخری command کے ساتھ خود کو replace کر دیتے ہیں، اس لیے بعض اوقات آپ کا program PID 1 تک پہنچ جاتا ہے۔ یہ shell اور عین string پر منحصر ہے، اس لیے اندازہ نہ لگائیں۔ اسے پڑھیں:
docker compose exec -T web cat /proc/1/cmdline | tr '\0' ' '; echoاگر PID 1 آپ کے program کے بجائے /bin/sh -c ... کے طور پر دکھائی دے تو دو حل ہیں۔ image میں exec form استعمال کریں، یا shell برقرار رکھتے ہوئے process کو exec کے ذریعے حوالے کریں:
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 چلاتا ہے۔ یہ signals آپ کے process کو forward کرتا ہے اور child processes کو reap کرتا ہے۔ stop_grace_period واقعی سست shutdown کے لیے زیادہ وقت فراہم کرتا ہے۔ اگر آپ کا program مختلف signal کا انتظار کرتا ہے تو stop_signal: SIGQUIT تبدیل کرتا ہے کہ Compose کون سا signal بھیجے۔ کسی image کی پہلے سے مطلوبہ setting دیکھنے کے لیے docker image inspect --format '{{.Config.StopSignal}}' nginx:1.27 پڑھیں۔
اگر کسی stack میں docker compose down ہر service کے لیے ہمیشہ دس سیکنڈ لیتا ہے تو اس کا مطلب ہے کہ کوئی بھی SIGTERM handle نہیں کر رہا۔ tooling کو ذمہ دار ٹھہرانے سے پہلے اسے درست کریں، اور ہر subcommand کے ذریعے حذف ہونے والی چیزوں کے لیے docker compose down اور stop کا فرق دیکھیں۔
یہی exec اور 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 چاہتے ہیں، لیکن initialization 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;'آؤٹ پٹ میں 200 دکھائی دینا چاہیے۔ اگر اب بھی 100 دکھائی دے تو docker compose config چلائیں اور تصدیق کریں کہ آپ کی توقع کے مطابق command merged output میں موجود ہے۔ Compose override files کو merge کرتے وقت command کو مکمل طور پر replace کرتا ہے، اس میں اضافہ نہیں کرتا۔ اس لیے دوسری file میں command: مقرر ہو تو وہ خاموشی سے غالب آ جاتی ہے۔
اوپر موجود ${POSTGRES_PASSWORD} کو Compose، container کے وجود میں آنے سے پہلے، host پر آپ کی .env file سے expand کرتا ہے۔ Compose میں environment files اور secrets میں بتایا گیا ہے کہ یہ value محفوظ طور پر کہاں رکھی جا سکتی ہے۔
docker compose run کے ساتھ ایک بار کی migration چلانا
docker compose run اسی service definition سے نیا container بناتا ہے اور service name کے بعد آپ جو command لکھتے ہیں، اس سے اصل command کو replace کر دیتا ہے۔ 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 collision نہیں کر سکتا۔ - Dependencies پہلے start ہوتی ہیں۔
depends_onمیں شامل ہر چیز آپ کی command سے پہلے start ہو جاتی ہے، جبکہ--no-depsاسے نظرانداز کرتا ہے۔ - Container کو
myproject-app-run-9f2c1aجیسا generated name دیا جاتا ہے، اس لیے یہ service container کے name سے کبھی متصادم نہیں ہوتا۔
Entrypoint کو بھی replace کرنے کے لیے ایک 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: دونوں کو مکمل طور پر نظرانداز کرتا ہے۔ ایسے task کے لیے run استعمال کریں جسے نئے container کی ضرورت ہو، اور چلتے ہوئے container کے اندر دیکھنے کے لیے exec استعمال کریں۔ Compose command کی مختصر رہنما میں باقی subcommands کا تقابلی جائزہ موجود ہے۔
میرا container فوراً کیوں exit ہو جاتا ہے؟
سب سے پہلے exit code دیکھیں، کیونکہ اس سے وجہ جلد محدود ہو جاتی ہے۔
docker compose ps -a
docker compose logs appExit code 0 اور کوئی output نہیں۔ Command چلی اور مکمل ہو گئی۔ عام وجہ یہ ہوتی ہے کہ entrypoint: override نے image کا CMD بھی ہٹا دیا، اس لیے entrypoint خالی argument list کے ساتھ چلا اور اس کے حوالے کرنے کے لیے کچھ نہیں تھا۔
ایسا error جو permission denied پر ختم ہو۔ Image کے اندر script پر executable bit نہیں ہے۔ عموماً repository میں file پر یہ bit کبھی set نہیں کی گئی ہوتی۔ Build کے وقت اسے COPY --chmod=0755 entrypoint.sh /entrypoint.sh سے set کریں۔
ایسا error جو no such file or directory پر ختم ہو، حالانکہ file image میں واضح طور پر موجود ہو۔ Script میں Windows line endings ہیں۔ اس کی پہلی line پھر #!/bin/sh کے ساتھ carriage return byte بھی پڑھتی ہے، اس لیے kernel ایسے interpreter کو تلاش کرتا ہے جس کے نام میں یہ byte شامل ہو، لیکن اسے نہیں پاتا۔ dos2unix entrypoint.sh چلائیں، پھر * text eol=lf کو .gitattributes میں شامل کریں تاکہ یہ مسئلہ دوبارہ نہ آئے۔
executable file not found in $PATH۔ command: میں درج binary image میں موجود نہیں ہے، یا آپ نے cd جیسا shell built-in لکھ دیا ہے، حالانکہ وہاں صرف کوئی حقیقی program چل سکتا ہے۔
ایسی image میں shell حاصل کرنا جس کا entrypoint شروع ہونے میں ناکام ہو
جب آپ کسی چیز کا معائنہ کرنے سے پہلے 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 probeجب آپ کو بار بار attach کرنے کے لیے container کو چلتا رکھنا ہو تو اسے ایسے process پر روک دیں جو کبھی ختم نہ ہو۔ اسے ایسی override file میں رکھیں جسے commit نہ کریں:
services:
app:
entrypoint: ["tail", "-f", "/dev/null"]
command: []command: [] لازمی نہیں ہے، کیونکہ entrypoint: set کرنے سے image کا CMD پہلے ہی صاف ہو چکا ہے، لیکن اسے لکھنے سے اگلی بار file پڑھنے والے شخص کے لیے ارادہ واضح رہتا ہے۔ اسے شروع کریں اور اندر جائیں:
docker compose -f compose.yaml -f compose.debug.yaml up -d app
docker compose exec app /bin/shاب اصل entrypoint کو ہاتھ سے چلائیں اور دیکھیں کہ یہ کہاں رک جاتا ہے۔ اس طرح error message آپ کے terminal پر ظاہر ہوتا ہے، نہ کہ ایسے container میں جو آدھے سیکنڈ بعد ختم ہو چکا ہو۔ اگر آپ اب بھی اپنا پہلا stack تیار کر رہے ہیں تو VPS پر پہلا Compose stack میں file layout کی وضاحت ہے جس پر اوپر کی تمام ہدایات مبنی ہیں۔
FAQ
میرے container کے docker compose up کے فوراً بعد exit ہونے کی وجہ کیا ہے؟
Exit code کے لیے docker compose ps -a دیکھیں۔ بغیر output کے Exit 0 عموماً اس وقت آتا ہے جب آپ نے service پر entrypoint: set کیا ہو۔ اس سے image کا CMD بھی clear ہو جاتا ہے، اس لیے entrypoint کو خالی argument list کے ساتھ چلایا جاتا ہے اور وہ مکمل ہو جاتا ہے۔ command: کے ذریعے arguments دوبارہ شامل کریں۔ permission denied پر ختم ہونے والی error کا مطلب ہے کہ entrypoint script پر executable bit موجود نہیں۔ موجود file کے لیے no such file or directory پر ختم ہونے والی error کا مطلب ہے کہ script میں Windows line endings ہیں، اس لیے اس کی shebang line ایسے interpreter کا نام دیتی ہے جو موجود نہیں۔
کیا Compose میں entrypoint set کرنے سے image کا CMD ختم ہو جاتا ہے؟
ہاں۔ اگر entrypoint non-null ہو تو Compose image میں اعلان کردہ default command کو نظرانداز کرتا ہے۔ یہ documented behaviour ہے اور docker run --entrypoint سے مطابقت رکھتا ہے۔ وجہ یہ ہے کہ image کا CMD اسی image کے ENTRYPOINT کے لیے arguments کے طور پر لکھا جاتا ہے۔ اس لیے entrypoint تبدیل کرنے کے بعد پرانے arguments کسی چیز سے متعلق نہیں رہتے۔ اگر نئے entrypoint کو اب بھی arguments درکار ہوں تو اسی service میں command: set کریں۔
کیا Compose میں string کی صورت میں دیا گیا command shell کے ذریعے چلتا ہے؟
نہیں۔ Dockerfile کے CMD کے برعکس، Compose میں string کی صورت میں دیا گیا command: arguments میں split کیا جاتا ہے اور براہِ راست execute ہوتا ہے؛ اسے /bin/sh -c wrapper کے ذریعے نہیں چلایا جاتا۔ اس لیے $VARIABLE کو container کے اندر shell expand نہیں کرتا۔ جب shell درکار ہو تو اسے خود call کریں، جیسا کہ command: /bin/sh -c 'echo "hello $$HOSTNAME"' میں دکھایا گیا ہے۔ دگنا $$ dollar sign کو escape کرتا ہے، اس لیے Compose اسے host پر expand کرنے کے بجائے container تک پہنچا دیتا ہے۔
ایک container کے لیے docker compose down کو دس seconds کیوں لگتے ہیں؟
Compose PID 1 کو SIGTERM بھیجتا ہے، stop_grace_period تک انتظار کرتا ہے، جو default طور پر 10 seconds ہے، اور پھر SIGKILL بھیجتا ہے۔ Kernel PID 1 پر default signal actions لاگو نہیں کرتا۔ اس لیے SIGTERM handler کے بغیر program اس signal کو نظرانداز کرتا ہے اور پورا وقت انتظار کرتا ہے۔ docker compose exec -T app cat /proc/1/cmdline | tr '\0' ' ' کے ذریعے معلوم کریں کہ PID 1 حقیقت میں کیا ہے۔ اگر یہ shell ہے تو image کو exec form پر منتقل کریں یا shell string کے اندر exec لکھیں۔ اگر process ایسے child processes بناتا ہے جنہیں وہ کبھی reap نہیں کرتا تو service پر init: true set کریں۔