Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,7 @@
./services/monitoring/loki.nix
./services/monitoring/longview.nix
./services/monitoring/mackerel-agent.nix
./services/monitoring/metricbeat.nix
./services/monitoring/monit.nix
./services/monitoring/munin.nix
./services/monitoring/nagios.nix
Expand Down
152 changes: 152 additions & 0 deletions nixos/modules/services/monitoring/metricbeat.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
{ config, lib, pkgs, ... }:

let
inherit (lib)
attrValues
literalExample
mkEnableOption
mkIf
mkOption
types
;
cfg = config.services.metricbeat;

settingsFormat = pkgs.formats.yaml {};

in
{
options = {

services.metricbeat = {

enable = mkEnableOption "metricbeat";

package = mkOption {
type = types.package;
default = pkgs.metricbeat;
defaultText = literalExample "pkgs.metricbeat";
example = literalExample "pkgs.metricbeat7";
description = ''
The metricbeat package to use
'';
};

modules = mkOption {
description = ''
Metricbeat modules are responsible for reading metrics from the various sources.

This is like <literal>services.metricbeat.settings.metricbeat.modules</literal>,
but structured as an attribute set. This has the benefit that multiple
NixOS modules can contribute settings to a single metricbeat module.

A module can be specified multiple times by choosing a different <literal>&lt;name></literal>
for each, but setting <xref linkend="opt-services.metricbeat.modules._name_.module"/> to the same value.

See <link xlink:href="https://www.elastic.co/guide/en/beats/metricbeat/current/metricbeat-modules.html"/>.
'';
default = {};
type = types.attrsOf (types.submodule ({ name, ... }: {
freeformType = settingsFormat.type;
options = {
module = mkOption {
type = types.str;
default = name;
defaultText = literalExample ''<name>'';
description = ''
The name of the module.

Look for the value after <literal>module:</literal> on the individual
module pages linked from <link xlink:href="https://www.elastic.co/guide/en/beats/metricbeat/current/metricbeat-modules.html"/>.
'';
};
};
}));
example = {
system = {
metricsets = ["cpu" "load" "memory" "network" "process" "process_summary" "uptime" "socket_summary"];
enabled = true;
period = "10s";
processes = [".*"];
cpu.metrics = ["percentages" "normalized_percentages"];
core.metrics = ["percentages"];
};
};
};

settings = mkOption {
type = types.submodule {
freeformType = settingsFormat.type;
options = {

name = mkOption {
type = types.str;
default = "";
description = ''
Name of the beat. Defaults to the hostname.
See <link xlink:href="https://www.elastic.co/guide/en/beats/metricbeat/current/configuration-general-options.html#_name"/>.
'';
};

tags = mkOption {
type = types.listOf types.str;
default = [];
description = ''
Tags to place on the shipped metrics.
See <link xlink:href="https://www.elastic.co/guide/en/beats/metricbeat/current/configuration-general-options.html#_tags_2"/>.
'';
};

metricbeat.modules = mkOption {
type = types.listOf settingsFormat.type;
default = [];
internal = true;
description = ''
The metric collecting modules. Use <xref linkend="opt-services.metricbeat.modules"/> instead.

See <link xlink:href="https://www.elastic.co/guide/en/beats/metricbeat/current/metricbeat-modules.html"/>.
'';
};
};
};
default = {};
description = ''
Configuration for metricbeat. See <link xlink:href="https://www.elastic.co/guide/en/beats/metricbeat/current/configuring-howto-metricbeat.html"/> for supported values.
'';
};

};
};

config = mkIf cfg.enable {

assertions = [
{
# empty modules would cause a failure at runtime
assertion = cfg.settings.metricbeat.modules != [];
message = "services.metricbeat: You must configure one or more modules.";
}
];

services.metricbeat.settings.metricbeat.modules = attrValues cfg.modules;

systemd.services.metricbeat = {
description = "metricbeat metrics shipper";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = ''
${cfg.package}/bin/metricbeat \
-c ${settingsFormat.generate "metricbeat.yml" cfg.settings} \
--path.data $STATE_DIRECTORY \
--path.logs $LOGS_DIRECTORY \
;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the semicolon needed for?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's for the backslash. This style of multi-line statement produces smaller diffs, making it easier to merge.

'';
Restart = "always";
DynamicUser = true;
ProtectSystem = "strict";
ProtectHome = "tmpfs";
StateDirectory = "metricbeat";
LogsDirectory = "metricbeat";
};
};
};
}
38 changes: 38 additions & 0 deletions nixos/tests/elk.nix
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,24 @@ let
'');
};

metricbeat = {
enable = true;
package = elk.metricbeat;
modules.system = {
metricsets = ["cpu" "load" "memory" "network" "process" "process_summary" "uptime" "socket_summary"];
enabled = true;
period = "5s";
processes = [".*"];
cpu.metrics = ["percentages" "normalized_percentages"];
core.metrics = ["percentages"];
};
settings = {
output.elasticsearch = {
hosts = ["127.0.0.1:9200"];
};
};
};

logstash = {
enable = true;
package = elk.logstash;
Expand Down Expand Up @@ -135,6 +153,16 @@ let
)


def has_metricbeat():
dictionary = {"query": {"match": {"event.dataset": {"query": "system.cpu"}}}}
return (
"curl --silent --show-error '${esUrl}/_search' "
+ "-H 'Content-Type: application/json' "
+ "-d '{}' ".format(json.dumps(dictionary))
+ "| jq '.hits.total > 0'"
)


start_all()

one.wait_for_unit("elasticsearch.service")
Expand All @@ -161,6 +189,12 @@ let
"curl --silent --show-error 'http://localhost:5601/api/status' | jq .status.overall.state | grep green"
)

with subtest("Metricbeat is running"):
one.wait_for_unit("metricbeat.service")

with subtest("Metricbeat metrics arrive in elasticsearch"):
one.wait_until_succeeds(has_metricbeat() + " | tee /dev/console | grep 'true'")

with subtest("Logstash messages arive in elasticsearch"):
one.wait_until_succeeds(total_hits("flowers") + " | grep -v 0")
one.wait_until_succeeds(total_hits("dragons") + " | grep 0")
Expand Down Expand Up @@ -190,12 +224,14 @@ in pkgs.lib.mapAttrs mkElkTest {
logstash = pkgs.logstash6;
kibana = pkgs.kibana6;
journalbeat = pkgs.journalbeat6;
metricbeat = pkgs.metricbeat6;
}
else {
elasticsearch = pkgs.elasticsearch6-oss;
logstash = pkgs.logstash6-oss;
kibana = pkgs.kibana6-oss;
journalbeat = pkgs.journalbeat6;
metricbeat = pkgs.metricbeat6;
};
ELK-7 =
if enableUnfree
Expand All @@ -204,11 +240,13 @@ in pkgs.lib.mapAttrs mkElkTest {
logstash = pkgs.logstash7;
kibana = pkgs.kibana7;
journalbeat = pkgs.journalbeat7;
metricbeat = pkgs.metricbeat7;
}
else {
elasticsearch = pkgs.elasticsearch7-oss;
logstash = pkgs.logstash7-oss;
kibana = pkgs.kibana7-oss;
journalbeat = pkgs.journalbeat7;
metricbeat = pkgs.metricbeat7;
};
}
13 changes: 10 additions & 3 deletions pkgs/misc/logging/beats/6.x.nix
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{ lib, fetchFromGitHub, elk6Version, buildGoPackage, libpcap, systemd }:
{ lib, fetchFromGitHub, elk6Version, buildGoPackage, libpcap, nixosTests, systemd }:

let beat = package : extraArgs : buildGoPackage (rec {
name = "${package}-${version}";
Expand All @@ -22,10 +22,17 @@ let beat = package : extraArgs : buildGoPackage (rec {
platforms = platforms.linux;
};
} // extraArgs);
in {
in rec {
filebeat6 = beat "filebeat" {meta.description = "Lightweight shipper for logfiles";};
heartbeat6 = beat "heartbeat" {meta.description = "Lightweight shipper for uptime monitoring";};
metricbeat6 = beat "metricbeat" {meta.description = "Lightweight shipper for metrics";};
metricbeat6 = beat "metricbeat" {
meta.description = "Lightweight shipper for metrics";
passthru.tests =
assert metricbeat6.drvPath == nixosTests.elk.ELK-6.elkPackages.metricbeat.drvPath;
{
elk = nixosTests.elk.ELK-6;
};
};
packetbeat6 = beat "packetbeat" {
buildInputs = [ libpcap ];
meta.broken = true;
Expand Down
13 changes: 10 additions & 3 deletions pkgs/misc/logging/beats/7.x.nix
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{ lib, fetchFromGitHub, elk7Version, buildGoPackage, libpcap, systemd }:
{ lib, fetchFromGitHub, elk7Version, buildGoPackage, libpcap, nixosTests, systemd }:

let beat = package : extraArgs : buildGoPackage (rec {
name = "${package}-${version}";
Expand All @@ -22,10 +22,17 @@ let beat = package : extraArgs : buildGoPackage (rec {
platforms = platforms.linux;
};
} // extraArgs);
in {
in rec {
filebeat7 = beat "filebeat" {meta.description = "Lightweight shipper for logfiles";};
heartbeat7 = beat "heartbeat" {meta.description = "Lightweight shipper for uptime monitoring";};
metricbeat7 = beat "metricbeat" {meta.description = "Lightweight shipper for metrics";};
metricbeat7 = beat "metricbeat" {
meta.description = "Lightweight shipper for metrics";
passthru.tests =
assert metricbeat7.drvPath == nixosTests.elk.ELK-7.elkPackages.metricbeat.drvPath;
{
elk = nixosTests.elk.ELK-7;
};
};
packetbeat7 = beat "packetbeat" {
buildInputs = [ libpcap ];
meta.description = "Network packet analyzer that ships data to Elasticsearch";
Expand Down