توی این درس یاد میگیری وقتی یک کانتینر رفتار عجیبی دارد، چطور داخلش را ببینی: خروجی برنامه را با docker logs (و -f برای دنبال کردن زنده)، یک دستور یا shell را داخل کانتینر در حال اجرا با docker exec، همهی جزئیات پیکربندی را با docker inspect و مصرف CPU و حافظه را با docker stats. این چهار دستور ابزار اصلی عیبیابی روزمرهات هستند.
تشبیه: دوربین مداربسته و کلید اتاق
Section titled “تشبیه: دوربین مداربسته و کلید اتاق”کانتینر مثل یک اتاق دربسته است. docker logs دوربین مداربستهای است که تمام چیزهایی که برنامه «گفته» را ضبط کرده؛ نیازی نیست وارد اتاق شوی. docker exec کلید اتاق است: وارد میشوی و با چشم خودت میبینی. docker inspect پروندهی مشخصات اتاق است (ابعاد، قفلها، ساکنین) و docker stats نمایشگر مصرف برق و آب آن.
چهار ابزار، یک نگاه
Section titled “چهار ابزار، یک نگاه”مثالهای عملی
Section titled “مثالهای عملی”مثال ۱: خواندن لاگ (docker logs)
Section titled “مثال ۱: خواندن لاگ (docker logs)”یک کانتینر داریم که هر ثانیه یک خط چاپ میکند (lx-log). لاگ کانتینر، خروجی استاندارد (stdout) و خطای استاندارد (stderr) پروسهی اصلی است:
docker logs --tail 3 lx-logtick 3tick 4tick 5گزینهی --tail 3 فقط سه خط آخر را نشان میدهد؛ برای لاگهای بلند حتماً از آن یا --since استفاده کن. زمانبندی هر خط را هم با -t (timestamps) میگیری:
docker logs -t --tail 2 lx-log | sed -E 's/^[0-9T:.-]+Z //' | head -2docker logs --since 3s lx-log | wc -l | tr -d ' 'tick 4tick 53دستور دوم تعداد خطهای ۳ ثانیهی اخیر را میشمارد (--since 3s).
مثال ۲: دنبال کردن زنده (docker logs -f)
Section titled “مثال ۲: دنبال کردن زنده (docker logs -f)”با -f (follow) لاگ مثل tail -f زنده دنبال میشود تا با Ctrl+C متوقفش کنی. اینجا ۴ ثانیه دنبالش میکنیم و بعد قطع میکنیم:
docker logs -f --tail 1 lx-log > follow.txt 2>&1 &p=$!; sleep 4; kill $p 2>/dev/null; wait $p 2>/dev/nullcat follow.txttick 5tick 6tick 7tick 8tick 9خطهای تازه همینطور که ساخته میشوند ظاهر شدند. در کار واقعی docker logs -f --tail 50 NAME را میزنی تا ۵۰ خط آخر را ببینی و بعد زنده ادامه بدهی.
docker logs دقیقاً چه چیزی را نشان میدهد؟
برنامه باید به stdout/stderr بنویسد تا در docker logs دیده شود؛ نوشتن فقط در فایل لاگ داخل کانتینر دیده نمیشود.
مثال ۳: ورود به کانتینر (docker exec)
Section titled “مثال ۳: ورود به کانتینر (docker exec)”docker exec lx-web ls /etc/nginxdocker exec lx-web cat /etc/nginx/conf.d/default.conf | head -8conf.dfastcgi.conffastcgi_paramsmime.typesmodulesnginx.confscgi_paramsuwsgi_paramsserver { listen 80; listen [::]:80; server_name localhost;
#access_log /var/log/nginx/host.access.log main;
location / {docker exec یک پروسهی تازه را داخل کانتینر در حال اجرا اجرا میکند (نه یک کانتینر جدید). اینجا فایلهای تنظیم nginx را پیدا کردیم: nginx.conf اصلی و conf.d/default.conf برای سایت پیشفرض. برای shell تعاملی از -it استفاده میکنی:
docker exec -it lx-web sh(با exit بیرون میآیی و کانتینر همچنان کار میکند.) حالت غیرتعاملی با ورودی استاندارد:
docker exec -i lx-web sh <<'LXEOF'whoamipwdnginx -vLXEOFroot/nginx version: nginx/1.31.6گزینههای مفید: -u کاربر، -w پوشه، -e VAR=مقدار:
docker exec -u nginx -w /etc/nginx -e GREETING=hi lx-web sh -c 'echo "کاربر: $(whoami) | پوشه: $(pwd) | $GREETING"'کاربر: nginx | پوشه: /etc/nginx | hiمثال ۴: پروندهی مشخصات (docker inspect)
Section titled “مثال ۴: پروندهی مشخصات (docker inspect)”docker inspect یک JSON بزرگ میدهد؛ معمولاً با --format فقط همان چیزی را که میخواهی بیرون میکشی (قالب Go template):
docker inspect lx-web --format 'وضعیت: {{.State.Status}}image: {{.Config.Image}}آدرس IP: {{.NetworkSettings.Networks.bridge.IPAddress}}پورتها: {{.NetworkSettings.Ports}}دستور: {{.Config.Cmd}}'وضعیت: runningimage: nginx:alpineآدرس IP: 172.17.0.4پورتها: map[80/tcp:[{0.0.0.0 8315} {:: 8315}]]دستور: [nginx -g daemon off;]برای ساختارهای تودرتو از json و یک ابزار JSON کمک میگیری:
docker inspect lx-web --format '{{json .Config.Env}}' | python3 -m json.tool | head -6[ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "NGINX_VERSION=1.31.6", "PKG_RELEASE=1", "DYNPKG_RELEASE=1", "NJS_VERSION=1.0.1",و برای کانتینر متوقفشده، کد خروج و اینکه OOM kill شده یا نه:
docker run --name lx-dead alpine sh -c 'exit 3' ; docker inspect lx-dead --format 'کد خروج: {{.State.ExitCode}} | OOMKilled: {{.State.OOMKilled}}'docker rm lx-dead >/dev/nullکد خروج: 3 | OOMKilled: falseمثال ۵: مصرف منابع (docker stats)
Section titled “مثال ۵: مصرف منابع (docker stats)”docker stats --no-stream --format 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.PIDs}}' lx-web lx-logNAME CPU % MEM USAGE / LIMIT PIDSlx-web 0.00% 7.496MiB / 3.825GiB 9lx-log 1.25% 1.008MiB / 3.825GiB 2docker stats بهصورت پیشفرض زنده است؛ با --no-stream فقط یک عکس لحظهای میگیری. ستونها: درصد CPU، حافظهی مصرفی (و سقف)، و تعداد پروسهها. روی کانتینر بیکار nginx عددها بسیار کوچکاند.
مثال ۶: کپی فایل از کانتینر (docker cp)
Section titled “مثال ۶: کپی فایل از کانتینر (docker cp)”docker cp lx-web:/etc/nginx/nginx.conf ./nginx.confhead -4 nginx.confuser nginx;worker_processes auto;docker cp فایل را بدون نیاز به exec بین کانتینر و میزبان جابهجا میکند (در هر دو جهت). برای برداشتن یک فایل تنظیم یا لاگ خیلی کاربردی است.
پشت پرده
Section titled “پشت پرده”لاگ کجا ذخیره میشود؟
Section titled “لاگ کجا ذخیره میشود؟”داکر خروجی پروسهی اصلی را با یک logging driver میگیرد. پیشفرض json-file است: هر خط در یک فایل JSON روی میزبان نوشته میشود و docker logs از همان میخواند.
docker inspect lx-web --format 'logging driver: {{.HostConfig.LogConfig.Type}}'logging driver: json-fileنتیجهی مهم: اگر برنامهات فقط داخل یک فایل (مثلاً /var/log/app.log) مینویسد، docker logs چیزی نشان نمیدهد. برنامههای کانتینری باید به stdout/stderr بنویسند (image رسمی nginx لاگهایش را به همین دلیل به stdout هدایت میکند). چرخش و محدود کردن لاگها در درس «لاگها در داکر» میآید.
exec یعنی یک پروسهی تازه در همان namespace ها
Section titled “exec یعنی یک پروسهی تازه در همان namespace ها”docker exec کانتینر تازه نمیسازد؛ یک پروسهی جدید داخل همان namespace ها و cgroup کانتینر شروع میکند. ببین:
docker exec -d lx-web sleep 30docker top lx-web | awk '{print $1, $8, $9, $10}' | head -5UID CMDroot nginx: master processstatd nginx: worker processstatd nginx: worker processstatd nginx: worker processپروسهی sleep 30 که با exec ساختیم، کنار پروسههای nginx در docker top دیده میشود. وقتی exec تمام شود، فقط همان پروسه میمیرد و کانتینر دست نمیخورد.
جدولهای مرجع
Section titled “جدولهای مرجع”| دستور | کاربرد | گزینههای مهم |
|---|---|---|
docker logs N |
خروجی پروسهی اصلی | -f زنده، --tail N، --since 10m، -t زمان |
docker exec N CMD |
اجرای دستور در کانتینر | -it تعاملی، -u کاربر، -w پوشه، -e متغیر، -d پسزمینه |
docker inspect N |
JSON کامل پیکربندی و وضعیت | --format '{{...}}' |
docker stats |
CPU، حافظه، شبکه، I/O | --no-stream |
docker top N |
پروسههای داخل کانتینر | |
docker cp A B |
کپی فایل | N:/path ./ و برعکس |
فیلدهای پرکاربرد docker inspect
Section titled “فیلدهای پرکاربرد docker inspect”| فیلد | یعنی |
|---|---|
.State.Status |
running، exited و … |
.State.ExitCode |
کد خروج |
.State.OOMKilled |
آیا بهخاطر کمبود حافظه کشته شد |
.NetworkSettings.Networks.bridge.IPAddress |
آدرس IP در شبکهی bridge |
.Config.Env |
متغیرهای محیطی |
.Mounts |
volume ها و bind mount ها |
.HostConfig.LogConfig.Type |
logging driver |
اشتباهات رایج
Section titled “اشتباهات رایج”۱) exec روی کانتینر متوقفشده
Section titled “۱) exec روی کانتینر متوقفشده”docker run -d --name lx-dead alpine true >/dev/null; sleep 1docker exec lx-dead lsdocker rm lx-deadError response from daemon: container de6cd05ca4f15480e871da78a36bc02629b32ab3cd29e0a5bcac41f6a0799a5a is not runninglx-deadexec فقط روی کانتینر در حال اجرا کار میکند. راهحل: اول docker start یا علت توقف را از docker logs بخوان.
۲) shell وجود ندارد
Section titled “۲) shell وجود ندارد”docker exec lx-web bashOCI runtime exec failed: exec failed: unable to start container process: exec: "bash": executable file not found in $PATHimage های کوچک (مثل Alpine) bash ندارند. راهحل: sh را امتحان کن.
۳) اسم کانتینر اشتباه
Section titled “۳) اسم کانتینر اشتباه”docker logs no-such-containerError response from daemon: No such container: no-such-containerاسم یا شناسه غلط است. راهحل: با docker ps -a اسم درست را ببین.
۴) انتظار لاگ از فایل
Section titled “۴) انتظار لاگ از فایل”اگر برنامه فقط به فایل مینویسد، docker logs خالی است (پشت پرده را ببین). راهحل: برنامه را به stdout/stderr هدایت کن.
آخرین ۲ خط لاگ کانتینر lx-log را نشان بده.
دیدن جواب
docker logs --tail 2 lx-logtick 22tick 23داخل کانتینر lx-web پیدا کن nginx روی چه پورتی گوش میدهد (فایل default.conf را بخوان و فقط خط listen را نشان بده) و بعد با docker inspect همانجا ثابت کن کانتینر روی همین پورت داخلی 80/tcp منتشر شده.
دیدن جواب
docker exec lx-web grep -n listen /etc/nginx/conf.d/default.conf | head -2docker inspect lx-web --format '{{range $p, $conf := .NetworkSettings.Ports}}{{$p}} -> {{(index $conf 0).HostPort}}{{end}}'2: listen 80;3: listen [::]:80;80/tcp -> 8315یک کانتینر lx-dead بساز که با کد خروج 7 تمام شود و قبل از تمام شدن یک خط خداحافظ چاپ کند. بعد بدون exec (چون متوقف است)، هم آن خط را از لاگ بخوان و هم کد خروج را از inspect بیرون بکش، و در آخر آن را حذف کن.
دیدن جواب
docker run --name lx-dead alpine sh -c 'echo خداحافظ; exit 7'docker logs lx-deaddocker inspect lx-dead --format 'کد خروج: {{.State.ExitCode}}'docker rm lx-deadخداحافظخداحافظکد خروج: 7lx-deadلاگ و inspect هر دو روی کانتینر متوقفشده هم کار میکنند؛ فقط exec نیاز به کانتینر در حال اجرا دارد.
آزمونک
Section titled “آزمونک”برای دیدن زندهی لاگ یک کانتینر از چه گزینهای استفاده میکنی؟
docker logs -f مثل tail -f خطهای تازه را نشان میدهد.
docker exec چه چیزی میسازد؟
پروسهی exec همان namespace ها و cgroup کانتینر را دارد.
اگر برنامه فقط داخل یک فایل لاگ مینویسد، docker logs چه نشان میدهد؟
برنامههای کانتینری باید به stdout/stderr بنویسند.
کد خروج یک کانتینر متوقفشده را از کجا میخوانی؟
inspect روی کانتینر متوقفشده هم کار میکند.
docker stats --no-stream چه میکند؟
بدون --no-stream خروجی زنده است و تا Ctrl+C ادامه دارد.
جمعبندی
Section titled “جمعبندی”docker logs(با-f،--tail،--since،-t) خروجی stdout/stderr پروسهی اصلی را نشان میدهد.docker execیک پروسهی تازه داخل کانتینر در حال اجرا میسازد (-itبرای shell،-u/-w/-eبرای تنظیم).docker inspect --formatهر فیلد پیکربندی و وضعیت را بیرون میکشد؛ روی کانتینر متوقفشده هم کار میکند.docker stats --no-streamمصرف منابع وdocker cpکپی فایل است.- مسیر عیبیابی: logs ← exec ← inspect ← stats.
| دستور | کاری که میکند |
|---|---|
docker logs NAME | لاگ کانتینر |
docker logs -f --tail 50 NAME | ادامهی زنده از ۵۰ خط آخر |
docker logs --since 10m NAME | لاگ ۱۰ دقیقهی اخیر |
docker exec -it NAME sh | shell تعاملی داخل کانتینر |
docker exec -u USER -w DIR NAME CMD | اجرا با کاربر و پوشهی مشخص |
docker inspect NAME | JSON کامل |
docker inspect --format "{{.State.ExitCode}}" NAME | یک فیلد مشخص |
docker stats --no-stream | عکس لحظهای مصرف منابع |
docker top NAME | پروسههای کانتینر |
docker cp NAME:/path ./ | کپی فایل از کانتینر |