ESC را فشار دهید تا بسته شود

جلوگیری از پر شدن دیسک VPS با لاگ Docker؛ آموزش Log Rotation و عیب‌یابی

فهرست

اگر فضای دیسک VPS بدون اینکه فایل بزرگی عمداً ذخیره کرده باشید مدام کم می‌شود، یکی از اولین جاهایی که باید بررسی کنید لاگ کانتینرهای Docker است. در نصب‌های پیش‌فرض Docker، درایور json-file می‌تواند بدون محدودیت مشخص رشد کند و روی سرورهای پرترافیک یا سرویس‌هایی که خطای تکراری تولید می‌کنند، فایل لاگ یک کانتینر به‌تنهایی چندین گیگابایت شود. در این راهنما ابتدا ثابت می‌کنیم مشکل واقعاً از Docker logs است، سپس درایور لاگ و مسیر فایل را پیدا می‌کنیم، برای local یا json-file Rotation می‌گذاریم، کانتینرها را به‌درستی Recreate می‌کنیم، نتیجه را Verify می‌کنیم و در پایان مسیر Rollback و Recovery را هم داریم.

چرا لاگ Docker می‌تواند دیسک VPS را پر کند؟

Docker خروجی stdout و stderr کانتینر را با Logging Driver مدیریت می‌کند. روی بسیاری از نصب‌ها مقدار پیش‌فرض json-file است. این Driver برای سازگاری گسترده استفاده می‌شود، اما اگر برای آن max-size و max-file تعریف نشده باشد، فایل لاگ می‌تواند پیوسته بزرگ شود.

رشد لاگ معمولاً در این سناریوها شدیدتر است:

  • وب‌اپلیکیشن یا API که Access Log پرحجم روی stdout می‌نویسد؛
  • Loop خطا در برنامه و چاپ چندباره Stack Trace؛
  • Containerی که health check یا retry بسیار پرتکرار دارد؛
  • سرویس‌های Node.js، PHP، Python یا Java که سطح Log روی debug مانده است؛
  • چند کانتینر روی VPS کم‌حجم که هر کدام چندصد مگابایت تا چند گیگابایت لاگ ساخته‌اند.

اگر مشکل شما «فایل حذف شده اما فضای دیسک آزاد نشده» است، آن سناریو متفاوت است و راهنمای رفع مشکل فضای دیسک با lsof +L1 را ببینید.

مرحله ۱: اول ثابت کنید واقعاً دیسک در حال پر شدن است

وضعیت filesystem را ببینید:

df -hT

اگر پارتیشن روت یا /var نزدیک 90 تا 100 درصد است، مصرف دایرکتوری‌های اصلی را مقایسه کنید:

sudo du -xhd1 /var 2>/dev/null | sort -h

در اغلب نصب‌های rootful، داده Docker زیر /var/lib/docker قرار دارد. برای دیدن اندازه کلی آن:

sudo du -sh /var/lib/docker 2>/dev/null

اگر Docker Root Directory شما جای دیگری است، مسیر درست را از این دستور بخوانید:

docker info --format '{{.DockerRootDir}}'

مرحله ۲: Logging Driver فعلی Docker را پیدا کنید

Driver پیش‌فرض daemon را ببینید:

docker info --format '{{.LoggingDriver}}'

خروجی متداول:

json-file

یا ممکن است:

local

برای یک کانتینر مشخص هم تنظیم واقعی را بررسی کنید:

docker inspect -f '{{json .HostConfig.LogConfig}}' CONTAINER_NAME

و مسیر فایل لاگ را بگیرید:

docker inspect -f '{{.LogPath}}' CONTAINER_NAME

اگر از json-file استفاده می‌کنید، مسیر معمولاً چیزی زیر شاخه containers در Docker Root Directory است.

مرحله ۳: کانتینر یا لاگ حجیم را پیدا کنید

برای دیدن کانتینرهای فعال:

docker ps --format 'table {{.Names}} | {{.ID}} | {{.Status}}'

سپس برای هر کانتینر مسیر لاگ را بخوانید و اندازه فایل را ببینید:

LOG=$(docker inspect -f '{{.LogPath}}' CONTAINER_NAME)
sudo ls -lh "$LOG"

اگر تعداد کانتینرها زیاد است، این Loop دید خوبی می‌دهد:

for c in $(docker ps -aq); do
  name=$(docker inspect -f '{{.Name}}' "$c" | sed 's#^/##')
  log=$(docker inspect -f '{{.LogPath}}' "$c")
  [ -n "$log" ] && sudo du -h "$log" 2>/dev/null | awk -v n="$name" '{print $1, n, $2}'
done | sort -h

نکته: فایل‌های داخلی Docker را به‌صورت روتین با ابزارهای بیرونی rotate یا حذف نکنید. Docker خودش باید چرخه لاگ را مدیریت کند؛ دستکاری مستقیم فایل برای کار روزمره روش استاندارد نیست.

مرحله ۴: روش پیشنهادی برای سرور جدید؛ استفاده از local logging driver

Docker برای جلوگیری از تمام‌شدن فضای دیسک، Driver محلی local را برای سناریوهای عمومی گزینه مناسبی می‌داند چون Rotation داخلی دارد و فرمت ذخیره‌سازی آن بهینه‌تر است.

فایل تنظیمات daemon را باز کنید:

sudo mkdir -p /etc/docker
sudo nano /etc/docker/daemon.json

اگر فایل تازه است:

{
  "log-driver": "local"
}

قبل از restart، JSON را بررسی کنید:

sudo python3 -m json.tool /etc/docker/daemon.json >/dev/null && echo OK

سپس Docker را restart کنید:

sudo systemctl restart docker

Readback:

docker info --format '{{.LoggingDriver}}'

باید ببینید:

local

مرحله ۵: اگر باید json-file بماند، Rotation را محدود کنید

اگر ابزار Log Collector یا Workflow شما به json-file وابسته است، می‌توانید محدودیت اندازه و تعداد فایل‌ها را فعال کنید:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

در این نمونه، هر فایل حداکثر حدود 10MB می‌شود و حداکثر سه فایل نگه داشته می‌شود. عدد مناسب را با توجه به ترافیک، مدت نگه‌داری لاگ و ظرفیت VPS انتخاب کنید.

مهم: مقدارهای داخل log-opts باید به‌صورت string نوشته شوند؛ یعنی "3" و "10m"، نه عدد خام.

پس از ذخیره:

sudo python3 -m json.tool /etc/docker/daemon.json
sudo systemctl restart docker

مرحله ۶: چرا تغییر daemon.json روی کانتینرهای قبلی اعمال نمی‌شود؟

یکی از رایج‌ترین اشتباه‌ها این است که کاربر daemon.json را اصلاح می‌کند، Docker را restart می‌کند و انتظار دارد همه کانتینرهای موجود فوراً Driver جدید بگیرند. تنظیم Logging Driver هنگام ایجاد کانتینر ثبت می‌شود؛ بنابراین کانتینرهای موجود باید Recreate شوند.

برای Docker Compose:

docker compose config

docker compose up -d --force-recreate

بعد از Recreate، Driver را Verify کنید:

docker inspect -f '{{json .HostConfig.LogConfig}}' CONTAINER_NAME

اگر از Docker Compose برای مدیریت چند کانتینر استفاده می‌کنید، Recreate را در زمان Maintenance Window انجام دهید تا اثر قطعی کنترل شود.

تنظیم Log Rotation فقط برای یک سرویس در Docker Compose

لازم نیست همیشه تنظیم را سراسری کنید. می‌توانید فقط یک سرویس پرلاگ را محدود کنید:

services:
  app:
    image: your-image:latest
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

سپس:

docker compose config

docker compose up -d --force-recreate app

Verify:

docker inspect -f '{{json .HostConfig.LogConfig}}' $(docker compose ps -q app)

اگر دیسک همین حالا 100٪ شده چه کار کنیم؟

وقتی دیسک کاملاً پر است، قبل از هر کار ابتدا سرویس پرلاگ را شناسایی کنید. اگر امکان دارد لاگ برنامه را از داخل خود برنامه کم کنید یا سرویس را موقتاً متوقف کنید:

docker stop CONTAINER_NAME

بعد Rotation را درست تنظیم و کانتینر را Recreate کنید.

اگر به‌خاطر 100٪ شدن دیسک حتی Recreate ممکن نیست، حذف یا truncate مستقیم فایل لاگ فقط یک راه Recovery اضطراری است، نه روش نگهداری روزمره. قبل از آن مسیر را دوباره با docker inspect Verify و کانتینر را متوقف کنید. برای محیط Production، Snapshot یا Backup و Maintenance Window داشته باشید.

اگر مشکل اصلی آزاد نشدن فضا بعد از حذف فایل است، مقاله بررسی فایل‌های deleted ولی باز با lsof را اجرا کنید.

Verify نهایی بعد از اصلاح

بعد از تغییر فقط به «کانتینر بالا آمد» اکتفا نکنید. این مراحل را اجرا کنید:

  1. Driver پیش‌فرض Docker را بخوانید.
  2. Driver واقعی کانتینر Recreate شده را با docker inspect بررسی کنید.
  3. مسیر لاگ و اندازه آن را ثبت کنید.
  4. چند دقیقه یا چند ساعت بعد رشد فایل را دوباره اندازه بگیرید.
  5. فضای دیسک را با df -h بررسی کنید.
  6. Health Check و سرویس اصلی برنامه را تست کنید.

دستورهای سریع:

docker info --format '{{.LoggingDriver}}'
docker inspect -f '{{json .HostConfig.LogConfig}}' CONTAINER_NAME
docker inspect -f '{{.LogPath}}' CONTAINER_NAME
df -hT
docker ps

خطاهای رایج هنگام تنظیم Docker Log Rotation

  • فقط restart کردن Docker: کانتینرهای موجود تنظیم Driver جدید را خودکار نمی‌گیرند و باید Recreate شوند.
  • اشتباه در JSON: یک کاما یا کوتیشن اشتباه می‌تواند باعث Fail شدن Docker daemon شود. قبل از restart حتماً فایل را parse کنید.
  • استفاده از عدد خام در log-opts: گزینه‌های max-size و max-file را به‌صورت string بنویسید.
  • Rotation بیرونی روی فایل داخلی Docker: دستکاری فایل *-json.log با logrotate سیستم ممکن است با مدیریت فایل توسط Docker تداخل ایجاد کند.
  • ندیدن علت اصلی: Rotation فقط جلوی رشد بی‌نهایت را می‌گیرد. اگر برنامه هزاران Error در دقیقه تولید می‌کند، ریشه مشکل Application باید اصلاح شود.
  • پر بودن inode به‌جای فضا: اگر df -h خوب است اما No space left on device می‌گیرید، df -i را هم ببینید.

برای دید بهتر روی مصرف CPU، RAM، Disk و Processها، مقاله دستورات مانیتورینگ سرورهای لینوکس مکمل مناسبی است.

Rollback و Recovery

قبل از تغییر، فایل قبلی را Backup بگیرید:

sudo cp -a /etc/docker/daemon.json /etc/docker/daemon.json.bak.$(date +%F-%H%M%S)

اگر بعد از تغییر Docker بالا نیامد:

sudo systemctl status docker --no-pager
sudo journalctl -u docker -n 100 --no-pager

فایل JSON را دوباره Validate کنید:

sudo python3 -m json.tool /etc/docker/daemon.json

اگر لازم است Backup را برگردانید و Docker را restart کنید:

sudo cp -a /etc/docker/daemon.json.bak.YYYY-MM-DD-HHMMSS /etc/docker/daemon.json
sudo systemctl restart docker

سپس با docker info و docker ps Readback بگیرید.

پیشنهاد عملی برای VPSهای Production

برای VPS معمولی که Log Collector خاصی ندارد، local انتخاب ساده و کم‌دردسرتری است. اگر به سازگاری json-file نیاز دارید، Rotation را صریحاً با max-size و max-file محدود کنید. علاوه بر آن:

  • برای Disk Usage هشدار 75٪، 85٪ و 95٪ تعریف کنید؛
  • سطح لاگ برنامه را روی Production از debug خارج کنید؛
  • رشد لاگ کانتینرهای پرترافیک را جداگانه مانیتور کنید؛
  • قبل از Recreate کانتینرهای مهم، Backup و Rollback Plan داشته باشید.

اگر برای اجرای چند کانتینر Production به منابع قابل ارتقا و دسترسی کامل سیستم‌عامل نیاز دارید، مشخصات سرور مجازی وان‌سرور را می‌توانید بر اساس RAM، CPU و فضای دیسک موردنیاز بررسی کنید.

جمع‌بندی

پر شدن ناگهانی دیسک VPS همیشه به معنی کمبود واقعی Storage نیست؛ گاهی فقط یک کانتینر با Logging بدون محدودیت چندین گیگابایت فایل ساخته است. مسیر درست این است که ابتدا با df و docker inspect علت را ثابت کنید، Driver و LogPath را بخوانید، سپس local یا Rotation کنترل‌شده json-file را اعمال کنید. بعد از تغییر حتماً کانتینرهای قدیمی را Recreate کنید و با Readback واقعی مطمئن شوید تنظیم روی همان کانتینر اعمال شده است. در نهایت اندازه لاگ، سلامت سرویس و فضای دیسک را دوباره Verify کنید و Backup فایل daemon.json را برای Rollback نگه دارید.

منابع فنی

Rate this post
اشتراک گذاری نوشته در:

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *