---
allowed-tools: Read, Write, Edit, Bash
argument-hint: [monitoring-type] | setup | dashboard | alerts | metrics | health | performance
description: Comprehensive deployment monitoring with observability, alerting, health checks, and performance tracking
---
# Deployment Monitoring & Observability
Setup comprehensive deployment monitoring: $ARGUMENTS
## Current Monitoring State
- Existing monitoring: !`kubectl get pods -n monitoring 2>/dev/null || docker ps | grep -E "(prometheus|grafana|jaeger)" || echo "No monitoring detected"`
- Health endpoints: !`curl -s https://api.example.com/health 2>/dev/null | jq -r '.status // "Unknown"' || echo "Health endpoint needed"`
- Metrics exposure: !`curl -s https://api.example.com/metrics 2>/dev/null | head -5 || echo "Metrics endpoint needed"`
- Log aggregation: !`kubectl get pods -n logging 2>/dev/null || echo "Log aggregation setup needed"`
- APM integration: Check for application performance monitoring setup
## Task
Implement comprehensive monitoring and observability for deployments with real-time insights, alerting, and automated response capabilities.
## Monitoring Architecture
### 1. **Core Monitoring Stack**
#### Prometheus Configuration
```yaml
# prometheus-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
namespace: monitoring
data:
prometheus.yml: |
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "/etc/prometheus/rules/*.yml"
scrape_configs:
# Application metrics
- job_name: 'myapp'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
- source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
action: replace
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
target_label: __address__
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
# Kubernetes cluster metrics
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_phase]
action: keep
regex: Running
# Node exporter for infrastructure metrics
- job_name: 'node-exporter'
kubernetes_sd_configs:
- role: endpoints
relabel_configs:
- source_labels: [__meta_kubernetes_endpoints_name]
action: keep
regex: node-exporter
# Deployment-specific metrics
- job_name: 'deployment-metrics'
static_configs:
- targets: ['deployment-exporter:9090']
metrics_path: /metrics
scrape_interval: 30s
alerting:
alertmanagers:
- static_configs:
- targets: ['alertmanager:9093']
---
# Prometheus Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus
namespace: monitoring
spec:
replicas: 1
selector:
matchLabels:
app: prometheus
template:
metadata:
labels:
app: prometheus
spec:
serviceAccountName: prometheus
containers:
- name: prometheus
image: prom/prometheus:v2.40.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'
- '--web.enable-admin-api'
ports:
- containerPort: 9090
volumeMounts:
- name: prometheus-config
mountPath: /etc/prometheus
- name: prometheus-storage
mountPath: /prometheus
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "2Gi"
cpu: "1000m"
volumes:
- name: prometheus-config
configMap:
name: prometheus-config
- name: prometheus-storage
persistentVolumeClaim:
claimName: prometheus-pvc
```
#### Grafana Dashboard Configuration
```yaml
# grafana-dashboard-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: deployment-dashboard
namespace: monitoring
data:
deployment-monitoring.json: |
{
"dashboard": {
"id": null,
"title": "Deployment Monitoring Dashboard",
"tags": ["deployment", "monitoring"],
"timezone": "browser",
"panels": [
{
"id": 1,
"title": "Deployment Status",
"type": "stat",
"targets": [
{
"expr": "up{job=\"myapp\"}",
"legendFormat": "{{instance}}"
}
],
"fieldConfig": {
"defaults": {
"thresholds": {
"steps": [
{"color": "red", "value": 0},
{"color": "green", "value": 1}
]
}
}
}
},
{
"id": 2,
"title": "Request Rate",
"type": "graph",
"targets": [
{
"expr": "rate(http_requests_total[5m])",
"legendFormat": "{{method}} {{status}}"
}
]
},
{
"id": 3,
"title": "Error Rate",
"type": "graph",
"targets": [
{
"expr": "rate(http_requests_total{status=~\"5..\"}[5m]) / rate(http_requests_total[5m]) * 100",
"legendFormat": "Error Rate %"
}
]
},
{
"id": 4,
"title": "Response Time",
"type": "graph",
"targets": [
{
"expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))",
"legendFormat": "95th percentile"
},
{
"expr": "histogram_quantile(0.50, rate(http_request_duration_seconds_bucket[5m]))",
"legendFormat": "50th percentile"
}
]
},
{
"id": 5,
"title": "Pod Resource Usage",
"type": "graph",
"targets": [
{
"expr": "rate(container_cpu_usage_seconds_total{pod=~\"myapp-.*\"}[5m]) * 100",
"legendFormat": "CPU Usage - {{pod}}"
},
{
"expr": "container_memory_usage_bytes{pod=~\"myapp-.*\"} / 1024 / 1024",
"legendFormat": "Memory Usage MB - {{pod}}"
}
]
},
{
"id": 6,
"title": "Deployment Events",
"type": "logs",
"targets": [
{
"expr": "{job=\"kubernetes-events\"} |= \"myapp\"",
"legendFormat": ""
}
]
}
],
"time": {
"from": "now-1h",
"to": "now"
},
"refresh": "30s"
}
}
```
### 2. **Application Health Monitoring**
#### Health Check Implementation
```javascript
// health-check.js - Application health endpoint
const express = require('express');
const { promisify } = require('util');
class HealthMonitor {
constructor() {
this.checks = new Map();
this.status = 'healthy';
this.lastCheck = new Date();
}
registerCheck(name, checkFunction, options = {}) {
this.checks.set(name, {
check: checkFunction,
timeout: options.timeout || 5000,
critical: options.critical || false,
lastStatus: null,
lastCheck: null,
errorCount: 0
});
}
async runHealthChecks() {
const results = {};
let overallHealthy = true;
for (const [name, config] of this.checks) {
try {
const startTime = Date.now();
const result = await Promise.race([
config.check(),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Health check timeout')), config.timeout)
)
]);
const duration = Date.now() - startTime;
results[name] = {
status: 'healthy',
duration,
details: result,
lastCheck: new Date().toISOString()
};
config.lastStatus = 'healthy';
config.errorCount = 0;
} catch (error) {
results[name] = {
status: 'unhealthy',
error: error.message,
lastCheck: new Date().toISOString()
};
config.lastStatus = 'unhealthy';
config.errorCount++;
if (config.critical) {
overallHealthy = false;
}
}
config.lastCheck = new Date();
}
this.status = overallHealthy ? 'healthy' : 'unhealthy';
this.lastCheck = new Date();
return {
status: this.status,
timestamp: this.lastCheck.toISOString(),
checks: results,
uptime: process.uptime(),
version: process.env.APP_VERSION || 'unknown'
};
}
setupEndpoints(app) {
// Liveness probe - basic application health
app.get('/health', async (req, res) => {
const health = await this.runHealthChecks();
const statusCode = health.status === 'healthy' ? 200 : 503;
res.status(statusCode).json(health);
});
// Readiness probe - ready to receive traffic
app.get('/ready', async (req, res) => {
const health = await this.runHealthChecks();
// Additional readiness checks
const readinessChecks = {
memoryUsage: process.memoryUsage().heapUsed / process.memoryUsage().heapTotal < 0.9,
activeConnections: true, // Check active connections if applicable
};
const isReady = health.status === 'healthy' &&
Object.values(readinessChecks).every(check => check);
res.status(isReady ? 200 : 503).json({
...health,
ready: isReady,
readinessChecks
});
});
// Startup probe - application has started
app.get('/startup', (req, res) => {
res.status(200).json({
status: 'started',
timestamp: new Date().toISOString(),
pid: process.pid,
uptime: process.uptime()
});
});
}
}
// Usage example
const healthMonitor = new HealthMonitor();
// Register health checks
healthMonitor.registerCheck('database', async () => {
// Database connectivity check
await db.query('SELECT 1');
return { connected: true };
}, { critical: true, timeout: 3000 });
healthMonitor.registerCheck('redis', async () => {
// Redis connectivity check
await redis.ping();
return { connected: true };
}, { critical: false, timeout: 2000 });
healthMonitor.registerCheck('external-api', async () => {
// External service check
const response = await fetch('https://api.external-service.com/health');
return { status: response.status, healthy: response.ok };
}, { critical: false, timeout: 5000 });
module.exports = healthMonitor;
```
### 3. **Custom Metrics and Instrumentation**
#### Application Metrics
```javascript
// metrics.js - Application metrics collection
const promClient = require('prom-client');
class DeploymentMetrics {
constructor() {
// Default metrics
promClient.collectDefaultMetrics({
prefix: 'myapp_',
timeout: 5000,
});
// Custom deployment metrics
this.deploymentInfo = new promClient.Gauge({
name: 'myapp_deployment_info',
help: 'Deployment information',
labelNames: ['version', 'environment', 'commit_sha']
});
this.httpRequestsTotal = new promClient.Counter({
name: 'myapp_http_requests_total',
help: 'Total HTTP requests',
labelNames: ['method', 'status_code', 'route']
});
this.httpRequestDuration = new promClient.Histogram({
name: 'myapp_http_request_duration_seconds',
help: 'HTTP request duration in seconds',
labelNames: ['method', 'status_code', 'route'],
buckets: [0.1, 0.5, 1, 2, 5]
});
this.activeConnections = new promClient.Gauge({
name: 'myapp_active_connections',
help: 'Number of active connections'
});
this.deploymentEvents = new promClient.Counter({
name: 'myapp_deployment_events_total',
help: 'Deployment events',
labelNames: ['event_type', 'status']
});
this.healthCheckStatus = new promClient.Gauge({
name: 'myapp_health_check_status',
help: 'Health check status (1 = healthy, 0 = unhealthy)',
labelNames: ['check_name']
});
// Business metrics
this.businessMetrics = {
activeUsers: new promClient.Gauge({
name: 'myapp_active_users',
help: 'Number of active users'
}),
transactionsTotal: new promClient.Counter({
name: 'myapp_transactions_total',
help: 'Total transactions processed',
labelNames: ['type', 'status']
}),
errorRate: new promClient.Gauge({
name: 'myapp_error_rate',
help: 'Application error rate percentage'
})
};
this.initializeMetrics();
}
initializeMetrics() {
// Set deployment information
this.deploymentInfo.set({
version: process.env.APP_VERSION || 'unknown',
environment: process.env.NODE_ENV || 'development',
commit_sha: process.env.GIT_COMMIT_SHA || 'unknown'
}, 1);
}
recordHttpRequest(req, res, duration) {
const labels = {
method: req.method,
status_code: res.statusCode,
route: req.route?.path || req.path
};
this.httpRequestsTotal.inc(labels);
this.httpRequestDuration.observe(labels, duration);
}
recordDeploymentEvent(eventType, status) {
this.deploymentEvents.inc({
event_type: eventType,
status: status
});
}
updateHealthCheckStatus(checkName, isHealthy) {
this.healthCheckStatus.set(
{ check_name: checkName },
isHealthy ? 1 : 0
);
}
updateActiveConnections(count) {
this.activeConnections.set(count);
}
// Middleware for Express.js
expressMiddleware() {
return (req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = (Date.now() - start) / 1000;
this.recordHttpRequest(req, res, duration);
});
next();
};
}
// Get metrics endpoint
getMetricsHandler() {
return async (req, res) => {
res.set('Content-Type', promClient.register.contentType);
const metrics = await promClient.register.metrics();
res.end(metrics);
};
}
}
module.exports = DeploymentMetrics;
```
### 4. **Alert Configuration**
#### Alertmanager Configuration
```yaml
# alertmanager-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: alertmanager-config
namespace: monitoring
data:
alertmanager.yml: |
global:
smtp_smarthost: 'smtp.gmail.com:587'
smtp_from: 'alerts@example.com'
smtp_auth_username: 'alerts@example.com'
smtp_auth_password: 'password'
route:
group_by: ['alertname', 'environment']
group_wait: 10s
group_interval: 10s
repeat_interval: 1h
receiver: 'default'
routes:
- match:
severity: critical
receiver: 'critical-alerts'
continue: true
- match:
alertname: DeploymentFailed
receiver: 'deployment-alerts'
continue: true
- match:
service: myapp
receiver: 'app-alerts'
receivers:
- name: 'default'
slack_configs:
- api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
channel: '#monitoring'
title: 'Alert: {{ range .Alerts }}{{ .Annotations.summary }}{{ end }}'
text: '{{ range .Alerts }}{{ .Annotations.description }}{{ end }}'
- name: 'critical-alerts'
slack_configs:
- api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
channel: '#critical-alerts'
title: '🚨 CRITICAL: {{ range .Alerts }}{{ .Annotations.summary }}{{ end }}'
text: '{{ range .Alerts }}{{ .Annotations.description }}{{ end }}'
email_configs:
- to: 'oncall@example.com'
subject: 'CRITICAL Alert: {{ range .Alerts }}{{ .Annotations.summary }}{{ end }}'
body: |
Alert Details:
{{ range .Alerts }}
- Alert: {{ .Annotations.summary }}
- Description: {{ .Annotations.description }}
- Severity: {{ .Labels.severity }}
- Environment: {{ .Labels.environment }}
{{ end }}
- name: 'deployment-alerts'
slack_configs:
- api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
channel: '#deployments'
title: '🚀 Deployment Alert: {{ range .Alerts }}{{ .Annotations.summary }}{{ end }}'
- name: 'app-alerts'
slack_configs:
- api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
channel: '#app-monitoring'
inhibit_rules:
- source_match:
severity: 'critical'
target_match:
severity: 'warning'
equal: ['alertname', 'environment', 'service']
```
#### Deployment Alert Rules
```yaml
# deployment-alert-rules.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: deployment-monitoring-rules
namespace: monitoring
spec:
groups:
- name: deployment-health
rules:
# Application availability
- alert: ApplicationDown
expr: up{job="myapp"} == 0
for: 1m
labels:
severity: critical
service: myapp
annotations:
summary: "Application instance is down"
description: "{{ $labels.instance }} has been down for more than 1 minute"
runbook_url: "https://wiki.example.com/runbooks/app-down"
# High error rate
- alert: HighErrorRate
expr: rate(myapp_http_requests_total{status_code=~"5.."}[5m]) / rate(myapp_http_requests_total[5m]) * 100 > 5
for: 2m
labels:
severity: critical
service: myapp
annotations:
summary: "High error rate detected"
description: "Error rate is {{ $value }}% for the last 5 minutes"
# Slow response times
- alert: SlowResponseTime
expr: histogram_quantile(0.95, rate(myapp_http_request_duration_seconds_bucket[5m])) > 2
for: 5m
labels:
severity: warning
service: myapp
annotations:
summary: "Slow response times detected"
description: "95th percentile response time is {{ $value }}s"
# Memory usage
- alert: HighMemoryUsage
expr: container_memory_usage_bytes{pod=~"myapp-.*"} / container_spec_memory_limit_bytes * 100 > 80
for: 5m
labels:
severity: warning
service: myapp
annotations:
summary: "High memory usage"
description: "Pod {{ $labels.pod }} memory usage is {{ $value }}%"
# CPU usage
- alert: HighCPUUsage
expr: rate(container_cpu_usage_seconds_total{pod=~"myapp-.*"}[5m]) * 100 > 80
for: 10m
labels:
severity: warning
service: myapp
annotations:
summary: "High CPU usage"
description: "Pod {{ $labels.pod }} CPU usage is {{ $value }}%"
- name: deployment-events
rules:
# Deployment failed
- alert: DeploymentFailed
expr: increase(kube_deployment_status_replicas_unavailable{deployment=~"myapp-.*"}[5m]) > 0
for: 2m
labels:
severity: critical
service: myapp
annotations:
summary: "Deployment has failed pods"
description: "Deployment {{ $labels.deployment }} has {{ $value }} unavailable replicas"
# Deployment stuck
- alert: DeploymentStuck
expr: kube_deployment_spec_replicas{deployment=~"myapp-.*"} != kube_deployment_status_ready_replicas{deployment=~"myapp-.*"}
for: 10m
labels:
severity: warning
service: myapp
annotations:
summary: "Deployment appears stuck"
description: "Deployment {{ $labels.deployment }} has been in progress for more than 10 minutes"
# Pod crash looping
- alert: PodCrashLooping
expr: rate(kube_pod_container_status_restarts_total{pod=~"myapp-.*"}[5m]) > 0.1
for: 2m
labels:
severity: critical
service: myapp
annotations:
summary: "Pod is crash looping"
description: "Pod {{ $labels.pod }} is restarting frequently"
- name: business-metrics
rules:
# Transaction failure rate
- alert: HighTransactionFailureRate
expr: rate(myapp_transactions_total{status="failed"}[5m]) / rate(myapp_transactions_total[5m]) * 100 > 1
for: 5m
labels:
severity: warning
service: myapp
annotations:
summary: "High transaction failure rate"
description: "Transaction failure rate is {{ $value }}%"
# Low active users (potential issue indicator)
- alert: LowActiveUsers
expr: myapp_active_users < 10 and hour() > 8 and hour() < 18 # During business hours
for: 15m
labels:
severity: warning
service: myapp
annotations:
summary: "Unusually low active user count"
description: "Only {{ $value }} active users during business hours"
```
### 5. **Log Aggregation and Analysis**
#### Fluentd Configuration
```yaml
# fluentd-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: fluentd-config
namespace: logging
data:
fluent.conf: |
@type tail
@id myapp_logs
path /var/log/containers/myapp-*.log
pos_file /var/log/fluentd-myapp.log.pos
tag kubernetes.myapp
format json
time_key time
time_format %Y-%m-%dT%H:%M:%S.%NZ
@type kubernetes_metadata
@id kubernetes_metadata
@type parser
key_name log
reserve_data true
@type json
time_key timestamp
time_format %Y-%m-%dT%H:%M:%S.%L%z
# Deployment event logs
@type record_transformer
enable_ruby true
deployment_info ${record.dig("kubernetes", "labels", "deployment") || "unknown"}
environment ${record.dig("kubernetes", "labels", "environment") || "unknown"}
version ${record.dig("kubernetes", "labels", "version") || "unknown"}
log_level ${record["level"] || "info"}
component ${record["component"] || "application"}
# Error log alerts
@type grep
key log_level
pattern /error|fatal|panic/i
alert_type error
needs_attention true
@type elasticsearch
@id out_es_myapp
hosts elasticsearch.logging.svc.cluster.local:9200
logstash_format true
logstash_prefix myapp-deployment
include_tag_key true
tag_key @log_name
flush_interval 10s
@type file
path /var/log/fluentd-buffers/myapp.buffer
flush_mode interval
retry_type exponential_backoff
flush_thread_count 2
flush_interval 5s
retry_forever
retry_max_interval 30
chunk_limit_size 2M
queue_limit_length 8
overflow_action block
```
### 6. **Performance Monitoring**
#### APM Integration with Jaeger
```javascript
// tracing.js - Distributed tracing setup
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { JaegerExporter } = require('@opentelemetry/exporter-jaeger');
const { Resource } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
const jaegerExporter = new JaegerExporter({
endpoint: process.env.JAEGER_ENDPOINT || 'http://jaeger-collector:14268/api/traces',
});
const sdk = new NodeSDK({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'myapp',
[SemanticResourceAttributes.SERVICE_VERSION]: process.env.APP_VERSION || 'unknown',
[SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: process.env.NODE_ENV || 'development',
}),
traceExporter: jaegerExporter,
instrumentations: [
getNodeAutoInstrumentations({
// Customize instrumentation
'@opentelemetry/instrumentation-http': {
requestHook: (span, request) => {
span.setAttribute('deployment.version', process.env.APP_VERSION);
span.setAttribute('deployment.environment', process.env.NODE_ENV);
},
},
}),
],
});
sdk.start();
// Custom deployment tracing
const { trace, context } = require('@opentelemetry/api');
class DeploymentTracer {
constructor() {
this.tracer = trace.getTracer('deployment-monitor', '1.0.0');
}
traceDeploymentEvent(eventName, metadata, callback) {
const span = this.tracer.startSpan(`deployment.${eventName}`, {
attributes: {
'deployment.event': eventName,
'deployment.version': metadata.version,
'deployment.environment': metadata.environment,
'deployment.timestamp': new Date().toISOString(),
},
});
return context.with(trace.setSpan(context.active(), span), async () => {
try {
const result = await callback();
span.setStatus({ code: trace.SpanStatusCode.OK });
span.setAttribute('deployment.result', 'success');
return result;
} catch (error) {
span.setStatus({
code: trace.SpanStatusCode.ERROR,
message: error.message,
});
span.setAttribute('deployment.result', 'failure');
span.setAttribute('deployment.error', error.message);
throw error;
} finally {
span.end();
}
});
}
}
module.exports = { DeploymentTracer, sdk };
```
### 7. **Monitoring Dashboard Setup Script**
#### Complete Monitoring Setup
```bash
#!/bin/bash
# setup-monitoring.sh
set -e
NAMESPACE_MONITORING="monitoring"
NAMESPACE_LOGGING="logging"
APP_NAME="myapp"
log() {
echo -e "\033[32m[$(date '+%Y-%m-%d %H:%M:%S')] $1\033[0m"
}
error() {
echo -e "\033[31m[ERROR] $1\033[0m"
exit 1
}
# Create namespaces
create_namespaces() {
log "Creating monitoring namespaces..."
kubectl create namespace $NAMESPACE_MONITORING --dry-run=client -o yaml | kubectl apply -f -
kubectl create namespace $NAMESPACE_LOGGING --dry-run=client -o yaml | kubectl apply -f -
# Add labels
kubectl label namespace $NAMESPACE_MONITORING monitoring=enabled --overwrite
kubectl label namespace $NAMESPACE_LOGGING logging=enabled --overwrite
}
# Deploy Prometheus
deploy_prometheus() {
log "Deploying Prometheus..."
# Create service account
cat < port-forward-monitoring.sh < port-forward-logging.sh <