اجرا، لاگ، exec و پورت
Running containers, logs, exec, and ports
حالا Docker سالم است و وقت کار با container واقعی رسیده. این فصل میخواهد کاری کند که وقتی یک container ناپدید شد، فوراً بسته شد یا با وجود Running بودن از مرورگر باز نشد، بهجای حدسزدن بتوانی قدمبهقدم بفهمی چه اتفاقی افتاده است.
In Chapter 2 we proved the Engine was healthy. Now the question changes: once a container exists, how do we reason about its state, main process, logs, exit behavior, and a service that can be Running yet still unreachable from the browser?
hello-world کجا رفت؟Run hello-world slowly this time
در فصل قبل hello-world را اجرا کردیم و پیامش را دیدیم. حالا یک سؤال ساده: بعد از چاپ آن پیام، container هنوز زنده است یا تمام شده؟ و اگر تمام شده، آیا پاک هم شده؟ بیایید بهجای جواب حفظی، خودمان ببینیم.
In the previous chapter, docker run hello-world was a health check. Now we use the same command as a lifecycle experiment. Predict first: after the message prints, is the container still Running? If not, has it disappeared completely?
docker run hello-world docker ps docker ps -a --filter ancestor=hello-world
Hello from Docker! ... # docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES # docker ps -a CONTAINER ID IMAGE STATUS NAMES a1b2c3d4e5f6 hello-world Exited (0) 4 seconds ago lucid_morse
اگر docker ps چیزی نشان نداد، عجله نکن و نگو «پس هیچ containerی ساخته نشد». این دستور فقط containerهای در حال اجرا را نشان میدهد. با docker ps -a میبینی همان container هنوز وجود دارد، فقط پردازشش تمام شده و وضعیتش Exited است.
docker ps lists only Running containers, so an empty table does not mean “no container was created.” docker ps -a reveals the container identity and history: it was created, its process ran, exited with code zero, and the state became Exited.
image الگو/خروجی ساختهشده است؛ container یک instance با شناسه و وضعیت خودش. میتوانی از یک image ده container بسازی و هرکدام چرخهٔ عمر مستقلی داشته باشند.
An image is the artifact/template; a container is an instance with its own identity and state. You can create ten containers from one image and each has an independent lifecycle.
docker run پشت صحنه چه کارهایی میکند؟docker run looks like one action, but several stages happen underneath
وقتی docker run IMAGE میزنی، از بیرون فقط یک دستور میبینی. اما Docker چند کار را پشت سر هم انجام میدهد: image را پیدا میکند، اگر لازم باشد میگیرد، یک container از روی آن میسازد و بعد پردازش اصلی را راه میاندازد. فهم همین چند مرحله بعداً خیلی از خطاها را قابلفهم میکند.
docker run IMAGE is one command from the user’s perspective. Conceptually, the Engine resolves the image, pulls if policy requires and the image is absent, creates container identity/configuration, starts the main process, and in foreground mode attaches its streams to the CLI.
این نمودار وابستگی/flow مفهومی را نشان میدهد؛ دستور واقعی Engine جزئیات پیادهسازی بیشتری دارد. نکتهٔ آموزشی این است که «اجرا» هم ساخت دارد هم راهاندازی.
This is a conceptual flow rather than an implementation trace. The teaching point is that “run” includes both create and start.
برای اینکه این دو مرحله را ببینی، یک بار میانبُر را کنار بگذار:
To make those stages visible, temporarily avoid the shortcut:
docker create --name created-demo hello-world docker ps -a --filter name=created-demo docker start -a created-demo docker ps -a --filter name=created-demo docker rm created-demo
بعد از create شناسه و تنظیمات وجود دارد ولی پردازش هنوز شروع نشده و وضعیت معمولاً Created است. start -a همان container را اجرا میکند؛ container دوم نمیسازد. -a خروجی را اتصال میکند تا hello-world را ببینی.
After create, identity and configuration exist but the process has not started, so state is normally Created. start -a runs that same container rather than creating another one. -a attaches output so you can observe hello-world.
توقف container با حذفشدنش یکی نیستRead lifecycle from state, not guesswork
یک container را مثل برنامهای تصور کن که پروندهای برای خودش دارد. ممکن است برنامه تمام شود، اما پروندهٔ container هنوز باقی بماند؛ برای همین بعد از توقف هم میتوانی وضعیت، کد خروج و لاگهایش را ببینی.
توقف به معنی remove نیست. container میتواند Exited باشد و شناسه، تنظیمات و لاگها آن هنوز وجود داشته باشند تا وقتی که حذفش کنی.
Stopping is not removal. A container can be Exited while its identity, configuration, and logs remain until you remove it.
docker run -d --name web nginx:alpine
docker ps --filter name=web
docker stop web
docker ps
docker ps -a --filter name=web
docker start web
docker restart web
docker inspect web --format \
'status={{.State.Status}} running={{.State.Running}} exit={{.State.ExitCode}}'
هر دستور یک وضعیت transition مشخص دارد. stop پردازش اصلی را متوقف میکند ولی مورد را نگه میدارد. start همان شناسه را دوباره راه میاندازد. restart convenience operation است؛ container جدیدی نمیسازد.
Each command has a specific state effect. stop stops the main process while keeping the object. start runs the same identity again. restart is a convenience operation; it does not create a new container.
چرا یک بار ترمینال گیر میکند و یک بار آزاد میماند؟Foreground and detached: the difference is CLI attachment, not whether the process is real
برای دیدن تفاوت، یک سرویس واقعیتر لازم داریم. Nginx را اول بدون -d اجرا میکنیم. اینبار ترمینال آزاد نمیشود، چون CLI هنوز به اجرای container وصل است. بعد همان مثال را جداشده اجرا میکنیم تا فرقشان را با چشم ببینیم.
We need a long-running service, so use the official nginx:alpine image. First run it without -d:
docker run --name foreground-web nginx:alpine # observe startup output # press Ctrl+C docker ps docker ps -a --filter name=foreground-web
وقتی در حالت متصل Ctrl+C میزنی، وقفه به پردازش اصلی میرسد و در این مثال Nginx متوقف میشود. مهم است این را با docker stop یکی نگیری؛ چیزی که میبینی نتیجهٔ نحوهٔ اتصال ترمینال و مدیریت سیگنال توسط همان پردازش است.
In attached mode, the CLI is connected to the container process streams, so the terminal remains occupied. When you press Ctrl+C, an interrupt/signal travels through the attachment path to the main process, and in this scenario Nginx stops. Do not simplify this into “Docker always ran stop”; exact behavior depends on attachment and the process’s signal handling.
docker run -d --name web nginx:alpine docker ps --filter name=web
-d پردازش را «background Linux پردازش معمولی روی شل تو» نمیکند؛ container را راهاندازی میکند و CLI بعد از گرفتن شناسه برمیگردد، درحالیکه Engine چرخهٔ عمر پردازش را ادامه میدهد.
-d does not turn the workload into an ordinary background process of your shell. It starts the container and returns control to the CLI while the Engine continues managing the process lifecycle.
name collision یک مشکل برنامه نیست
A name collision is not an application failure
docker run -d --name web nginx:alpine docker: Error response from daemon: Conflict. The container name "/web" is already in use. docker ps -a --filter name=web
Engine حتی به مرحلهٔ راهاندازی Nginx جدید نرسیده؛ شناسه موردنظر از قبل اشغال است. بنابراین debug کردن image یا پورت اشتباه است. مورد موجود را پیدا کن و تصمیم بگیر استفادهٔ دوباره، rename یا remove شود.
The Engine never reached the point of starting a new Nginx process; the requested identity is already taken. Debugging the image or port is therefore the wrong direction. Find the existing object and decide whether to reuse, rename, or remove it.
اگر برنامه چیزی برای گفتن داشته باشد، کجا دنبالش بگردیم؟Logs: what did the process write to stdout and stderr?
فرض کن container بالا آمده ولی نمیدانی داخلش چه میگذرد. اولین جای خوب برای سرککشیدن معمولاً لاگهاست. برنامههای containerized معمولاً پیامهایشان را روی stdout و stderr مینویسند و Docker، بسته به تنظیم ثبت لاگ، میتواند آنها را با docker logs در اختیارت بگذارد.
Many containerized applications write normal logs to stdout and errors to stderr. Docker captures those streams through the container’s logging configuration, and with drivers that support reading, docker logs returns them. It is not shell history; it is process/application output.
docker logs web docker logs --tail 20 web docker logs -f web
docker logs -f یعنی «از این لحظه به بعد هم پیامهای جدید را نشانم بده». اگر روی همین دستور Ctrl+C بزنی، فقط دنبالکردن لاگ را قطع میکنی. container باید همچنان در حال اجرا باشد؛ با docker ps خودت بررسیاش کن.
docker logs -f is not just a snapshot; it follows new log output. Pressing Ctrl+C on the logs command stops the following client, not the container. Verify that claim with docker ps afterward.
برای این فصل فرض میکنیم ثبت لاگ تنظیمات از docker logs پشتیبانی میکند. اگر container با driverای اجرا شود که خواندن را پشتیبانی نمیکند—مثلاً ثبت لاگ را عمداً غیرفعال کرده باشی—رفتار متفاوت است. جزئیات درایورها را فعلاً وارد scope نمیکنیم.
This chapter assumes the container’s logging configuration supports docker logs. A driver that does not support reading—or logging disabled entirely—changes the behavior. Logging-driver configuration is intentionally outside this chapter’s scope.
یک درخواست بفرست تا لاگ معنی پیدا کندGenerate a real request so logs are not just decorative text
دیدن چند خط لاگ آماده خیلی آموزشی نیست. بهتر است خودمان یک درخواست بسازیم و بعد همان درخواست را در لاگ پیدا کنیم. برای این کار Nginx را روی یک پورت از سیستم خودمان در دسترس قرار میدهیم و با curl به آن درخواست میزنیم.
To generate a request log, run Nginx with a published port. We use the syntax now, then unpack its meaning carefully in the ports section.
docker rm -f web 2>/dev/null || true docker run -d --name web -p 8080:80 nginx:alpine curl -I http://localhost:8080 docker logs --tail 5 web
127.0.0.1 - - [23/Sep/2026:10:15:22 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/8.x" "-"
حالا لاگ یک مشاهده قابلاتصال به رفتار واقعی است: متد، مسیر و وضعیت کد همان درخواست را نشان میدهد. اگر مرورگر میگوید خطا ولی هیچ درخواست لاگ جدیدی نمیآید، ممکن است مشکل قبل از رسیدن درخواست به برنامه باشد—مثلاً پورت نگاشت.
Now the log is tied to a real observation: method, path, and status code correspond to the request you sent. If the browser fails and no new request log appears, the problem may be before the application receives the request—for example at port publishing.
docker exec یعنی یک پردازش تازه در همان containerdocker exec: you are not entering a “new container”; you are starting another process
وقتی میگویی «وارد container شدم»، از نظر فنی اتفاق دقیقتری افتاده: docker exec یک دستور تازه را داخل همان container در حال اجرا راه میاندازد. container دوم ساخته نمیشود و image هم تغییر نمیکند. فقط یک پردازش دیگر کنار پردازش اصلی اجرا میشود.
docker exec runs another command inside an existing Running container. It creates neither a new image nor a second container. The exec process exists only while the container’s primary process is running.
docker exec web nginx -v docker exec web pwd docker exec -it web sh
| Part | Meaning | Common misunderstanding |
|---|---|---|
exec | دستور تازه در container Runningnew command in a Running container | container جدید نمیسازدdoes not create a new container |
-i | STDIN را باز نگه میداردkeeps STDIN open | بهتنهایی ترمینال کامل نمیسازدdoes not alone allocate a terminal |
-t | pseudo-TTY میسازدallocates a pseudo-TTY | برای دستور غیرinteractive همیشه لازم نیستnot required for every noninteractive command |
sh | یک شل فایل اجرایی معمولاً موجود در imageهای کوچکa shell commonly present in small images | bash تضمینشده نیستbash is not guaranteed |
docker stop web docker exec web sh Error response from daemon: container ... is not running
این خطا خودش مدل را تأیید میکند: exec «محیط ذخیرهشدهای که هر وقت خواستیم باز میکنیم» نیست؛ دستور جدیدی است که برای اجرا به container Running نیاز دارد.
This error reinforces the model: exec is not “opening a saved environment at any time”; it starts a new command and therefore requires the container to be Running.
چرا زندهبودن پردازش اصلی، عمر container را تعیین میکند؟Why does the main process matter so much? Container lifecycle is tied to it
hello-world سریع تمام شد ولی Nginx میتواند ساعتها Running بماند. تفاوت اصلی در این است که پردازش اصلی آنها چه میکند. تا وقتی پردازش اصلی زنده است، container هم Running میماند؛ وقتی آن پردازش تمام شود، container از حالت Running خارج میشود.
You do not need every namespace or init-system detail yet. One practical rule is enough: a container’s lifecycle revolves around its primary process. When that process exits, the container leaves Running state.
docker start web docker top web
UID PID PPID C STIME TTY TIME CMD root 18432 18410 0 10:24 ? 00:00:00 nginx: master process nginx -g daemon off; 101 18471 18432 0 10:24 ? 00:00:00 nginx: worker process
docker top processهای container را از دید Docker/سیستم میزبان ابزارها نشان میدهد؛ PIDهایی که میبینی الزاماً همان شمارههایی نیستند که پردازش داخل فضای نام PID خودش میبیند. نکتهٔ این فصل شماره PID نیست؛ رابطهٔ پردازش اصلی و Running وضعیت است.
docker top shows container processes through Docker/host-side tooling; the host PIDs shown are not necessarily the same numbers a process sees inside its own PID namespace. The lesson here is not PID numbering; it is the relationship between the primary process and Running state.
docker run --name one-shot hello-world docker ps -a --filter name=one-shot docker run -d --name long-lived nginx:alpine docker ps --filter name=long-lived
hello-world یک کار کوتاه دارد؛ پردازش تمام میشود و container Exited میشود. Nginx master پردازش زنده میماند، پس container Running میماند. Docker «خودش تصمیم نگرفته» یکی را ببندد و دیگری را باز نگه دارد؛ چرخهٔ عمر پردازش تفاوت را ساخته است.
hello-world performs a short task; the process ends and the container becomes Exited. Nginx keeps its master process alive, so the container remains Running. Docker did not arbitrarily choose one to keep alive; process lifecycle created the difference.
کد خروج یک سرنخ است، نه جواب نهاییAn exit code is evidence, not a complete diagnosis
اگر container بسته شد، کد خروج یکی از اولین چیزهایی است که نگاه میکنیم. صفر معمولاً یعنی خود پردازش کارش را موفق تمام کرده و عدد غیرصفر یعنی مشکلی گزارش شده. اما خود عدد علت را توضیح نمیدهد؛ برای فهمیدن «چرا» باید لاگ و دستور اجرا را هم ببینیم.
Exit code zero usually means the process reported successful completion by convention; non-zero indicates some failure or abnormal result. The number alone rarely explains why. Read it alongside the command, logs, and execution context.
docker run --name exit0 alpine sh -c 'exit 0'
docker run --name exit7 alpine sh -c 'echo broken >&2; exit 7'
docker inspect exit0 --format '{{.State.Status}} {{.State.ExitCode}}'
docker inspect exit7 --format '{{.State.Status}} {{.State.ExitCode}}'
docker logs exit7
exited 0 exited 7 broken
برای exit7، عدد 7 میگوید پردازش موفق تمام نشده، ولی متن broken context بیشتری میدهد. در برنامه واقعی هم همین مدل را حفظ کن: وضعیت → کد خروج → لاگها → دستور/تنظیمات.
For exit7, the number tells you the process did not report success, while broken gives more context. Use the same model for real apps: state → exit code → logs → command/configuration.
قبل از پاککردن container، چیزی برای بررسی باقی بگذارRemove discards the object; collect useful evidence first
docker stop web
docker logs --tail 20 web
docker inspect web --format '{{json .State}}'
docker rm web
وقتی container خراب است، اولین واکنش خیلیها این است که پاکش کنند و از نو بسازند. گاهی جواب میدهد، ولی یک مشکل دارد: همان چیزی را که میتوانستی بررسی کنی از بین میبری. بهتر است اول وضعیت، لاگ و تنظیمات لازم را برداری و بعد سراغ پاکسازی بروی.
During debugging, removing too early can destroy the very object you wanted to inspect. Capture the required state/log/configuration first, then clean up.
docker rm -f دقیقاً چه shortcutی است؟
What exactly does docker rm -f shortcut?
-f برای container Running یعنی Docker آن را بهاجبار متوقف/kill میکند و سپس مورد را remove میکند. برای آزمایشگاه پاکسازی مفید است، ولی نباید پیشفرض عیبیابی عادت باشد.
For a Running container, -f means Docker forcibly stops/kills it and then removes the object. It is useful for lab cleanup, but it should not become the default debugging reflex.
docker rm -f long-lived
اگر مشکل واقعی واقعی داری، قبل از rm -f حداقل وضعیت، لاگها و تنظیمات مهم را ذخیره کن. بعد از remove بسیاری از آن شواهد از مسیر عادی Docker CLI دیگر در دسترس نیستند.
During a real incident, capture at least state, logs, and relevant configuration before rm -f. After removal, much of that evidence is no longer available through normal Docker CLI paths.
container روشن است؛ چرا localhost هنوز جواب نمیدهد؟The container is Running; why can’t the browser reach it?
Running بودن فقط میگوید پردازش اصلی هنوز زنده است. هنوز معلوم نیست راهی از سیستم تو تا سرویس داخل container ساخته شده باشد. اینجا باید دو پورت را از هم جدا کنیم: پورتی که روی سیستم خودت باز میکنی و پورتی که برنامه داخل container روی آن گوش میدهد.
Running only tells you the primary process is alive. It proves nothing yet about the network path from host to application. For that path, separate two numbers: the host port and the container port.
در -p 8080:80 سمت چپ سیستم میزبان است و سمت راست container. درخواست به سیستم میزبان:8080 میآید و به container:80 هدایت میشود.
In -p 8080:80, the left side is the host and the right side is the container. Requests to host:8080 are routed to container:80.
docker run -d --name web -p 8080:80 nginx:alpine docker ps --filter name=web curl -I http://localhost:8080
PORTS 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp
این خروجی میگوید پورت سیستم میزبان 8080 انتشار شده و به پورت 80 داخل container map میشود. اگر دستور را اشتباه -p 80:8080 بنویسی، مفهوم را برعکس کردهای: سیستم میزبان:80 → container:8080، درحالیکه Nginx پیشفرض روی 80 گوش میدهد.
This output says host port 8080 is published and mapped to port 80 in the container. If you accidentally use -p 80:8080, you reverse the model: host:80 → container:8080, while default Nginx listens on 80.
در برنامه خودت، فقط انتشار کافی نیست اگر پردازش داخل container صرفاً روی loopback خودش گوش بدهد. serviceهایی که باید از مسیر انتشار قابلدسترسی باشند معمولاً باید روی interface مناسب مثل 0.0.0.0 داخل container گوش دهند. Nginx image این جزئیات را برای مثال ما از قبل درست دارد.
For your own application, publishing is not enough if the process listens only on its own loopback interface. Services intended to be reachable through publishing generally need to listen on an appropriate interface such as 0.0.0.0 inside the container. The Nginx image already handles this for our example.
سه خرابی شبیه هم که راهحل یکسان ندارندThree port failures that look similar but need different diagnoses
پورت سیستم میزبان از قبل اشغال استThe host port is already in use
docker run -d --name web2 -p 8080:80 nginx:alpine # representative error Bind for 0.0.0.0:8080 failed: port is already allocated
این خطا قبل از برنامه درخواست رخ میدهد. باید صاحب پورت سیستم میزبان را پیدا کنی یا پورت سیستم میزبان دیگری انتخاب کنی؛ تغییر container پورت Nginx بدون دلیل راهحل نیست.
This fails before an application request exists. Find the owner of the host port or choose another host port; changing Nginx’s container port without reason is not the fix.
نگاشت برعکس استThe mapping is reversed
-p 80:8080 یعنی سیستم میزبان:80 به container:8080؛ اگر سرویس داخل container روی 80 است، درخواست به portی میرود که چیزی روی آن گوش نمیدهد.
-p 80:8080 means host:80 to container:8080. If the service listens on container port 80, traffic is sent to the wrong internal port.
container Running است ولی برنامه هنوز آماده/listening نیستThe container is Running but the app is not ready/listening
Running فقط چرخهٔ عمر پردازش را ثابت میکند. لاگها و actual listen تنظیمات را بخوان. سلامت/readiness مفهوم جداست و در فصلهای بعد عمیقتر میشود.
Running proves process lifecycle only. Read logs and actual listen configuration. Health/readiness is a separate concept covered more deeply later.
وقتی docker ps کافی نیست، سؤال دقیقتر بپرسdocker inspect: when summary tables are not enough
docker ps برای نگاه سریع عالی است، ولی گاهی سؤال دقیقتری داری: این container دقیقاً با چه commandی ساخته شده؟ کد خروجش چند بوده؟ چه پورتی map شده؟ آنوقت docker inspect به درد میخورد. لازم نیست همهٔ JSON را بخوانی؛ فقط همان چیزی را بپرس که لازم داری.
docker ps is excellent for an overview, but inspect gives lower-level object configuration and state. You do not need to read the entire JSON every time; use --format to ask a focused question.
docker inspect web --format \
'name={{.Name}} status={{.State.Status}} exit={{.State.ExitCode}}'
docker inspect web --format \
'image={{.Config.Image}} cmd={{json .Config.Cmd}}'
docker inspect web --format \
'ports={{json .NetworkSettings.Ports}}'
قدرت inspect در این نیست که «اطلاعات زیاد» دارد؛ در این است که احتمال را با فیلد مشخص امتحان میکنی. اگر سؤال پورت است، لازم نیست environment variableهای بیربط را بخوانی.
The power of inspect is not merely “lots of information”; it lets you test a hypothesis with specific fields. If your question is about port bindings, there is little value in scanning unrelated environment variables.
وقتی container کار نمیکند، این مسیر را قدمبهقدم بروDebugging workflow: from object existence to the application itself
فرض کن فقط میدانیم «سایت باز نمیشود». اگر مستقیم port، image و Dockerfile را با هم دستکاری کنیم، هر بار چند چیز عوض میشود و فهمیدن علت سختتر میشود. بهتر است از سادهترین سؤال شروع کنیم و هر مرحله فقط یک چیز را روشن کند.
When someone says “the container does not work,” do not jump to the end of the chain. Do not memorize this as a ritual; understand the logic: each question establishes a prerequisite for the next.
اگر container وجود ندارد، لاگها آن را debug نمیکنی. اگر Exited است، قبل از پورت نگاشت علت خروج را بخوان. اگر Running است، آنوقت پردازش، پورت و برنامه پاسخ معنی پیدا میکنند.
If the container does not exist, there are no container logs to debug. If it is Exited, diagnose exit before ports. If it is Running, then process, port, and application response become relevant.
1. Object: docker ps -a --filter name=web 2. State: status=_____ exit=_____ 3. Logs: last meaningful line = _____ 4. Process/config: command=_____ ports=_____ 5. Request: curl result = _____ 6. Diagnosis: _____ 7. Fix + verification: _____
از جدول docker ps بیشتر از اسم container بخوانRead docker ps as a diagnostic table, not just a container list
وقتی تازه Docker یاد میگیری، معمولاً فقط ستون NAMES را نگاه میکنی. اما همین جدول کوچک خیلی بیشتر میگوید: container از چه imageای آمده، الآن در چه وضعیتی است، چه commandی دارد و آیا پورتی از آن روی سیستم میزبان باز شده یا نه.
When you are new to Docker, your eyes often go straight to NAME. But this small table answers several questions at once: which image created the container, what command it uses, whether it is Running or Exited, whether ports are published, and what human-friendly identity it has.
docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a1b2c3d4e5f6 nginx:alpine "/docker-entrypoint…" 2 minutes ago Up 2 minutes 0.0.0.0:8080->80/tcp web c7d8e9f0a1b2 hello-world "/hello" 8 minutes ago Exited (0) 8 minutes ago one-shot
در ردیف اول، Up 2 minutes میگوید پردازش اصلی هنوز زنده است؛ ستون PORTS یک نگاشت سیستم میزبان را نشان میدهد. در ردیف دوم، Exited (0) میگوید پردازش تمام شده و کد خروج صفر داشته. همین دو ردیف نشان میدهند «container وجود دارد» و «container Running است» دو سؤال جدا هستند.
In the first row, Up 2 minutes says the main process is still alive and PORTS shows a host mapping. In the second, Exited (0) says the process ended with exit code zero. These two rows make the distinction clear: “the container exists” and “the container is Running” are separate questions.
ستون دستور هم سرنخ است، نه همیشه کل حقیقت. برای دستور/تنظیمات دقیقتر از docker inspect استفاده کن. جدول ps برای triage سریع عالی است؛ inspect برای سؤال دقیقتر.
The COMMAND column is a clue, not always the full truth. Use docker inspect for exact command/configuration. docker ps is excellent for quick triage; inspect answers focused questions.
وقتی docker stop میزنی، Docker چطور فرصت خاموششدن میدهد؟Stop, signals, and kill: why the main process is not just theory
توقف خوب این نیست که پردازش را همان لحظه نابود کنیم. docker stop اول به پردازش اصلی فرصت میدهد خودش مرتب کارش را جمع کند. اگر در زمان تعیینشده خارج نشود، Docker سراغ توقف اجباری میرود. فعلاً همین مدل برایمان کافی است.
docker stop attempts a controlled shutdown: a stop signal reaches the primary process and Docker waits for graceful exit for a period; if the process does not exit, forced termination eventually follows. You do not need every image-specific signal detail yet, but you should distinguish “request shutdown” from “remove the object.”
docker run -d --name signal-demo nginx:alpine
docker inspect signal-demo --format \
'status={{.State.Status}} pid={{.State.Pid}}'
docker stop signal-demo
docker inspect signal-demo --format \
'status={{.State.Status}} exit={{.State.ExitCode}} finished={{.State.FinishedAt}}'
بعد از توقف، مورد هنوز برای inspect و لاگها وجود دارد. این دقیقاً دلیل خوبی است که در مشکل واقعی واقعی اول توقف/لاگ/inspect را از remove جدا نگه داری. docker kill ابزار دیگری است و برای این فصل لازم نیست آن را به پیشفرض خاموشکردن تبدیل کنی.
After stop, the object still exists for inspect and logs. That is exactly why, during a real incident, it helps to keep stop/log/inspect separate from removal. docker kill is another tool, but this chapter does not treat it as a default shutdown strategy.
یک container، چند پردازش؛ exec را با چشم ببینObserve exec in the process list: one container, multiple processes
این جمله که «exec یک پردازش جدید میسازد» ممکن است خشک به نظر برسد. پس بیایید واقعاً یک پردازش کوتاه sleep داخل همان container راه بیندازیم و همزمان با docker top ببینیم کنار پردازشهای Nginx ظاهر میشود.
The sentence “exec starts a new process inside an existing container” is easy to forget if it stays abstract. Make that process observable.
docker start web docker top web docker exec web sh -c 'sleep 30' &
docker top web # representative extra process UID PID PPID ... CMD root 18432 ... nginx: master process nginx -g daemon off; 101 18471 18432 nginx: worker process root 19102 ... sleep 30
container جدیدی ساخته نشد؛ NAME همان web است. فقط یک پردازش موقت دیگر کنار processهای Nginx دیده میشود. بعد از ۳۰ ثانیه sleep تمام میشود ولی container هنوز Running است، چون پردازش اصلی Nginx زنده است.
No new container was created; the NAME is still web. A temporary process appears beside the Nginx processes. After thirty seconds, sleep exits while the container remains Running because the Nginx primary process is still alive.
وقتی داخل container برای debug شل باز میکنی، شل بخشی از برنامه image نیست که ناگهان «فعال شده باشد»؛ یک exec پردازش اضافه کردهای. تغییرات موقتی شل هم نباید جای راهحل در Dockerfile/تنظیمات را بگیرند.
When you open a debug shell inside a container, you did not reveal a hidden permanent shell session; you started an exec process. Temporary shell changes should not replace a real fix in Dockerfile or configuration.
یک نمونهٔ واقعی: container بالا است، ولی سایت باز نمیشودCase study: “the container is Up but localhost does not respond”
این سناریو زیاد پیش میآید: docker ps میگوید container بالا است، اما localhost جواب نمیدهد. وسوسهبرانگیز است که فوری پورت را عوض کنیم. اینبار این کار را نمیکنیم؛ از اول مسیر را میخوانیم و در هر مرحله فقط یک احتمال را کنار میگذاریم.
This is one of the best scenarios for practicing diagnostic order because it is tempting to start changing ports immediately. Instead, walk the chain and let each step answer one question.
docker ps -a --filter name=web NAMES STATUS PORTS web Up 3 minutes 0.0.0.0:8080->80/tcp
مورد وجود دارد، پردازش اصلی هنوز Up است و نگاشت ظاهراً 8080→80 است. پس «container اصلاً راهاندازی نشده» و «پورت انتشار نشده» فعلاً hypothesisهای ضعیفتری هستند.
The object exists, the primary process remains Up, and the mapping appears to be 8080→80. “The container never started” and “no port was published” are now weaker hypotheses.
docker logs --tail 20 web docker top web
اگر لاگها راهاندازی عادی Nginx را نشان دهند و پردازش list master/worker را داشته باشد، برنامه چرخهٔ عمر هم معقول است. حالا درخواست واقعی میفرستیم.
If logs show normal Nginx startup and the process list contains master/worker processes, application lifecycle looks plausible. Now send a real request.
curl -v http://localhost:8080/
اگر connection refused شد ولی PORTS درست به نظر میرسد، inspect bindings و سیستم میزبان-side conflict/firewall را بررسی میکنی. اگر connection برقرار شد ولی HTTP خطا گرفتی، درخواست به برنامه رسیده و مسئله یک لایه جلوتر است. اگر درخواست لاگ جدید در Nginx ظاهر شد، میدانی مسیر شبکه تا برنامه طی شده.
If you get connection refused while PORTS looks correct, inspect bindings and possible host-side conflict/firewall behavior. If the connection succeeds but HTTP returns an application error, traffic reached a later layer. If a new Nginx request log appears, you know the network path reached the application.
نکتهٔ اصلی این case study دستور خاص نیست؛ ترتیب کمکردن ambiguity است. هر مشاهده باید تعداد hypothesisها را کمتر کند.
The main lesson is not any particular command; it is reducing ambiguity in order. Every observation should eliminate hypotheses.
بهجای غرقشدن در JSON، از inspect سؤال مشخص بپرسTurn inspect into questions instead of dumping thousands of JSON lines
خروجی کامل docker inspect میتواند خیلی طولانی باشد. اگر از قبل ندانی دنبال چه هستی، بین دهها فیلد گم میشوی. بهتر است قبل از دستور، سؤال را بنویسی: «وضعیت چیست؟ دستور چیست؟ پورتها چطور map شدهاند؟» بعد فقط همان فیلد را بگیری.
When a container misbehaves, raw docker inspect NAME can return so much data that the useful fact disappears. Write the question before the command.
| Question | Focused command | What the answer helps decide |
|---|---|---|
| الان وضعیت چیست؟What is state now? | --format '{{.State.Status}} {{.State.ExitCode}}' | Running/Exited و مسیر بعدی تشخیصRunning/Exited and next diagnostic path |
| از چه image/commandی ساخته شده؟Which image/command? | --format '{{.Config.Image}} {{json .Config.Cmd}}' | آیا خروجی ساختهشده/تنظیمات مورد انتظار اجرا شدهwhether expected artifact/configuration is running |
| پورت bindings چه هستند؟What are the port bindings? | --format '{{json .NetworkSettings.Ports}}' | سیستم میزبان↔container نگاشت واقعیactual host↔container mapping |
وقتی پاسخ سؤال اول Exited است، سؤال پورت معمولاً اولویت ندارد. وقتی Running و لاگها سالماند، پورت/تنظیمات ارزش بررسی پیدا میکند. این یعنی inspect فقط ابزار نمایش نیست؛ بخشی از ترتیب استدلال است.
If the first answer is Exited, port configuration is usually not the first priority. If the container is Running and logs look healthy, port/configuration becomes more relevant. Inspect is not just a display tool; it participates in diagnostic reasoning.
۱۸ تمرین؛ این بار از روی رفتار container جواب بده18 exercises: reason about state instead of memorizing commands
تمرینهای آخر عمداً کمی شبیه مسئلههای واقعیترند. قبل از بازکردن پاسخ، برای خودت بگو الآن چه چیزی را میدانم، چه چیزی را هنوز نمیدانم و کدام دستور میتواند سؤال بعدی را جواب بدهد.
The final third should not become shallower than the opening. The later scenarios deliberately combine multiple pieces of evidence. Before revealing an answer, ask, “what do I still not know?”
چرا بعد از docker run hello-world ممکن است docker ps خالی باشد ولی docker ps -a container را نشان دهد؟
Why can docker ps be empty after docker run hello-world while docker ps -a still shows the container?
پاسخ و دلیل · Solution and reasoning
چون hello-world پردازش کوتاهمدت دارد و بعد از چاپ پیام خروج میکند. ps فقط Runningها را میبیند؛ ps -a Exited را هم نشان میدهد. مورد هنوز وجود دارد تا remove شود.
hello-world has a short-lived process that exits after printing. ps lists only Running containers; ps -a also lists Exited ones. The object remains until removed.
docker create چه چیزی میسازد که هنوز Running نیست؟
What does docker create create even though nothing is Running yet?
پاسخ و دلیل · Solution and reasoning
container شناسه و تنظیمات را میسازد اما پردازش اصلی را راهاندازی نمیکند. با docker ps -a وضعیت Created را میبینی و بعد docker start همان مورد را اجرا میکند.
It creates container identity and configuration without starting the main process. docker ps -a can show Created state, and docker start later runs that same object.
-d چه چیزی را تغییر میدهد و چه چیزی را تغییر نمیدهد؟
What does -d change, and what does it not change?
پاسخ و دلیل · Solution and reasoning
-d اتصال رفتار CLI را عوض میکند و کنترل ترمینال را برمیگرداند؛ پردازش container همچنان توسط Engine اجرا و مدیریت میشود. حالت جداشده بودن به معنی «پردازش مجازی یا غیرفعال» نیست.
-d changes CLI attachment behavior and returns your terminal; the container process still runs under Engine management. Detached does not mean “virtual” or inactive.
چرا name collision را با logهای Nginx debug نمیکنی؟
Why would you not debug a name collision with Nginx logs?
پاسخ و دلیل · Solution and reasoning
چون خطا قبل از راهاندازی برنامه رخ داده؛ Engine نتوانسته شناسه تازه با آن name بسازد. مدرک درست docker ps -a --filter name=... است، نه برنامه لاگ.
The failure happens before the new application starts; the Engine cannot create another identity with that name. The relevant evidence is docker ps -a --filter name=..., not application logs.
بعد از docker logs -f web، Ctrl+C میزنی. چه چیزی را باید با docker ps انتظار داشته باشی؟
After docker logs -f web, you press Ctrl+C. What should docker ps show?
پاسخ و دلیل · Solution and reasoning
container باید همچنان Running باشد؛ تو دنبالکردن کلاینت را قطع کردی، نه container را. اگر Running نیست، باید دلیل جداگانهای مثل پردازش خروج وجود داشته باشد.
The container should still be Running; you stopped the log-following client, not the container. If it is no longer Running, another cause such as process exit must explain it.
docker exec -it web bash ناموفق میشود ولی container Running است. یک احتمال ساده چیست؟
docker exec -it web bash fails while the container is Running. What is one simple hypothesis?
پاسخ و دلیل · Solution and reasoning
ممکن است image اصلاً bash نداشته باشد؛ imageهای minimal معمولاً فقط sh دارند. با docker exec -it web sh یا بررسی executableهای موجود احتمال را تست کن.
The image may simply not contain bash; minimal images often provide only sh. Test with docker exec -it web sh or inspect available executables.
چرا docker exec روی container Exited کار نمیکند؟
Why does docker exec not work on an Exited container?
پاسخ و دلیل · Solution and reasoning
exec قرار است پردازش تازهای در container Running اجرا کند. وقتی primary پردازش و وضعیت زمان اجرا متوقف شده، محیط اجرایی لازم برای exec وجود ندارد. در صورت مناسب بودن میتوان همان container را راهاندازی کرد و بعد exec زد.
exec starts another process in a Running container. Once the primary process and runtime state have stopped, the environment required for exec is not active. If appropriate, start the same container first and then exec.
کد خروج صفر در hello-world چه چیزی میگوید و چه چیزی نمیگوید؟
What does exit code zero for hello-world tell you, and what does it not tell you?
پاسخ و دلیل · Solution and reasoning
میگوید پردازش اصلی طبق convention با success تمام شده. نمیگوید container باید Running بماند؛ اتفاقاً success task کوتاهمدت میتواند کاملاً طبیعی Exited باشد.
It says the main process reported successful completion by convention. It does not mean the container should remain Running; a successful short-lived task naturally ends in Exited state.
چرا کد خروج غیرصفر را بهتنهایی تشخیص کامل نمیدانیم؟
Why is a non-zero exit code not a complete diagnosis by itself?
پاسخ و دلیل · Solution and reasoning
چون عدد فقط نتیجهٔ پردازش را خلاصه میکند. برای علت باید دستور، لاگها و context اجرا را بخوانی. دو برنامهٔ متفاوت میتوانند یک کد خروج یکسان را برای علتهای متفاوت استفاده کنند.
The number only summarizes process outcome. To understand cause, inspect the command, logs, and execution context. Different programs can use the same non-zero code for different reasons.
در -p 8080:80 هر عدد متعلق به کجاست؟
In -p 8080:80, what does each number belong to?
پاسخ و دلیل · Solution and reasoning
8080 پورت سیستم میزبان است؛ 80 container پورت. درخواست به سیستم میزبان:8080 وارد میشود و Docker آن را به سرویس داخل container:80 هدایت میکند.
8080 is the host port; 80 is the container port. Requests arrive at host:8080 and Docker routes them to the service at container:80.
container Up است ولی ستون PORTS خالی است. مرورگر با localhost:8080 جواب نمیگیرد. اولین نتیجه چیست؟
The container is Up but PORTS is empty. The browser cannot reach localhost:8080. What is the first conclusion?
پاسخ و دلیل · Solution and reasoning
هنوز نگاشت سیستم میزبان نساختهای. Running بودن پردازش داخل container را ثابت میکند، نه انتشار شدن پورت. container را با نگاشت درست recreate کن.
No host mapping has been established. Running proves the internal process lifecycle, not published ports. Recreate the container with the required mapping.
-p 80:8080 گذاشتهای ولی Nginx داخل container روی 80 است. چه اشتباهی کردهای؟
You used -p 80:8080, but Nginx listens on 80 inside the container. What is wrong?
پاسخ و دلیل · Solution and reasoning
نگاشت را برعکس کردهای: سیستم میزبان:80 را به container:8080 فرستادهای، ولی سرویس روی container:80 است. اگر هدفت سیستم میزبان:8080 باشد، درستش -p 8080:80 است.
The mapping is reversed: host:80 is being sent to container:8080, while the service listens on container:80. For host:8080, use -p 8080:80.
container Exited است. teammate مستقیم پورت نگاشت را تغییر میدهد. چرا ترتیبش ضعیف است؟
The container is Exited. A teammate immediately changes port mapping. Why is that a weak diagnostic order?
پاسخ و دلیل · Solution and reasoning
چون پورت فقط وقتی معنا دارد که پردازش لازم زنده باشد. اول وضعیت، کد خروج و لاگها را بخوان؛ اگر PID 1 مرده، هدایت شبکه مشکل اصلی نیست. تغییر پورت ممکن است فقط یک متغیر اضافی وارد عیبیابی کند.
Ports matter only after the required process is alive. Inspect state, exit code, and logs first. If PID 1 died, network routing is not the primary problem. Changing ports only adds another variable.
container Running است، نگاشت درست است، اما درخواست ناموفق میشود. دو مدرک بعدی چیست؟
The container is Running and the mapping looks correct, yet the request fails. What two pieces of evidence come next?
پاسخ و دلیل · Solution and reasoning
لاگها برای اینکه برنامه واقعاً listening/سالم رفتار داشته یا خطا داده؛ و پردازش/listen تنظیمات برای اینکه سرویس روی پورت/interface مورد انتظار گوش میدهد. سپس curl و برنامه-specific بررسیها معنی پیدا میکنند.
Read logs to see whether the app actually reached listening/healthy behavior or emitted errors, and inspect process/listen configuration to confirm the expected port/interface. Then curl and app-specific checks become meaningful.
قبل از docker rm -f broken-app در مشکل واقعی واقعی چه چیزهایی را ثبت میکنی؟
Before docker rm -f broken-app during a real incident, what would you capture?
پاسخ و دلیل · Solution and reasoning
حداقل docker ps -a وضعیت، کد خروج، relevant لاگها و تنظیمات/inspect لازم مثل image، دستور و پورت bindings. چون remove مورد را از مسیر عادی inspection حذف میکند. پاکسازی بعد از مدرک انجام میشود.
At minimum capture docker ps -a state, exit code, relevant logs, and necessary inspect/configuration such as image, command, and port bindings. Removal discards the object from normal inspection paths, so evidence comes first.
docker logs خطا میدهد که ثبت لاگ درایور خواندن را پشتیبانی نمیکند. آیا این یعنی برنامه هیچ logی تولید نکرده؟
docker logs says the configured logging driver does not support reading. Does that prove the app produced no logs?
پاسخ و دلیل · Solution and reasoning
نه. این فقط میگوید مسیر docker logs برای تنظیمات فعلی قابلخواندن نیست. باید ثبت لاگ درایور/تنظیمات را بررسی کنی؛ نبودن خروجی در این دستور با نبودن برنامه لاگ یکی نیست.
No. It only says the docker logs retrieval path is unavailable for the current logging configuration. Inspect the logging driver/configuration; failure to retrieve here is not proof that the application emitted nothing.
برای یک container پنج fact جمع کن: name، image، وضعیت، پورت و پردازش. دستور مناسب برای هرکدام پیشنهاد بده.
Collect five facts for one container: name, image, state, port, and process. Suggest a command for each.
پاسخ و دلیل · Solution and reasoning
name/image/وضعیت/PORTS را سریع با docker ps -a میبینی؛ جزئیات وضعیت و image تنظیمات با docker inspect؛ processها با docker top. نکته این نیست که پنج دستور جدا حفظ کنی؛ میتوانی یک دستور چند fact بدهد، بعد برای سؤال دقیقتر inspect کنی.
docker ps -a quickly gives name/image/state/PORTS; docker inspect gives focused state and image configuration; docker top shows processes. The point is not memorizing five separate commands—one command can answer several facts, then inspect refines the question.
یک گزارش برای «localhost:8080 جواب نمیدهد» بنویس بدون اینکه از همان اول Docker را راهاندازی دوباره کنی.
Write a report for “localhost:8080 does not respond” without restarting Docker first.
پاسخ و دلیل · Solution and reasoning
نمونه: ۱) با docker ps -a وجود و وضعیت container را دیدم. ۲) اگر Exited بود خروج/لاگ را بررسی کردم؛ اگر Up بود ادامه دادم. ۳) لاگها نشان داد برنامه/Nginx روی پورت داخلی مورد انتظار listening است. ۴) PORTS/inspect را بررسی کردم که سیستم میزبان:8080 به container پورت درست map شده باشد. ۵) با curl -v درخواست را تست کردم و لاگ متناظر را نگاه کردم. ۶) فقط بر اساس همین مدرک راهحل را انتخاب کردم و همان آزمایش را تکرار کردم.
Example: 1) use docker ps -a to prove the object exists and read state. 2) If Exited, inspect exit/logs; if Up, continue. 3) Read logs to confirm the app/Nginx is listening on the expected internal port. 4) Inspect PORTS/bindings to confirm host:8080 maps to the correct container port. 5) Test with curl -v and look for the corresponding request log. 6) Choose a fix from that evidence and repeat the same verification.
آزمایشگاه: یک container را اجرا کن، خرابش کن و خودت پیدایش کنLab: run a web container, break it, and repair it without random commands
هدف فقط این نیست که آخر کار Nginx دوباره باز شود. میخواهیم مسیر فکریات معلوم باشد: چه چیزی دیدی، از آن چه نتیجهای گرفتی، بعد کدام دستور را زدی و چرا. اگر فقط با چند بار حذفکردن و ساختن دوباره به جواب برسی، تمرین اصلی را از دست دادهای.
The goal is not merely to make Nginx respond. Produce a small report that preserves state and evidence from start to finish.
- حالت متصل را مشاهده کنObserve foreground behavior
Nginx را بدون
-dاجرا کن، اتصال را حس کن، سپس وضعیت بعد از وقفه را باps -aببین.Run Nginx without
-d, observe attachment, then inspect post-interrupt state withps -a. - حالت جداشده با name مشخصRun detached with a stable name
lab-webرا با-dبساز و container شناسه/وضعیت را ثبت کن.Create
lab-webwith-dand record container ID/status. - لاگ واقعی تولید کنGenerate real logs
با پورت انتشار درخواست بزن و یک access لاگ متناظر ذخیره کن.
Publish a port, send a request, and capture the matching access log.
- exec و پردازش مدرکCollect exec and process evidence
docker execوdocker topرا استفاده کن و فرق main پردازش با exec پردازش را توضیح بده.Use
docker execanddocker top, then explain the difference between the main process and an exec process. - خرابی پورت #1Port failure #1
نگاشت را عمداً برعکس بساز و از PORTS + curl + لاگها تشخیص بده.
Deliberately reverse the mapping and diagnose it using PORTS, curl, and logs.
- خرابی پورت #2Port failure #2
پورت سیستم میزبان را با container دوم اشغال کن و خطا allocation را از برنامه خطا جدا کن.
Occupy the host port with a second container and distinguish allocation failure from application failure.
- کد خروج کنترلشدهControlled exit code
یک Alpine one-shot با خروج غیرصفر بساز و وضعیت، کد و لاگ را کنار هم ثبت کن.
Create a one-shot Alpine container with a non-zero exit and record state, code, and log together.
- پاکسازی بعد از مدرکClean up after evidence
بعد از ذخیره گزارش، containerها را پاک کن. اگر از
rm -fاستفاده کردی، توضیح بده چرا در این مرحله امن و عمدی است.After saving the report, remove the containers. If you use
rm -f, explain why it is deliberate and acceptable at this stage.
معیار موفقیت · Success criteria
اگر برای هر خطا بتوانی بگویی «این مورد وجود داشت/نداشت، وضعیت این بود، پردازش این را گفت، نگاشت این بود، و این راهحل را به همین دلیل انتخاب کردم»، آزمایشگاه موفق است. اگر فقط با راهاندازی دوباره یا recreate تصادفی به جواب رسیدی، هنوز عیبیابی نکردهای.
The lab succeeds when every failure can be explained as “the object did/did not exist, state was this, the process said this, the mapping was this, therefore I chose this fix.” If random restarts or recreations happened to work, you have not yet demonstrated debugging.
در پایان یک گزارش کوتاه و قابلفهم تحویل بدهLab handoff: produce a short incident report, not scattered screenshots
| Stage | Evidence | Sentence you should be able to write |
|---|---|---|
| Identity/state | docker ps -a | مورد وجود دارد و وضعیت فعلی آن ... استthe object exists and current state is ... |
| Process | docker top / inspect state | پردازش اصلی زنده است/نیست و exec پردازش جداستthe main process is/is not alive and exec is separate |
| Application output | docker logs | آخرین مشاهده معنادار برنامه این است ...the last meaningful application observation is ... |
| Network | PORTS + inspect bindings | پورت سیستم میزبان ... به container پورت ... map شدهhost port ... maps to container port ... |
| Client test | curl -v | درخواست تا کدام مرز پیش رفتهhow far the request progressed |
گزارش خوب لازم نیست رسمی و طولانی باشد. کافی است نفر بعدی بفهمد container در چه وضعیتی بود، لاگ چه میگفت، پورت چطور map شده بود، چه چیزی را تغییر دادی و با چه آزمایشی مطمئن شدی مشکل حل شده است.
If the report ends with “after a few restarts it worked,” diagnosis is still incomplete. If you can connect symptom, observation, hypothesis, fix, and verification, even a small failure becomes reusable experience for later chapters.
مرجع سریعQuick reference and official sources
docker run IMAGE docker run -d --name NAME IMAGE docker create --name NAME IMAGE docker start NAME docker stop NAME docker restart NAME docker rm NAME docker rm -f NAME
docker ps docker ps -a docker logs NAME docker logs -f NAME docker exec -it NAME sh docker top NAME docker inspect NAME docker run -p HOST:CONTAINER IMAGE
اگر فقط یک الگو از این فصل یادت بماند، این باشد: اول ببین container اصلاً وجود دارد یا نه؛ بعد وضعیت و لاگش را بخوان؛ اگر Running بود سراغ پردازش و پورت برو؛ و در آخر با یک درخواست واقعی خود برنامه را امتحان کن.
A container combines identity, configuration, and process lifecycle. Running proves the process is alive, not that the application is healthy or reachable. Debug from object existence and state, then logs/process/configuration/ports, and only then the real request.
docker run · docker logs · docker exec · docker top · docker inspect