SSD Nodes Learn 🎉 VPS $4.99/মাস থেকে
নির্দেশিকা Matt Connorদ্বারা Matt Connor · আপডেট করা হয়েছে 2026-08-08

Docker Compose-এ command বনাম entrypoint: পার্থক্য কী

ENTRYPOINT প্রোগ্রাম চালায়, আর command তার argument দেয়। Compose-এ চারটি override সমন্বয় দেখুন এবং কেন entrypoint সেট করলে image-এর CMD মুছে যায় তা জানুন।

Docker Compose-এর command বনাম entrypoint: একটি নিয়মে

Docker Compose-এ entrypoint: চালু হওয়া প্রোগ্রাম নির্ধারণ করে, আর command: সেই প্রোগ্রামে পাঠানো argument নির্ধারণ করে। Container-এর process হলো entrypoint list, যার শেষে command list যুক্ত হয়। এই পৃষ্ঠার অন্য সব আচরণ ওই একটি বাক্য থেকেই বোঝা যায়।

এই দুটি key Dockerfile-এর দুটি instruction-এর সঙ্গে সম্পর্কিত। entrypoint: image-এর ENTRYPOINT প্রতিস্থাপন করে। command: image-এর CMD প্রতিস্থাপন করে। এগুলো স্বাধীন নয়। এখানেই সাধারণত সমস্যা হয়: entrypoint: নির্ধারণ করলেও image-এর CMD বাদ পড়ে। Compose specification-এ বিষয়টি সরাসরি বলা আছে। entrypoint non-null হলে Compose image থেকে আসা default command উপেক্ষা করে।

ইমেজে ইতিমধ্যে ঘোষিত বিষয়গুলো দেখুন

কোনো কিছু override করার আগে ইমেজের সঙ্গে থাকা ঘোষণাগুলো দেখুন।

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_* variable পড়ে, privileges কমিয়ে postgres user-এ নিয়ে যায় এবং শেষে তাকে দেওয়া arguments exec করে। কোন অংশটি পরিবর্তন করতে চান, সিদ্ধান্তটি মূলত তার ওপরই নির্ভর করে। Database-এ কোনো flag পাঠাতে হলে command: প্রতিস্থাপন করুন। entrypoint: প্রতিস্থাপন করলে এই setup-এর কোনো অংশই আর চালানো হবে না।

চারটি সমন্বয়, একটি ছোট ছবিতে দেখানো

এমন একটি image তৈরি করুন, যার একমাত্র কাজ হলো এটি যে argument list দিয়ে শুরু হয়েছে তা print করা।

FROM alpine:3.20
ENTRYPOINT ["/bin/echo", "ep"]
CMD ["cmd"]
docker build -t argdemo .
services:
  demo:
    image: argdemo

প্রতিটি edit-এর পরে docker compose up চালান এবং এটি যে এক লাইনের log লিখেছে তা পড়ুন।

  • কোনো key set করা নেই। Process হলো /bin/echo ep cmd এবং log-এ ep cmd দেখা যায়।
  • শুধু command: ["cmd2"] Process হলো /bin/echo ep cmd2। Entrypoint অপরিবর্তিত থাকে, শুধু argument পরিবর্তিত হয়।
  • শুধু entrypoint: ["/bin/echo", "ep2"] Process হলো /bin/echo ep2 এবং log-এ ep2 দেখা যায়। Image-এর cmd বাদ পড়ে, এবং কোনো warning দেখানো হয় না।
  • উভয় key set করা আছে। Process হলো /bin/echo ep2 cmd2। সম্পূর্ণ argument list নিয়ন্ত্রণ করার একমাত্র পরিস্থিতি এটি।

entrypoint সেট করলে image-এর CMD কেন মুছে যায়

একটি image-এর CMD ওই image-এর ENTRYPOINT-এর জন্য ডিফল্ট argument list হিসেবে লেখা থাকে। entrypoint প্রতিস্থাপন করলে সেই argument-গুলো এমন একটি 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 কোনো error message ছাড়াই code 0 দিয়ে বন্ধ হয়ে যায়। নিজে 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 চলে এবং আপনার program তার child হিসেবে চলে।

Compose একই নিয়ম অনুসরণ করে না, যা অনেককে বিভ্রান্ত করে। command:-এর একটি string argument-এ বিভক্ত হয়ে সরাসরি execute হয়; কোনো /bin/sh -c wrapper ব্যবহৃত হয় না। Compose reference-এ বিষয়টি স্পষ্টভাবে বলা আছে: command field image-এ নির্ধারিত SHELL context-এর মধ্যে চলে না। তাই shell feature প্রয়োজন হলে আপনাকেই নিজে shell invoke করতে হবে।

এই কারণেই command: echo "hello $$HOSTNAME" আক্ষরিক text hello $HOSTNAME print করে। কোনো shell string-টি process করেনি, তাই কিছু expand হয়নি। shell ব্যবহার করতে চাইলে shell-কে explicitভাবে invoke করুন:

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 signal-এর default action PID 1-এর ক্ষেত্রে প্রয়োগ করে না। তাই PID 1 হিসেবে চলার সময় কোনো process SIGTERM handler ইনস্টল না করলে সেটি SIGTERM উপেক্ষা করে। Process-টি পুরো grace period ধরে চলতে থাকে। এরপর তাকে সরাসরি kill করা হয়। এতে খোলা connection বা commit না-করা transaction বন্ধ হয়ে যেতে পারে।

আপনার program-এর সামনে একটি shell থাকলে এই সমস্যা হওয়ার সম্ভাবনা বাড়ে। কারণ shell-টি PID 1 হয় এবং অধিকাংশ shell child process-এ signal forward করে না। কিছু shell -c string-এর final command দিয়ে নিজেদের 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 process fork না করে shell process-কে আপনার program দিয়ে replace করে। তাই আপনার program PID 1 উত্তরাধিকারসূত্রে পায় এবং signal গ্রহণ করে।

কিছু program child process তৈরি করে, কিন্তু সেগুলো কখনও reap করে না। এতে zombie process থেকে যায়, কারণ PID 1-ই reaper হিসেবেও কাজ করে। Compose-এর জন্য একটি switch আছে:

services:
  web:
    image: myapp:1.4
    init: true
    stop_grace_period: 30s

init: true একটি ছোট init process-কে PID 1 হিসেবে চালায়। এটি আপনার process-এ signal forward করে এবং child process 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-এর জন্য সবসময় 10 seconds সময় নেয়, সেটি জানায় যে কোনো process SIGTERM পরিচালনা করছে না। Tooling-কে দোষ দেওয়ার আগে এটি ঠিক করুন। প্রতিটি subcommand কী সরিয়ে ফেলে তা জানতে 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 healthcheck সত্যিকারের ব্যর্থতা দেখায় তা লেখা-এ ব্যাখ্যা করা হয়েছে।

একটি অফিসিয়াল 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;'

আউটপুটে 200 দেখা উচিত। যদি এখনও 100 দেখা যায়, docker compose config চালান এবং merged output-এ প্রত্যাশিত command আছে কি না নিশ্চিত করুন। Compose override file merge করার সময় command-কে সরাসরি প্রতিস্থাপন করে; এর সঙ্গে নতুন কিছু যোগ করে না। তাই দ্বিতীয় কোনো file-এ command: সেট থাকলে সেটিই নীরবে কার্যকর হয়।

উপরের ${POSTGRES_PASSWORD}-টি container তৈরি হওয়ার আগে host-এ আপনার .env file থেকে Compose expand করে। সেই value নিরাপদে কোথায় রাখা যায়, তা Compose-এ environment file ও secret-এ ব্যাখ্যা করা হয়েছে।

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
  • --rm command শেষ হলে container মুছে দেয়। এটি না থাকলে প্রতিটি run-এর পরে একটি stopped container থেকে যায়, যা docker compose ps -a-এ দেখা যায়।
  • Port publish করা হয় না। একটি run container service-এর ports: উপেক্ষা করে, যদি না আপনি --service-ports যোগ করেন। তাই এটি ইতিমধ্যে চালু থাকা service-এর সঙ্গে port সংঘর্ষ তৈরি করতে পারে না।
  • Dependency আগে চালু হয়। depends_on-এ থাকা সবকিছু আপনার command চালানোর আগে চালু হয়, আর --no-deps সেটি এড়িয়ে যায়।
  • Container-এর জন্য myproject-app-run-9f2c1a-এর মতো একটি generated name ব্যবহার করা হয়। তাই এটি কখনো service container-এর নামের সঙ্গে সংঘর্ষ তৈরি করে না।

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-এ বাকি subcommand-গুলো পাশাপাশি দেখানো হয়েছে।

আমার container সঙ্গে সঙ্গে বন্ধ হয়ে যায় কেন?

দ্রুত কারণ নির্ধারণ করতে প্রথমে exit code দেখুন।

docker compose ps -a
docker compose logs app

Exit code 0 এবং কোনো output নেই। Command চলেছে এবং শেষ হয়েছে। সবচেয়ে সাধারণ কারণ হলো একটি entrypoint: override image-এর CMD-ও সরিয়ে দিয়েছে। ফলে entrypoint খালি argument list নিয়ে চলেছে এবং hand off করার মতো কিছু পায়নি।

permission denied-এ শেষ হওয়া error। Image-এর ভেতরে script-টির executable bit নেই। সাধারণত repository-এর file-এ এই bit কখনো set করা হয়নি। Build time-এ COPY --chmod=0755 entrypoint.sh /entrypoint.sh ব্যবহার করে এটি set করুন।

Image-এ স্পষ্টভাবে দেখা যায় এমন file-এর জন্য no such file or directory-এ শেষ হওয়া error। Script-এ Windows line ending আছে। ফলে এর প্রথম line-এর শেষে carriage return byte-সহ #!/bin/sh পড়া হয়। তাই kernel নামের মধ্যে ওই byte-সহ interpreter খোঁজে এবং কিছু পায় না। dos2unix entrypoint.sh চালান। এরপর * text eol=lf-কে .gitattributes-এ যোগ করুন, যাতে সমস্যাটি আবার না ঘটে।

executable file not found in $PATH command:-এ উল্লেখ করা binary image-এ নেই। অথবা যেখানে কেবল একটি প্রকৃত program ব্যবহার করা যায়, সেখানে আপনি cd-এর মতো shell built-in লিখেছেন।

যে image-এর entrypoint ব্যর্থ হয়, তাতে shell চালু করা

কিছু পরীক্ষা করার আগেই entrypoint বন্ধ হয়ে গেলে, সেটি প্রতিস্থাপন করুন:

docker compose run --rm --entrypoint /bin/sh app

এতে executable file not found in $PATH ফেরত এলে image-এ কোনো shell নেই। Distroless এবং scratch-ভিত্তিক image-এ প্রায়ই 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: সেট করলেই image-এর CMD ইতিমধ্যে মুছে যায়। তবে এটি লিখলে পরবর্তী পাঠকের জন্য উদ্দেশ্যটি স্পষ্ট থাকে। এটি চালু করে ভিতরে প্রবেশ করুন:

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 code দেখতে docker compose ps -a পরীক্ষা করুন। কোনো output ছাড়া Exit 0 সাধারণত বোঝায়, আপনি service-এ entrypoint: সেট করেছেন। এতে image-এর CMD-ও মুছে যায়। ফলে entrypoint খালি argument list নিয়ে চলে এবং শেষ হয়ে যায়। command: ব্যবহার করে argument-গুলো আবার যোগ করুন। permission denied-এ শেষ হওয়া error-এর অর্থ entrypoint script-এ executable bit নেই। বিদ্যমান কোনো file-এর ক্ষেত্রে no such file or directory-এ শেষ হওয়া error-এর অর্থ script-এ Windows line ending আছে। ফলে তার shebang line এমন interpreter-এর নাম দিচ্ছে, যা সেখানে নেই।

Compose-এ entrypoint সেট করলে কি image-এর CMD সরিয়ে যায়?

হ্যাঁ। entrypoint non-null হলে Compose image-এ ঘোষিত default command উপেক্ষা করে। এটি documented behaviour এবং docker run --entrypoint-এর সঙ্গে সামঞ্জস্যপূর্ণ। কারণ image-এর CMD সেই image-এর ENTRYPOINT-এর জন্য argument হিসেবে লেখা থাকে। তাই entrypoint প্রতিস্থাপন করলে পুরোনো argument-গুলো আর কোনো কিছুর সঙ্গে যুক্ত থাকে না। নতুন entrypoint-এর এখনও argument প্রয়োজন হলে একই service-এ command: সেট করুন।

Compose-এ একটি string হিসেবে দেওয়া command কি shell-এর মাধ্যমে চলে?

না। Dockerfile-এর CMD-এর বিপরীতে, Compose-এর command:-এ দেওয়া string argument-এ বিভক্ত হয় এবং সরাসরি execute হয়। কোনো /bin/sh -c wrapper ব্যবহার করা হয় না। তাই container-এর ভেতরে shell $VARIABLE expand করে না। shell প্রয়োজন হলে command: /bin/sh -c 'echo "hello $$HOSTNAME"'-এর মতো নিজে shell 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 action প্রয়োগ করে না। তাই কোনো 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 তৈরি করে কিন্তু সেগুলো কখনও reap না করলে service-এ init: true সেট করুন।

#docker-compose#entrypoint#command#containers#debugging