~/icsd.ir — bash
SYSTEM_ONLINE

Observability

Observability یعنی توانایی فهم وضعیت داخلی سیستم از روی خروجی‌هایش. در میکروسرویس، به دلیل توزیع‌شدگی، observability از monitoring فراتر می‌رود.

۱۲.۱ مقدمه

Observability یعنی توانایی فهم وضعیت داخلی سیستم از روی خروجی‌هایش. در میکروسرویس، به دلیل توزیع‌شدگی، observability از monitoring فراتر می‌رود.

۱۲.۲ سه ستون Observability

📝 Logs

رویدادهای discrete که در طول زمان اتفاق می‌افتند. «چه چیزی اتفاق افتاد؟»

📊 Metrics

اعداد aggregate شده در طول زمان. «وضعیت سیستم چطور است؟»

🔍 Traces

مسیر یک request در سراسر سرویس‌ها. «request کجا زمان از دست داد؟»

۱۲.۳ Logging

Structured Logging

به جای string ساده، JSON با فیلدهای مشخص استفاده کنید.


import structlog
import logging

# تنظیم structlog
structlog.configure(
    processors=[
        structlog.contextvars.merge_contextvars,
        structlog.processors.add_log_level,
        structlog.processors.TimeStamper(fmt="iso"),
        structlog.processors.StackInfoRenderer(),
        structlog.processors.format_exc_info,
        structlog.processors.JSONRenderer(),
    ],
    wrapper_class=structlog.make_filtering_bound_logger(logging.INFO),
    logger_factory=structlog.PrintLoggerFactory(),
)

logger = structlog.get_logger()

# استفاده
logger.info(
    "order_placed",
    order_id=123,
    user_id=456,
    total=1500000,
    items_count=3,
)

# خروجی JSON:
# {"event": "order_placed", "order_id": 123, "user_id": 456,
#  "total": 1500000, "items_count": 3, "timestamp": "2026-05-01T10:30:00Z",
#  "level": "info"}
    

Correlation ID

ID مشترک برای trace کردن یک request در سرویس‌های مختلف.


from contextvars import ContextVar
from fastapi import FastAPI, Request
import uuid

correlation_id: ContextVar[str] = ContextVar("correlation_id")

app = FastAPI()

@app.middleware("http")
async def add_correlation_id(request: Request, call_next):
    cid = request.headers.get("X-Correlation-ID") or str(uuid.uuid4())
    correlation_id.set(cid)
    
    response = await call_next(request)
    response.headers["X-Correlation-ID"] = cid
    return response

# در هر log
logger = structlog.get_logger()
logger.info("processing", correlation_id=correlation_id.get())
    

ELK Stack

  • Elasticsearch: ذخیره و search لاگ‌ها
  • Logstash: جمع‌آوری و پردازش
  • Kibana: visualization و query
  • Filebeat: ارسال لاگ از سرور

Loki — جایگزین سبک

Grafana Loki — ELK سبک‌تر، فقط label-based.

Best Practices

  • Log به stdout/stderr (نه فایل)
  • JSON structured logging
  • سطح‌بندی صحیح (DEBUG، INFO، WARN، ERROR)
  • هرگز credentials در log!
  • Correlation ID در همه لاگ‌ها
  • Log rotation برای disk
  • Sampling در high-volume services

۱۲.۴ Metrics با Prometheus

Prometheus استاندارد industry برای metrics است.

چهار نوع Metric

  • Counter: فقط افزایش (تعداد requests)
  • Gauge: بالا/پایین (تعداد active connections)
  • Histogram: توزیع مقادیر (request duration)
  • Summary: مثل Histogram با pre-calculated quantiles

Instrumentation در FastAPI


from prometheus_client import Counter, Histogram, Gauge, generate_latest
from fastapi import FastAPI, Response
import time

# Metrics
requests_total = Counter(
    "http_requests_total",
    "Total HTTP requests",
    ["method", "endpoint", "status"]
)
request_duration = Histogram(
    "http_request_duration_seconds",
    "HTTP request duration",
    ["method", "endpoint"],
    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0)
)
active_users = Gauge("active_users", "Currently active users")

app = FastAPI()

@app.middleware("http")
async def metrics_middleware(request, call_next):
    start = time.time()
    response = await call_next(request)
    duration = time.time() - start
    
    requests_total.labels(
        method=request.method,
        endpoint=request.url.path,
        status=response.status_code,
    ).inc()
    
    request_duration.labels(
        method=request.method,
        endpoint=request.url.path,
    ).observe(duration)
    
    return response

@app.get("/metrics")
async def metrics():
    return Response(content=generate_latest(), media_type="text/plain")
    

RED Method (طلایی)

سه metric اصلی برای request-driven services:

  • Rate: تعداد requests در ثانیه
  • Errors: تعداد errors
  • Duration: latency distribution

USE Method

برای resources (CPU، Memory، Disk):

  • Utilization: درصد مصرف
  • Saturation: کارهای منتظر
  • Errors: خطاها

PromQL Queries مفید


# Rate of errors
rate(http_requests_total{status=~"5.."}[5m])

# 95th percentile latency
histogram_quantile(0.95, 
    rate(http_request_duration_seconds_bucket[5m])
)

# Error rate %
sum(rate(http_requests_total{status=~"5.."}[5m])) 
  / sum(rate(http_requests_total[5m])) * 100

# CPU usage
rate(container_cpu_usage_seconds_total[5m]) * 100
    

۱۲.۵ Grafana — Dashboard

Grafana برای visualization metrics از Prometheus، Loki، Elasticsearch و… استفاده می‌شود.


# docker-compose.yml
services:
  prometheus:
    image: prom/prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"
  
  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    environment:
      GF_SECURITY_ADMIN_PASSWORD: admin
    volumes:
      - grafana_data:/var/lib/grafana
      - ./dashboards:/etc/grafana/provisioning/dashboards
    

# prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: "user-service"
    static_configs:
      - targets: ["user-service:8000"]
  
  - job_name: "product-service"
    static_configs:
      - targets: ["product-service:8000"]
  
  - job_name: "kubernetes-pods"
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: true
    

۱۲.۶ Distributed Tracing

برای فهم اینکه یک request در کدام سرویس چقدر زمان برد و کجا fail شد.

OpenTelemetry — استاندارد جدید


from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.sdk.resources import Resource
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor

# تنظیم
resource = Resource(attributes={
    "service.name": "order-service",
    "service.version": "1.2.3",
    "deployment.environment": "production",
})

provider = TracerProvider(resource=resource)
processor = BatchSpanProcessor(
    OTLPSpanExporter(endpoint="http://jaeger:4317", insecure=True)
)
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)

# Auto-instrumentation
FastAPIInstrumentor.instrument_app(app)
HTTPXClientInstrumentor().instrument()
SQLAlchemyInstrumentor().instrument(engine=db_engine)

# Manual span
tracer = trace.get_tracer(__name__)

@app.post("/orders")
async def create_order(data):
    with tracer.start_as_current_span("create_order") as span:
        span.set_attribute("user_id", data.user_id)
        span.set_attribute("items_count", len(data.items))
        
        with tracer.start_as_current_span("validate_inventory"):
            await validate_inventory(data.items)
        
        with tracer.start_as_current_span("save_order"):
            order = await save_order(data)
            span.set_attribute("order_id", order.id)
        
        with tracer.start_as_current_span("publish_event"):
            await publish_event("order.placed", order)
        
        return order
    

Jaeger — UI برای trace ها


services:
  jaeger:
    image: jaegertracing/all-in-one:latest
    ports:
      - "16686:16686"  # UI
      - "4317:4317"    # OTLP gRPC
      - "4318:4318"    # OTLP HTTP
    environment:
      COLLECTOR_OTLP_ENABLED: "true"
    

پس از اجرا، در http://localhost:16686 trace ها قابل مشاهده هستند.

۱۲.۷ Alerting

Prometheus Alertmanager برای اعلام مشکلات.


# alerts.yml
groups:
  - name: service_alerts
    rules:
      - alert: HighErrorRate
        expr: |
          sum(rate(http_requests_total{status=~"5.."}[5m]))
            / sum(rate(http_requests_total[5m])) > 0.05
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "High error rate ({{ $value | humanizePercentage }})"
          
      - alert: HighLatency
        expr: |
          histogram_quantile(0.95,
            rate(http_request_duration_seconds_bucket[5m])
          ) > 1
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "P95 latency > 1s"
      
      - alert: PodCrashLooping
        expr: rate(kube_pod_container_status_restarts_total[15m]) > 0
        for: 5m
        labels:
          severity: critical
    

Alertmanager Routing


route:
  group_by: ["alertname", "service"]
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  receiver: default
  routes:
    - match:
        severity: critical
      receiver: pagerduty
    - match:
        severity: warning
      receiver: slack

receivers:
  - name: default
    email_configs:
      - to: alerts@shop.com
  
  - name: slack
    slack_configs:
      - api_url: https://hooks.slack.com/...
        channel: "#alerts"
  
  - name: pagerduty
    pagerduty_configs:
      - service_key: ...
    

۱۲.۸ SLI، SLO، SLA

  • SLI (Service Level Indicator): یک metric که اندازه می‌گیریم (مثلاً availability)
  • SLO (Service Level Objective): هدف داخلی (مثلاً 99.9% availability)
  • SLA (Service Level Agreement): قرارداد با مشتری (با penalty)

Error Budget

اگر SLO 99.9% است، error budget = 0.1% = ~43 دقیقه downtime در ماه. اگر این budget مصرف شود، deploy های جدید متوقف می‌شوند تا stability برگردد.

۱۲.۹ بهترین تجربیات

  1. سه ستون با هم. logs، metrics، traces مکمل یکدیگرند.
  2. OpenTelemetry standard. برای آینده‌نگری.
  3. Correlation ID همیشه. در logs و traces.
  4. RED/USE Method. برای metrics.
  5. Dashboard هر سرویس. RED metrics + business metrics.
  6. Alert فقط actionable. alert fatigue واقعی است.
  7. SLO-based alerting. نه threshold دلخواه.
  8. Sampling در tracing. 100% trace هزینه‌بر است.
  9. Log اطلاعات حساس را scrub کنید.
  10. Cardinality کنترل شده. labels زیاد metric را منفجر می‌کند.

۱۲.۱۰ خلاصه فصل

آنچه آموختیم:
  • سه ستون Observability: Logs، Metrics، Traces
  • Structured logging با structlog و ELK
  • Prometheus metrics با ۴ نوع: Counter، Gauge، Histogram، Summary
  • RED Method و USE Method
  • Distributed Tracing با OpenTelemetry و Jaeger
  • Grafana برای dashboard
  • Alerting با Alertmanager
  • SLI، SLO، SLA و Error Budget
در فصل بعد: امنیت میکروسرویس — mTLS، JWT، Service Mesh، Zero Trust.

نمایش سایت

رنگ سایت
حالت نمایش
اندازهٔ متن
خوانایی

این تنظیمات فقط روی مرورگر شما ذخیره می‌شود.