nixpkgs/nixos/modules/services/monitoring/graphite.nix

287 lines
8.9 KiB
Nix

{ config, pkgs, ... }:
with pkgs.lib;
let
cfg = config.services.graphite;
writeTextOrNull = f: t: if t == null then null else pkgs.writeTextDir f t;
dataDir = cfg.dataDir;
configDir = pkgs.buildEnv {
name = "graphite-config";
paths = lists.filter (el: el != null) [
(writeTextOrNull "carbon.conf" cfg.carbon.config)
(writeTextOrNull "storage-agregation.conf" cfg.carbon.storageAggregation)
(writeTextOrNull "storage-schemas.conf" cfg.carbon.storageSchemas)
(writeTextOrNull "blacklist.conf" cfg.carbon.blacklist)
(writeTextOrNull "whitelist.conf" cfg.carbon.whitelist)
(writeTextOrNull "rewrite-rules.conf" cfg.carbon.rewriteRules)
(writeTextOrNull "relay-rules.conf" cfg.carbon.relayRules)
(writeTextOrNull "aggregation-rules.conf" cfg.carbon.aggregationRules)
];
};
carbonOpts = name: with config.ids; ''
--nodaemon --syslog --prefix=${name} --pidfile ${dataDir}/${name}.pid ${name}
'';
carbonEnv = {
PYTHONPATH = "${pkgs.python27Packages.carbon}/lib/python2.7/site-packages";
GRAPHITE_ROOT = dataDir;
GRAPHITE_CONF_DIR = configDir;
GRAPHITE_STORAGE_DIR = dataDir;
};
in {
###### interface
options.services.graphite = {
dataDir = mkOption {
type = types.path;
default = "/var/db/graphite";
description = ''
Data directory for graphite.
'';
};
web = {
enable = mkOption {
description = "Whether to enable graphite web frontend";
default = false;
type = types.uniq types.bool;
};
host = mkOption {
description = "Graphite web frontend listen address";
default = "127.0.0.1";
type = types.str;
};
port = mkOption {
description = "Graphite web frontend port";
default = 8080;
type = types.int;
};
};
carbon = {
config = mkOption {
description = "Content of carbon configuration file";
default = ''
[cache]
# Listen on localhost by default for security reasons
UDP_RECEIVER_INTERFACE = 127.0.0.1
PICKLE_RECEIVER_INTERFACE = 127.0.0.1
LINE_RECEIVER_INTERFACE = 127.0.0.1
CACHE_QUERY_INTERFACE = 127.0.0.1
# Do not log every update
LOG_UPDATES = False
LOG_CACHE_HITS = False
'';
type = types.str;
};
enableCache = mkOption {
description = "Whether to enable carbon cache, the graphite storage daemon";
default = false;
type = types.uniq types.bool;
};
storageAggregation = mkOption {
description = "Defines how to aggregate data to lower-precision retentions";
default = null;
type = types.uniq (types.nullOr types.string);
example = ''
[all_min]
pattern = \.min$
xFilesFactor = 0.1
aggregationMethod = min
'';
};
storageSchemas = mkOption {
description = "Defines retention rates for storing metrics";
default = "";
type = types.uniq (types.nullOr types.string);
example = ''
[apache_busyWorkers]
pattern = ^servers\.www.*\.workers\.busyWorkers$
retentions = 15s:7d,1m:21d,15m:5y
'';
};
blacklist = mkOption {
description = "Any metrics received which match one of the experssions will be dropped";
default = null;
type = types.uniq (types.nullOr types.string);
example = "^some\.noisy\.metric\.prefix\..*";
};
whitelist = mkOption {
description = "Only metrics received which match one of the experssions will be persisted";
default = null;
type = types.uniq (types.nullOr types.string);
example = ".*";
};
rewriteRules = mkOption {
description = "Regular expression patterns that can be used to rewrite metric names in a search and replace fashion";
default = null;
type = types.uniq (types.nullOr types.string);
example = ''
[post]
_sum$ =
_avg$ =
'';
};
enableRelay = mkOption {
description = "Whether to enable carbon relay, the carbon replication and sharding service";
default = false;
type = types.uniq types.bool;
};
relayRules = mkOption {
description = "Relay rules are used to send certain metrics to a certain backend.";
default = null;
type = types.uniq (types.nullOr types.string);
example = ''
[example]
pattern = ^mydata\.foo\..+
servers = 10.1.2.3, 10.1.2.4:2004, myserver.mydomain.com
'';
};
enableAggregator = mkOption {
description = "Whether to enable carbon agregator, the carbon buffering service";
default = false;
type = types.uniq types.bool;
};
aggregationRules = mkOption {
description = "Defines if and how received metrics will be agregated";
default = null;
type = types.uniq (types.nullOr types.string);
example = ''
<env>.applications.<app>.all.requests (60) = sum <env>.applications.<app>.*.requests
<env>.applications.<app>.all.latency (60) = avg <env>.applications.<app>.*.latency
'';
};
};
};
###### implementation
config = mkIf (cfg.carbon.enableAggregator || cfg.carbon.enableCache || cfg.carbon.enableRelay || cfg.web.enable) {
systemd.services.carbonCache = {
enable = cfg.carbon.enableCache;
description = "Graphite Data Storage Backend";
wantedBy = [ "multi-user.target" ];
after = [ "network-interfaces.target" ];
environment = carbonEnv;
serviceConfig = {
ExecStart = "${pkgs.twisted}/bin/twistd ${carbonOpts "carbon-cache"}";
User = "graphite";
Group = "graphite";
};
restartTriggers = [
pkgs.pythonPackages.carbon
cfg.carbon.config
cfg.carbon.storageAggregation
cfg.carbon.storageSchemas
cfg.carbon.rewriteRules
];
preStart = ''
mkdir -m 0700 -p ${cfg.dataDir}/whisper
if [ "$(id -u)" = 0 ]; then chown -R graphite:graphite ${cfg.dataDir}; fi
'';
};
systemd.services.carbonAggregator = {
enable = cfg.carbon.enableAggregator;
description = "Carbon Data Aggregator";
wantedBy = [ "multi-user.target" ];
after = [ "network-interfaces.target" ];
environment = carbonEnv;
serviceConfig = {
ExecStart = "${pkgs.twisted}/bin/twistd ${carbonOpts "carbon-aggregator"}";
User = "graphite";
Group = "graphite";
};
restartTriggers = [
pkgs.pythonPackages.carbon cfg.carbon.config cfg.carbon.aggregationRules
];
};
systemd.services.carbonRelay = {
enable = cfg.carbon.enableRelay;
description = "Carbon Data Relay";
wantedBy = [ "multi-user.target" ];
after = [ "network-interfaces.target" ];
environment = carbonEnv;
serviceConfig = {
ExecStart = "${pkgs.twisted}/bin/twistd ${carbonOpts "carbon-relay"}";
User = "graphite";
Group = "graphite";
};
restartTriggers = [
pkgs.pythonPackages.carbon cfg.carbon.config cfg.carbon.relayRules
];
};
systemd.services.graphiteWeb = {
enable = cfg.web.enable;
description = "Graphite Web Interface";
wantedBy = [ "multi-user.target" ];
after = [ "network-interfaces.target" ];
environment = {
PYTHONPATH = "${pkgs.python27Packages.graphite_web}/lib/python2.7/site-packages";
DJANGO_SETTINGS_MODULE = "graphite.settings";
GRAPHITE_CONF_DIR = "/etc/graphite/";
GRAPHITE_STORAGE_DIR = dataDir;
};
serviceConfig = {
ExecStart = ''
${pkgs.python27Packages.waitress}/bin/waitress-serve \
--host=${cfg.web.host} --port=${toString cfg.web.port} \
--call django.core.handlers.wsgi:WSGIHandler'';
User = "graphite";
Group = "graphite";
};
preStart = ''
if ! test -e ${dataDir}/db-created; then
mkdir -m 0700 -p ${dataDir}/{whisper/,log/webapp/}
if [ "$(id -u)" = 0 ]; then chown -R graphite:graphite ${cfg.dataDir}; fi
# populate database
${pkgs.python27Packages.graphite_web}/bin/manage-graphite.py syncdb --noinput
# create index
${pkgs.python27Packages.graphite_web}/bin/build-index.sh
touch ${dataDir}/db-created
fi
'';
restartTriggers = [
pkgs.python27Packages.graphite_web
pkgs.python27Packages.waitress
];
};
environment.systemPackages = [
pkgs.pythonPackages.carbon
pkgs.python27Packages.graphite_web
pkgs.python27Packages.waitress
];
users.extraUsers = singleton {
name = "graphite";
uid = config.ids.uids.graphite;
description = "Graphite daemon user";
home = dataDir;
};
users.extraGroups.graphite.gid = config.ids.gids.graphite;
};
}