3ac171cefb
The advantage of putting the PID file under the ephemeral /run is that when the machine crashes /run gets cleared allowing graphite to start once the machine is rebooted. We also set the PIDFile systemd option so that systemd knows the correct PID and enables systemd to remove the file after service shut down.
591 lines
18 KiB
Nix
591 lines
18 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
with lib;
|
|
|
|
let
|
|
cfg = config.services.graphite;
|
|
writeTextOrNull = f: t: if t == null then null else pkgs.writeTextDir f t;
|
|
|
|
dataDir = cfg.dataDir;
|
|
|
|
graphiteApiConfig = pkgs.writeText "graphite-api.yaml" ''
|
|
time_zone: ${config.time.timeZone}
|
|
search_index: ${dataDir}/index
|
|
${optionalString (cfg.api.finders != []) ''finders:''}
|
|
${concatMapStringsSep "\n" (f: " - " + f.moduleName) cfg.api.finders}
|
|
${optionalString (cfg.api.functions != []) ''functions:''}
|
|
${concatMapStringsSep "\n" (f: " - " + f) cfg.api.functions}
|
|
${cfg.api.extraConfig}
|
|
'';
|
|
|
|
seyrenConfig = {
|
|
SEYREN_URL = cfg.seyren.seyrenUrl;
|
|
MONGO_URL = cfg.seyren.mongoUrl;
|
|
GRAPHITE_URL = cfg.seyren.graphiteUrl;
|
|
} // cfg.seyren.extraConfig;
|
|
|
|
pagerConfig = pkgs.writeText "alarms.yaml" cfg.pager.alerts;
|
|
|
|
configDir = pkgs.buildEnv {
|
|
name = "graphite-config";
|
|
paths = lists.filter (el: el != null) [
|
|
(writeTextOrNull "carbon.conf" cfg.carbon.config)
|
|
(writeTextOrNull "storage-aggregation.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 /run/${name}/${name}.pid ${name}
|
|
'';
|
|
|
|
mkPidFileDir = name: ''
|
|
mkdir -p /run/${name}
|
|
chmod 0700 /run/${name}
|
|
chown -R graphite:graphite /run/${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.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;
|
|
};
|
|
};
|
|
|
|
api = {
|
|
enable = mkOption {
|
|
description = ''
|
|
Whether to enable graphite api. Graphite api is lightweight alternative
|
|
to graphite web, with api and without dashboard. It's advised to use
|
|
grafana as alternative dashboard and influxdb as alternative to
|
|
graphite carbon.
|
|
|
|
For more information visit
|
|
<link xlink:href="http://graphite-api.readthedocs.org/en/latest/"/>
|
|
'';
|
|
default = false;
|
|
type = types.bool;
|
|
};
|
|
|
|
finders = mkOption {
|
|
description = "List of finder plugins to load.";
|
|
default = [];
|
|
example = [ pkgs.python27Packages.graphite_influxdb ];
|
|
type = types.listOf types.package;
|
|
};
|
|
|
|
functions = mkOption {
|
|
description = "List of functions to load.";
|
|
default = [
|
|
"graphite_api.functions.SeriesFunctions"
|
|
"graphite_api.functions.PieFunctions"
|
|
];
|
|
type = types.listOf types.str;
|
|
};
|
|
|
|
host = mkOption {
|
|
description = "Graphite web service listen address.";
|
|
default = "127.0.0.1";
|
|
type = types.str;
|
|
};
|
|
|
|
port = mkOption {
|
|
description = "Graphite api service port.";
|
|
default = 8080;
|
|
type = types.int;
|
|
};
|
|
|
|
package = mkOption {
|
|
description = "Package to use for graphite api.";
|
|
default = pkgs.python27Packages.graphite_api;
|
|
type = types.package;
|
|
};
|
|
|
|
extraConfig = mkOption {
|
|
description = "Extra configuration for graphite api.";
|
|
default = ''
|
|
whisper:
|
|
directories:
|
|
- ${dataDir}/whisper
|
|
'';
|
|
example = literalExample ''
|
|
allowed_origins:
|
|
- dashboard.example.com
|
|
cheat_times: true
|
|
influxdb:
|
|
host: localhost
|
|
port: 8086
|
|
user: influxdb
|
|
pass: influxdb
|
|
db: metrics
|
|
cache:
|
|
CACHE_TYPE: 'filesystem'
|
|
CACHE_DIR: '/tmp/graphite-api-cache'
|
|
'';
|
|
type = types.str;
|
|
};
|
|
};
|
|
|
|
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.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.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.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
|
|
'';
|
|
};
|
|
};
|
|
|
|
seyren = {
|
|
enable = mkOption {
|
|
description = "Whether to enable seyren service.";
|
|
default = false;
|
|
type = types.bool;
|
|
};
|
|
|
|
port = mkOption {
|
|
description = "Seyren listening port.";
|
|
default = 8081;
|
|
type = types.int;
|
|
};
|
|
|
|
seyrenUrl = mkOption {
|
|
default = "http://localhost:${toString cfg.seyren.port}/";
|
|
description = "Host where seyren is accessible.";
|
|
type = types.str;
|
|
};
|
|
|
|
graphiteUrl = mkOption {
|
|
default = "http://${cfg.web.host}:${toString cfg.web.port}";
|
|
description = "Host where graphite service runs.";
|
|
type = types.str;
|
|
};
|
|
|
|
mongoUrl = mkOption {
|
|
default = "mongodb://${config.services.mongodb.bind_ip}:27017/seyren";
|
|
description = "Mongodb connection string.";
|
|
type = types.str;
|
|
};
|
|
|
|
extraConfig = mkOption {
|
|
default = {};
|
|
description = ''
|
|
Extra seyren configuration. See
|
|
<link xlink:href='https://github.com/scobal/seyren#config' />
|
|
'';
|
|
type = types.attrsOf types.str;
|
|
example = literalExample ''
|
|
{
|
|
GRAPHITE_USERNAME = "user";
|
|
GRAPHITE_PASSWORD = "pass";
|
|
}
|
|
'';
|
|
};
|
|
};
|
|
|
|
pager = {
|
|
enable = mkOption {
|
|
description = ''
|
|
Whether to enable graphite-pager service. For more information visit
|
|
<link xlink:href="https://github.com/seatgeek/graphite-pager"/>
|
|
'';
|
|
default = false;
|
|
type = types.bool;
|
|
};
|
|
|
|
redisUrl = mkOption {
|
|
description = "Redis connection string.";
|
|
default = "redis://localhost:${toString config.services.redis.port}/";
|
|
type = types.str;
|
|
};
|
|
|
|
graphiteUrl = mkOption {
|
|
description = "URL to your graphite service.";
|
|
default = "http://${cfg.web.host}:${toString cfg.web.port}";
|
|
type = types.str;
|
|
};
|
|
|
|
alerts = mkOption {
|
|
description = "Alerts configuration for graphite-pager.";
|
|
default = ''
|
|
alerts:
|
|
- target: constantLine(100)
|
|
warning: 90
|
|
critical: 200
|
|
name: Test
|
|
'';
|
|
example = literalExample ''
|
|
pushbullet_key: pushbullet_api_key
|
|
alerts:
|
|
- target: stats.seatgeek.app.deal_quality.venue_info_cache.hit
|
|
warning: .5
|
|
critical: 1
|
|
name: Deal quality venue cache hits
|
|
'';
|
|
type = types.lines;
|
|
};
|
|
};
|
|
|
|
beacon = {
|
|
enable = mkEnableOption "graphite beacon";
|
|
|
|
config = mkOption {
|
|
description = "Graphite beacon configuration.";
|
|
default = {};
|
|
type = types.attrs;
|
|
};
|
|
};
|
|
};
|
|
|
|
###### implementation
|
|
|
|
config = mkMerge [
|
|
(mkIf cfg.carbon.enableCache {
|
|
systemd.services.carbonCache = let name = "carbon-cache"; in {
|
|
description = "Graphite Data Storage Backend";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "network-interfaces.target" ];
|
|
environment = carbonEnv;
|
|
serviceConfig = {
|
|
ExecStart = "${pkgs.twisted}/bin/twistd ${carbonOpts name}";
|
|
User = "graphite";
|
|
Group = "graphite";
|
|
PermissionsStartOnly = true;
|
|
PIDFile="/run/${name}/${name}.pid";
|
|
};
|
|
preStart = mkPidFileDir name + ''
|
|
|
|
mkdir -p ${cfg.dataDir}/whisper
|
|
chmod 0700 ${cfg.dataDir}/whisper
|
|
chown -R graphite:graphite ${cfg.dataDir}
|
|
'';
|
|
};
|
|
})
|
|
|
|
(mkIf cfg.carbon.enableAggregator {
|
|
systemd.services.carbonAggregator = let name = "carbon-aggregator"; in {
|
|
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 name}";
|
|
User = "graphite";
|
|
Group = "graphite";
|
|
PIDFile="/run/${name}/${name}.pid";
|
|
};
|
|
preStart = mkPidFileDir name;
|
|
};
|
|
})
|
|
|
|
(mkIf cfg.carbon.enableRelay {
|
|
systemd.services.carbonRelay = let name = "carbon-relay"; in {
|
|
description = "Carbon Data Relay";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "network-interfaces.target" ];
|
|
environment = carbonEnv;
|
|
serviceConfig = {
|
|
ExecStart = "${pkgs.twisted}/bin/twistd ${carbonOpts name}";
|
|
User = "graphite";
|
|
Group = "graphite";
|
|
PIDFile="/run/${name}/${name}.pid";
|
|
};
|
|
preStart = mkPidFileDir name;
|
|
};
|
|
})
|
|
|
|
(mkIf (cfg.carbon.enableCache || cfg.carbon.enableAggregator || cfg.carbon.enableRelay) {
|
|
environment.systemPackages = [
|
|
pkgs.pythonPackages.carbon
|
|
];
|
|
})
|
|
|
|
(mkIf cfg.web.enable {
|
|
systemd.services.graphiteWeb = {
|
|
description = "Graphite Web Interface";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "network-interfaces.target" ];
|
|
path = [ pkgs.perl ];
|
|
environment = {
|
|
PYTHONPATH = "${pkgs.python27Packages.graphite_web}/lib/python2.7/site-packages";
|
|
DJANGO_SETTINGS_MODULE = "graphite.settings";
|
|
GRAPHITE_CONF_DIR = configDir;
|
|
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";
|
|
PermissionsStartOnly = true;
|
|
};
|
|
preStart = ''
|
|
if ! test -e ${dataDir}/db-created; then
|
|
mkdir -p ${dataDir}/{whisper/,log/webapp/}
|
|
chmod 0700 ${dataDir}/{whisper/,log/webapp/}
|
|
|
|
# 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
|
|
|
|
chown -R graphite:graphite ${cfg.dataDir}
|
|
fi
|
|
'';
|
|
};
|
|
|
|
environment.systemPackages = [ pkgs.python27Packages.graphite_web ];
|
|
})
|
|
|
|
(mkIf cfg.api.enable {
|
|
systemd.services.graphiteApi = {
|
|
description = "Graphite Api Interface";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "network-interfaces.target" ];
|
|
environment = {
|
|
PYTHONPATH =
|
|
"${cfg.api.package}/lib/python2.7/site-packages:" +
|
|
concatMapStringsSep ":" (f: f + "/lib/python2.7/site-packages") cfg.api.finders;
|
|
GRAPHITE_API_CONFIG = graphiteApiConfig;
|
|
LD_LIBRARY_PATH = "${pkgs.cairo}/lib";
|
|
};
|
|
serviceConfig = {
|
|
ExecStart = ''
|
|
${pkgs.python27Packages.waitress}/bin/waitress-serve \
|
|
--host=${cfg.api.host} --port=${toString cfg.api.port} \
|
|
graphite_api.app:app
|
|
'';
|
|
User = "graphite";
|
|
Group = "graphite";
|
|
PermissionsStartOnly = true;
|
|
};
|
|
preStart = ''
|
|
if ! test -e ${dataDir}/db-created; then
|
|
mkdir -p ${dataDir}/cache/
|
|
chmod 0700 ${dataDir}/cache/
|
|
|
|
touch ${dataDir}/db-created
|
|
|
|
chown -R graphite:graphite ${cfg.dataDir}
|
|
fi
|
|
'';
|
|
};
|
|
})
|
|
|
|
(mkIf cfg.seyren.enable {
|
|
systemd.services.seyren = {
|
|
description = "Graphite Alerting Dashboard";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "network-interfaces.target" "mongodb.service" ];
|
|
environment = seyrenConfig;
|
|
serviceConfig = {
|
|
ExecStart = "${pkgs.seyren}/bin/seyren -httpPort ${toString cfg.seyren.port}";
|
|
WorkingDirectory = dataDir;
|
|
User = "graphite";
|
|
Group = "graphite";
|
|
};
|
|
preStart = ''
|
|
if ! test -e ${dataDir}/db-created; then
|
|
mkdir -p ${dataDir}
|
|
chown -R graphite:graphite ${dataDir}
|
|
fi
|
|
'';
|
|
};
|
|
|
|
services.mongodb.enable = mkDefault true;
|
|
})
|
|
|
|
(mkIf cfg.pager.enable {
|
|
systemd.services.graphitePager = {
|
|
description = "Graphite Pager Alerting Daemon";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "network-interfaces.target" "redis.service" ];
|
|
environment = {
|
|
REDIS_URL = cfg.pager.redisUrl;
|
|
GRAPHITE_URL = cfg.pager.graphiteUrl;
|
|
};
|
|
serviceConfig = {
|
|
ExecStart = "${pkgs.pythonPackages.graphite_pager}/bin/graphite-pager --config ${pagerConfig}";
|
|
User = "graphite";
|
|
Group = "graphite";
|
|
};
|
|
};
|
|
|
|
services.redis.enable = mkDefault true;
|
|
|
|
environment.systemPackages = [ pkgs.pythonPackages.graphite_pager ];
|
|
})
|
|
|
|
(mkIf cfg.beacon.enable {
|
|
systemd.services.graphite-beacon = {
|
|
description = "Grpahite Beacon Alerting Daemon";
|
|
wantedBy = [ "multi-user.target" ];
|
|
serviceConfig = {
|
|
ExecStart = ''
|
|
${pkgs.pythonPackages.graphite_beacon}/bin/graphite-beacon \
|
|
--config ${pkgs.writeText "graphite-beacon.json" (builtins.toJSON cfg.beacon.config)}
|
|
'';
|
|
User = "graphite";
|
|
Group = "graphite";
|
|
};
|
|
};
|
|
})
|
|
|
|
(mkIf (
|
|
cfg.carbon.enableCache || cfg.carbon.enableAggregator || cfg.carbon.enableRelay ||
|
|
cfg.web.enable || cfg.api.enable ||
|
|
cfg.seyren.enable || cfg.pager.enable || cfg.beacon.enable
|
|
) {
|
|
users.extraUsers = singleton {
|
|
name = "graphite";
|
|
uid = config.ids.uids.graphite;
|
|
description = "Graphite daemon user";
|
|
home = dataDir;
|
|
};
|
|
users.extraGroups.graphite.gid = config.ids.gids.graphite;
|
|
})
|
|
];
|
|
}
|