Menu

DevOps & Cloud Topic

DevOps Monitoring, Logging & Observability

Understanding the Three Pillars

Metrics

  • What: Numerical measurements over time

  • Examples: CPU usage, request rate, error count

  • Tools: Prometheus, CloudWatch, Datadog

Logs

  • What: Timestamped events and records

  • Examples: Application logs, access logs, error stacks

  • Tools: ELK Stack, Loki, CloudWatch Logs

Traces

  • What: End-to-end request journey through distributed systems

  • Examples: Request flow through microservices

  • Tools: Jaeger, Zipkin, OpenTelemetry


Prometheus Deep Dive

1. Architecture & Core Concepts

Prometheus Components:

text

Prometheus Server:
  - Time Series Database (TSDB)
  - Data Retrieval Worker
  - HTTP Server for querying

Service Discovery:
  - Static configs
  - Kubernetes
  - Consul, AWS EC2

Exporters:
  - Node Exporter (hardware metrics)
  - Application Exporters
  - Custom Exporters

Alertmanager:
  - Handles alerts from Prometheus
  - Deduplication, grouping, routing

Key Data Model Concepts:

yaml

Time Series: 
  - Identified by metric name + labels
  - Example: http_requests_total{method="POST", handler="/api"}

Metric Types:
  - Counter: Monotonically increasing (requests, errors)
  - Gauge: Can go up/down (memory usage, temperature)
  - Histogram: Samples observations into buckets (request duration)
  - Summary: Similar to histogram but with quantiles

2. Prometheus Configuration

Basic Configuration File:

yaml

# prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - "alerting_rules.yml"
  - "recording_rules.yml"

alerting:
  alertmanagers:
    - static_configs:
        - targets: ["alertmanager:9093"]

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'node_exporter'
    static_configs:
      - targets: ['node-exporter:9100']
    scrape_interval: 30s
    metrics_path: /metrics

  - job_name: 'kubernetes-pods'
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: true

Kubernetes Service Discovery:

yaml

apiVersion: v1
kind: Service
metadata:
  name: my-app
  annotations:
    prometheus.io/scrape: "true"
    prometheus.io/port: "8080"
    prometheus.io/path: "/metrics"
spec:
  ports:
  - port: 8080
    targetPort: 8080
  selector:
    app: my-app

3. PromQL (Prometheus Query Language)

Essential Queries:

promql

# Basic queries
http_requests_total
http_requests_total{handler="/api", status="200"}
http_requests_total{status=~"5.."}

# Rate and increase
rate(http_requests_total[5m])
increase(http_requests_total[1h])

# Aggregation operators
sum by (handler) (rate(http_requests_total[5m]))
avg by (instance) (node_memory_MemAvailable_bytes)

# Mathematical operations
node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100

# Histogram quantiles
histogram_quantile(0.95, 
  rate(http_request_duration_seconds_bucket[5m])
)

# Alerting conditions
up == 0
rate(http_requests_total{status="500"}[5m]) > 0.1
node_load1 > 0.8

Recording Rules:

yaml

# recording_rules.yml
groups:
  - name: example
    rules:
      - record: job:http_requests:rate5m
        expr: sum by (job) (rate(http_requests_total[5m]))
      
      - record: job:errors_per_request:ratio
        expr: |
          sum by (job) (rate(http_requests_total{status=~"5.."}[5m]))
          /
          sum by (job) (rate(http_requests_total[5m]))

Grafana for Visualization

1. Dashboard Creation

Panel Configuration:

json

{
  "dashboard": {
    "title": "API Monitoring",
    "panels": [
      {
        "title": "Request Rate",
        "type": "graph",
        "targets": [
          {
            "expr": "sum(rate(http_requests_total[5m]))",
            "legendFormat": "{{handler}}"
          }
        ],
        "yaxes": [
          {"format": "reqps"},
          {"format": "short"}
        ]
      }
    ]
  }
}

Advanced Dashboard Features:

yaml

Variables:
  - name: environment
    query: label_values(environment)
    refresh: 1

  - name: instance
    query: label_values(up, instance)
    refresh: 2

Annotations:
  - from alertmanager for highlighting incidents

Templating:
  - Dynamic dashboard elements based on variables

Alerting:
  - Direct alert rules in Grafana 8+

2. Useful Grafana Queries

System Monitoring:

promql

# CPU Usage
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

# Memory Usage
(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100

# Disk Usage
100 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"} * 100)

# Network Traffic
rate(node_network_receive_bytes_total[5m])

Application Monitoring:

promql

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

# Request Duration (p95)
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))

# Throughput
rate(http_requests_total[5m])

# Saturation (Queue Length)
avg_over_time(message_queue_length[5m])

Alertmanager & Alerting

1. Alert Rules Configuration

Prometheus Alert Rules:

yaml

# alerting_rules.yml
groups:
- name: example
  rules:
  - alert: HighErrorRate
    expr: |
      job:errors_per_request:ratio > 0.05
    for: 5m
    labels:
      severity: critical
      team: backend
    annotations:
      summary: "High error rate on {{ $labels.job }}"
      description: "Error rate is {{ $value | humanizePercentage }}"
      runbook_url: "https://example.com/runbook/high-error-rate"

  - alert: InstanceDown
    expr: up == 0
    for: 2m
    labels:
      severity: critical
    annotations:
      summary: "Instance {{ $labels.instance }} down"
      description: "{{ $labels.instance }} has been down for more than 2 minutes"

  - alert: HighMemoryUsage
    expr: |
      (node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) 
      / node_memory_MemTotal_bytes * 100 > 90
    for: 10m
    labels:
      severity: warning
    annotations:
      summary: "High memory usage on {{ $labels.instance }}"

2. Alertmanager Configuration

Routing and Notifications:

yaml

# alertmanager.yml
global:
  smtp_smarthost: 'smtp.example.com:587'
  smtp_from: '[email protected]'
  smtp_auth_username: 'alertmanager'
  smtp_auth_password: 'password'

route:
  group_by: ['alertname', 'cluster']
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 1h
  receiver: 'slack-notifications'
  
  routes:
    - match:
        severity: critical
      receiver: 'pagerduty-critical'
    
    - match_re:
        service: ^(mysql|redis)
      receiver: 'database-team'

receivers:
  - name: 'slack-notifications'
    slack_configs:
      - api_url: 'https://hooks.slack.com/services/...'
        channel: '#alerts'
        send_resolved: true
  
  - name: 'pagerduty-critical'
    pagerduty_configs:
      - service_key: 'your-pagerduty-key'
  
  - name: 'database-team'
    email_configs:
      - to: '[email protected]'

inhibit_rules:
  - source_match:
      severity: 'critical'
    target_match:
      severity: 'warning'
    equal: ['alertname', 'cluster']

OpenTelemetry for Observability

1. OpenTelemetry Architecture

Core Components:

text

Instrumentation:
  - Automatic (agents)
  - Manual (SDK)

Collector:
  - Receivers (OTLP, Jaeger, Zipkin)
  - Processors (batch, filter, transform)
  - Exporters (Jaeger, Prometheus, logging)

Backends:
  - Jaeger, Zipkin, Tempo
  - Commercial solutions

2. Implementation Examples

Python Application Instrumentation:

python

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor
import flask

# Initialize tracing
trace.set_tracer_provider(TracerProvider())
tracer = trace.get_tracer(__name__)

# Configure exporter
otlp_exporter = OTLPSpanExporter(endpoint="http://collector:4317")
span_processor = BatchSpanProcessor(otlp_exporter)
trace.get_tracer_provider().add_span_processor(span_processor)

# Instrument Flask app
app = flask.Flask(__name__)
FlaskInstrumentor().instrument_app(app)
RequestsInstrumentor().instrument()

@app.route("/api/users/<user_id>")
def get_user(user_id):
    with tracer.start_as_current_span("get_user") as span:
        span.set_attribute("user.id", user_id)
        
        # Your business logic
        user_data = fetch_user_data(user_id)
        
        span.set_attribute("user.found", bool(user_data))
        return user_data

def fetch_user_data(user_id):
    with tracer.start_as_current_span("fetch_user_data") as span:
        # This will be automatically traced due to requests instrumentation
        response = requests.get(f"http://user-service/users/{user_id}")
        return response.json()

OpenTelemetry Collector Configuration:

yaml

# otel-collector-config.yml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

  jaeger:
    protocols:
      grpc:
        endpoint: 0.0.0.0:14250
      thrift_compact:
        endpoint: 0.0.0.0:6831
      thrift_binary:
        endpoint: 0.0.0.0:6832

processors:
  batch:
    timeout: 10s
    send_batch_size: 1000
  
  memory_limiter:
    check_interval: 5s
    limit_mib: 2000
    spike_limit_mib: 500

exporters:
  logging:
    loglevel: debug
  
  prometheus:
    endpoint: "0.0.0.0:8889"
    const_labels:
      service: "my-application"
  
  jaeger:
    endpoint: jaeger:14250
    insecure: true

service:
  pipelines:
    traces:
      receivers: [otlp, jaeger]
      processors: [memory_limiter, batch]
      exporters: [logging, jaeger]
    
    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [prometheus]

Log Pipelines (ELK/EFK Stack)

1. ELK Stack Architecture

Components:

text

Beats (Filebeat, Metricbeat):
  - Lightweight data shippers

Logstash:
  - Data processing pipeline
  - Parsing, filtering, transforming

Elasticsearch:
  - Search and analytics engine
  - Indexing and storage

Kibana:
  - Visualization and exploration

2. Filebeat Configuration

Kubernetes Log Collection:

yaml

# filebeat.yml
filebeat.inputs:
- type: container
  paths:
    - /var/log/containers/*.log
  processors:
    - add_kubernetes_metadata:
        host: ${NODE_NAME}
        matchers:
        - logs_path:
            logs_path: "/var/log/containers/"

output.elasticsearch:
  hosts: ["elasticsearch:9200"]
  username: "elastic"
  password: "${ELASTIC_PASSWORD}"

setup.kibana:
  host: "kibana:5601"

Logstash Pipeline:

ruby

# logstash.conf
input {
  beats {
    port => 5044
  }
}

filter {
  # Parse JSON logs
  if [message] =~ /^{.*}$/ {
    json {
      source => "message"
      target => "parsed_json"
    }
  }
  
  # Parse common log formats
  grok {
    match => { "message" => "%{COMBINEDAPACHELOG}" }
  }
  
  # Add timestamp
  date {
    match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
  }
  
  # GeoIP for IP addresses
  geoip {
    source => "clientip"
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "logs-%{+YYYY.MM.dd}"
  }
  
  # For debugging
  stdout { 
    codec => rubydebug 
  }
}

3. EFK with Fluentd/Fluent Bit

Fluent Bit Configuration:

yaml

# fluent-bit-config.yml
apiVersion: v1
kind: ConfigMap
metadata:
  name: fluent-bit-config
data:
  fluent-bit.conf: |
    [SERVICE]
        Daemon       Off
        Log_Level    info
        Parsers_File parsers.conf
        HTTP_Server  On
        HTTP_Listen  0.0.0.0
        HTTP_Port    2020

    [INPUT]
        Name              tail
        Path              /var/log/containers/*.log
        Parser            docker
        Tag               kube.*
        Refresh_Interval  5
        Mem_Buf_Limit     5MB
        Skip_Long_Lines   On

    [FILTER]
        Name                kubernetes
        Match               kube.*
        Kube_URL            https://kubernetes.default.svc:443
        Kube_CA_File        /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        Kube_Token_File     /var/run/secrets/kubernetes.io/serviceaccount/token
        Kube_Tag_Prefix     kube.var.log.containers.
        Merge_Log           On
        Merge_Log_Key       data
        K8S-Logging.Parser  On
        K8S-Logging.Exclude On

    [OUTPUT]
        Name            es
        Match           *
        Host            elasticsearch
        Port            9200
        Logstash_Format On
        Logstash_Prefix fluent-bit
        Replace_Dots    On
        Retry_Limit     False

SLIs, SLOs, and Error Budgets

1. Key Concepts

Definitions:

yaml

SLI (Service Level Indicator):
  - What you measure
  - Examples: Availability, Latency, Throughput

SLO (Service Level Objective):
  - Target value for SLI
  - Example: 99.9% availability

SLA (Service Level Agreement):
  - Contract with consequences for missing SLOs

Error Budget:
  - 1 - SLO
  - Example: 0.1% downtime = 43.2 minutes/month

2. Implementing SLOs

Common SLIs for Web Services:

promql

# Availability SLI
# Requests that succeed (non-5xx) / total requests
sum(rate(http_requests_total{status!~"5.."}[5m])) 
/ 
sum(rate(http_requests_total[5m]))

# Latency SLI  
# Requests faster than threshold / total requests
sum(rate(http_request_duration_seconds_bucket{le="0.1"}[5m]))
/
sum(rate(http_request_duration_seconds_count[5m]))

# Throughput SLI
rate(http_requests_total[5m])

SLO Configuration with Prometheus:

yaml

# slo-rules.yml
groups:
- name: slos
  rules:
  - record: slo:requests:availability30d
    expr: |
      sum_over_time(
        sum(rate(http_requests_total{status!~"5.."}[5m]))[30d:]
      )
      /
      sum_over_time(
        sum(rate(http_requests_total[5m]))[30d:]
      )
  
  - record: slo:requests:error_budget_remaining
    expr: |
      (1 - 0.999) - (1 - slo:requests:availability30d)
  
  - alert: ErrorBudgetBurnRateHigh
    expr: |
      slo:requests:error_budget_remaining < 0.5
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "Error budget consumption high"

3. Error Budget Policy

Multi-Tier Alerting:

yaml

# Multi-level SLO alerts
- alert: ErrorBudgetBurnRateHigh
  expr: rate(slo:requests:error_budget_remaining[1h]) < -0.1
  for: 5m
  labels:
    severity: warning

- alert: ErrorBudgetCritical
  expr: slo:requests:error_budget_remaining < 0.1
  for: 2m
  labels:
    severity: critical

- alert: ErrorBudgetExhausted
  expr: slo:requests:error_budget_remaining <= 0
  labels:
    severity: page

Incident Response & On-Call

1. Incident Management Process

Phases of Incident Response:

yaml

Detection:
  - Alerts trigger
  - User reports
  - Monitoring dashboards

Declaration:
  - Create incident
  - Assemble response team
  - Set severity level

Investigation:
  - Gather data
  - Form hypotheses
  - Identify root cause

Resolution:
  - Implement fix
  - Verify recovery
  - Document actions

Post-Mortem:
  - Timeline reconstruction
  - Root cause analysis
  - Action items

2. On-Call Best Practices

Runbook Example:

markdown

# High Error Rate Runbook

## Symptoms
- Error rate > 5% for 5 minutes
- Alert: HighErrorRate

## Immediate Actions
1. Check dashboard: https://grafana.example.com/d/api-monitoring
2. Identify affected services from error labels
3. Check recent deployments: https://github.com/org/repo/deployments

## Investigation
1. Examine error logs:
   ```bash
   kubectl logs -l app=api-service --since=10m | grep ERROR
  1. Check metrics:

    • Error rate by endpoint

    • Latency percentiles

    • Resource utilization

  2. Common causes:

    • Database connection issues

    • Downstream service failures

    • Resource exhaustion

Resolution Steps

  1. If recent deployment: consider rollback

  2. Scale affected service if resource-constrained

  3. Restart pods if stuck state suspected

Escalation

  • If unresolved in 15 minutes: escalate to senior engineer

  • If affecting customers: notify product team

text

### 3. **Monitoring-Driven Development**

#### Service Level Specifications:
```yaml
# service-level-spec.yml
service: user-api
slo_period: 30d

indicators:
  availability:
    sli: |
      sum(rate(http_requests_total{job="user-api",status!~"5.."}[5m]))
      /
      sum(rate(http_requests_total{job="user-api"}[5m]))
    slo: 0.999  # 99.9%
  
  latency:
    sli: |
      sum(rate(http_request_duration_seconds_bucket{job="user-api",le="0.1"}[5m]))
      /
      sum(rate(http_request_duration_seconds_count{job="user-api"}[5m]))
    slo: 0.95  # 95% under 100ms

alerts:
  - name: user-api-high-error-rate
    condition: |
      rate(http_requests_total{job="user-api",status=~"5.."}[5m])
      /
      rate(http_requests_total{job="user-api"}[5m]) > 0.01
    duration: 2m
    severity: warning

Production Deployment Patterns

1. Monitoring Stack Deployment

Kubernetes Monitoring Stack:

yaml

# monitoring-stack.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: monitoring

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: prometheus
  namespace: monitoring
spec:
  replicas: 1
  selector:
    matchLabels:
      app: prometheus
  template:
    metadata:
      labels:
        app: prometheus
    spec:
      containers:
      - name: prometheus
        image: prom/prometheus:v2.30.0
        args:
          - '--config.file=/etc/prometheus/prometheus.yml'
          - '--storage.tsdb.path=/prometheus'
          - '--web.console.libraries=/etc/prometheus/console_libraries'
          - '--web.console.templates=/etc/prometheus/consoles'
          - '--storage.tsdb.retention.time=30d'
          - '--web.enable-lifecycle'
        ports:
        - containerPort: 9090
        volumeMounts:
        - name: prometheus-config
          mountPath: /etc/prometheus
        - name: prometheus-storage
          mountPath: /prometheus
      volumes:
      - name: prometheus-config
        configMap:
          name: prometheus-config
      - name: prometheus-storage
        persistentVolumeClaim:
          claimName: prometheus-pvc

2. High-Availability Setup

Prometheus HA with Thanos:

yaml

# thanos-sidecar for Prometheus
- name: thanos-sidecar
  image: thanosio/thanos:v0.24.0
  args:
    - sidecar
    - --prometheus.url=http://localhost:9090
    - --grpc-address=0.0.0.0:10901
    - --http-address=0.0.0.0:10902
    - --tsdb.path=/prometheus
  ports:
  - name: grpc
    containerPort: 10901
  - name: http
    containerPort: 10902
  volumeMounts:
  - name: prometheus-storage
    mountPath: /prometheus

 

Best Practices Summary:

  • Start Simple: Basic metrics and logs before complex tracing

  • Define SLOs Early: Build monitoring around service objectives

  • Automate Everything: Infrastructure as code for monitoring stack

  • Document Runbooks: Clear procedures for common incidents

  • Review Regularly: Continuously improve alerts and dashboards

  • Security First: Secure your monitoring data and access

This extensive observability foundation will enable you to build reliable, well-monitored systems with effective incident response capabilities.

Comments (0)

No comments yet. Be the first to comment!

AlphaAI Assistant

Hello! I'm AlphaAI

What would you like to learn today?