Files
Floweynt cbfdc8c4f2
diff-tag / dix (push) Successful in 6m11s
flake-check / flake-check (push) Successful in 52s
lint / lint (push) Successful in 1m49s
fix CI runs
2026-09-10 18:30:14 -05:00

387 lines
10 KiB
Nix

{
name,
hostInfo,
dsl,
pkgs,
lib,
containers,
...
}:
let
inherit (dsl) mkContainer;
inherit (hostInfo.paths) data;
nodePort = 9100;
cadvPort = 9200;
containerNetworkRulesFile =
let
mkRules =
metric: record:
lib.mapAttrsToList (cname: c: {
inherit record;
expr = "rate(${metric}{device=\"${c.iface}\"}[5m])";
labels = {
container = cname;
};
}) containers;
in
pkgs.writeText "container-network.yml" (
builtins.toJSON {
groups = [
{
name = "container-network";
rules =
mkRules "node_network_receive_bytes_total" "container_network_receive_rate"
++ mkRules "node_network_transmit_bytes_total" "container_network_transmit_rate";
}
];
}
);
containerDefinedRulesFile = pkgs.writeText "container-defined.yml" (
builtins.toJSON {
groups = [
{
name = "container-defined";
rules = lib.mapAttrsToList (cname: _: {
record = "container_defined";
expr = "1";
labels = {
container = cname;
};
}) containers;
}
];
}
);
alloyConfig = pkgs.writeText "config.alloy" ''
loki.source.journal "journal" {
max_age = "12h"
path = "/var/log/journal"
labels = { job = "systemd-journal" }
relabel_rules = loki.relabel.journal.rules
forward_to = [loki.write.local.receiver]
}
loki.relabel "journal" {
forward_to = []
rule {
source_labels = ["__journal__systemd_unit"]
target_label = "unit"
}
rule {
source_labels = ["__journal__hostname"]
target_label = "host"
}
rule {
source_labels = ["__journal_syslog_identifier"]
target_label = "syslog_id"
}
rule {
source_labels = ["__journal_priority_keyword"]
target_label = "level"
}
rule {
source_labels = ["__journal__systemd_machine_name"]
target_label = "container"
}
}
loki.write "local" {
endpoint {
url = "http://127.0.0.1:3100/loki/api/v1/push"
}
}
'';
dashboards = lib.filterAttrs (name: _: lib.hasSuffix "-dashboard" name) (
dsl.listMap ./overseer (_: path: import path { inherit pkgs lib; })
);
dashboardDir = pkgs.linkFarm "grafana-dashboards" (
lib.mapAttrsToList (name: dashboard: {
name = "${lib.removeSuffix "-dashboard" name}.json";
path = dashboard;
}) dashboards
);
landingRoot = pkgs.writeTextDir "index.html" ''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Overseer</title>
<style>
body { font-family: sans-serif; max-width: 480px; margin: 4rem auto; }
ul { padding: 0; list-style: none; }
li { margin: .5rem 0; }
</style>
</head>
<body>
<h1>Overseer</h1>
<ul>
<li><a data-host="stats.localhost">stats.localhost</a> — Grafana</li>
<li><a data-host="prometheus.localhost">prometheus.localhost</a> — Prometheus</li>
</ul>
<script>
const p = location.port && location.port !== "80" ? ":" + location.port : "";
document.querySelectorAll("a[data-host]").forEach(a => {
const h = a.dataset.host;
a.href = "http://" + h + p;
a.textContent = h + p;
});
</script>
</body>
</html>
'';
in
let
c =
mkContainer {
inherit name;
net.access = [
{
to = "@host";
tcp = [
nodePort
cadvPort
];
}
];
svc.autoStart = true;
resources.fs = {
"/var/lib/loki" = {
hostPath = "${data}/overseer/loki/data";
isReadOnly = false;
};
"/var/lib/prometheus2" = {
hostPath = "${data}/overseer/prometheus/data";
isReadOnly = false;
};
"/var/lib/grafana" = {
hostPath = "${data}/overseer/grafana";
isReadOnly = false;
};
"/var/log/journal" = {
hostPath = "/var/log/journal";
isReadOnly = true;
};
};
containerConfig = _: {
services.loki = {
enable = true;
configuration = {
auth_enabled = false;
server = {
http_listen_port = 3100;
grpc_listen_port = 9096;
};
common = {
instance_addr = "127.0.0.1";
path_prefix = "/var/lib/loki";
storage.filesystem = {
chunks_directory = "/var/lib/loki/chunks";
rules_directory = "/var/lib/loki/rules";
};
replication_factor = 1;
ring.kvstore.store = "inmemory";
};
schema_config.configs = [
{
from = "2020-10-24";
store = "tsdb";
object_store = "filesystem";
schema = "v13";
index = {
prefix = "index_";
period = "24h";
};
}
];
};
};
services.alloy = {
enable = true;
configPath = alloyConfig;
};
services.prometheus = {
enable = true;
port = 9090;
globalConfig.scrape_interval = "15s";
scrapeConfigs = [
{
job_name = "node";
static_configs = [ { targets = [ "${hostAddr}:${toString nodePort}" ]; } ];
}
{
job_name = "cadvisor";
static_configs = [ { targets = [ "${hostAddr}:${toString cadvPort}" ]; } ];
}
{
job_name = "prometheus";
static_configs = [ { targets = [ "localhost:9090" ]; } ];
}
];
ruleFiles = [
containerNetworkRulesFile
containerDefinedRulesFile
(dsl.mkContainerLimitsRuleFile pkgs containers)
];
rules = [
''
groups:
- name: containers
rules:
- alert: ContainerFailed
expr: node_systemd_unit_state{name=~"container@.*",state="failed"} == 1
for: 1m
labels:
severity: critical
annotations:
summary: "{{ $labels.name }} entered failed state"
- alert: ContainerOOMKill
expr: increase(container_oom_events_total{id=~"/machine.slice/container@.+\\.service.*"}[5m]) > 0
labels:
severity: warning
annotations:
summary: "OOM kill in {{ $labels.id }}"
''
];
};
services.grafana = {
enable = true;
dataDir = "/var/lib/grafana";
settings = {
server.http_port = 3000;
server.http_addr = "127.0.0.1";
# WORKAROUND: Grafana 10+ requires an explicit secret_key; omitting it
# causes auth session tokens to be regenerated on every restart
security.secret_key = "SW2YcwTIb9zpOOhoPsMm";
"auth".disable_login_form = true;
"auth.anonymous" = {
enabled = true;
org_role = "Admin";
};
};
provision = {
enable = true;
datasources.settings = {
apiVersion = 1;
datasources = [
{
name = "Prometheus";
type = "prometheus";
uid = "prometheus";
access = "proxy";
url = "http://localhost:9090";
isDefault = true;
jsonData.timeInterval = "15s";
}
{
name = "Loki";
type = "loki";
uid = "loki";
access = "proxy";
url = "http://127.0.0.1:3100";
}
];
};
dashboards.settings = {
apiVersion = 1;
providers = [
{
name = "default";
type = "file";
disableDeletion = true;
options.path = toString dashboardDir;
}
];
};
};
};
services.nginx = {
enable = true;
virtualHosts."_" = {
listen = [
{
addr = "0.0.0.0";
port = 80;
ssl = false;
extraParameters = [ "default_server" ];
}
];
root = "${landingRoot}";
};
virtualHosts."stats.localhost" = {
listen = [
{
addr = "0.0.0.0";
port = 80;
ssl = false;
}
];
locations."/" = {
proxyPass = "http://127.0.0.1:3000";
proxyWebsockets = true;
extraConfig = "proxy_set_header Host $host;";
};
};
virtualHosts."prometheus.localhost" = {
listen = [
{
addr = "0.0.0.0";
port = 80;
ssl = false;
}
];
locations."/" = {
proxyPass = "http://127.0.0.1:9090";
};
};
};
systemd.services = lib.genAttrs [ "loki" "alloy" "prometheus" "grafana" "nginx" ] (_: {
serviceConfig.FailureAction = "exit-force";
});
};
net.expose.tcp = [ 80 ];
resources.mem = "2G";
}
// {
hostConfig = {
services.prometheus.exporters.node = {
enable = true;
listenAddress = "0.0.0.0";
port = nodePort;
enabledCollectors = [
"systemd"
"processes"
];
disabledCollectors = [
"netstat"
"softnet"
];
# radeon sometimes breaks on kexec
extraFlags = [
"--collector.systemd.enable-task-metrics"
"--collector.hwmon.chip-exclude=radeon"
];
};
services.cadvisor = {
enable = true;
listenAddress = "0.0.0.0";
port = cadvPort;
};
};
};
inherit (c) hostAddr;
in
c