پرش به محتوا
آرش حاتمی

hatamiarash7/ Redis-Watcher

سال توسعه: ۱۴۰۵ متن‌باز

پایش فرمان‌های Redis با Redis Watcher

سرویس پایش جریان MONITOR در Redis، با ثبت ساختاریافتهٔ فرمان‌ها، متریک‌های Prometheus، هشدار تلگرام و وب‌هوک و تشخیص نقش Primary و Replica.

هنگام بررسی رفتار یک برنامه، دانستن میزان مصرف حافظه یا تعداد اتصال‌های Redis همیشه کافی نیست؛ گاهی لازم است ببینیم چه فرمان‌هایی، از چه مبدأیی و روی کدام دیتابیس اجرا می‌شوند. دستور MONITOR این جریان را در اختیار می‌گذارد، اما دنبال‌کردن دستی آن برای ثبت رویدادها و ارسال هشدار مناسب نیست.

پروژهٔ Redis Watcher یک سرویس نوشته‌شده با Go است که خروجی MONITOR را دریافت می‌کند و رویدادهای مشاهده‌شده را به لاگ ساختاریافته، متریک‌های Prometheus و اعلان تبدیل می‌کند. قواعد هشدار را می‌توان بر اساس نام فرمان یا الگوی متن آن تنظیم کرد.

روش کار و قابلیت‌ها

برنامه از طریق TCP یا Unix Socket به Redis متصل می‌شود. از هر خط دریافتی، زمان، شمارهٔ دیتابیس، مبدأ، نام فرمان و آرگومان‌ها استخراج می‌شوند. پس از اعمال فیلتر، رویداد به خروجی‌های فعال و موتور هشدار فرستاده می‌شود و شمارنده‌های مربوط به آن به‌روزرسانی می‌شوند.

خروجی‌ها شامل این موارد هستند:

  • نمایش رویدادها در stdout با قالب JSON یا متن؛
  • نوشتن فایل با چرخش خودکار، محدودیت نگه‌داری و فشرده‌سازی لاگ‌های قدیمی؛
  • ارسال رویدادها از طریق TCP یا UDP به گیرندهٔ سازگار؛
  • انتشار متریک‌های فرمان‌ها، اتصال، خطاها و صف‌های داخلی در Prometheus؛
  • ارسال هشدار از طریق تلگرام، وب‌هوک یا Pushgateway.

اتصال قطع‌شده با فاصله‌های افزایشی دوباره برقرار می‌شود. هر خروجی صف محدودی دارد تا کندی یک مقصد بلافاصله دریافت رویدادها را متوقف نکند. اتصال اختیاری به Sentry نیز برای ثبت خطاهای خود سرویس و رهگیری عملکرد وجود دارد. راهنمای مخزن جزئیات این قابلیت‌ها را توضیح می‌دهد.

راه‌اندازی محلی

برای ساخت برنامه به Git، Make و طبق فایل go.mod، Go نسخهٔ ۱٫۲۶٫۳ یا جدیدتر نیاز دارید. Redis باید در دسترس باشد و کاربر اتصال اجازهٔ اجرای MONITOR و، با فعال‌بودن بررسی نقش، INFO replication را داشته باشد.

git clone https://github.com/hatamiarash7/Redis-Watcher.git
cd Redis-Watcher
cp config.example.yaml config.yaml

برای شروع روی یک Redis محلی، محتوای config.yaml را با نمونهٔ زیر جایگزین کنید. این مثال از TCP استفاده می‌کند و رویدادها را در ترمینال نشان می‌دهد:

redis:
  network: tcp
  address: 127.0.0.1:6379

role_check:
  enabled: true
  interval: 5s
  allow_replica: false

filter:
  ignored_commands:
    - PING
    - INFO
    - AUTH
    - SELECT
    - HELLO

outputs:
  - type: stdout
    enabled: true
    format: json

metrics:
  enabled: true
  address: 127.0.0.1:9100
  path: /metrics
  track_source_ip: false

alerts:
  enabled: false

گزینه‌های حذف‌شده از پیش‌فرض داخلی استفاده می‌کنند. در این مثال، خواندن جریان مهلت پایان ندارد و فاصلهٔ تلاش مجدد اتصال از ۱ ثانیه تا سقف ۳۰ ثانیه افزایش می‌یابد. مقادیر فایل نمونهٔ مخزن با پیش‌فرض داخلی یکسان نیستند؛ برای نمونه، فایل نمونه خروجی فایل را فعال می‌کند، اما در تنظیمات بالا فقط stdout فعال است.

اگر Redis احراز هویت دارد، می‌توانید نام کاربری و رمز را از طریق REDIS_WATCHER_REDIS_USERNAME و REDIS_WATCHER_REDIS_PASSWORD به محیط اجرای سرویس بدهید. متغیرهای محیطی پشتیبانی‌شده بر YAML اولویت دارند؛ همهٔ گزینه‌های YAML الزاماً معادل محیطی ندارند.

سپس برنامه را بسازید و اجرا کنید:

make build
./bin/redis-watcher --config config.yaml

در ترمینال دیگری، با ابزار redis-cli دو فرمان خواندنی اجرا کنید:

redis-cli -h 127.0.0.1 -p 6379 DBSIZE
redis-cli -h 127.0.0.1 -p 6379 GET redis-watcher:example

کلید بالا صرفاً یک نام نمونه است. در صورت برقرارشدن اتصال پایش، رویدادهای این فرمان‌ها در خروجی JSON دیده می‌شوند؛ مقدار پاسخ GET بخشی از رویداد نیست. اطلاعات اتصال redis-cli را هم باید با تنظیمات واقعی Redis هماهنگ کنید.

اجرای Docker روی لینوکس

برای استفاده از ایمیج آماده با همین فایل تنظیمات و Redis روی همان میزبان لینوکسی:

docker run -d --name redis-watcher \
  --network host \
  -v "$(pwd)/config.yaml:/etc/redis-watcher/config.yaml:ro" \
  hatamiarash7/redis-watcher:latest

docker logs -f redis-watcher

در این روش، شبکهٔ میزبان باعث می‌شود 127.0.0.1 داخل کانتینر به سرویس‌های همان میزبان دسترسی داشته باشد و نیازی به -p نیست. فایل تنظیمات باید برای کاربر غیرریشهٔ کانتینر خواندنی باشد. اگر Redis در شبکهٔ Docker دیگری قرار دارد، آدرس و شبکه را متناسب با آن تنظیم کنید.

تنظیم هشدارها

موتور هشدار دو نوع قاعده دارد: suspicious_commands برای تطبیق نام اصلی فرمان و patterns برای عبارت منظم روی متن بازسازی‌شدهٔ فرمان و آرگومان‌ها. تطبیق الگوها به بزرگی و کوچکی حروف حساس نیست.

برای یک آزمایش ساده، بخش alerts قبلی را با تنظیم زیر جایگزین کنید. این قاعده عمداً روی خواندن کلید نمونه هشدار می‌دهد و به معنی مشکوک‌بودن عمومی فرمان GET نیست:

alerts:
  enabled: true
  patterns:
    - "^GET redis-watcher:example$"
  telegram:
    enabled: true
    timeout: 5s

توکن ربات و شناسهٔ گفت‌وگوی مقصد را از طریق REDIS_WATCHER_ALERTS_TELEGRAM_BOT_TOKEN و REDIS_WATCHER_ALERTS_TELEGRAM_CHAT_ID در محیط سرویس تنظیم کنید و برنامه را دوباره اجرا کنید. در Docker نیز باید این متغیرها را به محیط کانتینر منتقل کنید. پس از اجرای دوبارهٔ GET بالا، در صورت دسترسی ربات به مقصد، اعلان ارسال می‌شود.

محدودکنندهٔ پیش‌فرض برای هر ترکیب فرمان و IP مبدأ، حداکثر ۵ هشدار در بازهٔ ۶۰ ثانیه اجازه می‌دهد. ارسال ناموفق هر کانال به‌طور پیش‌فرض حداکثر ۳ بار، شامل تلاش اول، امتحان می‌شود. فعال‌کردن alerts.enabled به‌تنهایی کافی نیست؛ دست‌کم یک کانال هم باید فعال و تنظیم شده باشد.

برای انتخاب یک زیرفرمان مشخص از patterns استفاده کنید؛ در پیاده‌سازی موتور هشدار، فهرست suspicious_commands با نام اصلی مثل CLIENT مقایسه می‌شود، نه عبارت کامل CLIENT KILL.

متریک‌ها و وضعیت سرویس

با تنظیمات بالا، خروجی Prometheus و وضعیت آمادگی از روی همان میزبان در دسترس هستند:

curl --fail http://127.0.0.1:9100/metrics
curl --fail http://127.0.0.1:9100/readyz

برای Prometheus روی همین میزبان:

scrape_configs:
  - job_name: redis-watcher
    static_configs:
      - targets: ["127.0.0.1:9100"]

اگر Prometheus در کانتینر یا میزبان دیگری است، آدرس هدف و metrics.address باید متناسب با شبکهٔ مانیتورینگ تغییر کنند. نرخ فرمان‌های مشاهده‌شده به تفکیک نام را می‌توان با این عبارت دید:

sum by (command) (
  rate(redis_watcher_commands_total[5m])
)

متریک redis_watcher_monitor_connected وضعیت اتصال پایش را نشان می‌دهد. شمارنده‌های redis_watcher_monitor_dropped_events_total و redis_watcher_dropped_events_total نیز برای تشخیص ازدست‌رفتن رویدادها در مسیر دریافت و خروجی‌ها کاربرد دارند.

مسیر /healthz فقط زنده‌بودن سرور HTTP را اعلام می‌کند. در مقابل، /readyz اتصال MONITOR، مشخص‌بودن نقش Redis و وضعیت خطای خروجی‌ها و کانال‌های هشدار را بررسی می‌کند و در وضعیت ناآماده پاسخ 503 می‌دهد.

فیلتر filter.ignored_commands رویداد را پیش از رسیدن به لاگ و هشدار حذف می‌کند و فقط شمارندهٔ جداگانهٔ رویدادهای نادیده‌گرفته‌شده افزایش می‌یابد. گزینهٔ metrics.ignored_commands محدودتر است و مانع ثبت لاگ یا بررسی هشدار نمی‌شود. همچنین track_source_ip: false متریک فرمان‌ها به تفکیک IP را غیرفعال می‌کند؛ IP از لاگ‌ها و برچسب متریک فرمان‌های مشکوک حذف نمی‌شود.

رفتار در Primary و Replica

بررسی نقش به‌طور پیش‌فرض فعال است و هر ۵ ثانیه با INFO replication انجام می‌شود. وقتی نمونهٔ Redis به‌عنوان Replica شناسایی شود، اتصال MONITOR متوقف می‌شود و پس از تشخیص Primary شدن دوباره برقرار می‌شود. سرور HTTP متریک‌ها در این فاصله همچنان فعال است.

این قابلیت برای استقرار کنار گره‌های تحت مدیریت Sentinel مفید است، اما برنامه آدرس Primary را از Sentinel کشف نمی‌کند؛ نقش همان آدرس تنظیم‌شده را می‌سنجد. بنابراین برای دنبال‌کردن جابه‌جایی نقش‌ها می‌توان کنار هر گره یک Watcher اجرا کرد.

برای پایش عمدی Replica، گزینهٔ role_check.allow_replica را true کنید. رویدادهای حاصل از Replication معادل مشاهدهٔ مستقیم همهٔ درخواست‌های کلاینت روی Primary نیستند و مبدأ آن‌ها نیز لزوماً IP اصلی کاربر نیست. انتخاب محل پایش باید با هدف بررسی و هزینهٔ اجرای MONITOR هماهنگ باشد.

محدودیت‌های مهم

  • پوشش تابع خروجی Redis است. طبق مستندات رسمی MONITOR، برخی فرمان‌ها، از جمله فرمان‌های مدیریتی، در این جریان منتشر نمی‌شوند و رفتار میان نسخه‌ها تفاوت دارد. حضور نامی مثل CONFIG یا SHUTDOWN در فهرست هشدار، تضمین نمی‌کند اجرای آن قابل مشاهده باشد.
  • MONITOR هزینهٔ پردازشی دارد. تبدیل و ارسال جریان فرمان‌ها می‌تواند توان عملیاتی Redis را کاهش دهد. فیلترکردن رویداد در Watcher این هزینه را از سمت Redis حذف نمی‌کند.
  • ثبت بدون اتلاف تضمین نمی‌شود. سیاست پیش‌فرض pipeline.drop_on_full: true هنگام پرشدن صف رویداد را کنار می‌گذارد. قطع اتصال هم شکافی ایجاد می‌کند که با اتصال مجدد بازپخش نمی‌شود.
  • آرگومان‌ها می‌توانند دادهٔ حساس داشته باشند. نام کلیدها و مقدارهای ارسالی ممکن است وارد لاگ یا اعلان شوند؛ دسترسی به فایل‌ها و مقصدهای ارسال باید متناسب با همین محتوا باشد.

Redis Watcher برای بررسی جریان فرمان‌ها و افزودن هشدار و مشاهده‌پذیری به Redis کاربرد دارد. کد پروژه با مجوز MIT منتشر شده است.