mirror of
https://github.com/oddlama/nix-config.git
synced 2025-10-10 23:00:39 +02:00
feat(monitoring): remove location, add nginx upstream monitoring option
This commit is contained in:
parent
2024c3bfd5
commit
18b2002c27
26 changed files with 352 additions and 218 deletions
|
@ -13,6 +13,7 @@
|
|||
./kanidm.nix
|
||||
./meta.nix
|
||||
./netbird-client.nix
|
||||
./nginx-upstream-monitoring.nix
|
||||
./oauth2-proxy.nix
|
||||
./promtail.nix
|
||||
./secrets.nix
|
||||
|
|
|
@ -8,6 +8,13 @@
|
|||
mkOption
|
||||
types
|
||||
;
|
||||
|
||||
defaultOptions = {
|
||||
network = mkOption {
|
||||
type = types.str;
|
||||
description = "The network to which this endpoint is associated.";
|
||||
};
|
||||
};
|
||||
in {
|
||||
options = {
|
||||
globals = mkOption {
|
||||
|
@ -97,120 +104,94 @@ in {
|
|||
monitoring = {
|
||||
ping = mkOption {
|
||||
type = types.attrsOf (types.submodule {
|
||||
options = {
|
||||
hostv4 = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
description = "The IP/hostname to ping via ipv4.";
|
||||
default = null;
|
||||
};
|
||||
options =
|
||||
defaultOptions
|
||||
// {
|
||||
hostv4 = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
description = "The IP/hostname to ping via ipv4.";
|
||||
default = null;
|
||||
};
|
||||
|
||||
hostv6 = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
description = "The IP/hostname to ping via ipv6.";
|
||||
default = null;
|
||||
hostv6 = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
description = "The IP/hostname to ping via ipv6.";
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
|
||||
location = mkOption {
|
||||
type = types.str;
|
||||
description = "A location tag added to this metric.";
|
||||
};
|
||||
|
||||
network = mkOption {
|
||||
type = types.str;
|
||||
description = "The network to which this endpoint is associated.";
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
http = mkOption {
|
||||
type = types.attrsOf (types.submodule {
|
||||
options = {
|
||||
url = mkOption {
|
||||
type = types.str;
|
||||
description = "The url to connect to.";
|
||||
};
|
||||
options =
|
||||
defaultOptions
|
||||
// {
|
||||
url = mkOption {
|
||||
type = types.either (types.listOf types.str) types.str;
|
||||
description = "The url to connect to.";
|
||||
};
|
||||
|
||||
location = mkOption {
|
||||
type = types.str;
|
||||
description = "A location tag added to this metric.";
|
||||
};
|
||||
expectedStatus = mkOption {
|
||||
type = types.int;
|
||||
default = 200;
|
||||
description = "The HTTP status code to expect.";
|
||||
};
|
||||
|
||||
network = mkOption {
|
||||
type = types.str;
|
||||
description = "The network to which this endpoint is associated.";
|
||||
};
|
||||
expectedBodyRegex = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
description = "A regex pattern to expect in the body.";
|
||||
default = null;
|
||||
};
|
||||
|
||||
expectedStatus = mkOption {
|
||||
type = types.int;
|
||||
default = 200;
|
||||
description = "The HTTP status code to expect.";
|
||||
skipTlsVerification = mkOption {
|
||||
type = types.bool;
|
||||
description = "Skip tls verification when using https.";
|
||||
default = false;
|
||||
};
|
||||
};
|
||||
|
||||
expectedBodyRegex = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
description = "A regex pattern to expect in the body.";
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
dns = mkOption {
|
||||
type = types.attrsOf (types.submodule {
|
||||
options = {
|
||||
server = mkOption {
|
||||
type = types.str;
|
||||
description = "The DNS server to query.";
|
||||
};
|
||||
options =
|
||||
defaultOptions
|
||||
// {
|
||||
server = mkOption {
|
||||
type = types.str;
|
||||
description = "The DNS server to query.";
|
||||
};
|
||||
|
||||
domain = mkOption {
|
||||
type = types.str;
|
||||
description = "The domain to query.";
|
||||
};
|
||||
domain = mkOption {
|
||||
type = types.str;
|
||||
description = "The domain to query.";
|
||||
};
|
||||
|
||||
record-type = mkOption {
|
||||
type = types.str;
|
||||
description = "The record type to query.";
|
||||
default = "A";
|
||||
record-type = mkOption {
|
||||
type = types.str;
|
||||
description = "The record type to query.";
|
||||
default = "A";
|
||||
};
|
||||
};
|
||||
|
||||
location = mkOption {
|
||||
type = types.str;
|
||||
description = "A location tag added to this metric.";
|
||||
};
|
||||
|
||||
network = mkOption {
|
||||
type = types.str;
|
||||
description = "The network to which this endpoint is associated.";
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
tcp = mkOption {
|
||||
type = types.attrsOf (types.submodule {
|
||||
options = {
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
description = "The IP/hostname to connect to.";
|
||||
};
|
||||
options =
|
||||
defaultOptions
|
||||
// {
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
description = "The IP/hostname to connect to.";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
description = "The port to connect to.";
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
description = "The port to connect to.";
|
||||
};
|
||||
};
|
||||
|
||||
location = mkOption {
|
||||
type = types.str;
|
||||
description = "A location tag added to this metric.";
|
||||
};
|
||||
|
||||
network = mkOption {
|
||||
type = types.str;
|
||||
description = "The network to which this endpoint is associated.";
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
|
|
81
modules/nginx-upstream-monitoring.nix
Normal file
81
modules/nginx-upstream-monitoring.nix
Normal file
|
@ -0,0 +1,81 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}: let
|
||||
inherit
|
||||
(lib)
|
||||
attrNames
|
||||
filterAttrs
|
||||
flip
|
||||
mapAttrs'
|
||||
mkOption
|
||||
nameValuePair
|
||||
types
|
||||
;
|
||||
in {
|
||||
options.services.nginx.upstreams = mkOption {
|
||||
type = types.attrsOf (types.submodule {
|
||||
options.monitoring = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
description = "Whether to add a global monitoring entry for this upstream";
|
||||
default = false;
|
||||
};
|
||||
|
||||
path = mkOption {
|
||||
type = types.str;
|
||||
description = "The path to query.";
|
||||
default = "";
|
||||
};
|
||||
|
||||
expectedStatus = mkOption {
|
||||
type = types.int;
|
||||
default = 200;
|
||||
description = "The HTTP status code to expect.";
|
||||
};
|
||||
|
||||
expectedBodyRegex = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
description = "A regex pattern to expect in the body.";
|
||||
default = null;
|
||||
};
|
||||
|
||||
useHttps = mkOption {
|
||||
type = types.bool;
|
||||
description = "Whether to use https to connect to this upstream when monitoring";
|
||||
default = false;
|
||||
};
|
||||
|
||||
skipTlsVerification = mkOption {
|
||||
type = types.bool;
|
||||
description = "Skip tls verification when using https.";
|
||||
default = false;
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
config = let
|
||||
monitoredUpstreams = filterAttrs (_: x: x.monitoring.enable) config.services.nginx.upstreams;
|
||||
in {
|
||||
globals.monitoring.http = flip mapAttrs' monitoredUpstreams (
|
||||
upstreamName: upstream: let
|
||||
schema =
|
||||
if upstream.monitoring.useHttps
|
||||
then "https"
|
||||
else "http";
|
||||
in
|
||||
nameValuePair "${config.node.name}-upstream-${upstreamName}" {
|
||||
url = map (server: "${schema}://${server}${upstream.monitoring.path}") (attrNames upstream.servers);
|
||||
network = "local-${config.node.name}";
|
||||
inherit
|
||||
(upstream.monitoring)
|
||||
expectedBodyRegex
|
||||
expectedStatus
|
||||
skipTlsVerification
|
||||
;
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
|
@ -21,6 +21,7 @@
|
|||
optional
|
||||
optionalAttrs
|
||||
optionals
|
||||
toList
|
||||
types
|
||||
;
|
||||
|
||||
|
@ -44,16 +45,14 @@ in {
|
|||
description = "Additional secrets to replace in pre-start. The attr name will be searched and replaced in the config with the value read from the given file.";
|
||||
};
|
||||
|
||||
globalMonitoring = {
|
||||
enable = mkEnableOption "monitor the global infrastructure from this node.";
|
||||
availableNetworks = mkOption {
|
||||
type = types.listOf types.str;
|
||||
example = ["internet"];
|
||||
description = ''
|
||||
The networks that can be reached from this node.
|
||||
Only global entries with a matching network will be monitored from here.
|
||||
'';
|
||||
};
|
||||
availableMonitoringNetworks = mkOption {
|
||||
type = types.listOf types.str;
|
||||
example = ["internet"];
|
||||
description = ''
|
||||
Any of the global monitoring definitions which has a network from this list
|
||||
will automatically be monitored via telegraf. Set this to any networks that
|
||||
can be reached from this node. This includes `local-<node.name>` by default.
|
||||
'';
|
||||
};
|
||||
|
||||
influxdb2 = {
|
||||
|
@ -87,6 +86,9 @@ in {
|
|||
};
|
||||
|
||||
config = mkIf (!minimal && cfg.enable) {
|
||||
# Monitor anything that can only be monitored from this node
|
||||
meta.telegraf.availableMonitoringNetworks = ["local-${config.node.name}"];
|
||||
|
||||
assertions = [
|
||||
{
|
||||
assertion = !config.boot.isContainer;
|
||||
|
@ -185,6 +187,75 @@ in {
|
|||
};
|
||||
temp = {};
|
||||
wireguard = {};
|
||||
|
||||
ping = concatLists (flip mapAttrsToList globals.monitoring.ping (
|
||||
name: pingCfg:
|
||||
optionals (elem pingCfg.network cfg.availableMonitoringNetworks) (
|
||||
concatLists (forEach ["hostv4" "hostv6"] (
|
||||
attr:
|
||||
optional (pingCfg.${attr} != null) {
|
||||
method = "native";
|
||||
urls = [pingCfg.${attr}];
|
||||
ipv4 = attr == "hostv4";
|
||||
ipv6 = attr == "hostv6";
|
||||
tags = {
|
||||
inherit name;
|
||||
inherit (pingCfg) network;
|
||||
ip_version =
|
||||
if attr == "hostv4"
|
||||
then "v4"
|
||||
else "v6";
|
||||
};
|
||||
fieldinclude = [
|
||||
"percent_packet_loss"
|
||||
"average_response_ms"
|
||||
];
|
||||
}
|
||||
))
|
||||
)
|
||||
));
|
||||
|
||||
http_response = concatLists (flip mapAttrsToList globals.monitoring.http (
|
||||
name: httpCfg:
|
||||
optional (elem httpCfg.network cfg.availableMonitoringNetworks) {
|
||||
urls = toList httpCfg.url;
|
||||
method = "GET";
|
||||
response_status_code = httpCfg.expectedStatus;
|
||||
response_string_match = mkIf (httpCfg.expectedBodyRegex != null) httpCfg.expectedBodyRegex;
|
||||
insecure_skip_verify = httpCfg.skipTlsVerification;
|
||||
follow_redirects = true;
|
||||
tags = {
|
||||
inherit name;
|
||||
inherit (httpCfg) network;
|
||||
};
|
||||
}
|
||||
));
|
||||
|
||||
dns_query = concatLists (flip mapAttrsToList globals.monitoring.dns (
|
||||
name: dnsCfg:
|
||||
optional (elem dnsCfg.network cfg.availableMonitoringNetworks) {
|
||||
servers = [dnsCfg.server];
|
||||
domains = [dnsCfg.domain];
|
||||
record_type = dnsCfg.record-type;
|
||||
tags = {
|
||||
inherit name;
|
||||
inherit (dnsCfg) network;
|
||||
};
|
||||
}
|
||||
));
|
||||
|
||||
net_response = concatLists (flip mapAttrsToList globals.monitoring.tcp (
|
||||
name: tcpCfg:
|
||||
optional (elem tcpCfg.network cfg.availableMonitoringNetworks) {
|
||||
address = "${tcpCfg.host}:${toString tcpCfg.port}";
|
||||
protocol = "tcp";
|
||||
tags = {
|
||||
inherit name;
|
||||
inherit (tcpCfg) network;
|
||||
};
|
||||
fieldexclude = ["result_type" "string_found"];
|
||||
}
|
||||
));
|
||||
}
|
||||
// optionalAttrs config.services.smartd.enable {
|
||||
sensors = {};
|
||||
|
@ -200,74 +271,6 @@ in {
|
|||
}
|
||||
// optionalAttrs (config.networking.wireless.enable || config.networking.wireless.iwd.enable) {
|
||||
wireless = {};
|
||||
}
|
||||
// optionalAttrs cfg.globalMonitoring.enable {
|
||||
ping = concatLists (flip mapAttrsToList globals.monitoring.ping (
|
||||
name: pingCfg:
|
||||
optionals (elem pingCfg.network cfg.globalMonitoring.availableNetworks) (
|
||||
concatLists (forEach ["hostv4" "hostv6"] (
|
||||
attr:
|
||||
optional (pingCfg.${attr} != null) {
|
||||
method = "native";
|
||||
urls = [pingCfg.${attr}];
|
||||
ipv4 = attr == "hostv4";
|
||||
ipv6 = attr == "hostv6";
|
||||
tags = {
|
||||
inherit name;
|
||||
inherit (pingCfg) location network;
|
||||
ip_version =
|
||||
if attr == "hostv4"
|
||||
then "v4"
|
||||
else "v6";
|
||||
};
|
||||
fieldpass = [
|
||||
"percent_packet_loss"
|
||||
"average_response_ms"
|
||||
];
|
||||
}
|
||||
))
|
||||
)
|
||||
));
|
||||
|
||||
http_response = concatLists (flip mapAttrsToList globals.monitoring.http (
|
||||
name: httpCfg:
|
||||
optional (elem httpCfg.network cfg.globalMonitoring.availableNetworks) {
|
||||
urls = [httpCfg.url];
|
||||
method = "GET";
|
||||
response_status_code = httpCfg.expectedStatus;
|
||||
response_string_match = mkIf (httpCfg.expectedBodyRegex != null) httpCfg.expectedBodyRegex;
|
||||
tags = {
|
||||
inherit name;
|
||||
inherit (httpCfg) location network;
|
||||
};
|
||||
}
|
||||
));
|
||||
|
||||
dns_query = concatLists (flip mapAttrsToList globals.monitoring.dns (
|
||||
name: dnsCfg:
|
||||
optional (elem dnsCfg.network cfg.globalMonitoring.availableNetworks) {
|
||||
servers = [dnsCfg.server];
|
||||
domains = [dnsCfg.domain];
|
||||
record_type = dnsCfg.record-type;
|
||||
tags = {
|
||||
inherit name;
|
||||
inherit (dnsCfg) location network;
|
||||
};
|
||||
}
|
||||
));
|
||||
|
||||
net_response = concatLists (flip mapAttrsToList globals.monitoring.tcp (
|
||||
name: tcpCfg:
|
||||
optional (elem tcpCfg.network cfg.globalMonitoring.availableNetworks) {
|
||||
address = "${tcpCfg.host}:${toString tcpCfg.port}";
|
||||
protocol = "tcp";
|
||||
tags = {
|
||||
inherit name;
|
||||
inherit (tcpCfg) location network;
|
||||
};
|
||||
fieldexclude = ["result_type" "string_found"];
|
||||
}
|
||||
));
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue