מדריך: הקמת שרת לוגים עם syslog-ng

08/03/2022

שרת לוגים הוא בסך הכל סרביס שכולם פונים אליו עם הודעות הלוג שלהם והוא כותב את המידע לאיזשהו יעד. ברירת מחדל פשוטה היא לכתוב את כל הלוגים לקבצים וזה יתאים לנו בשביל התחלה, אבל הרבה פעמים נרצה לנתב את הלוגים החוצה משרת הלוגים גם לבסיס נתונים או אפילו לשירות איחסון לוגים בענן. תוכנת syslog-ng היא מערכת ניהול לוגים פופולרית שמאפשרת גם שמירה מקומית של הלוגים וגם יש לה אינטגרציה מצוינת עם שירותי איחסון לוגים בענן כך שיהיה קל לעבור לשם כשנצטרך.

1. יצירת אימג' לסרביס הלוגים

בשביל לכתוב סרביס לוגים נפעיל בתוך קונטיינר תוכנית בשם syslog-ng שמקבלת לוגים מהסרביסים השונים ושומרת אותם ליעד שנבחר. כך נראה קובץ קונפיגורציה פשוט של syslog-ng:

@version: 3.19

source s_net {
  tcp( ip("0.0.0.0"));
  udp( ip("0.0.0.0"));
  syslog( ip("0.0.0.0"));
 };

destination logfiles {
  file("/var/log/syslog-ng/$HOST/$PROGRAM.log"
      create-dirs(yes));
};

log {
  source(s_net);
  destination(logfiles);
};

אחרי תיאור הגירסה אנחנו מגדירים "מקורות", כלומר מאיפה לוגים נשלחים אלינו, ו"יעדים", כלומר לאן הלוג ייכתב. בלוק log מחבר בין מקור ליעד והוא שאחראי על הקסם. כרגע היעד הוא קבצים. המשתנים $HOST ו $PROGRAM בתוך שם קובץ היעד יתורגמו לשם השרת ששלח את ההודעה ושם התוכנית שרצה שם.

נשמור את הקונפיגורציה בקובץ בשם syslog-ng.conf ואת הקובץ נשמור בתיקיה בשם logserver. באותה תיקיה אני מוסיף גם קובץ Dockerfile עם התוכן הבא:

FROM debian:buster

RUN apt-get update && apt-get upgrade -y && apt-get install -y syslog-ng
COPY syslog-ng.conf /etc/syslog-ng/

EXPOSE 514/udp 514/tcp 601/tcp 6514/tcp

ENTRYPOINT ["/usr/sbin/syslog-ng", "-F", "-f", "/etc/syslog-ng/syslog-ng.conf", "--no-caps"]

הקובץ מתקין את syslog-ng על אימג' של דביאן ומפעיל אותו עם קובץ הקונפיגורציה שיצרנו.

2. יצירת אפליקציית Node.JS שכותבת לסרביס הלוגים

בואו נראה שזה עובד ובשביל זה נייצר אפליקציית Node.JS שתכתוב ללוג. בתיקיה חדשה בשם client צרו קובץ בשם main.js עם התוכן הבא:

const winston = require('winston');
require('winston-syslog').Syslog;

const logger = winston.createLogger({
  levels: winston.config.syslog.levels,
  transports: [
    new winston.transports.Syslog({
      host: process.env.SYSLOG_NG_HOST,
      app_name: 'client-demo',
    }),
  ]
});

console.log('--- Writing To Syslog');

logger.info("Hello World");
logger.end();

פקודת console.log תכתוב את הפלט לקונסול כך שנראה את זה במסוף של דוקר, ופקודת logger.info תשתמש בספריית winston-syslog כדי לשלוח את ההודעה לשרת לוג מרוחק. שימו לב שכתובת השרת מגיעה לתוכנית דרך משתנה סביבה ולכן אנחנו מצפים למצוא את הערך למשתנה זה בקובץ ה docker-compose הראשי.

באותה תיקיה צרו גם קובץ package.json עם התוכן הבא:

{
  "name": "client",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "winston": "^3.3.3",
    "winston-syslog": "^2.4.4"
  }
}

3. הפעלה מתוך Docker Compose

נבדוק את הכל כדי לוודא שאפשר לכתוב ולקרוא לוגים מכמה סרביסים. בתיקיה הראשית כתבו קובץ docker-compose.yml עם התוכן הבא:

version: '3.9'

services:
  logger:
    build: ./logserver
    volumes:
      - logs:/var/log/syslog-ng/

  client1:
    build: ./client
    command: ["sleep", "infinity"]
    environment:
      SYSLOG_NG_HOST: logger

  client2:
    build: ./client
    command: ["sleep", "infinity"]
    environment:
      SYSLOG_NG_HOST: logger


volumes:
  logs:

והפעילו עם:

$ docker compose up

עכשיו יש לנו שתי מכונות client שלא עושות כלום, אבל כוללות את התוכנית שלנו שיודעת לכתוב ללוג, ומכונת server אחת שמקשיבה להודעות לוג נכנסות. נפתח שלושה חלונות חדשים ונתחבר לכל אחת מהמכונות:

# First Terminal
$ docker compose exec logger /bin/bash

# Second Terminal
$ docker compose exec client1 /bin/bash

# Third Terminal
$ docker compose exec client2 /bin/bash

מהמסופים שמריצים את client1 ו client2 נריץ את הפקודה:

$ node main.js

ומהמסוף שמריץ את ה logger ניכנס לתיקיית הלוגים כדי לראות את התוצאות:

$ cat /var/log/syslog-ng/*/* 
Jul  8 06:27:35 32-setup-syslogng_client1_1 client-demo[16]: {"message":"Hello World","level":"info"}
Jul  8 06:27:45 32-setup-syslogng_client2_1 client-demo[16]: {"message":"Hello World","level":"info"}

4. הוספת Log Destination

בואו נעדכן את שרת הלוגים כדי שיכתוב קובץ לוג נוסף הפעם אחד שמרכז את כל הלוגים הנכנסים מכל המקורות - לא משנה מאיזה שרת ומאיזו תוכנית. אני יודע, קל מאוד בעזרת כלי יוניקס לראות במכה אחת את כל הלוגים ממספר קבצים, אבל חישבו על זה יותר בתור דוגמה לאיך מוסיפים יעד לוג נוסף.

עדכנו את קובץ הגדרות הלוג syslog-ng.conf והוסיפו את הבלוג הבא:

destination alllogs {
  file("/var/log/syslog-ng/all.log"
      create-dirs(yes));
};

לאחר מכן בתוך בלוק log הוסיפו את ה destination החדש:

log {
  source(s_net);
  destination(logfiles);
  destination(alllogs);
};

נפעיל מחדש וניכנס לשרת הלוגים. שם נוכל למצוא בתיקיית /var/log/syslog-ng את הקובץ החדש all.log הפעם עם כל הודעות הלוג משני הלקוחות שלנו. (שימו לב שהקובץ יופיע רק אחרי שהפעלתם את הלקוחות עצמם וכתבתם הודעות ללוג).

5. סיכום

העבודה עם syslog-ng איפשרה לנו להרים בקלות סרביס של שמירת לוגים שמקבל לוגים מכל הסרביסים האחרים במערכת ושומר אותם במקום אחד. אני בחרתי לשמור את הלוג לקבצים אבל במערכות גדולות יותר הרבה פעמים הלוג יישמר לבסיס נתונים שם אפשר יהיה להריץ עליו חיפושים או להקפיץ התראות בזמן אמת.

בשביל לאחסן את הלוגים בענן נצטרך בסך הכל לפתוח חשבון בשרת לוגים בענן ולהוסיף עוד destination להגדרות ה syslog שיצרנו. לדוגמה כדי לחבר את הלוגים למערכת papertrail אני מוסיף בלוק שנראה בערך ככה:

destination d_papertrail {
    udp("logsN.papertrailapp.com" port(XXXXX));
};