about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRobert Hensing <robert@roberthensing.nl>2021-03-19 17:16:10 +0100
committerRobert Hensing <robert@roberthensing.nl>2021-05-31 10:42:08 +0200
commit5699d027eccea705b25bf84da147708f2021eb01 (patch)
tree8bfccac1c929b8de1226e1473fe1caacae78e84a
parent774fe1878b045411e6bdd0dd90d8581e82b10993 (diff)
nixos/metricbeat: init
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/services/monitoring/metricbeat.nix152
-rw-r--r--nixos/tests/elk.nix38
-rw-r--r--pkgs/misc/logging/beats/6.x.nix13
-rw-r--r--pkgs/misc/logging/beats/7.x.nix13
5 files changed, 211 insertions, 6 deletions
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index c45f3268b975d..bbbd9362d0b11 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -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
diff --git a/nixos/modules/services/monitoring/metricbeat.nix b/nixos/modules/services/monitoring/metricbeat.nix
new file mode 100644
index 0000000000000..b285559eaa9b3
--- /dev/null
+++ b/nixos/modules/services/monitoring/metricbeat.nix
@@ -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 \
+            ;
+        '';
+        Restart = "always";
+        DynamicUser = true;
+        ProtectSystem = "strict";
+        ProtectHome = "tmpfs";
+        StateDirectory = "metricbeat";
+        LogsDirectory = "metricbeat";
+      };
+    };
+  };
+}
diff --git a/nixos/tests/elk.nix b/nixos/tests/elk.nix
index fee350de65b5c..db51aede0de9a 100644
--- a/nixos/tests/elk.nix
+++ b/nixos/tests/elk.nix
@@ -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;
@@ -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")
@@ -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")
@@ -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
@@ -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;
     };
 }
diff --git a/pkgs/misc/logging/beats/6.x.nix b/pkgs/misc/logging/beats/6.x.nix
index ce80b174d3274..81b8ba0ab5bfd 100644
--- a/pkgs/misc/logging/beats/6.x.nix
+++ b/pkgs/misc/logging/beats/6.x.nix
@@ -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}";
@@ -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;
diff --git a/pkgs/misc/logging/beats/7.x.nix b/pkgs/misc/logging/beats/7.x.nix
index 43ea85508c690..77e14e96c54ea 100644
--- a/pkgs/misc/logging/beats/7.x.nix
+++ b/pkgs/misc/logging/beats/7.x.nix
@@ -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}";
@@ -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";