Entah bagaimana saya harus berurusan dengan metrik untuk API kami, seperti biasa (tidak ada waktu?!) Untuk ditambahkan nanti - sangat sulit dan belum diimplementasikan - itu berarti saatnya untuk mengimplementasikannya. Setelah beberapa pengembaraan di internet, sistem pemantauan yang paling populer, menurut saya, adalah Prometheus.
Menggunakan Prometheus, kita dapat memonitor berbagai sumber daya komputer, seperti: memori, prosesor, disk, beban jaringan. Mungkin juga penting bagi kita untuk menghitung jumlah panggilan ke metode API kita atau untuk mengukur waktu pelaksanaannya, karena semakin besar beban pada sistem, semakin mahal waktu hentinya. Dan di sini Prometheus datang membantu kami. Menurut saya, artikel ini memberikan poin utama untuk memahami karya Prometheus dan untuk menambahkan koleksi metrik ke API. Karena itu, kita mulai dengan yang paling dangkal, dengan deskripsi kecil.
Prometheus adalah sistem open source dan Time Series DBMS yang ditulis dalam Go dan dikembangkan oleh SoundCloud. Ini memiliki dokumentasi resmi dan dukungan untuk bahasa seperti: Go, Java atau Scala, Python, Ruby. Ada dukungan tidak resmi untuk bahasa lain, seperti: C #, C ++, C, Bash, Lua untuk Nginx, Lua untuk Tarantool dan lainnya, seluruh daftar ada di situs resmi Prometheus.
Semua layanan Prometheus tersedia sebagai gambar Docker di Docker Hub atau Quay.io.
Prometheus diluncurkan oleh docker run -p 9090:9090 prom/prometheus
, yang memulainya dengan konfigurasi default dan menetapkan port localhost:9090
untuk itu. Setelah itu, Prometheus UI akan tersedia di localhost:9090
.
Prometheus adalah sistem pemantauan yang mencakup berbagai alat untuk mengonfigurasi pemantauan aplikasi (titik akhir) menggunakan protokol HTTP. Saat menghubungkan ke Prometheus, API HTTP tidak mendukung "auth dasar". Jika Anda ingin menggunakan otentikasi dasar untuk terhubung ke Prometheus, kami sarankan Anda menggunakan Prometheus bersama dengan server proxy terbalik dan menggunakan otentikasi pada tingkat proxy. Anda dapat menggunakan proksi terbalik apa pun dengan Prometheus.
Komponen utama Prometheus:
- server yang mengumpulkan metrik, menyimpannya ke database dan membersihkannya;
- paket untuk mengumpulkan metrik di API;
- Pushgateway - komponen untuk menerima metrik dari aplikasi yang permintaan Tariknya tidak dapat digunakan;
- Eksportir - alat untuk mengekspor metrik dari aplikasi dan layanan pihak ketiga, dipasang pada mesin target;
- AlertManager - manajer pemberitahuan (peringatan), peringatan didefinisikan dalam file konfigurasi dan ditetapkan oleh seperangkat aturan untuk metrik.
Jika selama operasi ada kepatuhan dengan aturan, peringatan dipicu dan dikirim ke penerima yang ditentukan melalui Email, Slack atau yang lainnya.
Objek yang bekerja dengan Prometheus disebut metrik yang diterima dari target baik melalui Pushgateway atau melalui Eksportir.
Saat mengumpulkan metrik, beberapa metode mentransmisikannya digunakan:
- Prometheus meminta metrik dari target melalui permintaan Tarik, pengaturan yang ditentukan dalam file konfigurasi di bagian scrape_config untuk setiap pekerjaan.
Ketika sistem mengumpulkan data, Anda dapat mengontrol frekuensi pengumpulan dan membuat beberapa konfigurasi pengumpulan data untuk memilih frekuensi yang berbeda untuk objek yang berbeda; - Eksportir memungkinkan Anda mengumpulkan metrik dari berbagai objek, misalnya: basis data (MongoDB, SQL, dll.), Pialang pesan (RabbitMQ, EMQ, NSQ, dll.), Penyeimbang beban HTTP, dll;
- Pushgateway. Ini dapat digunakan jika perlu, ketika aplikasi tidak dapat memberikan kemampuan untuk secara langsung memberikan metrik ke Prometheus; atau saat menggunakan pekerjaan batch yang tidak memiliki kemampuan untuk menggunakan permintaan tarik Prometheus.
Dengan demikian, semua metrik yang diterima akan disimpan oleh Prometheus dalam database dengan cap waktu.
Konfigurasi
Prometheus dikonfigurasi menggunakan flag baris perintah dan file konfigurasi yang disediakan dalam format YAML. Bendera baris perintah memungkinkan Anda untuk mengonfigurasi parameter yang tidak dapat diubah, seperti: jalur, volume data yang disimpan di disk dan di memori, dll. File konfigurasi memungkinkan Anda untuk mengkonfigurasi segala sesuatu yang berkaitan dengan pekerjaan dan mengatur file aturan yaml yang dimuat. Semuanya ditulis dalam file konfigurasi global, ini memungkinkan Anda untuk mengatur pengaturan umum untuk semua orang dan menyoroti pengaturan untuk bagian konfigurasi yang berbeda secara terpisah. Pengaturan yang polling Prometheus dikonfigurasi dalam file konfigurasi di bagian scrape_configs.
Prometheus dapat memuat ulang file konfigurasi selama operasi, jika konfigurasi baru tidak valid, maka itu tidak akan diterapkan. --web.enable-lifecycle
/-/reload
file konfigurasi dipicu dengan mengirimkan perintah SIGHUP Prometheus atau mengirim permintaan HTTP POST ke /-/reload
, asalkan flag --web.enable-lifecycle
flag --web.enable-lifecycle
. Ini juga akan memuat ulang semua file aturan yang dikonfigurasi.
Jenis data apa yang digunakan
Prometheus menyimpan model data multidimensi khusus dan menggunakan bahasa permintaan untuk data multidimensi yang disebut PromQL. Prometheus menyimpan data dalam bentuk deret waktu, mendukung beberapa opsi penyimpanan:
- penyimpanan disk lokal: setiap 2 jam, data yang telah disangga dalam memori dikompresi dan disimpan dalam disk. Secara default, direktori ./data digunakan di direktori kerja untuk menyimpan file yang dikompresi;
- Repositori jarak jauh: Prometheus mendukung integrasi dengan repositori pihak ketiga (misalnya: Kafka, PostgreSQL, Amazon S3, dll.) Melalui adaptor Protocol Buffer.
Rangkaian waktu yang disimpan ditentukan oleh metrik dan metadata dalam bentuk pasangan nilai-kunci, meskipun, jika perlu, nama metrik tidak dapat digunakan dan metrik itu sendiri hanya akan terdiri dari metadata. Rangkaian waktu dapat secara resmi didefinisikan sebagai <metric name> {<metadata>}. Kuncinya adalah <nama metrik> {<metadata>} - apa yang kami ukur, dan nilainya adalah nilai aktual sebagai angka dengan tipe float64 (Prometheus hanya mendukung tipe ini). Deskripsi kunci berisi metadata (label), juga dijelaskan oleh pasangan nilai kunci: <label name> = "<label value>", <label name> = "<label value>", ...
Saat menyimpan metrik, tipe data berikut digunakan:
- Counter - menghitung jumlah selama periode waktu tertentu. Jenis metrik ini hanya dapat meningkat (Anda tidak dapat menggunakan nilai negatif) atau mengatur ulang nilai.
Mungkin cocok, misalnya, untuk menghitung jumlah permintaan per menit atau jumlah kesalahan per hari, jumlah paket jaringan yang dikirim / diterima, dll. - Gauge - menyimpan nilai yang mungkin menurun atau meningkat seiring waktu.
Gauge tidak menunjukkan perkembangan metrik selama periode waktu tertentu. Menggunakan Gauge, Anda dapat kehilangan perubahan metrik tidak teratur seiring waktu. - Histogram - menyimpan beberapa seri waktu: jumlah total semua nilai yang diamati; jumlah peristiwa yang diamati;
penghitung akumulatif (ember) - ditunjukkan dalam label sebagai le="<upper inclusive bound>"
.
Nilai dikumpulkan di area dengan batas atas custom (bucket). - Ringkasan - menyimpan beberapa seri waktu: jumlah total semua nilai yang diamati; jumlah peristiwa yang diamati;
aliran φ-kuantil (0 ≤ φ ≤ 1) dari peristiwa yang diamati - ditunjukkan dalam label sebagai quantile="<φ>"
.
Bagaimana data disimpan?
Prometheus merekomendasikan "memberikan" 2/3 RAM untuk aplikasi yang sedang berjalan.
Untuk menyimpan data dalam memori, Prometheus menggunakan file yang disebut chunk, setiap metrik memiliki file sendiri. Semua file chunk tidak dapat diubah, kecuali yang terakhir yang datanya ditulis. Data baru disimpan dalam chunk dan setiap 2 jam aliran latar belakang menggabungkan data dan menulisnya ke disk. Setiap blok dua jam terdiri dari direktori yang berisi satu atau lebih file chunk yang berisi semua sampel deret waktu untuk periode waktu itu, serta file metadata dan file indeks (yang mengindeks nama-nama metrik dan label untuk deret waktu dalam file chunk). Jika dalam satu jam Prometheus tidak menulis data ke chunck, maka data akan disimpan ke disk dan chunck baru akan dibuat untuk menulis data. Periode penyimpanan data maksimum di Prometheus adalah ~ 21 hari.
Karena ukuran memori diperbaiki, kinerja sistem tulis dan baca akan dibatasi oleh jumlah memori ini. Jumlah memori PTSDB ditentukan oleh periode waktu minimum, periode pengumpulan, dan jumlah metrik waktu.
Prometheus juga memiliki mekanisme WAL untuk mencegah kehilangan data.
Write ahead log (WAL) membuat serialisasi operasi yang dihafal pada media permanen dalam bentuk file log. Jika terjadi kegagalan, file WAL dapat digunakan untuk mengembalikan database ke kondisi konsisten dengan memulihkan dari log.
File log disimpan dalam direktori wal di segmen 128 MB. File-file ini mengandung data mentah yang belum dikompres, sehingga mereka secara signifikan lebih besar daripada file fragmen biasa.
Prometheus akan menyimpan setidaknya 3 file log, tetapi server dengan lalu lintas tinggi dapat melihat lebih dari tiga file WAL, karena perlu menyimpan setidaknya dua jam data mentah.
Hasil menggunakan WAL adalah pengurangan yang signifikan dalam jumlah permintaan tulis ke disk, seperti hanya file log yang perlu ditulis ke disk, dan tidak setiap bagian data yang telah diubah sebagai hasil operasi. File log ditulis secara berurutan dan dengan demikian biaya sinkronisasi log jauh lebih sedikit daripada biaya penulisan fragmen dengan data.
Prometheus menyimpan breakpoint berkala, yang secara default ditambahkan setiap 2 jam dengan mengompresi log untuk periode sebelumnya dan menyimpannya ke disk.
Semua breakpoint disimpan dalam direktori yang sama dengan checkpoint.ddd, di mana ddd adalah angka yang meningkat secara monoton. Oleh karena itu, ketika memulihkan dari kegagalan, itu dapat mengembalikan breakpoint dari katalog breakpoint dengan indikasi pesanan (.ddd).
Dengan menulis log WAL, Anda dapat kembali ke pos pemeriksaan mana pun yang menyediakan data log.
Apa yang terjadi dalam praktik?
Saat menambahkan ke proyek (.Net Framework), kami menggunakan paket Prometheus.Client.3.0.2 untuk mengumpulkan metrik. Untuk mengumpulkan metrik, metode dan kelas yang diperlukan telah ditambahkan ke proyek untuk menyimpan metrik hingga diterima oleh Prometheus.
Antarmuka IMetricsService awalnya didefinisikan yang berisi metode pengukur waktu untuk mengukur berapa lama metode bekerja:
public interface IMetricsService { Stopwatch StartTimer(); void StopTimer(Stopwatch timer, string controllerName, string actionName, string methodName = "POST"); }
Kami menambahkan kelas MetricsService, yang mengimplementasikan antarmuka IMetricsService dan menyimpan sementara metrik.
public class MetricsService : IMetricsService { private static Histogram _histogram; static MetricsService() { _histogram = CreateHistogram(); } public Stopwatch StartTimer() { try { var timer = new Stopwatch(); timer.Start(); return timer; } catch (Exception exception) { Logger.Error(exception); } return null; } public void StopTimer(Stopwatch timer, string controllerName, string actionName, string methodName = "POST") { try { if (timer == null) { throw new ArgumentException($"{nameof(timer)} can't be null."); } timer.Stop(); _histogram .WithLabels(controllerName, actionName, methodName) .Observe(timer.ElapsedMilliseconds, DateTimeOffset.UtcNow); } catch (Exception exception) { Logger.Error(exception); } } public static List<string> GetAllLabels() { var metricsList = new List<string>(); try { foreach (var keyValuePair in _histogram.Labelled) { var controllerName = keyValuePair.Key.Labels[0].Value; var actionName = keyValuePair.Key.Labels[1].Value; var methodName = keyValuePair.Key.Labels[2].Value; var requestDurationSum = keyValuePair.Value.Value.Sum; var requestCount = keyValuePair.Value.Value.Count; metricsList.Add($"http_request_duration_widget_sum{{controller={controllerName},action={actionName},method={methodName}}} {requestDurationSum}"); metricsList.Add($"http_request_duration_widget_count{{controller={controllerName},action={actionName},method={methodName}}} {requestCount}"); } _histogram = CreateHistogram(); } catch (Exception exception) { Logger.Error(exception); } return metricsList; } private static Histogram CreateHistogram() { var newMetrics = Metrics .WithCustomRegistry(new CollectorRegistry()) .CreateHistogram(name: "http_request_duration_web_api", help: "Histogram metrics of Web.Api", includeTimestamp: true, labelNames: new[] { "controller", "action", "method" }); var oldValue = _histogram; for (var i = 0; i < 10; i++) { var oldValue = Interlocked.Exchange<Histogram>(ref oldValue, newMetrics); if (oldValue != null) { return oldValue; } } return null; } }
Sekarang kita dapat menggunakan kelas kita untuk menyimpan metrik yang kita rencanakan untuk kumpulkan dalam metode Application_BeginRequest, Application_Error, Application_EndRequest. Di kelas Global.cs, kami menambahkan kumpulan metrik ke metode di atas.
private IMetricsService _metricsService; protected virtual void Application_BeginRequest(object sender, EventArgs e) { var context = new HttpContextWrapper(HttpContext.Current); var metricServiceTimer = _metricsService.StartTimer(); context.Items.Add("metricsService", _metricsService); context.Items.Add("metricServiceTimer", metricServiceTimer); } protected virtual void Application_EndRequest(object sender, EventArgs e) { WriteMetrics(new HttpContextWrapper(HttpContext.Current)); } protected void Application_Error(object sender, EventArgs e) { WriteMetrics(new HttpContextWrapper(HttpContext.Current)); } private void WriteMetrics(HttpContextBase context) { try { _metricsService = context.Items["metricsService"] as IMetricsService; if (_metricsService != null) { var timer = context.Items["metricServiceTimer"] as Stopwatch; string controllerName = null; string actionName = null; var rd = RouteTable.Routes.GetRouteData(context); if (rd != null) { controllerName = rd.GetRequiredString("controller"); actionName = rd.GetRequiredString("action"); } _metricsService.StopTimer(timer, controllerName, actionName, context.Request.HttpMethod); } } catch (Exception exception) { Logger.Error("Can't write metrics.", exception); } }
Tambahkan pengontrol baru, yang akan menjadi titik referensi untuk mengirim metrik API kami ke Prometheus:
public class MetricsController : Controller { [HttpGet] public string[] GetAllMetrics() { try { var metrics = MetricsService.GetAllLabels(); return metrics.ToArray(); } catch (Exception exception) { Logger.Error(exception); } return new string[] { }; } }
Langkah terakhir adalah mengonfigurasi konfigurasi Prometheus untuk mengumpulkan metrik di bagian scrape_configs, setelah itu kita bisa melihat metrik yang dikumpulkan sudah ada di Prometheus atau Grafana UI.
Fitur utama yang kami minati di Prometheus:
Model data multidimensi: metrik dan label.
Bahasa permintaan PromQL fleksibel. Dalam operator kueri yang sama, kita dapat menggunakan operasi seperti penggandaan, penambahan, penggabungan, dll. dapat dilakukan dengan beberapa metrik.
Mengumpulkan data berbasis HTTP menggunakan metode pull.
Kompatibel dengan metode push melalui Pushgateway.
Dimungkinkan untuk mengumpulkan metrik dari aplikasi lain melalui Eksportir.
Menyediakan mekanisme untuk mencegah kehilangan data.
Mendukung berbagai representasi grafis dari data.