کدنامهمرجع‌های مهندسی نرم‌افزار، به فارسی
Docker · فصل ۳Docker · Chapter 3

اجرا، لاگ، 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?

۸۵دقیقهٔ مطالعه و اجراminutes
۱۸تمرین با پاسخsolved exercises
۴نمودار مفهومیdiagrams
۱آزمایشگاه عملیhands-on lab

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?

first observation
docker run hello-world

docker ps
docker ps -a --filter ancestor=hello-world
representative result
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 را از همین‌جا جدا نگه دارKeep image and container separate from here on

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.

Conceptual docker run flow CLIrun request imageresolve / pull createidentity + config startmain process stateRunning / Exited

این نمودار وابستگی/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:

create vs start
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 هنوز باقی بماند؛ برای همین بعد از توقف هم می‌توانی وضعیت، کد خروج و لاگ‌هایش را ببینی.

Container lifecycle Imagecreate Createdstart Runningstop / process exit Exitedstart again or rm

توقف به معنی remove نیست. container می‌تواند Exited باشد و شناسه، تنظیمات و لاگ‌ها آن هنوز وجود داشته باشند تا وقتی که حذفش کنی.

Stopping is not removal. A container can be Exited while its identity, configuration, and logs remain until you remove it.

state transitions
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:

foreground
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.

detached
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

intentional collision
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.

read and follow logs
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.

ثبت لاگ درایور جزئیات مهمی داردLogging drivers matter

برای این فصل فرض می‌کنیم ثبت لاگ تنظیمات از 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.

request + log evidence
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
representative Nginx log
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.

exec experiments
docker exec web nginx -v
docker exec web pwd
docker exec -it web sh
PartMeaningCommon misunderstanding
execدستور تازه در container Runningnew command in a Running containercontainer جدید نمی‌سازدdoes not create a new container
-iSTDIN را باز نگه می‌داردkeeps STDIN openبه‌تنهایی ترمینال کامل نمی‌سازدdoes not alone allocate a terminal
-tpseudo-TTY می‌سازدallocates a pseudo-TTYبرای دستور غیرinteractive همیشه لازم نیستnot required for every noninteractive command
shیک شل فایل اجرایی معمولاً موجود در imageهای کوچکa shell commonly present in small imagesbash تضمین‌شده نیستbash is not guaranteed
expected failure
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.

host-side process view
docker start web
docker top web
representative docker top output
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.

compare hello-world and nginx
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.

controlled exit-code experiment
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
representative result
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

normal cleanup
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.

deliberate force cleanup
docker rm -f long-lived
اول مدرک، بعد destructionEvidence before destruction

اگر مشکل واقعی واقعی داری، قبل از 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.

Host to container port publishing hostlocalhost:8080 Docker publish rule-p 8080:80 containernginx :80

در -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.

publish + verify
docker run -d --name web -p 8080:80 nginx:alpine

docker ps --filter name=web
curl -I http://localhost:8080
representative PORTS column
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.

bind address هم می‌تواند مهم باشدThe bind address can matter too

در برنامه خودت، فقط انتشار کافی نیست اگر پردازش داخل 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
collision
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.

focused inspect queries
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 troubleshooting sequence 1. Does the container exist? · docker ps -a 2. Running or Exited? · state / exit code 3. What did the process say? · logs 4. What is configured? · inspect / process / ports 5. Can the client reach the app? · curl / browser

اگر 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.

minimal debug report
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.

read the columns
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.”

observe stop timing and state
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.

terminal A
docker start web
docker top web

docker exec web sh -c 'sleep 30' &
terminal B · while sleep is alive
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.

این مشاهده بعداً خیلی به درد می‌خوردThis observation becomes useful later

وقتی داخل 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.

Step 1 · object and state
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.

Step 2 · application evidence
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.

Step 3 · client 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.

QuestionFocused commandWhat 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?”

حل‌شدهSolved۰ / ۱۸
تمرین ۳٫1 · Exercise 3.1مقدماتی · Beginner۴ min

چرا بعد از 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.

تمرین ۳٫2 · Exercise 3.2مقدماتی · Beginner۴ min

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.

تمرین ۳٫3 · Exercise 3.3مقدماتی · Beginner۴ min

-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.

تمرین ۳٫4 · Exercise 3.4مقدماتی · Beginner۴ min

چرا 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.

تمرین ۳٫5 · Exercise 3.5تمرین · Practice۵ min

بعد از 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.

تمرین ۳٫6 · Exercise 3.6تمرین · Practice۵ min

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.

تمرین ۳٫7 · Exercise 3.7تمرین · Practice۵ min

چرا 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.

تمرین ۳٫8 · Exercise 3.8تمرین · Practice۵ min

کد خروج صفر در 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.

تمرین ۳٫9 · Exercise 3.9تمرین · Practice۵ min

چرا کد خروج غیرصفر را به‌تنهایی تشخیص کامل نمی‌دانیم؟

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.

تمرین ۳٫10 · Exercise 3.10تمرین · Practice۵ min

در -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.

تمرین ۳٫11 · Exercise 3.11تمرین · Practice۵ min

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.

تمرین ۳٫12 · Exercise 3.12تمرین · Practice۵ min

-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.

تمرین ۳٫13 · Exercise 3.13سناریو · Scenario۶ min

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.

تمرین ۳٫14 · Exercise 3.14سناریو · Scenario۶ min

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.

تمرین ۳٫15 · Exercise 3.15سناریو · Scenario۶ min

قبل از 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.

تمرین ۳٫16 · Exercise 3.16سناریو · Scenario۶ min

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.

تمرین ۳٫17 · Exercise 3.17سناریو · Scenario۷ min

برای یک 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.

تمرین ۳٫18 · Exercise 3.18سناریو · Scenario۸ min

یک گزارش برای «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

حدود ۴۰ دقیقهabout 40 minutes

هدف فقط این نیست که آخر کار Nginx دوباره باز شود. می‌خواهیم مسیر فکری‌ات معلوم باشد: چه چیزی دیدی، از آن چه نتیجه‌ای گرفتی، بعد کدام دستور را زدی و چرا. اگر فقط با چند بار حذف‌کردن و ساختن دوباره به جواب برسی، تمرین اصلی را از دست داده‌ای.

The goal is not merely to make Nginx respond. Produce a small report that preserves state and evidence from start to finish.

  1. حالت متصل را مشاهده کنObserve foreground behavior

    Nginx را بدون -d اجرا کن، اتصال را حس کن، سپس وضعیت بعد از وقفه را با ps -a ببین.

    Run Nginx without -d, observe attachment, then inspect post-interrupt state with ps -a.

  2. حالت جداشده با name مشخصRun detached with a stable name

    lab-web را با -d بساز و container شناسه/وضعیت را ثبت کن.

    Create lab-web with -d and record container ID/status.

  3. لاگ واقعی تولید کنGenerate real logs

    با پورت انتشار درخواست بزن و یک access لاگ متناظر ذخیره کن.

    Publish a port, send a request, and capture the matching access log.

  4. exec و پردازش مدرکCollect exec and process evidence

    docker exec و docker top را استفاده کن و فرق main پردازش با exec پردازش را توضیح بده.

    Use docker exec and docker top, then explain the difference between the main process and an exec process.

  5. خرابی پورت #1Port failure #1

    نگاشت را عمداً برعکس بساز و از PORTS + curl + لاگ‌ها تشخیص بده.

    Deliberately reverse the mapping and diagnose it using PORTS, curl, and logs.

  6. خرابی پورت #2Port failure #2

    پورت سیستم میزبان را با container دوم اشغال کن و خطا allocation را از برنامه خطا جدا کن.

    Occupy the host port with a second container and distinguish allocation failure from application failure.

  7. کد خروج کنترل‌شدهControlled exit code

    یک Alpine one-shot با خروج غیرصفر بساز و وضعیت، کد و لاگ را کنار هم ثبت کن.

    Create a one-shot Alpine container with a non-zero exit and record state, code, and log together.

  8. پاک‌سازی بعد از مدرک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

StageEvidenceSentence you should be able to write
Identity/statedocker ps -aمورد وجود دارد و وضعیت فعلی آن ... استthe object exists and current state is ...
Processdocker top / inspect stateپردازش اصلی زنده است/نیست و exec پردازش جداستthe main process is/is not alive and exec is separate
Application outputdocker logsآخرین مشاهده معنادار برنامه این است ...the last meaningful application observation is ...
NetworkPORTS + inspect bindingsپورت سیستم میزبان ... به container پورت ... map شدهhost port ... maps to container port ...
Client testcurl -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
مدل ماندگار فصلDurable model

اگر فقط یک الگو از این فصل یادت بماند، این باشد: اول ببین 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.

منابع رسمی بررسی‌شدهOfficial references checked

docker run · docker logs · docker exec · docker top · docker inspect