הגדרת לוגים וטרייסינג: ELK, Loki, Fluentd, Jaeger, Tempo
ראינו את זה קורה: שירות נופל בלילה, מהנדס תורן מקבל התראה, פותח את Kibana — ורואה 40,000 שורות ללא הקשר. בקשה ל-/api/checkout החזירה 500, אבל איפה בדיוק היא נכשלה: באפליקציית Rails, בשירות ספק התשלומים במורד הזרם, או ב-Redis במהלך פסילת מטמון? ללא לוגים מובנים וטרייסינג מבוזר, מענה על שאלה זו אורך שעות. לצוות שלנו יש ניסיון של 8+ שנים ב-DevOps ו-50+ הטמעות של אובזרביליטי.
למה ELK נחנק עם תצורה נאיבית?
התקנה סטנדרטית של Elasticsearch + Logstash + Kibana במצב "הכל על צומת אחד" עובדת מצוין עד שנפח הלוגים עולה על כמה ג'יגה-בייט ביום. אחרי זה מגיעה הבעיה הקלאסית: Logstash צורך CPU על פענוח grok, Elasticsearch נכנס להשהיות GC, אינדקסים גדלים ללא שליטה, ומדיניות שמירה לא מוגדרת.
מצב קונקרטי: קלאסטר production על שלושה צמתי Elasticsearch 8.x, Logstash מנתח לוגי nginx access דרך grok עם תבנית %{COMBINEDAPACHELOG}. בעומס שיא של 50k rps, Logstash צובר backlog בתור קבוע ומתחיל להפיל אירועים. פתרון — מעבר ל-Filebeat → צינור ingest של Elasticsearch במקום Logstash עבור תבניות פשוטות, תוך שמירת Logstash רק לטרנספורמציות מורכבות.
צינור ingest עבור nginx ב-Elasticsearch:
PUT _ingest/pipeline/nginx-access { "processors": [ { "grok": { "field": "message", "patterns": ["%{IPORHOST:client_ip} - %{DATA:user} \\[%{HTTPDATE:timestamp}\\] \"%{WORD:method} %{DATA:path} HTTP/%{NUMBER:http_version}\" %{NUMBER:status:int} %{NUMBER:bytes:long}"] }}, { "date": { "field": "timestamp", "formats": ["dd/MMM/yyyy:HH:mm:ss Z"] }}, { "remove": { "field": "timestamp" }} ] } זה מסיר את Logstash מהנתיב הקריטי ומפחית את זמן האחזור מה-ingestion מ-2-3 שניות ל-200-300ms. תיעוד צומת ingest של Elasticsearch
ELK לעומת Loki: לא כדור כסף, אלא פשרות שונות
Loki מ-Grafana Labs בנוי על ארכיטקטורה שונה מהותית: הוא לא מייצר אינדקס לתוכן הלוגים, רק ל-labelים. זה הופך אותו לזול יותר באחסון (הפחתת עלות של 70% בהשוואה ל-ELK עבור אותו נפח לוגים) ומהיר יותר בכתיבה — אבל איטי יותר בחיפוש טקסט חופשי.
מתי Loki מנצח:
- סביבת Kubernetes שבה לוגים כבר מובנים לפי namespace/pod/container
- צוות שכבר משתמש ב-Grafana ו-Prometheus — ממשק אחד ללא Kibana
- נפח לוגים גדול, אבל חיפוש בעיקר לפי labelים ידועים (
PUT _ingest/pipeline/nginx-access { "processors": [ { "grok": { "field": "message", "patterns": ["%{IPORHOST:client_ip} - %{DATA:user} \\[%{HTTPDATE:timestamp}\\] \"%{WORD:method} %{DATA:path} HTTP/%{NUMBER:http_version}\" %{NUMBER:status:int} %{NUMBER:bytes:long}"] }}, { "date": { "field": "timestamp", "formats": ["dd/MMM/yyyy:HH:mm:ss Z"] }}, { "remove": { "field": "timestamp" }} ] })
מתי ELK מנצח:
- צורך בחיפוש טקסט מלא בגוף הלוג: ביקורת, ציות, חיפוש PII
- אגרגציות מורכבות:
{status_code="404", method="GET", path="/api/v1/users"}לפי user_id עם פילטר סטטוס HTTP - השקעות קיימות ב-Kibana Dashboards והתראות Watcher
טעות אופיינית במעבר ל-Loki — להכניס ל-labelים כל מה שהיה שדות ב-Elasticsearch. scrape_configs: - job_name: kubernetes-pods kubernetes_sd_configs: - role: pod pipeline_stages: - cri: {} - match: selector: '{namespace="kube-system"}' action: drop - labels: app: namespace: pod: — זה אנטי-תבנית. Loki יוצר stream נפרד לכל שילוב label ייחודי. בקרדינליות גבוהה (path עם UUID), מספר ה-streams מתפוצץ ו-Loki מתחיל לעבוד לאט יותר מ-Elasticsearch.
תצורת Promtail נכונה עבור K8s עם פילטור:
scrape_configs: - job_name: kubernetes-pods kubernetes_sd_configs: - role: pod pipeline_stages: - cri: {} - match: selector: '{namespace="kube-system"}' action: drop - labels: app: namespace: pod: רק שלושה labelים. כל השאר — ב-line_format לחיפוש דרך |= או regexp.
טרייסינג מבוזר: Jaeger לעומת Tempo
לוגים אומרים לך מה נכשל. טרייסינג מראה למה ואיפה בדיוק בשרשרת המיקרוסרוויסים.
Jaeger — מנוסה ומוכח, נולד ב-Uber ב-2015, בוגר CNCF. תומך ב-Cassandra ו-Elasticsearch כ-backend. Tempo — מ-Grafana Labs, מאחסן טרייסים באחסון אובייקטים (S3, GCS), ללא אינדקס, רק לפי trace ID. זול יותר בקנה מידה של 1M+ spans לדקה (עד פי 3 עלות אחסון נמוכה יותר).
ההבדל העיקרי: ב-Jaeger אפשר למצוא טרייס לפי תגים (http.status_code=500, db.statement). ב-Tempo — רק אם אתה יודע את ה-trace ID או משתמש ב-TraceQL עם מטריקות מ-Prometheus (דרך Grafana Tempo + מטריקות מ-spans דרך spanmetrics connector ב-OpenTelemetry Collector).
OpenTelemetry Collector כמקבל אוניברסלי:
receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 http: endpoint: 0.0.0.0:4318 jaeger: protocols: grpc: thrift_http: processors: batch: timeout: 1s send_batch_size: 1024 tail_sampling: decision_wait: 10s policies: - name: errors-policy type: status_code status_code: { status_codes: [ERROR] } - name: slow-traces type: latency latency: { threshold_ms: 500 } exporters: otlp/tempo: endpoint: tempo:4317 tls: insecure: true jaeger: endpoint: jaeger-collector:14250 Tail sampling כאן הוא קריטי — בלעדיו ב-10k rps אתה שומר 10k טרייסים בשנייה, שרובם לא מעניינים. כלל: שמור את כל השגיאות + כל הבקשות האיטיות (>500ms) + 1-5% אקראיים לבסיס.
Fluentd ו-Fluent Bit: ההבדל משנה
Fluentd — Ruby, הרבה פלאגינים, ניתוב גמיש. Fluent Bit — C, צריכת זיכרון מינימלית (< 1MB RSS), עבור סוכנים על צמתים. Fluent Bit צורך פי 10 פחות זיכרון מ-Fluentd. ב-K8s הסכימה הסטנדרטית: Fluent Bit כ-DaemonSet אוסף לוגים מהצמתים → Fluentd כאגרגטור מבצע טרנספורמציה וניתוב למספר יעדים (Elasticsearch + S3 לארכיון).
תצורת Fluent Bit לניתוח לוגי JSON ממכולות:
[FILTER] Name parser Match kube.* Key_Name log Parser json Reserve_Data On Preserve_Key Off [FILTER] Name record_modifier Match kube.* Remove_key stream Remove_key _p receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 http: endpoint: 0.0.0.0:4318 jaeger: protocols: grpc: thrift_http: processors: batch: timeout: 1s send_batch_size: 1024 tail_sampling: decision_wait: 10s policies: - name: errors-policy type: status_code status_code: { status_codes: [ERROR] } - name: slow-traces type: latency latency: { threshold_ms: 500 } exporters: otlp/tempo: endpoint: tempo:4317 tls: insecure: true jaeger: endpoint: jaeger-collector:14250 — שומר את שדות ה-pod המקוריים (namespace, pod_name) במהלך הניתוח. ללא זה, מטא-דאטה של Kubernetes אובד.
איך אנחנו מעריכים ובונים
בהתחלה — ביקורת על המצב הנוכחי: נפח לוגים ליום, דרישות שמירה (ציות לעיתים קרובות דורש 1-3 שנים), הסטאק הנוכחי, האם קיים טרייסינג בכלל. ללא המספרים האלה, תכנון ארכיטקטורה הוא חסר משמעות — ההבדל בין 10GB ליום ל-1TB ליום משנה הכל.
מה כלול בעבודה
אנחנו מספקים חבילת אובזרביליטי מלאה מפתח עד סוף:
- מסמך ארכיטקטורה עם בחירת סטאק והצדקה
- סקריפטים של אינסטרומנטציה לשירותים שלך (אינטגרציית OpenTelemetry SDK)
- Helm charts ומודולי Terraform לפריסה
- Runbook למהנדסים תורנים (איך למצוא טרייסים, לקרוא span trees, להסלים)
- סשן הדרכה לצוות שלך (1-2 שעות)
- תמיכה לאחר פריסה למשך שבועיים
שלבי העבודה:
- ביקורת — נפח, מבנה הלוגים, נקודות איסוף, נקודות כאב נוכחיות (נפילות, אחזור, אזורים עיוורים)
- עיצוב — בחירת סטאק (ELK/Loki/היברידי), סכימת sharding לאינדקסים, מדיניות שמירה, ILM ב-Elasticsearch או compactor ב-Loki
- אינסטרומנטציה — הוספת OpenTelemetry SDK לשירותים, מבנה לוגים (JSON במקום טקסט פשוט), correlation ID דרך כל השירותים
- פריסה — Helm charts עבור Loki Stack או ECK (Elastic Cloud on Kubernetes), הגדרת PVC לנתונים חמים + S3 לארכיון
- התראות ודשבורדים — Grafana עבור Loki/Tempo, Kibana עבור ELK, התראות SLO על שיעור שגיאות ואחוזוני אחזור
- תיעוד — runbook לתורנים: איך למצוא טרייס לפי request ID, איך לקרוא span tree, תבניות שגיאה אופייניות
ציר זמן
| היקף המשימה | ציר זמן |
|---|---|
| הגדרת Loki + Promtail עבור קלאסטר K8s | 3–5 ימים |
| סטאק ELK מלא עם ILM והתראות | 1–2 שבועות |
| טרייסינג מבוזר (Jaeger/Tempo) + אינסטרומנטציה של 5-10 שירותים | 2–3 שבועות |
| פלטפורמת אובזרביליטי מלאה (לוגים + מטריקות + טרייסים) | 4–6 שבועות |
העלות מחושבת באופן אישי לאחר ביקורת תשתית. אנחנו מבטיחים SLA עבור צינור האובזרביליטי שלך — צור קשר כדי להעריך את הפרויקט שלך.
מה לבדוק לפני ההטמעה
-
קרדינליות labelים ב-Loki — בדוק ייחודיות של כל label דרך
[FILTER] Name parser Match kube.* Key_Name log Parser json Reserve_Data On Preserve_Key Off [FILTER] Name record_modifier Match kube.* Remove_key stream Remove_key _pלפני פריסה ל-production - מחזור חיים של אינדקס ב-Elasticsearch — ללא ILM, אינדקסים חמים יצרכו SSD תוך שבוע בנפח > 50GB ליום
- שיעור דגימה ב-Jaeger — 100% הסתברותי יהרוג את Cassandra בכל תעבורה משמעותית
- Correlation ID — אם חסר בלוגים של כל השירותים, טרייסינג חסר תועלת: אי אפשר לקשר span לשורת לוג
-
מגבלות משאבים ל-Elasticsearch —
Reserve_Data Onו-heap לא יותר מ-50% מזיכרון הצומת, אחרת מטמון דפי ה-OS יורעב
מלכודות נפוצות ופתרונות
| מלכודת | פתרון |
|---|---|
| קפיצת CPU של Logstash בעומס שיא | העבר ל-Filebeat + צינור ingest עבור תבניות פשוטות |
| פיצוץ labelים ב-Loki (קרדינליות גבוהה) | הגבל ל-3 labelים; השתמש במטא-דאטה מובנה בשורת הלוג |
| טרייסים ללא קורלציה ללוגים | הזרקת trace_id להקשר הלוג דרך OpenTelemetry SDK |
| Elasticsearch heap >50% מזיכרון הצומת | הגדר label_values() והשאר זיכרון למטמון דפי OS |
סיפקנו את ההתקנה הזו ללקוחות שמתעדים 2TB ליום. אם אתה רוצה לראות מספרים עבור המקרה שלך, נעריך את הפרויקט — פשוט כתוב לנו.







