ריכוז לוגים עם ELK Stack ליישומי ווב
אחרי עוד תקרית ייצור עם שגיאות 5xx, בילינו 3 שעות בחיפוש בלוגים על עשרה שרתים. זה חזר על עצמו כל חודש. כאשר יישום ווב משרת אלפי משתמשים, הלוגים נוצרים בכמויות עצומות—עד 50 GB ליום על 10 שרתים. ללא ריכוז, מציאת שגיאה היא כמו חיפוש מחט בערמת שחת. ELK Stack פותר את זה: כל הלוגים זורמים לאחסון מרכזי עם חיפוש מהיר בשניות. בניסיון שלנו, זמן התגובה לאירועים יורד ב-70% לאחר אימוץ ELK. התראות טלגרם מודיעות על 5xx, בקשות איטיות, שגיאות יישום. אנו מטפלים במחזור המלא: מהתקנת Elasticsearch ועד דשבורדים והתראות. משך: 2 עד 7 ימים תלוי במורכבות.
כיצד ELK Stack פותר בעיות איסוף וניתוח לוגים
ELK הוא שילוב של Elasticsearch (אחסון וחיפוש), Logstash (ניתוח והמרה), ו-Kibana (ויזואליזציה). Filebeat מעביר לוגים מהשרתים. התוצאה היא נקודת כניסה אחת לכל הלוגים, מה שמפשט משמעותית את ניטור הלוגים וחקירת שגיאות.
בחירת סכמה: ELK לעומת EFK לעומת ללא Logstash
| סכמה | מורכבות | ביצועים | גמישות |
|---|---|---|---|
| ELK | גבוהה | בינונית | גבוהה |
| EFK | בינונית | גבוהה יותר | בינונית |
| ללא Logstash | נמוכה | גבוהה | נמוכה |
Logstash מצטיין ביכולות: הוא מנתח פורמטי לוג ישנים באמצעות grok, מעשיר נתונים (geoip, useragent). אנו משתמשים בו ב-80% מהפרויקטים. עם זאת, Elasticsearch Ingest Pipelines מהירים יותר: הם מעבדים עד 15,000 אירועים/שנייה—פי 3 יותר מ-Logstash (5,000). אבל Logstash מטפל בנתונים לא מובנים שבהם Ingest נכשל.
כיצד אנו מתקינים ELK לפרויקט שלך
Docker Compose לסביבת בדיקה
אנו משתמשים בקובץ ה-compose הבא:
---
version: '3.8'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.13.0
environment:
- discovery.type=single-node
- xpack.security.enabled=true
- xpack.security.http.ssl.enabled=false
- ELASTIC_PASSWORD=changeme
- "ES_JAVA_OPTS=-Xms2g -Xmx2g"
volumes:
- esdata:/usr/share/elasticsearch/data
ports:
- "9200:9200"
ulimits:
memlock:
soft: -1
hard: -1
kibana:
image: docker.elastic.co/kibana/kibana:8.13.0
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
- ELASTICSEARCH_USERNAME=kibana_system
- ELASTICSEARCH_PASSWORD=changeme
ports:
- "5601:5601"
depends_on:
- elasticsearch
logstash:
image: docker.elastic.co/logstash/logstash:8.13.0
volumes:
- ./logstash/pipeline:/usr/share/logstash/pipeline
- ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml
ports:
- "5044:5044"
- "5000:5000"
depends_on:
- elasticsearch
volumes:
esdata: צינור Logstash: ניתוח לוגי Nginx Access ולוגי JSON
input {
beats {
port => 5044
}
tcp {
port => 5000
codec => json_lines
}
}
filter {
if [fields][log_type] == "nginx_access" {
grok {
match => { "message" => '%{IPORHOST:client_ip} - %{DATA:user} \[%{HTTPDATE:timestamp}\] "%{WORD:method} %{DATA:request} HTTP/%{NUMBER:http_version}" %{NUMBER:status_code:int} %{NUMBER:bytes_sent:int} "%{DATA:referrer}" "%{DATA:user_agent}" %{NUMBER:request_time:float}' }
}
date {
match => ["timestamp", "dd/MMM/yyyy:HH:mm:ss Z"]
target => "@timestamp"
}
geoip {
source => "client_ip"
target => "geoip"
}
useragent {
source => "user_agent"
target => "ua"
}
mutate {
remove_field => ["message", "timestamp"]
}
}
if [fields][log_type] == "app_json" {
json {
source => "message"
target => "app"
}
mutate {
remove_field => ["message"]
}
}
}
output {
if [fields][log_type] == "nginx_access" {
elasticsearch {
hosts => ["http://elasticsearch:9200"]
user => "elastic"
password => "changeme"
index => "nginx-access-%{+YYYY.MM.dd}"
}
} else {
elasticsearch {
hosts => ["http://elasticsearch:9200"]
user => "elastic"
password => "changeme"
index => "app-logs-%{+YYYY.MM.dd}"
}
}
} שליחת לוגים מ-Laravel
דרך handler מותאם של Monolog:
class LogstashLogger {
public function __invoke(array $config): Logger {
$handler = new SocketHandler("tcp://{$config['host']}:{$config['port']}");
$handler->setFormatter(new JsonFormatter());
return new Logger('app', [$handler]);
}
}כעת version: '3.8' services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:8.13.0 environment: - discovery.type=single-node - xpack.security.enabled=true - xpack.security.http.ssl.enabled=false - ELASTIC_PASSWORD=changeme - "ES_JAVA_OPTS=-Xms2g -Xmx2g" volumes: - esdata:/usr/share/elasticsearch/data ports: - "9200:9200" ulimits: memlock: soft: -1 hard: -1 kibana: image: docker.elastic.co/kibana/kibana:8.13.0 environment: - ELASTICSEARCH_HOSTS=http://elasticsearch:9200 - ELASTICSEARCH_USERNAME=kibana_system - ELASTICSEARCH_PASSWORD=changeme ports: - "5601:5601" depends_on: - elasticsearch logstash: image: docker.elastic.co/logstash/logstash:8.13.0 volumes: - ./logstash/pipeline:/usr/share/logstash/pipeline - ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml ports: - "5044:5044" - "5000:5000" depends_on: - elasticsearch volumes: esdata: שולח JSON ישירות ל-Logstash.
בפרויקט אחד עם עומס של 10,000 RPS, הגדרנו קלאסטר Elasticsearch עם 3 צמתים עם ILM ו-Logstash עם תבניות grok לניתוח לוגי יישום ספציפיים. כתוצאה מכך, זמן חיפוש השגיאות ירד מ-40 דקות ל-10 שניות. זה אפשר לצוות להגיב מהר יותר לאירועים ולהפחית את MTTR ב-65%.
למה ILM הוא חובה
ללא ILM, אינדקסים גדלים ללא שליטה, וממלאים את הדיסק תוך חודש. אנו מגדירים מדיניות: hot (5 GB או יום אחד) → warm (3 ימים) → cold (30 ימים) → מחיקה (90 ימים). זה נעשה דרך תבנית אינדקס. זה הבסיס לאחסון לוגים חסכוני. בנוסף, ניתן להגדיר rollover לפי גודל או גיל כדי למנוע עומס על צמתים.
ביצועי Elasticsearch: טיפים מעשיים
- Heap: לא יותר מ-50% מ-RAM ולעולם לא יעלה על 31 GB (בגלל compressed oops)
- מספר shards: 1 shard ≈ 20–40 GB של נתונים. Oversharding היא טעות נפוצה
- Slow log:
input { beats { port => 5044 } tcp { port => 5000; codec => json_lines } } filter { if [fields][log_type] == "nginx_access" { grok { match => { "message" => '%{IPORHOST:client_ip} - %{DATA:user} \[%{HTTPDATE:timestamp}\] "%{WORD:method} %{DATA:request} HTTP/%{NUMBER:http_version}" %{NUMBER:status_code:int} %{NUMBER:bytes_sent:int} "%{DATA:referrer}" "%{DATA:user_agent}" %{NUMBER:request_time:float}' } } date { match => ["timestamp", "dd/MMM/yyyy:HH:mm:ss Z"]; target => "@timestamp" } geoip { source => "client_ip"; target => "geoip" } useragent { source => "user_agent"; target => "ua" } mutate { remove_field => ["message", "timestamp"] } } if [fields][log_type] == "app_json" { json { source => "message"; target => "app" } mutate { remove_field => ["message"] } } } output { if [fields][log_type] == "nginx_access" { elasticsearch { hosts => ["http://elasticsearch:9200"]; user => "elastic"; password => "changeme"; index => "nginx-access-%{+YYYY.MM.dd}" } } else { elasticsearch { hosts => ["http://elasticsearch:9200"]; user => "elastic"; password => "changeme"; index => "app-logs-%{+YYYY.MM.dd}" } } } - השבתת swap:
class LogstashLogger { public function __invoke(array $config): Logger { $handler = new SocketHandler("tcp://{$config['host']}:{$config['port']}"); $handler->setFormatter(new JsonFormatter()); return new Logger('app', [$handler]); } }
השוואה: Logstash לעומת Ingest Pipelines
| פרמטר | Logstash | Ingest Pipelines |
|---|---|---|
| ביצועים | ~5k אירועים/שנייה | ~15k אירועים/שנייה |
| גמישות | Grok, העשרה, ניתוב | רק ניתוח פשוט |
| מורכבות | דורש הגדרת שרת | מובנה ב-ES |
להמרות מורכבות, Logstash הוא בלתי ניתן להחלפה. מעבדי צינור מובנים מטפלים במשימות טיפוסיות אך לא יכולים לעבוד עם תבניות טקסט שרירותיות.
מה כלול בהתקנת ELK
- פריסת קלאסטר Elasticsearch עם הגדרות אופטימליות (shards, ILM, אבטחה)
- הגדרת Logstash לניתוח לוגי Nginx, PHP ויישום
- חיבור Filebeat על כל השרתים
- יצירת דשבורדים ב-Kibana לניטור לוגים (5xx, זמן השהיה, תעבורה)
- הגדרת ILM לחיסכון במקום בדיסק
- שילוב התראות לטלגרם או אימייל
- תיעוד לתפעול והדרכת צוות
תהליך עבודה
- ניתוח — אנו לומדים את הלוגים הנוכחיים שלך, מקורות ודרישות אחסון
- עיצוב — בחירת סכמה (ELK/EFK), תכנון ILM ודשבורדים
- יישום — פריסת קלאסטר, הגדרת צינורות
- בדיקה — אימות קליטת נתונים, התראות, מהירות חיפוש
- השקה — פריסה לייצור, מסירת תיעוד
אנו מבטיחים SLA של 99.9% לקלאסטר. למהנדסים שלנו יש מעל 5 שנות ניסיון והם ביצעו 30+ פרויקטים. צור קשר כדי לדון בפרויקט שלך ולתזמן יישום ELK להפחתת זמן חיפוש השגיאות. המחיר נקבע לאחר ניתוח תשתית.







