راهاندازی سیستم لاگ متمرکز برای چند سرور با Grafana Loki و Alloy
وقتی فقط یک سرور دارید، اجرای journalctl یا بررسی فایلهای داخل /var/log معمولاً کافی است. با افزایش تعداد سرورها، همین کار ساده وقتگیر میشود: برای پیدا کردن یک خطا باید وارد چند ماشین شوید، بازه زمانی هر کدام را بررسی کنید و امیدوار باشید لاگ موردنظر هنوز پاک نشده باشد.
در این راهنما، یک معماری ساده برای راهاندازی لاگ متمرکز با Grafana Loki میسازیم. Loki و Grafana روی یک سرور مرکزی اجرا میشوند و Grafana Alloy لاگهای هر سرور لینوکسی را جمعآوری و ارسال میکند. در پایان میتوانیم همه لاگها را از یک رابط جستوجو کنیم، با LogQL فیلتر بسازیم و برای نگهداری دادهها محدودیت مشخصی بگذاریم.
معماری مورد استفاده
برای نمونه، این زیرساخت را در نظر میگیریم:
- یک سرور مرکزی با آدرس خصوصی 10.10.0.10
- دو یا چند سرور مجازی یا اختصاصی بهعنوان منبع لاگ
- Docker و Docker Compose روی سرور مرکزی
- Docker روی سرورهای منبع برای اجرای Alloy
- ارتباط خصوصی میان سرورها، مانند VLAN، تونل WireGuard یا شبکه داخلی دیتاسنتر
هر سرور منبع، فایلهای لاگ محلی را با Alloy میخواند. Alloy برای هر جریان لاگ برچسبهایی مانند نام سرور، نوع سرویس و نام فایل میسازد و داده را به Loki میفرستد. Grafana نیز Loki را بهعنوان Data Source میخواند.
Loki برخلاف سامانههایی که متن کامل هر خط را ایندکس میکنند، بیشتر برچسبهای لاگ را ایندکس میکند. این روش معمولاً مصرف دیسک و حافظه کمتری دارد، اما انتخاب نادرست برچسبها میتواند کارایی را کاهش دهد.
Loki، Alloy و Grafana چه وظیفهای دارند؟
اجزای این سامانه مسئولیتهای جداگانهای دارند:
| مؤلفه | محل اجرا | وظیفه |
|---|---|---|
| Grafana Alloy | هر سرور منبع | خواندن، برچسبگذاری و ارسال لاگ |
| Grafana Loki | سرور مرکزی | ذخیره و جستوجوی لاگها |
| Grafana | سرور مرکزی | نمایش، جستوجو، داشبورد و هشدار |
| LogQL | داخل Grafana یا API | فیلتر و تحلیل دادههای Loki |
Promtail قبلاً ایجنت رایج Loki بود، اما Alloy گزینه جدیدتر Grafana برای جمعآوری سیگنالهای مشاهدهپذیری است. Alloy علاوه بر لاگ، برای متریک، تریس و پروفایل نیز قابل استفاده است. در این مقاله فقط مسیر جمعآوری لاگ را فعال میکنیم.
آمادهسازی سرور مرکزی
ابتدا یک پوشه برای سرویسها بسازید:
sudo mkdir -p /opt/central-logging/{loki,grafana/provisioning/datasources}
cd /opt/central-logging
فایل /opt/central-logging/loki/config.yaml را با محتوای زیر ایجاد کنید:
auth_enabled: false
server:
http_listen_port: 3100
common:
path_prefix: /loki
replication_factor: 1
ring:
kvstore:
store: inmemory
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
schema_config:
configs:
- from: 2024-01-01
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
compactor:
working_directory: /loki/compactor
retention_enabled: true
delete_request_store: filesystem
limits_config:
retention_period: 720h
مقدار 720h یعنی لاگها ۳۰ روز نگهداری شوند. این عدد را باید با ظرفیت دیسک، حجم روزانه لاگ و نیاز عملیاتی تنظیم کنید.
سپس Data Source مربوط به Grafana را در فایل /opt/central-logging/grafana/provisioning/datasources/loki.yaml قرار دهید:
apiVersion: 1
datasources:
- name: Loki
type: loki
access: proxy
url: http://loki:3100
isDefault: true
editable: false
حالا فایل compose.yaml را بسازید:
services:
loki:
image: grafana/loki:3.2.1
command: -config.file=/etc/loki/config.yaml
restart: unless-stopped
volumes:
- ./loki/config.yaml:/etc/loki/config.yaml:ro
- loki-data:/loki
ports:
- "10.10.0.10:3100:3100"
grafana:
image: grafana/grafana:11.3.1
restart: unless-stopped
environment:
GF_SECURITY_ADMIN_USER: admin
GF_SECURITY_ADMIN_PASSWORD: change-this-password
volumes:
- grafana-data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning:ro
ports:
- "10.10.0.10:3000:3000"
depends_on:
- loki
volumes:
loki-data:
grafana-data:
نسخههای تصویر نمونه هستند. پیش از استقرار، یک نسخه پایدار و پشتیبانیشده را انتخاب و همان نسخه را ثابت کنید. استفاده از تگ latest ممکن است پس از بهروزرسانی خودکار، تغییر ناسازگاری وارد کند.
سرویسها را اجرا کنید:
sudo docker compose up -d
sudo docker compose ps
آماده بودن Loki را بررسی کنید:
curl http://10.10.0.10:3100/ready
پاسخ ready نشان میدهد سرویس درخواست میپذیرد. رابط Grafana نیز روی پورت 3000 در دسترس است.
نصب و تنظیم Alloy روی سرورهای منبع
روی هر سرور منبع، پوشههای لازم را ایجاد کنید:
sudo mkdir -p /opt/alloy
cd /opt/alloy
فایل /opt/alloy/config.alloy را بسازید:
local.file_match "system_logs" {
path_targets = [
{
"__path__" = "/var/log/syslog",
"job" = "system",
"host" = "app-01",
},
{
"__path__" = "/var/log/auth.log",
"job" = "auth",
"host" = "app-01",
},
{
"__path__" = "/var/log/nginx/*.log",
"job" = "nginx",
"host" = "app-01",
},
]
}
loki.source.file "local_files" {
targets = local.file_match.system_logs.targets
forward_to = [loki.write.central.receiver]
}
loki.write "central" {
endpoint {
url = "http://10.10.0.10:3100/loki/api/v1/push"
}
}
در هر سرور، مقدار برچسب host را تغییر دهید؛ برای مثال db-01 یا web-02. بهتر است این نام ثابت و قابل تشخیص باشد.
فایل Compose مربوط به Alloy:
services:
alloy:
image: grafana/alloy:v1.5.1
command:
- run
- --storage.path=/var/lib/alloy/data
- /etc/alloy/config.alloy
restart: unless-stopped
volumes:
- ./config.alloy:/etc/alloy/config.alloy:ro
- alloy-data:/var/lib/alloy/data
- /var/log:/var/log:ro
volumes:
alloy-data:
Alloy برای ثبت محل خواندن فایلها به فضای ذخیرهسازی پایدار نیاز دارد. اگر volume مربوط به alloy-data حذف شود، ممکن است پس از شروع دوباره بخشی از فایلها مجدداً خوانده شوند.
ایجنت را اجرا و خروجی آن را بررسی کنید:
sudo docker compose up -d
sudo docker compose logs --tail=100 alloy
همین تنظیم را روی سرورهای دیگر تکرار کنید و برچسب host را برای هر ماشین تغییر دهید.
در توزیعهایی مانند Rocky Linux یا AlmaLinux ممکن است مسیر /var/log/messages و /var/log/secure بهجای syslog و auth.log استفاده شود. مسیرها را متناسب با توزیع خود تنظیم کنید.
دسترسی فایلها و عیبیابی ارسال
اگر هیچ لاگی به Loki نمیرسد، ابتدا بررسی کنید فایلها داخل کانتینر Alloy دیده میشوند:
sudo docker compose exec alloy ls -la /var/log
سپس دسترسی شبکه را آزمایش کنید:
curl http://10.10.0.10:3100/ready
در سرور مرکزی نیز لاگ Loki را ببینید:
sudo docker compose logs --tail=200 loki
خطاهای permission denied معمولاً به مجوز فایل یا محدودیتهایی مانند SELinux مربوطاند. خطاهای connection refused نیز اغلب از آدرس اشتباه، فایروال یا اجرا نشدن Loki ناشی میشوند.
پورت 3100 را روی اینترنت عمومی باز نکنید. در این پیکربندی، احراز هویت داخلی Loki غیرفعال است و فرض میشود ارتباط از شبکه خصوصی انجام میشود. برای محیط عمومی باید یک Reverse Proxy با TLS و احراز هویت در جلوی Loki قرار گیرد.
جستوجوی لاگها با LogQL
در Grafana وارد بخش Explore شوید و Data Source پیشفرض Loki را انتخاب کنید. برای مشاهده همه لاگهای یک سرور:
{host="app-01"}
برای مشاهده لاگهای Nginx همان سرور:
{host="app-01", job="nginx"}
برای پیدا کردن خطوطی که شامل عبارت error هستند:
{host="app-01"} |= "error"
جستوجوی بدون حساسیت به حروف کوچک و بزرگ:
{job="nginx"} |~ "(?i)error|timeout|failed"
اگر لاگ Nginx در قالب JSON نوشته میشود، میتوان فیلدها را استخراج کرد:
{job="nginx"} | json | status >= 500
برای شمارش خطاها در بازههای پنجدقیقهای:
sum by (host) (
count_over_time({job="nginx"} |~ " 5[0-9]{2} " [5m])
)
انتخاب درست برچسبها
برچسبهایی مانند host، job، environment و datacenter معمولاً تعداد مقادیر محدودی دارند و برای فیلتر اولیه مناسباند. دادههایی مانند شناسه درخواست، IP کاربر، URL کامل یا شناسه سفارش نباید برچسب شوند؛ چون برای هر خط ممکن است مقدار متفاوتی داشته باشند.
این وضعیت با نام cardinality بالا شناخته میشود و میتواند مصرف حافظه و حجم ایندکس را افزایش دهد. چنین مقادیری را داخل متن یا ساختار JSON لاگ نگه دارید و هنگام اجرای کوئری استخراج کنید.
برای شروع، همین سه برچسب کافی است:
host=app-01
job=nginx
environment=production
اگر چند محیط دارید، میتوانید environment را در بخش path_targets تنظیم کنید.
مدیریت نگهداری و فضای دیسک
محدودیت ۳۰روزه فقط نقطه شروع است. برای انتخاب مقدار مناسب، حجم واقعی داده را برای چند روز اندازه بگیرید. اگر مجموعه سرورها روزانه ۲۰ گیگابایت لاگ تولید کند، نگهداری ۳۰روزه بدون درنظر گرفتن فشردهسازی، رشد ایندکس و فضای آزاد به برنامهریزی دقیق نیاز دارد.
چند کار ساده جلوی پر شدن ناگهانی دیسک را میگیرد:
- برای volume لوکی فضای جداگانه در نظر بگیرید.
- میزان مصرف دیسک را مانیتور کنید و پیش از رسیدن به آستانه خطر هشدار بسازید.
- لاگهای کمارزش یا تکراری را پیش از ارسال حذف کنید.
- سطح لاگ برنامهها را در حالت عادی روی info نگه دارید و debug را موقت فعال کنید.
- نسخه پشتیبان تنظیمات را نگه دارید؛ Loki را جایگزین بکاپ امنیتی یا آرشیو قانونی فرض نکنید.
ذخیرهسازی filesystem برای یک استقرار کوچک و تکسروری مناسب است. اگر حجم داده یا نیاز به دسترسپذیری افزایش یافت، از Object Storage سازگار با S3 و معماری چندنمونهای Loki استفاده کنید.
ساخت داشبورد عملیاتی
یک داشبورد ساده میتواند شامل نرخ کل لاگ، تعداد خطاها به تفکیک سرور، خطاهای HTTP سری ۵۰۰ و تازهترین پیامهای سرویسهای حساس باشد. متغیری با نام host نیز بسازید تا اپراتور بتواند سرور را از بالای داشبورد انتخاب کند.
داشبورد جای Explore را نمیگیرد. داشبورد برای الگوهای شناختهشده مناسب است؛ Explore زمانی کاربرد دارد که خطایی تازه رخ داده و هنوز کوئری ثابتی برای آن ندارید.
برای هشدار نیز ابتدا شرایط مشخصی تعریف کنید؛ مثلاً بیشتر شدن تعداد خطاهای Nginx از یک آستانه در پنج دقیقه. هشدار روی هر عبارت error معمولاً نویز زیادی تولید میکند.
انتخاب زیرساخت مناسب برای سرور مرکزی
سرور Loki به دیسک پایدار، فضای کافی و ارتباط شبکه مناسب با سرورهای منبع نیاز دارد. مصرف CPU و حافظه به تعداد جریانها، حجم لاگ و پیچیدگی کوئریها بستگی دارد؛ بنابراین ظرفیت را با داده واقعی تنظیم کنید، نه فقط تعداد سرورها. اگر برای اجرای Loki و Grafana به یک ماشین مستقل نیاز دارید، میتوانید مشخصات سرور ابری آریاسرویس را بررسی کنید. برای محیط کوچک، بهتر است با منابع محدود شروع کنید و پس از اندازهگیری نرخ ورود لاگ و مصرف دیسک، ظرفیت را افزایش دهید.
جمعبندی
در این معماری، Alloy روی هر سرور فایلهای لاگ را میخواند، Loki آنها را روی سرور مرکزی ذخیره میکند و Grafana امکان جستوجو و ساخت داشبورد را میدهد. پس از راهاندازی اولیه، مهمترین کارها انتخاب برچسبهای کمتعداد، محدود کردن دسترسی شبکه، تعیین دوره نگهداری و مانیتور کردن فضای دیسک هستند.
راهاندازی لاگ متمرکز با Grafana Loki زمانی مفیدتر میشود که نامگذاری برچسبها میان همه سرورها یکسان باشد. پیش از اضافه کردن تعداد زیادی منبع، الگوی host، job و environment را مشخص کنید تا کوئریها و داشبوردها بعداً به بازنویسی گسترده نیاز نداشته باشند.



