رفتن به محتوا
LoopX

لاگ، exec و inspect

توی این درس یاد می‌گیری وقتی یک کانتینر رفتار عجیبی دارد، چطور داخلش را ببینی: خروجی برنامه را با docker logs (و -f برای دنبال کردن زنده)، یک دستور یا shell را داخل کانتینر در حال اجرا با docker exec، همه‌ی جزئیات پیکربندی را با docker inspect و مصرف CPU و حافظه را با docker stats. این چهار دستور ابزار اصلی عیب‌یابی روزمره‌ات هستند.

تشبیه: دوربین مداربسته و کلید اتاق

Section titled “تشبیه: دوربین مداربسته و کلید اتاق”

کانتینر مثل یک اتاق دربسته است. docker logs دوربین مداربسته‌ای است که تمام چیزهایی که برنامه «گفته» را ضبط کرده؛ نیازی نیست وارد اتاق شوی. docker exec کلید اتاق است: وارد می‌شوی و با چشم خودت می‌بینی. docker inspect پرونده‌ی مشخصات اتاق است (ابعاد، قفل‌ها، ساکنین) و docker stats نمایشگر مصرف برق و آب آن.

روش عیب‌یابی: اول لاگ (برنامه چه گفت)، بعد exec (داخل چه خبر است)، بعد inspect (پیکربندی)، و stats (منابع).

مثال ۱: خواندن لاگ (docker logs)

Section titled “مثال ۱: خواندن لاگ (docker logs)”

یک کانتینر داریم که هر ثانیه یک خط چاپ می‌کند (lx-log). لاگ کانتینر، خروجی استاندارد (stdout) و خطای استاندارد (stderr) پروسه‌ی اصلی است:

Terminal window
docker logs --tail 3 lx-log
خروجی
tick 3
tick 4
tick 5

گزینه‌ی --tail 3 فقط سه خط آخر را نشان می‌دهد؛ برای لاگ‌های بلند حتماً از آن یا --since استفاده کن. زمان‌بندی هر خط را هم با -t (timestamps) می‌گیری:

Terminal window
docker logs -t --tail 2 lx-log | sed -E 's/^[0-9T:.-]+Z //' | head -2
docker logs --since 3s lx-log | wc -l | tr -d ' '
خروجی
tick 4
tick 5
3

دستور دوم تعداد خط‌های ۳ ثانیه‌ی اخیر را می‌شمارد (--since 3s).

مثال ۲: دنبال کردن زنده (docker logs -f)

Section titled “مثال ۲: دنبال کردن زنده (docker logs -f)”

با -f (follow) لاگ مثل tail -f زنده دنبال می‌شود تا با Ctrl+C متوقفش کنی. اینجا ۴ ثانیه دنبالش می‌کنیم و بعد قطع می‌کنیم:

Terminal window
docker logs -f --tail 1 lx-log > follow.txt 2>&1 &
p=$!; sleep 4; kill $p 2>/dev/null; wait $p 2>/dev/null
cat follow.txt
خروجی
tick 5
tick 6
tick 7
tick 8
tick 9

خط‌های تازه همین‌طور که ساخته می‌شوند ظاهر شدند. در کار واقعی docker logs -f --tail 50 NAME را می‌زنی تا ۵۰ خط آخر را ببینی و بعد زنده ادامه بدهی.

⚡ بررسی سریع

docker logs دقیقاً چه چیزی را نشان می‌دهد؟

مثال ۳: ورود به کانتینر (docker exec)

Section titled “مثال ۳: ورود به کانتینر (docker exec)”
Terminal window
docker exec lx-web ls /etc/nginx
docker exec lx-web cat /etc/nginx/conf.d/default.conf | head -8
خروجی
conf.d
fastcgi.conf
fastcgi_params
mime.types
modules
nginx.conf
scgi_params
uwsgi_params
server {
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 استفاده می‌کنی:

Terminal window
docker exec -it lx-web sh

(با exit بیرون می‌آیی و کانتینر همچنان کار می‌کند.) حالت غیرتعاملی با ورودی استاندارد:

Terminal window
docker exec -i lx-web sh <<'LXEOF'
whoami
pwd
nginx -v
LXEOF
خروجی
root
/
nginx version: nginx/1.31.6

گزینه‌های مفید: -u کاربر، -w پوشه، -e VAR=مقدار:

Terminal window
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):

Terminal window
docker inspect lx-web --format 'وضعیت: {{.State.Status}}
image: {{.Config.Image}}
آدرس IP: {{.NetworkSettings.Networks.bridge.IPAddress}}
پورت‌ها: {{.NetworkSettings.Ports}}
دستور: {{.Config.Cmd}}'
خروجی
وضعیت: running
image: nginx:alpine
آدرس IP: 172.17.0.4
پورت‌ها: map[80/tcp:[{0.0.0.0 8315} {:: 8315}]]
دستور: [nginx -g daemon off;]

برای ساختارهای تودرتو از json و یک ابزار JSON کمک می‌گیری:

Terminal window
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 شده یا نه:

Terminal window
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)”
Terminal window
docker stats --no-stream --format 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.PIDs}}' lx-web lx-log
خروجی
NAME CPU % MEM USAGE / LIMIT PIDS
lx-web 0.00% 7.496MiB / 3.825GiB 9
lx-log 1.25% 1.008MiB / 3.825GiB 2

docker stats به‌صورت پیش‌فرض زنده است؛ با --no-stream فقط یک عکس لحظه‌ای می‌گیری. ستون‌ها: درصد CPU، حافظه‌ی مصرفی (و سقف)، و تعداد پروسه‌ها. روی کانتینر بی‌کار nginx عددها بسیار کوچک‌اند.

مثال ۶: کپی فایل از کانتینر (docker cp)

Section titled “مثال ۶: کپی فایل از کانتینر (docker cp)”
Terminal window
docker cp lx-web:/etc/nginx/nginx.conf ./nginx.conf
head -4 nginx.conf
خروجی
user nginx;
worker_processes auto;

docker cp فایل را بدون نیاز به exec بین کانتینر و میزبان جابه‌جا می‌کند (در هر دو جهت). برای برداشتن یک فایل تنظیم یا لاگ خیلی کاربردی است.

داکر خروجی پروسه‌ی اصلی را با یک logging driver می‌گیرد. پیش‌فرض json-file است: هر خط در یک فایل JSON روی میزبان نوشته می‌شود و docker logs از همان می‌خواند.

Terminal window
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 کانتینر شروع می‌کند. ببین:

Terminal window
docker exec -d lx-web sleep 30
docker top lx-web | awk '{print $1, $8, $9, $10}' | head -5
خروجی
UID CMD
root nginx: master process
statd nginx: worker process
statd nginx: worker process
statd nginx: worker process

پروسه‌ی sleep 30 که با exec ساختیم، کنار پروسه‌های nginx در docker top دیده می‌شود. وقتی exec تمام شود، فقط همان پروسه می‌میرد و کانتینر دست نمی‌خورد.

دستور کاربرد گزینه‌های مهم
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

۱) exec روی کانتینر متوقف‌شده

Section titled “۱) exec روی کانتینر متوقف‌شده”
Terminal window
docker run -d --name lx-dead alpine true >/dev/null; sleep 1
docker exec lx-dead ls
docker rm lx-dead
خروجی
Error response from daemon: container de6cd05ca4f15480e871da78a36bc02629b32ab3cd29e0a5bcac41f6a0799a5a is not running
lx-dead

exec فقط روی کانتینر در حال اجرا کار می‌کند. راه‌حل: اول docker start یا علت توقف را از docker logs بخوان.

Terminal window
docker exec lx-web bash
خروجی
OCI runtime exec failed: exec failed: unable to start container process: exec: "bash": executable file not found in $PATH

image های کوچک (مثل Alpine) bash ندارند. راه‌حل: sh را امتحان کن.

Terminal window
docker logs no-such-container
خروجی
Error response from daemon: No such container: no-such-container

اسم یا شناسه غلط است. راه‌حل: با docker ps -a اسم درست را ببین.

اگر برنامه فقط به فایل می‌نویسد، docker logs خالی است (پشت پرده را ببین). راه‌حل: برنامه را به stdout/stderr هدایت کن.

✎ تمرینآسان

آخرین ۲ خط لاگ کانتینر lx-log را نشان بده.

دیدن جواب
Terminal window
docker logs --tail 2 lx-log
خروجی
tick 22
tick 23
✎ تمرینمتوسط

داخل کانتینر lx-web پیدا کن nginx روی چه پورتی گوش می‌دهد (فایل default.conf را بخوان و فقط خط listen را نشان بده) و بعد با docker inspect همان‌جا ثابت کن کانتینر روی همین پورت داخلی 80/tcp منتشر شده.

دیدن جواب
Terminal window
docker exec lx-web grep -n listen /etc/nginx/conf.d/default.conf | head -2
docker 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 بیرون بکش، و در آخر آن را حذف کن.

دیدن جواب
Terminal window
docker run --name lx-dead alpine sh -c 'echo خداحافظ; exit 7'
docker logs lx-dead
docker inspect lx-dead --format 'کد خروج: {{.State.ExitCode}}'
docker rm lx-dead
خروجی
خداحافظ
خداحافظ
کد خروج: 7
lx-dead

لاگ و inspect هر دو روی کانتینر متوقف‌شده هم کار می‌کنند؛ فقط exec نیاز به کانتینر در حال اجرا دارد.

؟ آزمونک
  1. برای دیدن زنده‌ی لاگ یک کانتینر از چه گزینه‌ای استفاده می‌کنی؟

  2. docker exec چه چیزی می‌سازد؟

  3. اگر برنامه فقط داخل یک فایل لاگ می‌نویسد، docker logs چه نشان می‌دهد؟

  4. کد خروج یک کانتینر متوقف‌شده را از کجا می‌خوانی؟

  5. docker stats --no-stream چه می‌کند؟

  • 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 shshell تعاملی داخل کانتینر
docker exec -u USER -w DIR NAME CMDاجرا با کاربر و پوشه‌ی مشخص
docker inspect NAMEJSON کامل
docker inspect --format "{{.State.ExitCode}}" NAMEیک فیلد مشخص
docker stats --no-streamعکس لحظه‌ای مصرف منابع
docker top NAMEپروسه‌های کانتینر
docker cp NAME:/path ./کپی فایل از کانتینر