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

عیب‌یابی: exit code، OOM، پر شدن دیسک

Troubleshooting: exit codes, OOM, and disk exhaustion

دیروز کانتینر سالم بود. امروز در docker ps نیست و یکی می‌گوید «Docker خراب شده». قبل از قبول این تشخیص، وضعیت واقعی، کد خروج، log و منابع را کنار هم می‌خوانیم؛ بعد فقط علتِ ثابت‌شده را اصلاح می‌کنیم.

Yesterday the container was healthy. Today it is missing from docker ps, and someone says, “Docker crashed.” Before accepting that diagnosis, we read state, exit information, logs, and resource evidence together—then repair only what the evidence supports.

۱۶۰دقیقهٔ مطالعه و آزمایشminutes
۱۸تمرین با پاسخsolved exercises
۴نمودار تشخیصیdiagnostic diagrams
۳خرابی کنترل‌شده در آزمایشگاهcontrolled incidents

«Docker کرش کرده» هنوز فقط یک حدس است“Docker crashed” is only a guess so far

دیروز سرویس جواب می‌داد. امروز docker ps هیچ سطری برایش نشان نمی‌دهد و اولین واکنش همکار این است: «Docker کرش کرده.» فعلاً نه. تنها چیزی که می‌دانیم این است که container در فهرستِ درحال‌اجرا نیست. قبل از هر restart یا پاک‌سازی باید بفهمیم واقعاً چه اتفاقی افتاده است.

Yesterday the web service answered requests. Today your teammate runs the familiar command and sees no row for that container:

first observation · running containers only
docker ps

CONTAINER ID   IMAGE   COMMAND   CREATED   STATUS   PORTS   NAMES

اول docker ps -a را می‌زنیم و می‌بینیم container هنوز وجود دارد و با Exited (137) تمام شده. این عدد یک سرنخ تازه به ما داده، اما هنوز علت نیست. همین تفاوت قرار است ستون فقرات این فصل باشد: نشانه را از تشخیص جدا نگه دار.

docker ps lists running containers. An empty result says only that nothing is currently listed as running. To see stopped containers, widen the query:

include stopped containers
docker ps -a

CONTAINER ID   IMAGE          COMMAND          STATUS                      NAMES
8c31...        codenames-web  "python app.py"  Exited (137) 2 hours ago     web

حالا یک واقعیت داریم: کانتینر از اجرا خارج شده و وضعیت ثبت‌شده‌اش Exited (137) است. اما آیا ۱۳۷ ثابت می‌کند حافظه تمام شده؟ نه. آیا ثابت می‌کند Docker daemon کرش کرده؟ باز هم نه. این عدد سرنخ است، نه گزارش کامل حادثه.

Now we have a fact: the container exited, and Docker recorded Exited (137). Does 137 prove memory exhaustion? No. Does it prove the Docker daemon crashed? Also no. It is a clue, not a complete incident report.

قبل از تعمیر، مدرک را نگه دارPreserve evidence before repair

تا وقتی وضعیت، زمان پایان و log را ثبت نکرده‌ای، کانتینر را حذف یا دوباره راه‌اندازی نکن. پاک‌کردن زودهنگام ممکن است همان مدرکی را از بین ببرد که علت را از حدس جدا می‌کند.

Until you record state, finish time, and logs, do not remove or restart the container. Premature cleanup can erase the evidence that separates a diagnosis from a guess.

از نشانه تا علت، یک زنجیرهٔ کوتاه بسازFrom symptom to verification: one chain, not scattered guesses

در فصل ۳ با وضعیت، کد خروج و لاگ آشنا شدی؛ فصل ۱۰ هم فرق Running و healthy را جدا کرد. حالا همان ابزارها را مثل قطعات یک گزارش حادثه کنار هم می‌گذاریم: اول نشانه، بعد وضعیت، بعد لاگ و منابع، بعد علت محتمل، و در آخر همان آزمونی که ثابت کند اصلاح جواب داده است.

Chapter 3 introduced ps, logs, and the container lifecycle; Chapter 10 separated process liveness from application health. Now we order that evidence like an incident report. Each step chooses the next question; none alone authorizes cleanup or a larger resource limit.

Troubleshooting flows from symptom to state, evidence, cause, repair, and verification. 1 · Symptomwhat we saw 2 · Stateps · inspect 3 · Evidencelogs · resources 4 · Causebounded diagnosis 5 · Repairverify same path Each arrow means “use the previous evidence to choose the next check” — not “guess and restart”.
مسیر خواندن مدرک؛ پیکان‌ها ترتیب پرس‌وجو را نشان می‌دهند، نه رابطهٔ علت‌ومعلولی قطعی.An evidence-reading path; arrows show diagnostic order, not a guaranteed causal link.

این ترتیب عمداً محافظه‌کارانه است. اگر قبل از جمع‌کردن مدرک container را حذف کنی، ممکن است دقیقاً همان state و logی را که برای تشخیص لازم داشتی از بین ببری. اگر هم بدون تشخیص حافظه را زیاد کنی یا prune بزنی، شاید فقط علامت را موقتاً پنهان کرده باشی.

For every incident, keep two questions together: “What did we observe?” and “What does this evidence not prove?” Exited proves the process stopped; it does not explain why. This boundary prevents random cleanup, repeated restarts, and changing several settings at once.

اول وضعیت را بخوان؛ بعد سراغ معنی عدد بروRead state first, then interpret the number

docker ps -a به ما می‌گوید container هنوز هست یا نه و آخرین وضعیت ثبت‌شده چه بوده. بعد با docker inspect فقط همان سؤال‌هایی را می‌پرسیم که نیاز داریم: کد خروج چه بوده؟ OOMKilled چه می‌گوید؟ خطای Engine ثبت شده؟ چه زمانی تمام شده؟ چند بار restart شده؟

docker ps -a tells us whether the container still exists and its recorded status. Use inspect to ask specific questions about that object, not to stare at a large JSON document without a purpose.

one focused inspect query
docker inspect web --format 'status={{.State.Status}} exit={{.State.ExitCode}} oom={{.State.OOMKilled}} error={{.State.Error}} finished={{.State.FinishedAt}} restarts={{.RestartCount}}'
representative output · values depend on the incident
status=exited exit=137 oom=false error= finished=2026-09-24T08:41:12Z restarts=0

هدف این نیست که در JSON بزرگ inspect غرق شویم. هر فیلد باید یک فرض را محدود کند. اگر کد ۱۳۷ داریم ولی OOMKilled=false است، هنوز حق نداریم بگوییم «حافظه کم آمده». باید سراغ شواهد بعدی برویم.

This says the current state is exited, the last recorded code is 137, Docker recorded OOMKilled=false for this container, and no restarts were counted. An empty Error is not proof that nothing failed; this field simply provides no error text. Next, inspect logs and nearby events.

مدرک / Evidenceچه می‌گوید؟ / What it tells youچه چیزی را ثابت نمی‌کند؟ / What it does not prove
State.Statusrunning، exited یا restarting بودن کانتینر / Current lifecycle stateسلامت endpoint یا علت توقف / Endpoint health or stop cause
State.ExitCodeعددی که فرایند اصلی برای پایان گزارش کرد / The main process' reported resultمعنای اختصاصی برنامه یا علت سیستم‌عامل / Application meaning or OS cause
State.OOMKilledآیا Docker این پایان را OOM گزارش کرده؟ / Did Docker report an OOM kill?تمام فشار حافظهٔ میزبان در گذشته / Every kind of host memory pressure
State.Error و FinishedAtپیام ثبت‌شده و زمان پایان / Recorded error and finish timeاینکه log کامل است یا علت ریشه‌ای چیست / Complete logs or root cause
RestartCountتعداد تلاش‌های restart این container / Restart attempts for this containerاینکه تلاش آخر موفق یا سالم شده است / Whether the last attempt recovered

وقتی چند container در چرخه‌اند، خط زمان را با log بساز: docker logs --timestamps --since 30m web. اگر زمان رویداد مهم است و log کافی نیست، رویدادهای Docker را هم برای همان container محدود کن. نام دقیق شیء مهم است؛ خروجی سرویس دیگری را با حادثه قاطی نکن.

Build a timeline with docker logs --timestamps --since 30m web. If event timing matters and logs are insufficient, narrow Docker events to that container. The exact object name matters; do not mix another service's output into this incident.

optional event timeline · bounded query
docker events --since 30m --filter container=web

کد خروج سرنخ است، نه فرهنگ لغت علت‌هاExit codes: numeric clues, not a dictionary of causes

فرایند اصلی وقتی تمام می‌شود یک کد خروج باقی می‌گذارد. صفر معمولاً یعنی پایان موفق؛ مقدار غیرصفر یعنی برنامه یا محیط اجرا چیزی غیرعادی گزارش کرده. اما معنای دقیق بعضی کدها به shell، signal یا خود برنامه بستگی دارد. عدد را همیشه کنار دستور اجرا و لاگ همان نوبت بخوان.

When the container's main process ends, it leaves an exit code. Conventionally, zero means success and a non-zero value means the application or its runtime reported an unsuccessful result. Applications may define their own meanings, so read the number with that run's command and logs.

کد / Codeبرداشت رایج / Common readingمدرک بعدی / Next evidence
0پایان موفق طبق قرارداد برنامه / Successful completion by conventionآیا خروجی مطلوب واقعاً ساخته شد؟ / Did the expected output exist?
1خطای عمومی در بسیاری از برنامه‌ها؛ معنای دقیق وابسته به برنامه / Generic failure in many programs; app-definedمتن خطا، ورودی و آخرین مرحلهٔ موفق / Error text, input, last successful step
126در shell رایج: فرمان پیدا شد اما اجراشدنی نبود / Common shell convention: found, but not executableمجوز، نوع فایل و interpreter / Permissions, file type, interpreter
127در shell رایج: فرمان پیدا نشد / Common shell convention: command not foundنام binary و اینکه اصلاً در image هست / Binary name and image contents
137128 + 9 در قرارداد رایج؛ با SIGKILL سازگار است / Commonly corresponds to SIGKILLOOMKilled، رویدادها و logهای میزبان / OOM flag, events, host logs
143128 + 15؛ اغلب با SIGTERM سازگار است / Commonly corresponds to SIGTERMچه کسی stop کرد و آیا برنامه فرصت پایان داشت؟ / Who stopped it, and did it shut down cleanly?

مثلاً ۱۲۷ اغلب با «دستور پیدا نشد» دیده می‌شود و ۱۲۶ معمولاً یعنی فایل پیدا شده ولی قابل اجرا نیست. ۱۳۷ اغلب با SIGKILL هم‌خوانی دارد و ۱۴۳ با SIGTERM؛ اما حتی این‌ها هم تشخیص کامل نیستند. سؤال بعدی همیشه این است: «چه چیزی باعث این پایان شد؟»

The 128 + signal rule is a common Unix/shell convention for representing signal termination, not a universal promise about every program's codes. 137 often aligns with SIGKILL, but an application can return that number or another tool can kill the process. 143 alone does not tell us whether shutdown was expected or why the process received SIGTERM.

دو پایان شبیه، دو علت متفاوتSame-looking exit, different causes

docker kill، پایان ناشی از OOM و بعضی رخدادهای میزبان می‌توانند به وضعیت ۱۳۷ برسند. تا وقتی مدرک OOM یا رویداد مشخص نداری، عبارت «۱۳۷ یعنی کمبود RAM» را در گزارش ننویس.

docker kill, an OOM termination, and some host events can all lead to status 137. Without OOM evidence or a specific event, do not write “137 means out of RAM” in the incident report.

دو کد shell را می‌توان با آزمایشی کوچک دید؛ این خروجی معنای عمومیِ برنامهٔ خودت را تعیین نمی‌کند، فقط رفتار همین shell را نشان می‌دهد:

A tiny experiment shows two common shell cases. This does not define your application's meanings; it demonstrates this shell's behavior:

a command that cannot be found
docker run --name ch12-missing alpine:3.21 sh -c 'not-a-real-command'
docker inspect ch12-missing --format 'exit={{.State.ExitCode}}'
representative shell result
sh: not-a-real-command: not found
exit=127

اگر همین shell فرمان را شناخت ولی اجازهٔ اجرا نداشت، معمولاً ۱۲۶ می‌بینی. در یک image واقعی، اول Dockerfile و دستور را با docker inspect بخوان، بعد بررسی کن executable و interpreter واقعاً در image موجودند و مجوز اجرا دارند؛ rebuild تصادفی چیزی را ثابت نمی‌کند.

If the same shell recognizes a command but cannot execute it, 126 is common. In a real image, inspect the configured command first, then verify that the executable and interpreter exist and have execute permission. A random rebuild proves nothing.

۱۳۷ دیدی؟ حالا ثابت کن OOM بوده یا نهSaw 137? Separate signal, OOM, and a memory limit

سه چیز را یکی نکن: SIGKILL یعنی فرایند بی‌فرصت متوقف شده؛ OOM یعنی سازوکار کمبود حافظه فرایندی را برای آزادکردن حافظه قربانی کرده؛ و --memory سقفی است که برای container تعیین کرده‌ایم. کد ۱۳۷ می‌تواند با SIGKILL دیده شود، اما از روی همان عدد نمی‌فهمیم چه کسی و چرا آن signal را فرستاده است.

Keep three ideas separate: SIGKILL terminates a process without a graceful timeout; OOM is a memory-pressure mechanism choosing a process to kill; and --memory is a cap configured for a container. An OOM inside the container's cap is not the same as memory pressure across the host.

A bounded memory limit is exceeded; the process may be OOM-killed and Docker inspect records evidence. Memory boundary--memory=64msafe lab limit Allocations growprocess asks for morethan the limit Kernel actionOOM kill may stopthe process Docker evidenceExitCode +OOMKilled Exit 137 alone is not the diagnosis; combine it with OOMKilled and logs.
در آزمایش سقف حافظه را محدود می‌کنیم تا فشار روی میزبان کنترل‌شده بماند؛ سپس نتیجه را از state می‌خوانیم.The experiment caps memory to bound host impact, then checks the recorded container state.

برای آزمایش امن، container را عمداً با سقف حافظهٔ کوچک اجرا می‌کنیم و برنامه‌ای می‌گذاریم که از همان سقف عبور کند. بعد به‌جای حدس، ExitCode و OOMKilled را با هم می‌خوانیم. این آزمایش به میزبان فشار نامحدود وارد نمی‌کند؛ مرز از اول داخل همان container بسته شده است.

For a bounded reproduction, cap the container at 64 MiB and run a process that repeatedly allocates 8 MiB. Setting --memory-swap=64m equal to the cap avoids extra swap for this experiment. This is only a diagnostic reproduction; Chapter 13 covers production resource limits.

bounded OOM reproduction · Linux container engine
docker run --name ch12-oom --memory=64m --memory-swap=64m python:3.12-alpine python -c 'blocks=[]; exec("while True:\n blocks.append(bytearray(8*1024*1024))\n print(len(blocks)*8, flush=True)")'

docker inspect ch12-oom --format 'status={{.State.Status}} exit={{.State.ExitCode}} oom={{.State.OOMKilled}} finished={{.State.FinishedAt}}'
representative result · exact last allocation varies
8
16
24
...
status=exited exit=137 oom=true finished=2026-09-24T09:10:14Z

در این آزمایش، ترکیب exit=137 و oom=true با کشته‌شدن فرایند به‌خاطر عبور از سقف container سازگار است. خط آخر تخصیص بین Engineها و سربار image فرق می‌کند. اگر اجرای docker run بگوید محدودیت حافظه پشتیبانی نمی‌شود، آزمایش را بدون سقف تکرار نکن؛ تنظیمات میزبان/Engine را بررسی کن و همان‌جا متوقف شو.

Here, exit=137 together with oom=true is consistent with a process killed after exceeding its container cap. The last printed allocation varies with Engine and image overhead. If docker run says memory limits are unsupported, do not retry without the cap; inspect Engine/host support and stop there.

حالا حالت دشوارتر: exit=137 اما OOMKilled=false. این مدرک اجازه نمی‌دهد OOM خود container را اعلام کنیم. احتمال‌هایی مثل docker kill، توقف اجباری یا فشار حافظه در سطح میزبان را با زمان پایان، logها، رویدادها و logهای kernel/سیستم میزبان بررسی کن. اگر میزبان Docker Desktop است، مدرک را در VM مدیریت‌شده و وضعیت منابع خود Desktop هم دنبال کن.

Now consider the harder case: exit=137 but OOMKilled=false. That does not support declaring a container-level OOM. Check possibilities such as docker kill, forced termination, or host-level memory pressure against the finish time, logs, events, and host/kernel logs. With Docker Desktop, also inspect the managed VM and Desktop resource state.

Linux host evidence · only if you have access
journalctl -k --since '30 minutes ago' | grep -Ei 'out of memory|killed process'

این فرمان مخصوص Linux است و دسترسی دیدن kernel log ممکن است محدود باشد. در Windows یا Docker Desktop، نبودن همین فرمان به معنی نبودن مدرک نیست؛ از ابزار سیستم‌عامل میزبان و پنل منابع Desktop استفاده کن. برعکس، OOMKilled=true مدرک قوی برای OOM ثبت‌شدهٔ container است، اما علت طراحیِ پرمصرف برنامه را به‌تنهایی توضیح نمی‌دهد.

That command is Linux-specific, and access to kernel logs may be restricted. On Windows or Docker Desktop, the command's absence does not mean evidence is unavailable; use host OS tools and Desktop resource settings. Conversely, OOMKilled=true is strong evidence of a container OOM, but it does not by itself explain why the application used so much memory.

«no space left on device» می‌گوید جایی پر شده؛ نه اینکه کجا“No space left on device”—which filesystem, exactly?

یک build وسط نوشتن می‌ایستد یا pull با no space left on device تمام می‌شود. متن خطا واقعی است، ولی نام filesystem را به تو نمی‌دهد. ممکن است بلوک‌های پارتیشن میزبان پر شده باشند، inode تمام شده باشد، فضای مدیریت‌شدهٔ Docker پر باشد یا فقط mount کوچکی داخل container جا نداشته باشد.

A build stops while writing a layer, or an image pull fails with no space left on device. The error is real, but it does not name the exhausted filesystem. It could be the host partition, Docker's data storage, a temporary container mount, or even the inode pool.

No space left on device can refer to different exhausted filesystems or inode capacity. “No space left on device” the message does not name the exhausted target Host blocksdf -hfilesystem capacity Inodesdf -itoo many files Docker storagesystem dfimages · cache · data Target mountdf -h /targetvolume · tmpfs Find the actual write target before deleting anything.
اول مقصد نوشتن را پیدا کن، بعد همان filesystem را اندازه بگیر.Find the write target first; then measure that filesystem.

پس اولین واکنش نباید پاک‌کردن imageها باشد. اول باید محل واقعی نوشتن را پیدا کنیم. df -h ظرفیت بلوکی را نشان می‌دهد، df -i inode را، و docker system df فقط سهم منابع Docker را در فضای خودش دسته‌بندی می‌کند. هرکدام جواب سؤال متفاوتی هستند.

On Linux, df -h reports filesystem block capacity, while df -i reports inode availability. If bytes remain but inode usage is at 100%, huge numbers of tiny files can prevent creating another file. Pass the exact path to identify its filesystem.

inspect the actual host paths · Linux example
df -h / /var/lib/docker
df -i / /var/lib/docker

اسم /var/lib/docker فقط مثال رایج Linux است؛ آن را حدس نزن. از daemon بپرس داده‌اش کجاست:

/var/lib/docker is only a common Linux example; do not assume it. Ask the daemon where its data root is:

daemon-side storage location
docker info --format '{{.DockerRootDir}}'
docker info --format '{{.Driver}}'

این مسیر از دید daemon است. در Docker Engine روی Linux معمولاً مسیری روی میزبان می‌بینی؛ در Docker Desktop، imageها و containerهای Linux در محیط ذخیره‌سازی مدیریت‌شده قرار دارند و مسیر داخل VM الزاماً مسیر مستقیم Windows یا macOS نیست. برای Desktop، docker system df را با تنظیمات Resources و مقدار Disk usage / Disk image location مقایسه کن؛ df روی لپ‌تاپ ممکن است filesystem دیگری را نشان دهد.

This path is from the daemon's point of view. With Docker Engine on Linux it is usually a host path; with Docker Desktop, Linux images and containers live in managed storage, and a VM path is not necessarily a direct Windows or macOS path. Compare docker system df with Desktop Resources settings and disk-image usage; host df may describe a different filesystem.

کمبود فضا را در یک فضای کوچک و بی‌خطر بازسازی کنSee ENOSPC without filling a real disk

برای اینکه مفهوم را ببینیم لازم نیست دیسک لپ‌تاپ را پر کنیم. یک tmpfs کوچک داخل container می‌سازیم و بیشتر از ظرفیتش روی همان mount می‌نویسیم. خطا ظاهر می‌شود، درحالی‌که دیسک اصلی میزبان هنوز فضای آزاد دارد. همین آزمایش ثابت می‌کند عبارت «no space left» به‌تنهایی مقصد پرشده را مشخص نمی‌کند.

To reproduce the error, give a Linux container a temporary 1 MiB filesystem and try to write 2 MiB. The cap is on the temporary mount—not the host partition or Docker data root. This experiment requires a Linux container engine with tmpfs support; if you are using Windows containers or an environment without tmpfs, skip it. Never redirect the same write to a real disk.

bounded to a 1 MiB tmpfs mount
docker run --name ch12-enospc --mount type=tmpfs,dst=/scratch,tmpfs-size=1048576 alpine:3.21 sh -c 'dd if=/dev/zero of=/scratch/fill.bin bs=64K count=32'
representative output · exact wording varies by BusyBox
dd: error writing '/scratch/fill.bin': No space left on device

بعد از خطا، داخل همان container ظرفیت mount را می‌بینیم و بیرون container هم فضای میزبان را مقایسه می‌کنیم. دو تصویر متفاوت داریم و همین تفاوت نقطهٔ آموزشی آزمایش است: همیشه بپرس «دقیقاً کجا داشتیم می‌نوشتیم؟»

This means the filesystem containing /scratch/fill.bin ran out of room; it does not say the host disk is full. After the container exits, inspect state and mount before removing it:

identify the failing mount and process result
docker inspect ch12-enospc --format 'exit={{.State.ExitCode}} oom={{.State.OOMKilled}} mounts={{json .Mounts}}'
docker logs ch12-enospc

در خروجی inspect باید Type=tmpfs و مقصد /scratch دیده شود؛ خروجی dd هم می‌گوید نوشتن کجا شکست. این بار بایت‌های نوشته‌شده وارد Docker writable layer یا volume نشده‌اند. tmpfs از حافظه استفاده می‌کند، پس سقف آن را کوچک نگه دار و بعد از ثبت مدرک container را حذف کن تا mount آزاد شود.

Inspect should show Type=tmpfs mounted at /scratch; dd's output identifies the failed write. Those bytes did not go into the Docker writable layer or a volume. tmpfs consumes memory, so keep its cap small and remove the container after collecting evidence to release the mount.

وقتی مقصد درست را پیدا کردی، ببین چه چیزی فضا را مصرف کردهFind the consumer; “Docker uses space” is still too broad

اگر مشکل واقعاً در فضای Docker است، docker system df کمک می‌کند مصرف imageها، containerها، volumeها و build cache را از هم جدا ببینی. گزینهٔ -v جزئیات بیشتری می‌دهد، اما حتی عدد بزرگ هم مجوز حذف نیست. یک volume بزرگ ممکن است همان دادهٔ مهم پروژه باشد.

docker system df summarizes image, container, local-volume, and build-cache usage after you identify the right filesystem. -v adds detail such as image-unique space versus shared layers. Do not equate these numbers with host free space: one totals Docker objects; the other measures the backing filesystem's capacity.

start broad, then inspect categories
docker system df
docker system df -v
Several Docker resources can consume storage; each needs a separate ownership and cleanup decision. Docker / backing storage first identify the consumer Imageslayers Containerswritable layers Volumespersistent data Build cachebuilder data Logsdriver-dependent Large does not mean disposable — inspect ownership before prune.
مصرف‌های متفاوت را جدا ببین: log ممکن است با تنظیم driver رشد کند، حتی اگر مشکل از image نباشد.Separate the consumers: logs may grow according to driver configuration even when images are not the problem.
دسته / Categoryچه چیزی جا می‌گیرد؟ / What occupies space?پیش از حذف بپرس / Before removal, ask
Imagesلایه‌های مشترک و یکتای image / Shared and unique layersآیا tag یا container دیگری به آن نیاز دارد؟ / Does a tag or container still need it?
Containersلایهٔ نوشتنی containerهای متوقف‌شده / Writable layers of stopped containersآیا log یا فایل خروجی داخلش مدرک لازم است؟ / Does it contain evidence or needed output?
Volumesدادهٔ ماندگار برنامه، از جمله پایگاه‌داده / Persistent application data, including databasesچه چیزی این داده را مصرف می‌کند و backup کجاست؟ / Who consumes it, and where is its backup?
Build cacheلایه‌ها و cacheهای BuildKit برای buildهای بعدی / BuildKit records for future buildsآیا زمان build بعدی یا cache mount مهم است؟ / Is the next build's speed or cache mount important?
Logsبسته به logging driver و مقصد تنظیم‌شده / Depends on logging driver and destinationdriver کجاست، rotation دارد، و retention چقدر است؟ / Where does the driver store logs, and what is its retention?

لاگ‌ها هم بسته به logging driver و تنظیمات میزبان می‌توانند مصرف فضا داشته باشند. پس «Docker فضا گرفته» هنوز جملهٔ خیلی کلی‌ای است. باید بدانیم کدام دسته، کدام مسیر و کدام داده واقعاً مسئول رشد بوده است.

If Docker's summary does not explain the usage, inspect the daemon and container drivers separately. docker logs does not behave identically for every driver; some do not support local reading. An unrotated json-file setup can grow, while local has its own rotation behavior. Here we identify the driver and location, not perform full log tuning.

خلاصهٔ docker system df هم تضمین نمی‌کند فایل‌های log را در ردیفی مستقل نشان دهد. اگر جمع مصرف با دیسکِ پر جور درنمی‌آید، driver و مسیر ذخیرهٔ log را بررسی کن؛ ندیدن ردیف جداگانه، مدرک کوچک‌بودن log نیست.

The docker system df summary does not guarantee a separate row for log files. If the summary does not explain a full disk, inspect the logging driver and its storage path; no separate row is not proof that logs are small.

daemon default vs one container
docker info --format 'default={{.LoggingDriver}}'
docker inspect web --format 'driver={{.HostConfig.LogConfig.Type}} path={{.LogPath}}'

اگر data به volume مربوط بود، درس فصل ۶ را به خاطر بیاور: حذف container دادهٔ volume را حذف نمی‌کند؛ و حذف volume می‌تواند دادهٔ مهم را از بین ببرد. اگر build cache بزرگ است، با مصرف آن در buildهای بعدی معامله می‌کنی. اگر logها بزرگ‌اند، اول retention و مقصد را بفهم؛ حذف container فقط صورت مسئله را پنهان می‌کند.

If the data is in a volume, recall Chapter 6: removing a container does not remove its volume, while removing a volume can destroy important data. If build cache is large, consider the rebuild cost. If logs are large, understand destination and retention first; deleting a container only hides the symptom.

Prune آخر کار است، نه اولین واکنشPrune is not a substitute for diagnosis

بعد از تشخیص شاید واقعاً پاک‌سازی لازم باشد. اما image prune، container prune، builder prune و volume prune چیزهای یکسانی را حذف نمی‌کنند. مخصوصاً volume را فقط وقتی لمس کن که دقیقاً می‌دانی داده‌اش چیست و جای دیگری نسخهٔ لازم را داری.

Once you identify the actual consumer, cleanup may be appropriate—but prune commands remove different things. Before running one, ask: “Where is this data stored? Who uses it? What would I lose if it disappeared?” A volume may hold the only copy of a database you care about.

فرمان / Commandدامنهٔ معمول / Usual scopeریسک و بررسی / Risk and check
docker system prunecontainerهای متوقف، network بلااستفاده، imageهای dangling و build cache بلااستفاده / Stopped containers, unused networks, dangling images, unused build cachecontainer متوقف‌شده شاید log یا filesystem مدرک داشته باشد / A stopped container may retain evidence
docker system prune -aبه‌علاوهٔ imageهای بدون container وابسته، نه فقط dangling / Also unused images, not only dangling onesممکن است pull یا build دوباره لازم شود / May require a fresh pull or build
docker container pruneهمهٔ containerهای متوقف / Stopped containersاول docker ps -a و logهای لازم را ثبت کن / Inspect state and preserve needed logs first
docker image prune / -aپیش‌فرض imageهای dangling؛ با -a همهٔ imageهای بی‌مصرف / Dangling by default; all unused images with -atag محلی به‌تنهایی backup یا انتشار نیست / A local tag is not a backup or publication
docker builder prunebuild cache / Build cacheساخت بعدی می‌تواند کندتر و پرمصرف‌تر شود / Later builds may be slower and use more bandwidth
docker volume prunevolumeهای محلی بلااستفاده؛ دامنهٔ دقیق را با نسخهٔ CLI بخوان / Unused local volumes; check the installed CLI's exact scopeداده را با نام volume و backup بررسی کن / Check volume names and backups

فرمان بزرگی مثل docker system prune -a --volumes درمان عمومی «Docker فضا گرفته» نیست. چنین کاری ممکن است هم مدرک عیب‌یابی را پاک کند و هم داده‌ای را که هنوز لازم است. تشخیص باید قبل از پاک‌سازی بیاید، نه بعد از آن.

In current Docker documentation, docker system prune leaves volumes alone by default; its --volumes option also includes unused anonymous volumes. Removing all unused local volumes, including named ones, is a separate docker volume prune --all behavior. Because deletion is irreversible and version details matter, read the installed command's warning and never trust the word “unused” blindly.

فرمان پیش‌فرض این فصل: هیچ pruneای اجرا نکنThis chapter's default: run no prune command

برای عیب‌یابی، اول docker system df -v، containerها، logها و volumeها را بررسی کن. اگر پاک‌سازی واقعاً لازم شد، یک دستهٔ مشخص را انتخاب کن، محدوده را ببین، داده را پشتیبان بگیر و فرمان همان دسته را اجرا کن. docker system prune -a --volumes راه‌حل عمومی این درس نیست.

For diagnosis, inspect docker system df -v, containers, logs, and volumes first. If cleanup is genuinely needed, select one category, review its scope, back up data, and run that category's command. docker system prune -a --volumes is not this lesson's general fix.

هر نشانه فقط یک سؤال بعدی می‌سازدMap symptoms to hypotheses; test one boundary at a time

اگر اولین فرضت با مدرک جور نیست، کل سیستم را دست‌کاری نکن؛ فقط یک مرز عقب برو. آیا فرایند اصلاً شروع شده؟ لاگ چه می‌گوید؟ محدودیت حافظه درگیر است؟ filesystem مقصد ظرفیت دارد؟ هر بررسی باید یا یک احتمال را رد کند یا آن را قوی‌تر کند.

When your first interpretation does not match the evidence, step back one boundary: did the intended process start? Did the application log its reason? Did the container limit or target filesystem run out? This table does not give final diagnoses; it suggests the next discriminating check.

مشاهده / Observationفرضیهٔ ممکن / Possible hypothesisبررسی متمرکز / Focused check
Exited (1)برنامه خطای عمومی داده / Application reported generic failureفرمان، ورودی و docker logs / Command, input, logs
Exited (127)shell نتوانسته فرمان را پیدا کند / Shell could not find the commandنام binary و image واقعی / Binary name and actual image
Exited (126)فرمان پیدا شده اما اجراشدنی نیست / Found but not executablemode، shebang، interpreter و mount مجوزها / Mode, shebang, interpreter, mount permissions
137 + OOMKilled=falseOOM container ثابت نشده؛ شاید kill اجباری یا رخداد میزبان / Container OOM unproven; forced or host event possibleزمان، event و مدرک kernel/میزبان / Timeline, events, host/kernel evidence
OOMKilled=trueفرایند container قربانی OOM ثبت‌شده شده / Docker recorded an OOM killlimit، مصرف، و الگوی تخصیص برنامه / Limit, usage, application allocation pattern
log تکراری و restart count رو به افزایشrestart loop علت را تکرار می‌کند، نه اصلاح / Restart loop repeats the failureدستور شروع، کد آخر و اولین خطای هر تلاش / Start command, exit code, first error per attempt
فقط یک مسیر tmpfs پر استfilesystem mountشده کوچک است / Mounted filesystem is boundeddf -h /path داخل همان mount / Measure inside the target mount
df -h جا دارد؛ df -i پر استinodeها تمام شده‌اند / Inodes are exhaustedتعداد فایل‌ها و filesystem همان مسیر / File count on the target filesystem
build cache یا image بزرگمصرف Docker بالاست، ولی هنوز دلیل حذف نیست / High usage, not yet a deletion reasondocker system df -v و وابستگی‌های مصرف‌کننده / Detailed usage and consumers
volume بزرگ یا log گم‌شدهممکن است دادهٔ مهم باشد یا driver جای دیگری بنویسد / May be important data or a different log destinationmountها، backup و logging driver / Mounts, backup, logging driver

این روش شاید از restart تصادفی کندتر به نظر برسد، اما وقتی مشکل واقعی باشد سریع‌تر به علت می‌رسد و مهم‌تر از آن، می‌توانی برای نفر بعد توضیح بدهی چرا به این نتیجه رسیدی.

If df -h shows the host filesystem near capacity while docker system df accounts for less, investigate consumers outside Docker's object summary too, such as ordinary files, caches, or host logs. If DockerRootDir is on another filesystem, measure that path separately.

restart policy می‌تواند همان فرایند خراب را بارها دوباره آغاز کند. اگر RestartCount بالا می‌رود، container بین Restarting و Up می‌چرخد یا logها یک خطا را تکرار می‌کنند، به‌جای زیادکردن restart یا حذف سریع، یک تلاش را ثبت کن و علت مشترک را پیدا کن.

A restart policy can launch the same broken process repeatedly. If RestartCount rises, the container cycles between Restarting and Up, or logs repeat one error, record one attempt and find the shared cause instead of increasing restarts or deleting evidence.

۱۸ تمرین؛ از عدد به علت نپر18 exercises: from a number to an incident report

قبل از بازکردن پاسخ هر تمرین سه چیز بنویس: الان چه چیزی را واقعاً می‌دانیم، چه چیزی هنوز نامعلوم است و کم‌هزینه‌ترین بررسی بعدی چیست. در تمرین‌های آخر عمداً موقعیت‌هایی می‌بینی که یک اقدام عجولانه مثل prune یا حذف container می‌تواند مدرک یا داده را از بین ببرد.

Before opening each solution, predict what the current evidence says, what remains unknown, and which low-cost check should come next. In the later incidents, any repair that destroys data or evidence creates a new risk.

۱. Docker واقعاً crash کرده؟1. Did Docker actually crash?

docker ps سطری نشان نمی‌دهد. اولین دستور بعدی چیست و چرا هنوز نباید دربارهٔ daemon نتیجه بگیری؟

docker ps shows no row. What is the next command, and why is it too early to conclude the daemon failed?

سرنخ، نه تشخیص نهایی · Clue, not verdict

docker ps -a را بزن. دستور اول containerهای متوقف را پنهان می‌کند؛ خالی‌بودنش فقط فهرست running را توصیف می‌کند. بعد نام و state را پیدا کن و برای همان container سراغ inspect و لاگ‌ها برو.

Run docker ps -a. The first command hides stopped containers; its empty result describes only the running list. Find the matching name and state, then inspect that container and its logs.

۲. خروج صفر، پس همه‌چیز درست است؟2. Exit zero, so everything is correct?

یک کار کد 0 دارد، اما فایل گزارش مورد انتظار ساخته نشده. چه نتیجه‌ای می‌گیری؟

A job exited with 0, but the expected report file is missing. What do you conclude?

موفقیت فرایند با موفقیت هدف یکی نیست · Process success is not goal success

صفر طبق قرارداد معمول یعنی فرایند موفقیت گزارش کرده؛ ثابت نمی‌کند خروجی ساخته‌شده درست یا حتی تولید شده. مسیر خروجی، mount، گزارش پایانی و فرمان را بررسی کن و موفقیت را با همان فایل مورد انتظار بسنج.

Zero conventionally means the process reported success; it does not prove the artifact is correct or even exists. Check output path, mount, final logs, and command, then verify the actual expected file.

۳. چرا کد ۱۲۷ گرفتیم؟3. Why did this return 127?

container می‌گوید sh: migrate: not found. محتمل‌ترین مرز خراب کجاست؟

The container prints sh: migrate: not found. Which boundary most likely failed?

ابزار پیش از اجرا پیدا نشد · The executable was not found

در قرارداد رایج shell، ۱۲۷ یعنی فرمان پیدا نشد. دستور واقعی و مرحلهٔ Dockerfile را بخوان؛ ممکن است binary نصب نشده، مسیرش در PATH نیست یا نام executable فرق دارد. OOM و port مسئلهٔ این مدرک نیستند.

By common shell convention, 127 means the command was not found. Read the actual command and Dockerfile stage: the binary may be missing, absent from PATH, or named differently. OOM and ports are not implicated by this evidence.

۴. کد ۱۲۶ چه تفاوتی دارد؟4. How is 126 different?

فرمان پیدا می‌شود، ولی shell می‌گوید Permission denied. چه چیزهایی را قبل از rebuild می‌سنجی؟

The command is found, but the shell says Permission denied. What do you check before rebuilding?

اول قابل‌اجرا بودن را ثابت کن · Prove executability first

مجوز execute، مالکیت، mount با گزینهٔ noexec و shebang/interpreter را بررسی کن؛ نیز مطمئن شو فایل برای همان معماری و محیط است. ۱۲۶ در قرارداد shell یعنی اجرا نشد، نه اینکه برنامه شروع شد و منطقش شکست خورد.

Check execute permission, ownership, a possible noexec mount, and the shebang/interpreter; also confirm the file suits the target environment. Shell convention 126 means execution failed before the program's logic ran.

۵. ۱۳۷ و OOMKilled=false5. Exit 137 with OOMKilled=false

آیا می‌توانی در گزارش بنویسی «container به‌خاطر کمبود RAM کشته شد»؟

Can your report say “the container was killed because it ran out of RAM”?

نه؛ مدرک هنوز کافی نیست · No—the evidence is insufficient

۱۳۷ با SIGKILL سازگار است، اما false بودن OOMKilled، OOM ثبت‌شدهٔ container را تأیید نمی‌کند. docker kill، پایان اجباری یا رخداد میزبان را با زمان، event و log میزبان بررسی کن؛ تا آن موقع علت را نامشخص بنویس.

137 is consistent with SIGKILL, but OOMKilled=false does not support a recorded container OOM. Check docker kill, forced termination, or host events against timestamps and host logs; until then, report the cause as unknown.

۶. این بار OOMKilled=true است6. This time OOMKilled is true

کد ۱۳۷ و OOMKilled=true ثبت شده‌اند. کدام تشخیص موجه‌تر است و چه چیزی هنوز لازم است؟

The record shows exit 137 and OOMKilled=true. Which diagnosis is justified, and what is still missing?

نوع پایان روشن‌تر است، علت مصرف نه · The termination is clearer than the usage cause

می‌توان گفت Docker این پایان را OOM گزارش کرده. برای ریشه‌یابی، limit ثبت‌شده، زمان و مصرف حافظه، ورودی درخواست و الگوی تخصیص برنامه را مقایسه کن. پیام OOM به‌تنهایی نمی‌گوید limit را زیاد کنیم؛ شاید رشد نامحدود یا خواندن کل فایل در حافظه باشد.

You can say Docker reported an OOM termination. For root cause, compare the configured limit, memory usage and timing, request input, and allocation pattern. The OOM flag does not mean “raise the limit”; unbounded growth or loading a whole file may be the cause.

۷. ۱۴۳ همیشه stop عادی است؟7. Does 143 always mean a normal stop?

کانتینر با Exited (143) تمام شده. چه چیزی را می‌توانی بگویی و چه چیزی را نه؟

A container shows Exited (143). What can you say, and what can you not say?

با SIGTERM سازگار است، اما روایت لازم است · Consistent with SIGTERM, but context matters

۱۴۳ طبق قرارداد رایج با 128+15 و SIGTERM جور است. برای تشخیص shutdown عادی، زمان‌بندی deploy/stop، Docker events و logهای برنامه را کنار هم بگذار. برنامه هم می‌تواند عدد ۱۴۳ را خودش برگرداند.

By common convention, 143 aligns with 128+15 and SIGTERM. Correlate deploy/stop timing, Docker events, and application logs before calling it a normal shutdown. An application can also return 143 itself.

۸. Restart count هر دقیقه بیشتر می‌شود8. Restart count rises every minute

گزارش در هر تلاش همان خطای تنظیمات اتصال را نشان می‌دهد. آیا restart: always درمان است؟

Each attempt logs the same configuration error. Is restart: always a repair?

تکرار شکست را با بازیابی اشتباه نگیر · Repetition is not recovery

نه. سیاست راه‌اندازی مجدد فرایند را دوباره اجرا می‌کند و ممکن است حلقه را پنهان یا پر سروصدا کند. محل اتصال تنظیمات و مسیر فایل را برای یک تلاش بررسی کن، علت را اصلاح کن، سپس همان ورودی را دوباره اجرا و وضعیت و گزارش جدید را تأیید کن.

No. The policy retries the process and can hide or amplify the loop. Inspect the config mount and file path for one attempt, repair the cause, then rerun the same input and verify new state and logs.

۹. بایت هست، inode نیست9. Blocks remain; inodes do not

df -h فضا نشان می‌دهد، اما df -i مصرف inode را ۱۰۰٪ گزارش می‌کند. چرا ساخت فایل تازه شکست می‌خورد؟

df -h shows free space, but df -i reports 100% inode use. Why can creating a new file fail?

هر فایل هم inode می‌خواهد · Every file needs an inode

filesystem برای metadata هر فایل inode می‌خواهد؛ ممکن است بایت آزاد باشد ولی inode تازه موجود نباشد. تعداد فایل‌ها و مسیرهای پر از فایل کوچک را در همان filesystem پیدا کن. بزرگ‌کردن Docker image یا حذف یک volume نامربوط این ظرفیت را برنمی‌گرداند.

A filesystem needs an inode for each file's metadata; free bytes do not help if no inode remains. Locate directories with many small files on that filesystem. Enlarging an image or deleting an unrelated volume does not restore those inodes.

۱۰. tmpfs پر شد؛ میزبان هم؟10. tmpfs filled—did the host disk?

آزمایش محدود ما هنگام نوشتن روی /scratch خطای ENOSPC داده؛ mount در inspect از نوع tmpfs است. چه چیزی را ثابت می‌کند؟

Our bounded experiment got ENOSPC writing to /scratch, and inspect shows a tmpfs mount. What does that prove?

فقط همان filesystem پر شده · Only that filesystem is full

نوشتن از سقف tmpfs گذشته؛ این خطا دربارهٔ فضای Docker data root یا دیسک میزبان چیزی ثابت نمی‌کند. mount را داخل inspect پیدا کن و پس از ثبت خروجی container آزمایشی را حذف کن.

The write exceeded the tmpfs cap; it proves nothing about Docker's data root or host disk. Identify the mount in inspect and remove the disposable container after recording the output.

۱۱. build cache از همه بزرگ‌تر است11. Build cache is the largest category

docker system df -v cache زیادی نشان می‌دهد و build فردا مهم است. اولین کار چیست؟

docker system df -v shows substantial cache, and tomorrow's build matters. What is your first action?

عدد را با کارکردش بسنج · Weigh the usage against its purpose

buildها و cache mountهای لازم را بررسی کن و با مصرف، زمان rebuild و پارتیشن مقصد تصمیم بگیر. عدد بزرگ به‌تنهایی فرمان builder prune نیست؛ حذف cache می‌تواند build بعدی را کندتر و پرهزینه‌تر کند.

Review builds and cache mounts, then weigh usage against rebuild time and the backing filesystem. A large number alone does not justify builder prune; removing cache can make the next build slower and more expensive.

۱۲. image بزرگ و بدون container فعال12. A large image has no active container

image قدیمی هنوز tag دارد و container زنده‌ای از آن نمی‌بینی. آیا docker image prune -a بی‌خطر است؟

An old tagged image has no running container. Is docker image prune -a automatically safe?

نه؛ مصرف‌کننده فقط running نیست · No; consumers are not only running containers

container متوقف‌شده، pipeline یا rollback ممکن است به image نیاز داشته باشد. همهٔ containerها را با docker ps -a و imageها را با docker system df -v بسنج؛ اگر tag واقعاً لازم نیست و بازسازی/pull ممکن است، آن‌وقت دامنهٔ پاک‌سازی را انتخاب کن.

A stopped container, pipeline, or rollback may still need it. Review all containers with docker ps -a and image usage with docker system df -v. Prune only after confirming the tag is unnecessary and rebuilding/pulling it is acceptable.

۱۳. volume بلااستفاده ولی پُرداده13. An unused volume still contains data

docker system df یک volume بزرگ با صفر link نشان می‌دهد. حذفش می‌کنی؟

docker system df shows a large volume with zero links. Do you remove it?

اول مشخص کن «بلااستفاده» برای چه کسی · First ask “unused by whom?”

صفر link می‌گوید اکنون containerی آن را متصل نکرده؛ نمی‌گوید داده بی‌ارزش است. نام، برچسب، تاریخ، مالک پروژه و پشتیبان را پیدا کن و اگر داده باید بماند آن را بازیابی/منتقل کن. volume را به امید اینکه خالی باشد prune نکن.

Zero links says no current container attaches it; it does not say the data is worthless. Identify its name, labels, project owner, age, and backup; preserve or migrate the data if needed. Do not prune it on the assumption that it is empty.

۱۴. logها نیستند، اما برنامه روی stdout می‌نویسد14. Logs are missing, though the app writes to stdout

برنامه رویداد را به stdout می‌فرستد، اما docker logs web چیزی نمی‌دهد. چه فرضیه‌ای را اول می‌سنجی؟

The application writes events to stdout, but docker logs web is empty. Which hypothesis do you test first?

رفتار logging driver را بررسی کن · Inspect the logging driver

driver پیش‌فرض را با docker info و driver همان container را با docker inspect بخوان. driver ممکن است محلی reading را پشتیبانی نکند یا خروجی را به مقصد بیرونی بفرستد؛ بعد مقصد را بررسی کن. نبودن خروجی محلی به‌تنهایی ثابت نمی‌کند برنامه چیزی ننوشته.

Read the daemon default with docker info and the container's driver with docker inspect. The driver may not support local reading or may send output elsewhere; inspect that destination. Missing local output alone does not prove the app wrote nothing.

۱۵. روی Windows آزاد است، Docker می‌گوید پر15. Windows has space; Docker says full

در Windows چند گیگابایت آزاد می‌بینی، اما pull image روی Docker Desktop شکست می‌خورد. چرا این دو مشاهده تناقض ندارند؟

Windows shows several gigabytes free, but an image pull fails in Docker Desktop. Why can both observations be true?

دو filesystem یا لایهٔ ذخیره‌سازی متفاوت · Different filesystems or storage layers

Docker Desktop دادهٔ Linux را در محیط مجازی/مدیریت‌شده و با سقف خودش نگه می‌دارد. با docker system df مصرف daemon را ببین و Resources را در تنظیمات Desktop بررسی کن؛ فضای آزاد درایو Windows الزاماً ظرفیت image دیسک Docker را نشان نمی‌دهد.

Docker Desktop stores Linux data in managed virtual storage with its own capacity. Check daemon usage with docker system df and inspect Desktop Resources settings; free Windows drive space does not necessarily describe the Docker image disk's capacity.

۱۶. پیشنهاد «همه‌چیز را prune کن»16. Someone proposes “prune everything”

همکارت برای حل build error این را پیشنهاد می‌کند: docker system prune -a --volumes. پاسخ حرفه‌ای تو چیست؟

A teammate proposes docker system prune -a --volumes to fix a build error. What is your professional response?

دامنه و داده را قبل از حذف روشن کن · Establish scope and data before deleting

اول فایل‌سیستم، cache و object مصرف‌شده را با شواهد پیدا کن. این فرمان می‌تواند imageهای بدون container، cache و volumeهای بی‌مصرفِ مشخص‌شده را حذف کند؛ volume شاید داده داشته باشد. علت build را جدا تشخیص بده، volumeها را به مالکشان وصل کن و پاک‌سازی هدفمند و تأییدشده انجام بده.

First identify the filesystem, cache, and consumers with evidence. That command can remove images without containers, cache, and included unused volumes; a volume may still hold data. Diagnose the build failure separately, identify volume owners, and perform only scoped, confirmed cleanup.

۱۷. اصلاح کردی؛ چه چیزی بازیابی را ثابت می‌کند؟17. You repaired it—what proves recovery?

پس از تغییر تنظیم، container در Up است. برای بستن گزارش حادثه چه آزمونی کم است؟

After a config change, the container is Up. What test is missing before closing the incident?

همان وعدهٔ اولیه را دوباره بیازما · Repeat the original verification

درخواست یا کاری را اجرا کن که اول شکست خورد؛ سپس نتیجهٔ مورد انتظار را بخوان، وضعیت و سلامت و گزارش همان بازه را ثبت کن. Up فقط زنده‌بودن فرایند را می‌گوید و تغییر تنظیم هم به‌تنهایی اثبات تعمیر نیست.

Repeat the request or job that originally failed, then verify its expected result and record state/health and logs for that interval. Up proves process liveness only; changing configuration is not itself proof of repair.

۱۸. یک گزارش incident کوتاه بنویس18. Write a concise incident report

فرایند با ۱۳۷ تمام شده، OOMKilled=false است، log می‌گوید shutdown signal دریافت شد و event در همان دقیقه kill را ثبت کرده. گزارش یک‌جمله‌ای تو چیست؟

The process exited 137, OOMKilled=false, logs say a shutdown signal arrived, and an event records kill in the same minute. What is your one-sentence report?

واقعیت‌ها را از فرضیه جدا نگه دار · Keep facts separate from hypotheses

مثلاً: «کانتینر در ساعت ثبت‌شده با ۱۳۷ پایان یافت؛ Docker برای آن OOM ثبت نکرد، log دریافت signal را نشان می‌دهد و event هم‌زمان kill را ثبت کرده؛ بنابراین شواهد به توقف اجباری اشاره می‌کنند، نه OOM container.» اگر identity عامل kill معلوم نیست، همان بخش را نامشخص بگذار و برایش مدرک تازه جمع کن.

For example: “At the recorded time the container exited 137; Docker did not record a container OOM, logs show a signal, and a concurrent event records kill, so evidence points to forced termination rather than container OOM.” If the actor is unknown, say so and collect evidence before naming one.

آزمایشگاه incident: سه خرابی، سه مسیر تشخیصIncident lab: three failures, three evidence-based diagnoses

در این آزمایشگاه سه خطا را جدا می‌سازیم: برنامه خودش با کد مشخص خارج می‌شود؛ یک container از سقف حافظهٔ کوچک خودش عبور می‌کند؛ و نوشتن روی tmpfs محدود جا نمی‌شود. هر سناریو مستقل است تا نشانه‌هایشان با هم قاطی نشود.

On one lab host, reproduce three causes separately: an application returns a known code, a process exceeds a small container memory cap, and a write exceeds a 1 MiB tmpfs. None requires wiping Docker or writing to a real disk partition.

خروجی نهایی: برای هر incident یک گزارش کوتاه با symptom، مدرک، diagnosis، repair و بررسی بنویس. گزارش باید مشخص کند چه چیزی ثابت شد و چه چیزی هنوز نامعلوم است.Deliverable: write a short report for each incident with symptom, evidence, diagnosis, repair, and verification. State what is proven and what remains unknown.

برای هر مورد یک گزارش کوتاه بنویس: نشانهٔ اولیه، مدرکی که جمع کردی، تشخیص، اصلاح و همان آزمونی که بعد از اصلاح تکرار شد. اگر مرحلهٔ آخر را نداری، incident هنوز بسته نشده است.

This checks only the lab names. If one already exists, choose a new name and substitute it consistently throughout that incident; do not delete an existing container merely to free the name.

preflight · preserve anything already present
docker version
docker ps -a --filter name=lab12-

اگر سرور وصل نیست یا موتور خاموش است، همین‌جا توقف کن. در بخش OOM، اگر daemon محدودیت حافظه را نمی‌پذیرد، آن را بدون --memory دوباره اجرا نکن. بخش tmpfs هم به موتور Linux container نیاز دارد؛ اگر backend پشتیبانی نمی‌کند، آن آزمایش را در محیط مناسب اجرا کن.

If the Server is unavailable or the Engine is stopped, pause here. If the daemon rejects the memory cap in the OOM section, do not retry without --memory. The tmpfs incident also requires a Linux container engine; use a supported environment rather than replacing it with a real-disk write.

حادثهٔ A · برنامه با کد معلوم خارج می‌شودIncident A · the application exits with a known code

می‌خواهیم فرق «برنامه شکست گزارش کرد» با «Docker خراب شد» را ثابت کنیم. برنامه عمداً ۲۳ برمی‌گرداند؛ این کد shell خاصی نیست و در مثال معنای خود برنامه را دارد.

We will distinguish “the application reported failure” from “Docker failed.” The program deliberately returns 23; in this example it is application-defined, not a special shell code.

A1 · create evidence; keep the stopped container
docker run --name lab12-exit python:3.12-alpine python -c "import sys; print('configuration rejected', file=sys.stderr); sys.exit(23)"

فرمان non-zero برمی‌گردد؛ برای همین در بعضی shellها prompt خطا می‌بینی. این خودش دلیل پاک‌کردن container نیست. کد را با state و پیام برنامه کنار هم ثبت کن:

The command returns non-zero, so some shells display an error-style prompt. That is not a reason to remove the container. Record its code alongside state and application output:

A2 · state and application output
docker inspect lab12-exit --format 'status={{.State.Status}} exit={{.State.ExitCode}} oom={{.State.OOMKilled}} finished={{.State.FinishedAt}}'
docker logs lab12-exit
representative evidence
status=exited exit=23 oom=false finished=...
configuration rejected

تشخیص محدود و موجه: برنامه با کد خروج اختصاصی ۲۳ خارج شده و پیام خودش می‌گوید تنظیمات را رد کرده است. اصلاح را به همان فرضیه محدود می‌کنیم: ورودی یا تنظیم را درست می‌کنیم و فرمان را دوباره اجرا می‌کنیم.

A bounded diagnosis is justified: the application exited with its custom code 23, and its own message says it rejected configuration. Keep the repair tied to that hypothesis: correct the input/configuration and run a successful verification once.

A3 · corrected configuration path
docker run --name lab12-exit-fixed python:3.12-alpine python -c "print('configuration accepted; report completed')"
docker inspect lab12-exit-fixed --format 'status={{.State.Status}} exit={{.State.ExitCode}}'
docker logs lab12-exit-fixed

حادثهٔ B · محدودیت حافظهٔ container تمام می‌شودIncident B · the container memory cap is reached

این آزمایش فقط وقتی مجاز است که Engine محدودیت را بپذیرد. هر تکرار حافظه را داخل همین container سقف‌دار می‌خواهد؛ تخصیص‌ها هشت مگابایتی‌اند. --memory-swap را برابر سقف می‌گذاریم تا آزمایش swap نامحدود نداشته باشد.

Run this only if the Engine accepts the cap. Allocations occur inside this bounded container in 8 MiB chunks. Setting --memory-swap equal to the cap avoids unbounded swap for the experiment.

B1 · bounded allocation, never remove the cap
docker run --name lab12-oom --memory=64m --memory-swap=64m python:3.12-alpine python -c 'blocks=[]; exec("while True:\n blocks.append(bytearray(8*1024*1024))\n print(len(blocks)*8, flush=True)")'

پس از خروج، اول وضعیت را حفظ کن؛ نه docker rm و نه restart. انتظار داریم OOMKilled=true و اغلب exit ۱۳۷ ببینیم. اگر خروجی با انتظار فرق داشت، ثبت کن و بررسی کن؛ نتیجه را به زور با restart یا حذف محدودیت «درست» نکن.

After it exits, preserve state—do not remove or restart it yet. We expect OOMKilled=true and commonly exit 137. If the result differs, record and investigate it; do not “correct” the experiment with a restart or by removing the cap.

B2 · distinguish the exit number from Docker's OOM evidence
docker inspect lab12-oom --format 'status={{.State.Status}} exit={{.State.ExitCode}} oom={{.State.OOMKilled}} restarts={{.RestartCount}} finished={{.State.FinishedAt}}'
docker logs --tail=8 lab12-oom

در اصلاح، مسئله را «حافظهٔ بی‌نهایت بده» تعریف نکن. این برنامه برای جمع‌زدن یک بازه تمام داده را نگه می‌داشت؛ نسخهٔ بعدی از جمع‌زدن جریانی استفاده می‌کند و تنها نتیجه را نگه می‌دارد:

Do not define the repair as “give it unlimited memory.” The first program retained all data while summing a range; the corrected version streams the calculation and retains only the result:

B3 · lower-memory verification under the same cap
docker run --name lab12-oom-fixed --memory=64m --memory-swap=64m python:3.12-alpine python -c "print(sum(range(1000000)))"
docker inspect lab12-oom-fixed --format 'status={{.State.Status}} exit={{.State.ExitCode}} oom={{.State.OOMKilled}}'

این اصلاح آموزشی نشان می‌دهد شکل محاسبه روی اوج مصرف اثر دارد؛ در برنامهٔ واقعی باید همان درخواست شکست‌خورده را با دادهٔ واقعی و معیار درست دوباره بسنجی. سقف ۶۴ مگابایت توصیهٔ محیط عملیاتی نیست و تعیین عدد نهایی به فصل ۱۳ تعلق دارد.

This teaching repair shows that computation shape affects peak usage; a real application must be retested with the same failing request and representative data. A 64 MiB cap is not a production recommendation; selecting limits belongs in Chapter 13.

حادثهٔ C · مقصد موقت جا نداردIncident C · the temporary target has no room

اینجا سقف یک‌مگابایت روی خود tmpfs است. فرمان می‌خواهد ۲ مگابایت بنویسد؛ هرگز مسیر خروجی را به bind mount یا مسیر میزبان عوض نکن.

Here the 1 MiB cap is on tmpfs itself. The command attempts a 2 MiB write; never redirect its output to a bind mount or host path.

C1 · bounded ENOSPC reproduction
docker run --name lab12-enospc --mount type=tmpfs,dst=/scratch,tmpfs-size=1048576 alpine:3.21 sh -c 'dd if=/dev/zero of=/scratch/fill.bin bs=64K count=32'

خروجی No space left on device باید با inspect نشان دهد مقصد از نوع tmpfs بوده و کد خروج برنامه غیرصفر است. این مشاهده نمی‌گوید فضای Docker یا میزبان تمام است. اصلاح مناسب برای همین آزمایش این است که بار نوشتن را به ۵۱۲ کیلوبایت کاهش دهیم و همان mount را نگه داریم:

The No space left on device output should pair with inspect showing a tmpfs destination and a non-zero program result. It does not say Docker or host storage is full. For this experiment, reduce the write to 512 KiB while keeping the same mount:

C2 · verify the smaller write on the same bounded filesystem
docker inspect lab12-enospc --format 'exit={{.State.ExitCode}} mounts={{json .Mounts}}'
docker logs lab12-enospc
docker run --name lab12-enospc-fixed --mount type=tmpfs,dst=/scratch,tmpfs-size=1048576 alpine:3.21 sh -c 'dd if=/dev/zero of=/scratch/fill.bin bs=64K count=8'
docker inspect lab12-enospc-fixed --format 'status={{.State.Status}} exit={{.State.ExitCode}}'

گزارشت را برای هر حادثه در چهار یا پنج جمله کامل کن: نشانه چه بود؛ کدام خروجی مدرک شد؛ چه علت‌هایی رد یا تأیید شدند؛ چه اصلاح محدودی انجام شد؛ و همان آزمون اولیه چه نتیجه‌ای داد. عبارت «حل شد» بدون نتیجهٔ آزمون کافی نیست.

Finish each report in four or five concise statements: symptom; evidence; causes supported or ruled out; scoped repair; and result of the original verification. “Fixed” without a verification result is incomplete.

Incidentمدرک کلیدی / Key evidenceRepair و verification / Repair and verification
A · exit 23کد اختصاصی + پیام رد config / App-defined code and rejected-config logورودی اصلاح‌شده؛ همان برنامه exit 0 / Corrected input; same job succeeds
B · OOMOOMKilled، code، cap و خروجی allocation / OOM flag, code, cap, allocation outputالگوی کم‌حافظه؛ همان limit و کار قابل‌مقایسه / Lower-memory shape under the same cap
C · ENOSPCپیام dd + mount نوع tmpfs و سقف ۱ MiB / dd error, tmpfs mount and 1 MiB capنوشتن کوچک‌تر روی همان tmpfs / Smaller write on the same tmpfs

پیش از پاک‌سازی، state و log سه container را ثبت کن. بعد فقط نام‌های مخصوص همین lab را حذف کن؛ هیچ volumeای ساخته نشده و هیچ فرمان prune عمومی لازم نیست. اگر یکی از نام‌ها پیش از آزمایش وجود داشت، فقط containerهایی را حذف کن که خودت همین‌جا ساخته‌ای.

Record state and logs for all three containers before cleanup. Then remove only the names reserved for this lab; no volume was created and no global prune command is needed. If a name existed before the experiment, remove only containers you created here.

scoped cleanup · after collecting evidence
docker rm lab12-exit lab12-exit-fixed lab12-oom lab12-oom-fixed lab12-enospc lab12-enospc-fixed

پاک‌سازی موفق را با docker ps -a برای همین نام‌ها تأیید کن. نتیجه‌ای که باید با خودت ببری، فرمانی برای پاک‌کردن بیشتر نیست؛ توانایی ربط‌دادن هر خرابی به filesystem یا state مشخص و تکرار آزمون اصلی است.

Verify cleanup by checking these names in docker ps -a. The takeaway is not a more aggressive cleanup command; it is the ability to tie each failure to a specific state or filesystem and repeat the original test.

حادثه وقتی تمام می‌شود که همان مسیر دوباره سالم باشدAn incident closes when the expected result returns

در این فصل هیچ عددی را به‌تنهایی علت ننامیدیم. ۱۳۷ را با OOMKilled و لاگ سنجیدیم؛ کمبود فضا را به filesystem واقعی وصل کردیم؛ و فقط بعد از شناخت مصرف‌کننده سراغ پاک‌سازی رفتیم. این عادت از خود فرمان‌ها مهم‌تر است.

Review the chain: the symptom chose where to look; state showed what happened to the process; exit code and logs narrowed the execution story; resource and filesystem evidence identified the relevant limit or target; then we tested the repair with the original check. We never treated 137 alone as a cause or sacrificed data for “cleanup.”

دفعهٔ بعد که کسی گفت «Docker کرش کرده»، لازم نیست مخالفت کنی؛ فقط بپرس: «چه چیزی دیدیم که این را ثابت می‌کند؟» همین سؤال مسیر عیب‌یابی را از حدس به مهندسی برمی‌گرداند.

Chapter 13 turns to resources so we can design CPU and memory limits deliberately rather than raising a number blindly during an incident. This chapter used one small cap only to reproduce OOM; production diagnosis and tuning are a separate task.

قاعدهٔ آخرThe final rule

هر اصلاح باید به یک مدرک مشخص جواب بدهد و با تکرار همان درخواست، کار یا نوشتنِ شکست‌خورده تأیید شود. اگر هنوز نمی‌توانی بگویی کدام فایل‌سیستم پر بود یا چه چیزی فرایند را کشت، حادثه هنوز تشخیص داده نشده است.

Every repair should answer a specific piece of evidence and be verified by repeating the same failing request, job, or write. If you still cannot say which filesystem filled or what terminated the process, the incident is not yet diagnosed.

مرجع سریع برای لحظهٔ incidentQuick reference for the incident moment

docker ps -acontainer متوقف را هم ببین؛ از غیبت در docker ps نتیجه نگیرinclude stopped containers; absence from docker ps is not a daemon diagnosis
{{.State.ExitCode}} / {{.State.OOMKilled}}کد خروج را از مدرک OOM جدا بخوانseparate exit code from Docker's OOM evidence
{{.State.Error}} / {{.State.FinishedAt}}پیام و زمان پایان را کنار log بگذارcorrelate recorded error and finish time with logs
{{.RestartCount}}تکرار شکست را پیدا کن؛ restart به معنی recovery نیستspot repeated failure; restart does not mean recovery
df -h PATHبلوک آزاد filesystem همان مسیر را بخوانcheck free blocks on the target path's filesystem
df -i PATHinodeهای همان مقصد را هم بسنجcheck inode availability at the same target
docker system df -vمصرف image، container، volume و build cache را تفکیک کنseparate image, container, volume, and build-cache usage
docker info --format '{{.DockerRootDir}}'محل data را از دید daemon پیدا کنfind the data root from the daemon's point of view
docker inspect --format '{{.HostConfig.LogConfig.Type}}' NAMEوقتی log محلی نیست، driver همان container را بررسی کنinspect the container driver when local logs are missing

برای رفتار فعلی، مرجع رسمی docker inspect، docker ps و کدهای خروج و محدودیت حافظه و OOM را ببین. برای دیسک، docker system df، docker info و tmpfs را بخوان.

For current behavior, consult the official references for docker inspect, docker ps and exit codes, and memory constraints and OOM. For storage, read docker system df, docker info, and tmpfs.

برای پاک‌سازی هدفمند، اول راهنمای prune و محدودهٔ حذف را بخوان؛ برای log، تنظیم logging driver را ببین. در Docker Desktop، تنظیمات منابع و disk image مرجع فضای مدیریت‌شده است. حجم هر driver و پشتیبانی فرمان‌ها به Engine و محیط تو وابسته است.

Before targeted cleanup, read the official prune scope and deletion guide; for logs, consult logging-driver configuration. On Docker Desktop, resource and disk-image settings describe managed storage. Driver behavior and command support depend on your Engine and environment.