Monitoring aplikacji to krytyczny element każdego systemu produkcyjnego. Bez odpowiedniego monitorowania jesteś ślepy – nie wiesz kiedy aplikacja ma problemy, nie rozumiesz jej zachowania pod obciążeniem i nie możesz szybko reagować na incydenty. Prometheus zmienia to radykalnie, oferując profesjonalne rozwiązanie monitoringu które używają największe firmy technologiczne.
Dlaczego to ważne
W erze mikrousług i rozproszonych systemów tradycyjne narzędzia monitoringu stają się niewystarczające. Potrzebujesz rozwiązania które automatycznie odkrywa usługi, zbiera metryki w czasie rzeczywistym i pozwala łatwo analizować trendy. Prometheus rozwiązuje te problemy wprowadzając model „pull-based” – to serwer monitoringu aktywnie odpytuje aplikacje o metryki, zamiast czekać na ich wysłanie. To fundamentalnie inne podejście które sprawdza się w dynamicznych środowiskach.
Co się nauczysz:
- Czym jest Prometheus i jak działa jego architektura
- Instalację i podstawową konfigurację Prometheus
- Instrumentację aplikacji Java z Micrometer
- Definiowanie i zbieranie własnych metryk
- Tworzenie alertów i reguł monitoringu
- Integrację z Grafana do wizualizacji
- Best practices dla monitoringu w produkcji
Architektura Prometheus
Prometheus składa się z kilku kluczowych komponentów które współpracują ze sobą:
# prometheus.yml - podstawowa konfiguracja global: scrape_interval: 15s # Jak często zbierać metryki evaluation_interval: 15s # Jak często sprawdzać reguły alertów rule_files: - "alert_rules.yml" scrape_configs: # Monitoring samego Prometheus - job_name: 'prometheus' static_configs: - targets: ['localhost:9090'] # Monitoring aplikacji Spring Boot - job_name: 'spring-boot-app' metrics_path: '/actuator/prometheus' static_configs: - targets: ['localhost:8080'] # Monitoring systemu operacyjnego - job_name: 'node-exporter' static_configs: - targets: ['localhost:9100'] alerting: alertmanagers: - static_configs: - targets: - alertmanager:9093
Instalacja i uruchomienie
Najszybszy sposób na start z Prometheus to Docker Compose który uruchomi kompletny stack monitoringu:
# docker-compose.yml version: '3.7' services: prometheus: image: prom/prometheus:v2.1.0 container_name: prometheus ports: - "9090:9090" volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - ./alert_rules.yml:/etc/prometheus/alert_rules.yml command: - '--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=15d' - '--web.enable-lifecycle' grafana: image: grafana/grafana:4.6.3 container_name: grafana ports: - "3000:3000" environment: - GF_SECURITY_ADMIN_PASSWORD=admin volumes: - grafana-storage:/var/lib/grafana node-exporter: image: prom/node-exporter:v0.15.2 container_name: node-exporter ports: - "9100:9100" volumes: grafana-storage:
# Uruchomienie całego stacku docker-compose up -d # Sprawdzenie czy wszystko działa curl http://localhost:9090/targets # Status targets w Prometheus curl http://localhost:3000 # Grafana (admin/admin) curl http://localhost:9100/metrics # Node exporter metryki
Instrumentacja aplikacji Spring Boot
Spring Boot 2.0 wprowadza Micrometer – bibliotekę która standaryzuje metryki i ułatwia integrację z Prometheus:
<!-- pom.xml --> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> </dependency> </dependencies>
# application.properties management.endpoints.web.exposure.include=prometheus,health,info management.endpoint.prometheus.enabled=true management.metrics.export.prometheus.enabled=true # Włączenie szczegółowych metryk JVM management.metrics.enable.jvm=true management.metrics.enable.process=true management.metrics.enable.system=true
@RestController @RequestMapping("/api/orders") public class OrderController { private final MeterRegistry meterRegistry; private final Counter orderCounter; private final Timer orderProcessingTimer; public OrderController(MeterRegistry meterRegistry) { this.meterRegistry = meterRegistry; this.orderCounter = Counter.builder("orders_total") .description("Total number of orders") .tag("status", "created") .register(meterRegistry); this.orderProcessingTimer = Timer.builder("order_processing_duration") .description("Time spent processing orders") .register(meterRegistry); } @PostMapping public ResponseEntity<Order> createOrder(@RequestBody CreateOrderRequest request) { return orderProcessingTimer.recordCallable(() -> { try { Order order = orderService.createOrder(request); orderCounter.increment(); // Gauge dla aktualnej liczby zamówień w systemie Gauge.builder("orders_pending") .description("Current number of pending orders") .register(meterRegistry, this, obj -> orderService.getPendingOrdersCount()); return ResponseEntity.ok(order); } catch (Exception e) { Counter.builder("orders_errors_total") .description("Total number of order errors") .tag("error_type", e.getClass().getSimpleName()) .register(meterRegistry) .increment(); throw e; } }); } }
Typy metryk w Prometheus
Prometheus rozpoznaje cztery podstawowe typy metryk, każdy służy do innego celu:
@Component public class CustomMetrics { private final MeterRegistry meterRegistry; // Counter - liczba przetworzonych płatności private final Counter paymentCounter = Counter.builder("payments_processed_total") .description("Total payments processed") .tag("payment_method", "card") .register(meterRegistry); // Gauge - aktualna liczba użytkowników online private final AtomicInteger usersOnline = new AtomicInteger(0); // Histogram - czas odpowiedzi API private final Timer apiResponseTime = Timer.builder("api_response_time") .description("API response time") .publishPercentiles(0.5, 0.95, 0.99) // 50th, 95th, 99th percentile .register(meterRegistry); public CustomMetrics(MeterRegistry meterRegistry) { this.meterRegistry = meterRegistry; // Rejestracja Gauge Gauge.builder("users_online") .description("Current users online") .register(meterRegistry, usersOnline, AtomicInteger::get); } public void recordPayment(String method, BigDecimal amount) { paymentCounter.increment(Tags.of("payment_method", method)); // Histogram dla kwot płatności DistributionSummary.builder("payment_amount") .description("Payment amounts") .tag("method", method) .register(meterRegistry) .record(amount.doubleValue()); } @EventListener public void onUserLogin(UserLoginEvent event) { usersOnline.incrementAndGet(); } @EventListener public void onUserLogout(UserLogoutEvent event) { usersOnline.decrementAndGet(); } }
Zapytania PromQL
PromQL to język zapytań Prometheus który pozwala analizować metryki i tworzyć alerty:
# Podstawowe zapytania http_requests_total # Wszystkie requesty HTTP http_requests_total{status="200"} # Tylko successful requests http_requests_total{job="spring-app"} # Tylko z konkretnej aplikacji # Rate i increase - tempo zmian rate(http_requests_total[5m]) # Requests per second (5min window) increase(http_requests_total[1h]) # Wzrost w ciągu godziny # Aggregation functions sum(rate(http_requests_total[5m])) # Całkowity RPS avg(response_time_seconds) # Średni czas odpowiedzi max(memory_usage_bytes) # Maksymalne użycie pamięci # Percentile z histogram histogram_quantile(0.95, rate(api_response_time_bucket[5m]) # 95th percentile response time ) # Alerty i warunki rate(http_requests_total{status=~"5.."}[5m]) > 0.1 # Error rate > 10% up == 0 # Aplikacja nie odpowiada (time() - process_start_time_seconds) < 60 # Restart w ostatniej minucie
Alerting i reguły
Prometheus może automatycznie wykrywać problemy i wysyłać alerty:
# alert_rules.yml groups: - name: application_alerts rules: # Alert gdy aplikacja nie odpowiada - alert: ApplicationDown expr: up{job="spring-boot-app"} == 0 for: 1m labels: severity: critical annotations: summary: "Application {{ $labels.instance }} is down" description: "Application has been down for more than 1 minute" # Alert przy wysokim error rate - alert: HighErrorRate expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.1 for: 2m labels: severity: warning annotations: summary: "High error rate on {{ $labels.instance }}" description: "Error rate is {{ $value | humanizePercentage }}" # Alert przy wysokim użyciu pamięci - alert: HighMemoryUsage expr: (jvm_memory_used_bytes / jvm_memory_max_bytes) > 0.9 for: 5m labels: severity: warning annotations: summary: "High memory usage on {{ $labels.instance }}" description: "Memory usage is above 90%: {{ $value | humanizePercentage }}" # Alert przy długich czasach odpowiedzi - alert: SlowResponseTime expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 1 for: 3m labels: severity: warning annotations: summary: "Slow response times on {{ $labels.instance }}" description: "95th percentile response time is {{ $value }}s"
# alertmanager.yml global: smtp_smarthost: 'localhost:587' smtp_from: 'alertmanager@company.com' route: group_by: ['alertname'] group_wait: 10s group_interval: 10s repeat_interval: 1h receiver: 'web-team' receivers: - name: 'web-team' email_configs: - to: 'team@company.com' subject: 'ALERT: {{ .GroupLabels.alertname }}' body: | {{ range .Alerts }} Alert: {{ .Annotations.summary }} Description: {{ .Annotations.description }} {{ end }} slack_configs: - api_url: 'YOUR_SLACK_WEBHOOK_URL' channel: '#alerts' title: 'Production Alert' text: '{{ .CommonAnnotations.summary }}'
Integracja z Grafana
Grafana to idealne narzędzie do wizualizacji metryk z Prometheus:
{ "dashboard": { "title": "Application Monitoring", "panels": [ { "title": "Request Rate", "type": "graph", "targets": [ { "expr": "rate(http_requests_total[5m])", "legendFormat": "{{ instance }} - {{ status }}" } ] }, { "title": "Response Time (95th percentile)", "type": "singlestat", "targets": [ { "expr": "histogram_quantile(0.95, rate(api_response_time_bucket[5m]))", "legendFormat": "95th percentile" } ] }, { "title": "Memory Usage", "type": "graph", "targets": [ { "expr": "jvm_memory_used_bytes / jvm_memory_max_bytes * 100", "legendFormat": "{{ instance }} Memory %" } ] } ] } }
Best practices dla produkcji
- USE Method: Utilization, Saturation, Errors dla każdego zasobu
- RED Method: Rate, Errors, Duration dla każdego serwisu
- Four Golden Signals: Latency, Traffic, Errors, Saturation
@Configuration public class MetricsConfiguration { @Bean public MeterRegistryCustomizer<MeterRegistry> commonTags() { return registry -> registry.config() .commonTags("application", "order-service") .commonTags("version", getClass().getPackage().getImplementationVersion()) .commonTags("environment", System.getProperty("spring.profiles.active", "unknown")); } @Bean @ConditionalOnProperty("app.metrics.detailed.enabled") public TimedAspect timedAspect(MeterRegistry registry) { return new TimedAspect(registry); } } // Automatyczne mierzenie czasu wykonania metod @Service public class OrderService { @Timed(name = "order.creation.time", description = "Time taken to create order") public Order createOrder(CreateOrderRequest request) { // Business logic return order; } @Counted(name = "order.validation.attempts", description = "Order validation attempts") public void validateOrder(Order order) { // Validation logic } }
Tak! Prometheus świetnie skaluje się w dół. Nawet dla pojedynczej aplikacji daje ogromną wartość przez wgląd w performance i możliwość wczesnego wykrywania problemów. Docker Compose setup to dosłownie 5 minut pracy.
Domyślnie 15 dni lokalnie. Dla długoterminowego storage możesz skonfigurować remote storage (InfluxDB, Cortex) lub zwiększyć retention period. W praktyce większość alertów potrzebuje danych z ostatnich godzin/dni.
Histogram grupuje obserwacje w buckets i pozwala obliczać percentyle po stronie serwera. Summary oblicza percentyle po stronie klienta. Histogram jest bardziej elastyczny ale wymaga więcej zasobów.
Oczywiście! Prometheus ma biblioteki klienckie dla Python, Go, .NET, Node.js i wielu innych. Istnieją też exporters dla baz danych, message queues, i większości popularnych technologii.
Unikaj labelów z wartościami user ID, IP addresses, czy timestamps. Używaj labelów tylko dla kategorii (status codes, methods, regions). Jeśli potrzebujesz high-cardinality, rozważ sampling lub osobne narzędzie do detailed tracing.
Dla typowej aplikacji Prometheus zużywa ~200MB RAM i minimalny CPU. Zużycie rośnie liniowo z liczbą metryk i częstotliwością scrapingu. 1GB RAM wystarcza dla większości środowisk developerskich i małych produkcji.
Użyj 'amtool' do testowania konfiguracji Alertmanager. Możesz też tworzyć "test" metryki które celowo przekraczają thresholdy. W Prometheus UI możesz sprawdzać czy reguły alertów są poprawnie skonfigurowane.
Przydatne zasoby:
- Oficjalna dokumentacja Prometheus
- Biblioteka dashboardów Grafana
- Prometheus Java Client
- Dokumentacja Micrometer
- Kolekcja gotowych alertów
🚀 Zadanie dla Ciebie
Zbuduj kompletny monitoring stack: Stwórz aplikację Spring Boot z custom metrykami (liczba użytkowników online, czas procesowania zamówień). Skonfiguruj Prometheus + Grafana + Alertmanager używając Docker Compose. Stwórz dashboard z kluczowymi metrykami i skonfiguruj alert który wyśle email gdy error rate przekroczy 5%. Bonus: dodaj Node Exporter dla metryk systemu operacyjnego!
Czy już używasz Prometheus w swoich projektach? Jakie metryki uważasz za najważniejsze w monitoringu aplikacji? Podziel się swoimi doświadczeniami w komentarzach!