عیبیابی: 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.
«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:
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:
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.
تا وقتی وضعیت، زمان پایان و 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.
این ترتیب عمداً محافظهکارانه است. اگر قبل از جمعکردن مدرک 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.
docker inspect web --format 'status={{.State.Status}} exit={{.State.ExitCode}} oom={{.State.OOMKilled}} error={{.State.Error}} finished={{.State.FinishedAt}} restarts={{.RestartCount}}'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.Status | running، 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.
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 |
137 | 128 + 9 در قرارداد رایج؛ با SIGKILL سازگار است / Commonly corresponds to SIGKILL | OOMKilled، رویدادها و logهای میزبان / OOM flag, events, host logs |
143 | 128 + 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.
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:
docker run --name ch12-missing alpine:3.21 sh -c 'not-a-real-command'
docker inspect ch12-missing --format 'exit={{.State.ExitCode}}'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.
برای آزمایش امن، 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.
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}}'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.
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.
پس اولین واکنش نباید پاککردن 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.
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:
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.
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'
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:
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.
docker system df docker system df -v
| دسته / 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 destination | driver کجاست، 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.
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 prune | containerهای متوقف، network بلااستفاده، imageهای dangling و build cache بلااستفاده / Stopped containers, unused networks, dangling images, unused build cache | container متوقفشده شاید 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 -a | tag محلی بهتنهایی backup یا انتشار نیست / A local tag is not a backup or publication |
docker builder prune | build cache / Build cache | ساخت بعدی میتواند کندتر و پرمصرفتر شود / Later builds may be slower and use more bandwidth |
docker volume prune | volumeهای محلی بلااستفاده؛ دامنهٔ دقیق را با نسخهٔ 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.
برای عیبیابی، اول 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 executable | mode، shebang، interpreter و mount مجوزها / Mode, shebang, interpreter, mount permissions |
137 + OOMKilled=false | OOM 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 kill | limit، مصرف، و الگوی تخصیص برنامه / 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 bounded | df -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 reason | docker system df -v و وابستگیهای مصرفکننده / Detailed usage and consumers |
| volume بزرگ یا log گمشده | ممکن است دادهٔ مهم باشد یا driver جای دیگری بنویسد / May be important data or a different log destination | mountها، 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 هنوز بسته نشده است.
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.
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.
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:
docker inspect lab12-exit --format 'status={{.State.Status}} exit={{.State.ExitCode}} oom={{.State.OOMKilled}} finished={{.State.FinishedAt}}'
docker logs lab12-exitstatus=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.
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.
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.
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:
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.
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:
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 evidence | Repair و verification / Repair and verification |
|---|---|---|
| A · exit 23 | کد اختصاصی + پیام رد config / App-defined code and rejected-config log | ورودی اصلاحشده؛ همان برنامه exit 0 / Corrected input; same job succeeds |
| B · OOM | OOMKilled، 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.
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.
هر اصلاح باید به یک مدرک مشخص جواب بدهد و با تکرار همان درخواست، کار یا نوشتنِ شکستخورده تأیید شود. اگر هنوز نمیتوانی بگویی کدام فایلسیستم پر بود یا چه چیزی فرایند را کشت، حادثه هنوز تشخیص داده نشده است.
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 recoverydf -h PATHبلوک آزاد filesystem همان مسیر را بخوانcheck free blocks on the target path's filesystemdf -i PATHinodeهای همان مقصد را هم بسنجcheck inode availability at the same targetdocker system df -vمصرف image، container، volume و build cache را تفکیک کنseparate image, container, volume, and build-cache usagedocker info --format '{{.DockerRootDir}}'محل data را از دید daemon پیدا کنfind the data root from the daemon's point of viewdocker 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.