mirror of
https://github.com/librenms/librenms.git
synced 2024-10-07 16:52:45 +00:00
docker stats app (#12358)
* docker stat app support * Add missing doc and app * stylci * styleci * styleci * Add test data * add snmpsim * Update docker.inc.php test with use Number::Bi * typo * Add tests * Add tests Co-authored-by: Jellyfrog <Jellyfrog@users.noreply.github.com>
This commit is contained in:
@@ -92,6 +92,7 @@ by following the steps under the `SNMP Extend` heading.
|
||||
1. [Certificate](#certificate) - Certificate extend
|
||||
1. [C.H.I.P.](#chip) - SNMP extend
|
||||
1. [DHCP Stats](#dhcp-stats) - SNMP extend
|
||||
1. [Docker Stats](#docker-stats) - SNMP extend
|
||||
1. [Entropy](#entropy) - SNMP extend
|
||||
1. [EXIM Stats](#exim-stats) - SNMP extend
|
||||
1. [Fail2ban](#fail2ban) - SNMP extend
|
||||
@@ -468,6 +469,39 @@ The application should be auto-discovered as described at the top of
|
||||
the page. If it is not, please follow the steps set out under `SNMP
|
||||
Extend` heading top of page.
|
||||
|
||||
# Docker Stats
|
||||
|
||||
It allows you to know which container docker run and their stats.
|
||||
|
||||
This script require: jq
|
||||
|
||||
## SNMP Extend
|
||||
|
||||
1: Install jq
|
||||
```
|
||||
sudo apt install jq
|
||||
```
|
||||
|
||||
2: Copy the shell script to the desired host.
|
||||
|
||||
```
|
||||
wget https://github.com/librenms/librenms-agent/raw/master/snmp/docker-stats.sh -O /etc/snmp/docker-stats.sh
|
||||
```
|
||||
|
||||
3: Run `chmod +x /etc/snmp/docker-stats.sh`
|
||||
|
||||
|
||||
4: Edit your snmpd.conf file (usually /etc/snmp/snmpd.conf) and add:
|
||||
|
||||
```
|
||||
extend docker /etc/snmp/docker-stats.sh
|
||||
```
|
||||
|
||||
5: Restart snmpd on your host
|
||||
```
|
||||
systemctl restart snmpd
|
||||
```
|
||||
|
||||
# Entropy
|
||||
|
||||
A small shell script that checks your system's available random entropy.
|
||||
|
||||
36
includes/html/graphs/application/docker-common.inc.php
Normal file
36
includes/html/graphs/application/docker-common.inc.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
$name = 'docker';
|
||||
$app_id = $app['app_id'];
|
||||
$colours = 'mega';
|
||||
$dostack = 0;
|
||||
$printtotal = 0;
|
||||
$addarea = 1;
|
||||
$transparency = 15;
|
||||
|
||||
$unitlen = 20;
|
||||
$bigdescrlen = 25;
|
||||
$smalldescrlen = 25;
|
||||
|
||||
if (isset($vars['container'])) {
|
||||
$containers = [$vars['container']];
|
||||
} else {
|
||||
$containers = get_arrays_with_application($device, $app['app_id'], 'docker');
|
||||
}
|
||||
|
||||
$int = 0;
|
||||
while (isset($containers[$int])) {
|
||||
$container_name = $containers[$int];
|
||||
$rrd_filename = rrd_name($device['hostname'], ['app', $name, $app_id, $container_name]);
|
||||
|
||||
if (rrdtool_check_rrd_exists($rrd_filename)) {
|
||||
$rrd_list[] = [
|
||||
'filename' => $rrd_filename,
|
||||
'descr' => $container_name,
|
||||
'ds' => $rrdVar,
|
||||
];
|
||||
}
|
||||
$int++;
|
||||
}
|
||||
|
||||
require 'includes/html/graphs/generic_multi_line_exact_numbers.inc.php';
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
$unit_text = 'CPU (%)';
|
||||
$rrdVar = 'cpu_usage';
|
||||
$divider = 100;
|
||||
|
||||
require 'docker-common.inc.php';
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
$unit_text = 'Memory Limit';
|
||||
$rrdVar = 'mem_limit';
|
||||
|
||||
require 'docker-common.inc.php';
|
||||
7
includes/html/graphs/application/docker_mem_perc.inc.php
Normal file
7
includes/html/graphs/application/docker_mem_perc.inc.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
$unit_text = 'Memory (%)';
|
||||
$rrdVar = 'mem_perc';
|
||||
$divider = 100;
|
||||
|
||||
require 'docker-common.inc.php';
|
||||
6
includes/html/graphs/application/docker_mem_used.inc.php
Normal file
6
includes/html/graphs/application/docker_mem_used.inc.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
$unit_text = 'Memory used';
|
||||
$rrdVar = 'mem_used';
|
||||
|
||||
require 'docker-common.inc.php';
|
||||
6
includes/html/graphs/application/docker_pids.inc.php
Normal file
6
includes/html/graphs/application/docker_pids.inc.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
$unit_text = 'PIDs (count)';
|
||||
$rrdVar = 'pids';
|
||||
|
||||
require 'docker-common.inc.php';
|
||||
@@ -357,6 +357,13 @@ $graphs['voip-monitor'] = [
|
||||
'memoryusage',
|
||||
'openfiles',
|
||||
];
|
||||
$graphs['docker'] = [
|
||||
'cpu_usage',
|
||||
'pids',
|
||||
'mem_limit',
|
||||
'mem_used',
|
||||
'mem_perc',
|
||||
];
|
||||
|
||||
echo '<div class="panel panel-default">';
|
||||
echo '<div class="panel-heading">';
|
||||
|
||||
60
includes/html/pages/device/apps/docker.inc.php
Normal file
60
includes/html/pages/device/apps/docker.inc.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
$domain_list = get_arrays_with_application($device, $app['app_id'], 'docker');
|
||||
|
||||
print_optionbar_start();
|
||||
|
||||
$link_array = [
|
||||
'page' => 'device',
|
||||
'device' => $device['device_id'],
|
||||
'tab' => 'apps',
|
||||
'app' => 'docker',
|
||||
];
|
||||
|
||||
$containers_list = [];
|
||||
|
||||
foreach ($domain_list as $label) {
|
||||
$container = $label;
|
||||
|
||||
if ($vars['container'] == $container) {
|
||||
$label = sprintf('⚫ %s', $label);
|
||||
}
|
||||
|
||||
array_push($containers_list, generate_link($label, $link_array, ['container' => $container]));
|
||||
}
|
||||
|
||||
printf('%s | containers: %s', generate_link('All Containers', $link_array), implode(', ', $containers_list));
|
||||
|
||||
print_optionbar_end();
|
||||
|
||||
$graphs = [
|
||||
'docker_pids' => 'PIDs',
|
||||
'docker_mem_limit' => 'Container memory limit',
|
||||
'docker_mem_used' => 'Container memory used',
|
||||
'docker_cpu_usage' => 'Container CPU usage, %',
|
||||
'docker_mem_perc' => 'Container Memory usage, %',
|
||||
];
|
||||
|
||||
foreach ($graphs as $key => $text) {
|
||||
$graph_type = $key;
|
||||
$graph_array['height'] = '100';
|
||||
$graph_array['width'] = '215';
|
||||
$graph_array['to'] = time();
|
||||
$graph_array['id'] = $app['app_id'];
|
||||
$graph_array['type'] = 'application_' . $key;
|
||||
|
||||
if (isset($vars['container'])) {
|
||||
$graph_array['container'] = $vars['container'];
|
||||
}
|
||||
|
||||
echo '<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">' . $text . '</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">';
|
||||
include 'includes/html/print-graphrow.inc.php';
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
}
|
||||
69
includes/polling/applications/docker.inc.php
Normal file
69
includes/polling/applications/docker.inc.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
use LibreNMS\Exceptions\JsonAppException;
|
||||
use LibreNMS\Exceptions\JsonAppMissingKeysException;
|
||||
use LibreNMS\RRD\RrdDefinition;
|
||||
use LibreNMS\Util\Number;
|
||||
|
||||
$name = 'docker';
|
||||
$app_id = $app['app_id'];
|
||||
$output = 'OK';
|
||||
|
||||
function convertToBytes(string $from): ?int
|
||||
{
|
||||
$units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'];
|
||||
$number = substr($from, 0, -3);
|
||||
$suffix = substr($from, -3);
|
||||
|
||||
//B or no suffix
|
||||
if (is_numeric(substr($suffix, 0, 1))) {
|
||||
return (int) $from;
|
||||
}
|
||||
|
||||
$exponent = array_flip($units)[$suffix] ?? null;
|
||||
if ($exponent === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $number * (1024 ** $exponent);
|
||||
}
|
||||
|
||||
try {
|
||||
$docker_data = json_app_get($device, $name, 1)['data'];
|
||||
} catch (JsonAppMissingKeysException $e) {
|
||||
$docker_data = $e->getParsedJson();
|
||||
} catch (JsonAppException $e) {
|
||||
echo PHP_EOL . $name . ':' . $e->getCode() . ':' . $e->getMessage() . PHP_EOL;
|
||||
update_application($app, $e->getCode() . ':' . $e->getMessage(), []); // Set empty metrics and error message
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$rrd_name = ['app', $name, $app_id];
|
||||
$rrd_def = RrdDefinition::make()
|
||||
->addDataset('cpu_usage', 'GAUGE', 0, 100)
|
||||
->addDataset('pids', 'GAUGE', 0)
|
||||
->addDataset('mem_perc', 'GAUGE', 0, 100)
|
||||
->addDataset('mem_used', 'GAUGE', 0)
|
||||
->addDataset('mem_limit', 'GAUGE', 0);
|
||||
|
||||
$metrics = [];
|
||||
foreach ($docker_data as $data) {
|
||||
$container = $data['container'];
|
||||
|
||||
$rrd_name = ['app', $name, $app_id, $container];
|
||||
|
||||
$fields = [
|
||||
'cpu_usage' => ((float) $data['cpu']) * 100,
|
||||
'pids' => $data['pids'],
|
||||
'mem_limit' => Number::formatBi($data['memory']['limit']),
|
||||
'mem_used' => Number::formatBi($data['memory']['used']),
|
||||
'mem_perc' => ((float) $data['memory']['perc']) * 100,
|
||||
];
|
||||
|
||||
$metrics[$container] = $fields;
|
||||
$tags = ['name' => $container, 'app_id' => $app_id, 'rrd_def' => $rrd_def, 'rrd_name' => $rrd_name];
|
||||
data_update($device, 'app', $tags, $fields);
|
||||
}
|
||||
|
||||
update_application($app, $output, $metrics);
|
||||
219
tests/data/linux_docker-v1.json
Normal file
219
tests/data/linux_docker-v1.json
Normal file
@@ -0,0 +1,219 @@
|
||||
{
|
||||
"applications": {
|
||||
"discovery": {
|
||||
"applications": [
|
||||
{
|
||||
"app_type": "docker",
|
||||
"app_state": "UNKNOWN",
|
||||
"discovered": 1,
|
||||
"app_state_prev": null,
|
||||
"app_status": "",
|
||||
"app_instance": ""
|
||||
}
|
||||
],
|
||||
"application_metrics": []
|
||||
},
|
||||
"poller": {
|
||||
"applications": [
|
||||
{
|
||||
"app_type": "docker",
|
||||
"app_state": "OK",
|
||||
"discovered": 1,
|
||||
"app_state_prev": "UNKNOWN",
|
||||
"app_status": "",
|
||||
"app_instance": ""
|
||||
}
|
||||
],
|
||||
"application_metrics": [
|
||||
{
|
||||
"metric": "foobar_dashboard_1_cpu_usage",
|
||||
"value": 1,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_dashboard_1_mem_limit",
|
||||
"value": 15.36,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_dashboard_1_mem_perc",
|
||||
"value": 4,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_dashboard_1_mem_used",
|
||||
"value": 6.93,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_dashboard_1_pids",
|
||||
"value": 6,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_laravel.test_1_cpu_usage",
|
||||
"value": 9,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_laravel.test_1_mem_limit",
|
||||
"value": 15.36,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_laravel.test_1_mem_perc",
|
||||
"value": 17,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_laravel.test_1_mem_used",
|
||||
"value": 26.67,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_laravel.test_1_pids",
|
||||
"value": 4,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_meilisearch_1_cpu_usage",
|
||||
"value": 0,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_meilisearch_1_mem_limit",
|
||||
"value": 15.36,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_meilisearch_1_mem_perc",
|
||||
"value": 7,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_meilisearch_1_mem_used",
|
||||
"value": 10.86,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_meilisearch_1_pids",
|
||||
"value": 15,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_mysql_1_cpu_usage",
|
||||
"value": 33,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_mysql_1_mem_limit",
|
||||
"value": 15.36,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_mysql_1_mem_perc",
|
||||
"value": 90,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_mysql_1_mem_used",
|
||||
"value": 141.3,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_mysql_1_pids",
|
||||
"value": 38,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_redis_1_cpu_usage",
|
||||
"value": 23,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_redis_1_mem_limit",
|
||||
"value": 15.36,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_redis_1_mem_perc",
|
||||
"value": 3,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_redis_1_mem_used",
|
||||
"value": 5.06,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
},
|
||||
{
|
||||
"metric": "foobar_redis_1_pids",
|
||||
"value": 5,
|
||||
"value_prev": null,
|
||||
"app_type": "docker"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"os": {
|
||||
"discovery": {
|
||||
"devices": [
|
||||
{
|
||||
"sysName": "<private>",
|
||||
"sysObjectID": ".1.3.6.1.4.1.8072.3.2.10",
|
||||
"sysDescr": "Linux server 3.10.0-693.5.2.el7.x86_64 #1 SMP Fri Oct 20 20:32:50 UTC 2017 x86_64",
|
||||
"sysContact": "<private>",
|
||||
"version": "3.10.0-693.5.2.el7.x86_64",
|
||||
"hardware": "Generic x86 64-bit",
|
||||
"features": null,
|
||||
"os": "linux",
|
||||
"type": "server",
|
||||
"serial": null,
|
||||
"icon": "linux.svg",
|
||||
"location": "<private>"
|
||||
}
|
||||
]
|
||||
},
|
||||
"poller": {
|
||||
"devices": [
|
||||
{
|
||||
"sysName": "<private>",
|
||||
"sysObjectID": ".1.3.6.1.4.1.8072.3.2.10",
|
||||
"sysDescr": "Linux server 3.10.0-693.5.2.el7.x86_64 #1 SMP Fri Oct 20 20:32:50 UTC 2017 x86_64",
|
||||
"sysContact": "<private>",
|
||||
"version": "3.10.0-693.5.2.el7.x86_64",
|
||||
"hardware": "Generic x86 64-bit",
|
||||
"features": null,
|
||||
"os": "linux",
|
||||
"type": "server",
|
||||
"serial": null,
|
||||
"icon": "linux.svg",
|
||||
"location": "<private>"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
8
tests/snmpsim/linux_docker-v1.snmprec
Normal file
8
tests/snmpsim/linux_docker-v1.snmprec
Normal file
@@ -0,0 +1,8 @@
|
||||
1.3.6.1.2.1.1.1.0|4|Linux server 3.10.0-693.5.2.el7.x86_64 #1 SMP Fri Oct 20 20:32:50 UTC 2017 x86_64
|
||||
1.3.6.1.2.1.1.2.0|6|1.3.6.1.4.1.8072.3.2.10
|
||||
1.3.6.1.2.1.1.3.0|67|77550514
|
||||
1.3.6.1.2.1.1.4.0|4|<private>
|
||||
1.3.6.1.2.1.1.5.0|4|<private>
|
||||
1.3.6.1.2.1.1.6.0|4|<private>
|
||||
1.3.6.1.4.1.8072.1.3.2.2.1.21.6.100.111.99.107.101.114|2|1
|
||||
1.3.6.1.4.1.8072.1.3.2.3.1.2.6.100.111.99.107.101.114|4|{"version":"1","data":[{"container":"foobar_laravel.test_1","pids":4,"memory":{"used":"26.67MiB","limit":"15.36GiB","perc":"0.17%"},"cpu":"0.09%"},{"container":"foobar_dashboard_1","pids":6,"memory":{"used":"6.934MiB","limit":"15.36GiB","perc":"0.04%"},"cpu":"0.01%"},{"container":"foobar_meilisearch_1","pids":15,"memory":{"used":"10.86MiB","limit":"15.36GiB","perc":"0.07%"},"cpu":"0.00%"},{"container":"foobar_mysql_1","pids":38,"memory":{"used":"141.3MiB","limit":"15.36GiB","perc":"0.90%"},"cpu":"0.33%"},{"container":"foobar_redis_1","pids":5,"memory":{"used":"5.055MiB","limit":"15.36GiB","perc":"0.03%"},"cpu":"0.23%"}],"error":"0","errorString":""}
|
||||
Reference in New Issue
Block a user