SSD Nodes Learn 🎉 VPS $5.50/ماہ سے
تعلیمی Matt Connorتحریر: Matt Connor

Docker Compose exec سے interactive shell کیسے کھولیں

چلتی ہوئی Compose service میں `docker compose exec` سے shell کھولیں، اور بند service یا محفوظ طریقے سے نئے container کے لیے `run --rm` استعمال کریں۔

docker compose exec کے ساتھ interactive shell حاصل کریں

docker compose exec web bash پہلے سے چلنے والے web service کے container کے اندر interactive shell کھولتا ہے۔ exec کے بعد دیا گیا نام آپ کی compose.yaml میں موجود service name ہے، container name نہیں۔ اگر image میں bash موجود نہ ہو تو اس کے بجائے sh استعمال کریں۔

docker compose ps
docker compose exec web bash

پہلے docker compose ps چلائیں۔ اس میں web کی فہرست running state کے ساتھ دکھائی دینی چاہیے۔ اس کے بعد دوسرا command آپ کو container کے اندر prompt پر لے جاتا ہے، اور exit یا Ctrl-D دبانے سے آپ host پر واپس آ جاتے ہیں۔ آپ کے باہر نکلنے کے بعد service چلتی رہتی ہے، کیونکہ exec نے main process کے ساتھ ایک دوسرا process شروع کیا تھا۔ آپ کا shell بند ہونے سے PID 1 (process id 1) متاثر نہیں ہوتا۔ یہی وہ process ہے جسے چلانے کے لیے container بنایا گیا تھا۔

یہ container میں داخل ہونے کے دو طریقوں میں سے ایک ہے۔ exec پہلے سے موجود container سے جڑتا ہے۔ docker compose run اسی service definition سے ایک نیا container بناتا ہے۔ اس guide کی تقریباً باقی تمام باتیں اسی بنیادی فرق سے اخذ ہوتی ہیں۔

Compose میں -it اختیاری لیکن plain docker میں لازمی کیوں ہے

دو flags session کے interactive حصے کو کنٹرول کرتے ہیں۔ -i stdin کو کھلا رکھتا ہے، اس لیے آپ جو کچھ لکھتے ہیں وہ process تک پہنچتا ہے۔ -t ایک pseudo terminal مختص کرتا ہے، جسے TTY کہا جاتا ہے، تاکہ shell prompt دکھائے اور arrow keys کو handle کرے۔ plain docker exec میں دونوں flags بطور default بند ہوتے ہیں، اسی لیے آپ نے اب تک ہر مثال میں docker exec -it لکھا ہے۔ docker compose exec دونوں کو خود فعال کر دیتا ہے، اس لیے docker compose exec -it web bash اور docker compose exec web bash ایک ہی کام کرتے ہیں۔ Compose اب بھی -it قبول کرتا ہے تاکہ پرانی عادت کے مطابق کام جاری رہے۔

آپ چند سیکنڈ میں TTY کی عدم موجودگی محسوس کر لیتے ہیں۔ shell چلتا ہے، لیکن کوئی prompt نہیں دکھاتا اور Ctrl-C process تک نہیں پہنچتا۔ اس کے برعکس، جب آپ کو Compose سے TTY مختص نہ کرنے کے لیے کہنا ہو، تو اس کے لیے الگ flag اور نیچے الگ section موجود ہے۔

جب image میں bash نہ ہو تو کیا کریں

Alpine-based image سے bash مانگنے پر exec اس طرح ناکام ہوتا ہے:

OCI runtime exec failed: exec failed: unable to start container process: exec: "bash": executable file not found in $PATH: unknown

یہ پیغام exec کے مسئلے کی نشاندہی نہیں کرتا۔ اس کا مطلب ہے کہ مطلوبہ binary image میں موجود نہیں ہے۔ Alpine میں BusyBox شامل ہوتا ہے، جو ash کو /bin/sh کے طور پر فراہم کرتا ہے، لیکن bash بالکل شامل نہیں ہوتا۔ اس لیے sh طلب کریں:

docker compose exec web sh

Debian اور Ubuntu-based images، جن میں -slim tags بھی شامل ہیں، bash فراہم کرتی ہیں۔ bash سے command history اور بہتر completion ملتی ہے۔ اس لیے پہلے bash آزمائیں اور ناکام ہونے پر sh استعمال کریں۔ sh تقریباً ہر عام مقصد کی image میں موجود ہوتا ہے۔

کچھ images میں کوئی shell نہیں ہوتا۔ Distroless images اور FROM scratch بنائی گئی images میں صرف application binary اور اس کی libraries ہوتی ہیں۔ یہ جان بوجھ کر کیا جاتا ہے، کیونکہ موجود نہ ہونے والے shell کو آپ کے خلاف استعمال نہیں کیا جا سکتا۔ ایسی images میں sh اسی پیغام کے ساتھ ناکام ہوتا ہے، اور آزمانے کے لیے کچھ باقی نہیں رہتا۔ دو طریقے کام کرتے ہیں۔ Google کی distroless images ایسے :debug tags فراہم کرتی ہیں جن میں BusyBox shell شامل ہوتا ہے۔ اس لیے tag کو عارضی طور پر تبدیل کرکے داخل ہو سکتے ہیں۔ یا target کے namespaces کے اندر ایک الگ container شروع کریں:

CID=$(docker compose ps -q web)
docker run --rm -it --network "container:$CID" --pid "container:$CID" nicolaka/netshoot

اب آپ کے پاس netshoot کے tools موجود ہیں، جو application کے network کی طرف متوجہ ہیں۔ اس لیے curl localhost:8080 اور ss -lntp ایسا برتاؤ کرتے ہیں جیسے آپ اسی container کے اندر ہوں۔ آپ کو نظر آنے والا filesystem netshoot کا ہے، app کا نہیں۔ چونکہ process namespace shared ہے، اس لیے root ہونے پر ls /proc/1/root/ target کی اپنی files تک رسائی حاصل کرتا ہے۔

جب service چل نہ رہی ہو تو docker compose run --rm استعمال کریں

exec کے لیے چلتا ہوا container ضروری ہے۔ اسے بند service کی طرف بھیجیں تو یہ انکار کر دیتا ہے:

service "web" is not running

یہ آپ کے لیے کچھ شروع نہیں کرے گا۔ docker compose run یہ کام کرے گا:

docker compose run --rm web bash

run، web service definition سے نیا container بناتا ہے۔ اس میں وہی image، environment، volumes اور networks شامل ہوتے ہیں، لیکن service کی command کو آپ کی لکھی ہوئی command سے بدل دیا جاتا ہے۔ --rm آپ کے exit کرنے پر اس container کو حذف کر دیتا ہے۔ --rm کو شامل نہ کریں تو باقی رہ جانے والے containers myproject-web-run-4f1c2b جیسے ناموں کے تحت جمع ہوتے رہیں گے۔ docker compose ps -a انہیں دکھا دے گا، لیکن کوئی اور عمل انہیں خودکار طور پر صاف نہیں کرے گا۔

run کے دو رویے لوگوں کو حیران کرتے ہیں۔ یہ service کے ports publish نہیں کرتا، جب تک آپ --service-ports شامل نہ کریں۔ یہ جان بوجھ کر ایسا کرتا ہے، کیونکہ پہلے container کے port 8080 پر قابض رہتے ہوئے دوسرا container اسی host port کو bind کرے تو bind: address already in use کے ساتھ ناکام ہو جائے گا۔ یہ آپ کے shell کے ظاہر ہونے سے پہلے service کی depends_on کے تحت درج تمام services بھی شروع کر دیتا ہے۔ اس لیے اندر فوری جائزہ لینے سے database اور cache شروع ہو سکتے ہیں۔ --no-deps اس عمل کو روکتا ہے۔

run image کے ENTRYPOINT سے گزرتا ہے، جبکہ exec ایسا نہیں کرتا۔ exec آپ کی command کو موجودہ container میں براہ راست شروع کرتا ہے، اس لیے entrypoint script اسے نہیں دیکھتی۔ run کے تحت آپ کی bash اس script کو arguments کے طور پر ملتی ہے۔ بہت سی official images اپنی entrypoint کو exec "$@" پر ختم کرتی ہیں، اس لیے command براہ راست آگے منتقل ہو جاتی ہے اور آپ کو اپنا shell مل جاتا ہے۔ جو script اپنے arguments کی خود تشریح کرتی ہے وہ ان کے ساتھ مختلف کارروائی کرے گی۔ ایسی صورت میں اسی run کے لیے entrypoint تبدیل کریں:

docker compose run --rm --entrypoint sh web

یہ سب سے عام وجہ ہے کہ exec کے تحت کام کرنے والی command، run کے تحت مختلف رویہ دکھاتی ہے۔ command اور entrypoint کے درمیان تقسیم سے واضح ہوتا ہے کہ ہر بار image configuration کے کون سے حصے کو تبدیل کیا جا رہا ہے۔

exec یا run: انتخاب کیسے کریں

  • exec کے لیے running container ضروری ہے۔ run کے لیے یہ ضروری نہیں، اور run dependencies بھی start کر سکتا ہے۔
  • exec میں live process list اور موجودہ files نظر آتی ہیں، جن میں application کے start ہونے کے بعد لکھا گیا data بھی شامل ہے۔ run کو image کی صاف copy ملتی ہے، اس لیے یہ data وہاں موجود نہیں ہوتا۔
  • exec entrypoint کو bypass کرتا ہے۔ run اسے چلاتا ہے۔
  • run ایک container برقرار رکھتا ہے، جب تک آپ --rm نہ دیں۔

یہ دیکھنے کے لیے exec استعمال کریں کہ حقیقت میں کیا ہو رہا ہے۔ اسی environment کی عارضی copy، one-off migration command، یا ایسی صورت میں جب اصل service اتنی دیر running نہ رہے کہ اس میں exec کیا جا سکے، run --rm استعمال کریں۔

کارآمد exec flags: user، working directory اور replicas

زیادہ تر images غیر root user پر منتقل ہو جاتی ہیں، اس لیے آپ کے exec shell میں diagnostic tool install کرنے کا عمل یہاں رک جاتا ہے:

E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)

-u root اسی container میں root shell فراہم کرتا ہے:

docker compose exec -u root web sh

-w /srv/app صرف اس command کے لیے working directory مقرر کرتا ہے۔ -e KEY=value آپ کے session میں environment variable شامل کرتا ہے، service میں نہیں۔ جب کوئی service ایک سے زیادہ replica چلاتی ہے تو --index 2 طے کرتا ہے کہ آپ کس container میں پہنچیں گے۔ اگر آپ mounted directory میں file ownership کی وجہ تلاش کر رہے ہیں تو container images میں PUID اور PGID میں بتایا گیا ہے کہ numeric ids، user names نہیں، یہ طے کرتے ہیں کہ وہاں کون write کر سکتا ہے۔

ڈیٹابیس container کے اندر psql یا mysql shell حاصل کریں

Client پہلے ہی database image کے اندر موجود ہوتا ہے، اس لیے host پر client رکھنے اور port publish کرنے کی ضرورت نہیں:

docker compose exec db psql -U postgres -d app
docker compose exec db mariadb -u root -p

Postgres images میں psql، MySQL images میں mysql، اور MariaDB images میں mariadb شامل ہوتے ہیں۔ Connection container کے اندر سے بنتا ہے، اس لیے یہ اس وقت بھی کام کرتا ہے جب compose file میں database کا کوئی port publish نہ کیا گیا ہو۔ یہی زیادہ محفوظ انتظام ہے: جس port کو آپ نے publish ہی نہیں کیا، internet پر کوئی چیز اس تک رسائی حاصل نہیں کر سکتی۔

ایک غلطی لوگوں کا پورا دوپہر کا وقت ضائع کر دیتی ہے۔ Docker کو command نظر آنے سے پہلے آپ کا shell host پر variables expand کر دیتا ہے، اس لیے جب variable صرف container کے اندر موجود ہو تو -U "$POSTGRES_USER" خالی string بھیجتا ہے۔ Single quotes اور container کے اندر موجود shell اسے درست جگہ پر expand کرتے ہیں:

docker compose exec db sh -c 'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB"'

یہاں بغیر command کے docker compose run --rm db استعمال نہ کریں۔ اس سے اسی data volume کے خلاف دوسرا Postgres server شروع ہوتا ہے، اور وہ start ہونے سے انکار کر دیتا ہے:

FATAL:  lock file "postmaster.pid" already exists

Lock file اپنا کام کر رہی ہے، کیونکہ ایک ہی data directory میں دو servers کے لکھنے سے data خراب ہو سکتا ہے۔ Database چل رہی ہو تو running container میں exec کریں۔ Database کو Compose میں شامل کرنا چاہیے یا نہیں، یہ الگ فیصلہ ہے، اور database کو Docker یا host پر چلانا اس کے فوائد اور نقصانات بیان کرتا ہے۔

آغاز کے وقت console درکار services: stdin_open اور tty

exec اور run ان shells کے لیے ہیں جنہیں آپ دستی طور پر کھولتے ہیں۔ جس service کا main process فطری طور پر interactive ہو، اس کے لیے compose file میں دو keys درکار ہوتی ہیں:

services:
  console:
    image: python:3.12-slim
    command: python
    stdin_open: true
    tty: true

stdin_open: true، docker run -i ہے اور tty: true، docker run -t ہے۔ ان کے بغیر container شروع ہو کر code 0 کے ساتھ فوراً بند ہو جاتا ہے، اور docker compose ps -a، Exited (0) دکھاتا ہے۔ کوئی خرابی نہیں ہوئی۔ python، stdin پر terminal موجود نہ ہونے کی صورت میں فوری طور پر end of file پڑھتا ہے اور معمول کے مطابق بند ہو جاتا ہے۔ ایسے program کے لیے یہی درست رویہ ہے جس میں کوئی شخص input نہیں دے رہا۔

دونوں keys set ہونے کے بعد running process سے attach کریں:

docker attach $(docker compose ps -q console)

Ctrl-P کے بعد Ctrl-Q دبا کر detach کریں۔ اس سے process چلتا رہتا ہے۔ یہ sequence صرف اس وقت کام کرتی ہے جب container میں TTY اور open stdin دونوں موجود ہوں۔ اس کے بجائے Ctrl-C، PID 1 کو interrupt بھیجتا ہے اور service روک دیتا ہے۔

عام services کے لیے دونوں keys off رکھیں۔ web server stdin سے کبھی input نہیں پڑھتا، اور tty: true بہت سے programs کو colour output اور line buffering استعمال کرنے پر مجبور کرتا ہے، کیونکہ انہیں لگتا ہے کہ کوئی شخص output دیکھ رہا ہے۔ اس سے docker compose logs میں escape codes بھر جاتے ہیں۔

کرون اور CI میں scripted exec کیوں ناکام ہوتا ہے: -T flag

ایسی exec command جو آپ کے terminal میں چلتی ہے، cron job یا continuous integration (CI) runner کے اندر ناکام ہو جاتی ہے:

the input device is not a TTY

Compose بطور default pseudo terminal طلب کرتا ہے، لیکن cron job کو کوئی terminal فراہم نہیں کرتا۔ اس لیے آپ کی command چلنے سے پہلے ہی request ناکام ہو جاتی ہے۔ -T اس request کو بند کر دیتا ہے:

0 3 * * * docker compose -f /srv/app/compose.yaml exec -T db pg_dump -U postgres -Fc app > /srv/backups/app.dump

-T ایک دوسری وجہ سے بھی اہم ہے۔ TTY باہر جاتے ہوئے byte stream کو تبدیل کرتا ہے، اس لیے اس میں سے گزرنے والا compressed dump خراب حالت میں پہنچتا ہے۔ Redirect یا pipe کیا گیا ہر output -T استعمال کرتا ہے۔

cron سے متعلق دو مزید نکات ہیں۔ -f کو absolute path کے ساتھ دیں، کیونکہ cron job کو home directory سے چلاتا ہے جہاں compose file موجود نہیں ہوتی، اور Compose پھر no configuration file provided: not found کے ساتھ رک جاتا ہے۔ اس کے علاوہ، exec اس command کا exit code واپس کرتا ہے جسے اس نے چلایا ہو۔ اس لیے ناکام pg_dump، set -e کے تحت آپ کی script کو ناکام کر دیتا ہے، بجائے اس کے کہ خالی backup لکھ کر کامیابی ظاہر کرے۔ روزمرہ کی باقی commands Compose command cheat sheet میں جمع کی گئی ہیں؛ اسے ان scripts کے ساتھ رکھنا مفید ہے۔

کنٹینر کے اندر کی گئی تبدیلیاں کیوں ختم ہو جاتی ہیں

آپ exec کے ذریعے کوئی tool install کرتے ہیں، configuration file میں ترمیم کرتے ہیں، مسئلہ حل کرتے ہیں، اور ایک ہفتے بعد وہ fix ختم ہو جاتی ہے۔ یہ کنٹینر کی writable layer کا متوقع طرزِ عمل ہے۔ docker compose up -d image tag یا service definition میں کسی بھی تبدیلی کے بعد پرانے کنٹینر کو ختم کرتا ہے اور image سے نیا کنٹینر بناتا ہے، اس لیے ہر دستی ترمیم پرانے کنٹینر کے ساتھ ختم ہو جاتی ہے۔

docker compose restart مختلف طرز پر کام کرتا ہے۔ یہ اسی کنٹینر کو stop اور start کرتا ہے، اس لیے دستی ترامیم برقرار رہتی ہیں۔ اسی وجہ سے کوئی manual fix کئی ہفتوں تک برقرار دکھائی دے سکتی ہے، لیکن کسی غیر متعلقہ update کے دوران ختم ہو جاتی ہے۔ Named volumes اور bind mounts دونوں operations کے بعد برقرار رہتے ہیں، کیونکہ ان کا data کنٹینر کے باہر موجود ہوتا ہے۔ ڈیٹا برقرار رکھنے کے لیے کس کا انتخاب کرنا چاہیے، اس کی وضاحت bind mounts اور named volumes میں موجود ہے۔

اس لیے exec shell کو پڑھنے اور test کرنے کی جگہ سمجھیں۔ Fix معلوم ہونے کے بعد اسے ایسی جگہ لکھیں جہاں وہ برقرار رہے: package کو Dockerfile میں اور setting کو compose file میں شامل کریں۔ پھر docker compose up -d اسے apply کرنے کے لیے، اور ایک اور exec کے ذریعے تصدیق کریں کہ نئے کنٹینر میں واقعی یہ تبدیلی موجود ہے۔

FAQ

docker compose exec اور docker compose run میں کیا فرق ہے؟

exec پہلے سے چلنے والے container کے اندر، main process کے ساتھ، command چلاتا ہے اور image کے entrypoint کو نظرانداز کرتا ہے۔ run اسی service definition سے، اسی image، environment، volumes اور networks کے ساتھ نیا container بناتا ہے، آپ کی command کو entrypoint کے ذریعے چلاتا ہے، اور پہلے کسی بھی depends_on services کو شروع کرتا ہے۔ run service کے ports کو بھی unpublished رکھتا ہے، جب تک آپ --service-ports شامل نہ کریں۔ فعال service کا جائزہ لینے کے لیے exec استعمال کریں۔ جب service بند ہو یا آپ اسے متاثر نہ کرنا چاہتے ہوں تو run --rm استعمال کریں۔

docker compose exec یہ اطلاع کیوں دیتا ہے کہ service نہیں چل رہی؟

exec موجودہ container سے attach ہوتا ہے اور نیا container نہیں بنا سکتا، اس لیے بند یا crash ہونے والی service service "web" is not running دیتی ہے۔ docker compose ps -a چیک کریں، جو exited containers کو Exited (1) جیسی status کے ساتھ دکھاتا ہے، اور docker compose logs web پڑھیں تاکہ معلوم ہو کہ وہ کیوں بند ہوئی۔ اگر پھر بھی shell کھولنی ہو تو docker compose run --rm --entrypoint sh web چلائیں۔ یہ اسی service definition سے نیا container بناتا ہے اور خراب start command کو چلنے نہیں دیتا۔

جب image میں bash نہ ہو تو shell کیسے کھولوں؟

docker compose exec web bash کا exec: "bash": executable file not found in $PATH کے ساتھ fail ہونا اس بات کی علامت ہے کہ image میں bash موجود نہیں، جو Alpine پر مبنی images کے لیے معمول کی بات ہے۔ docker compose exec web sh استعمال کریں، کیونکہ BusyBox /bin/sh فراہم کرتا ہے۔ Distroless اور scratch images میں کوئی shell موجود نہیں ہوتا، اس لیے کوئی بھی exec command کام نہیں کرے گی۔ اگر publisher فراہم کرتا ہو تو image کا :debug tag استعمال کریں، یا target کے namespaces میں debug container شروع کرنے کے لیے docker run --rm -it --network "container:$CID" --pid "container:$CID" nicolaka/netshoot چلائیں، جہاں $CID، docker compose ps -q web سے آتا ہے۔

cron میں میری exec command "the input device is not a TTY" کے ساتھ کیوں fail ہوتی ہے؟

docker compose exec default طور پر pseudo terminal طلب کرتا ہے، جبکہ cron کوئی pseudo terminal فراہم نہیں کرتا، اس لیے آپ کی command چلنے سے پہلے درخواست fail ہو جاتی ہے۔ اسے بند کرنے کے لیے -T شامل کریں: docker compose exec -T db pg_dump -U postgres app۔ redirected یا piped output کے لیے بھی -T استعمال کریں، کیونکہ TTY byte stream کو تبدیل کر کے binary dump کو خراب کر سکتا ہے۔ cron میں compose file کا absolute path دینے کے لیے -f بھی پاس کریں، ورنہ Compose no configuration file provided: not found کے ساتھ exit ہو جاتا ہے۔

کیا exec کے ذریعے container کے اندر کی گئی تبدیلیاں restart کے بعد برقرار رہتی ہیں؟

یہ docker compose restart کے بعد برقرار رہتی ہیں، کیونکہ اسی container کو دوبارہ استعمال کیا جاتا ہے۔ کسی image یا configuration کی تبدیلی کے بعد docker compose up -d پر یہ تبدیلیاں ختم ہو جاتی ہیں، کیونکہ container کو image سے دوبارہ بنایا جاتا ہے اور اس کی writable layer ضائع ہو جاتی ہے۔ named volumes یا bind mounts میں لکھی گئی data دونوں صورتوں میں برقرار رہتی ہے، کیونکہ وہ container کے باہر موجود ہوتی ہے۔ exec کے ذریعے diagnostic تبدیلیاں کریں، پھر مستقل version کو Dockerfile یا compose file میں شامل کریں۔