Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support "cluster" scope in Metricbeat elasticsearch module #18547

Merged
merged 26 commits into from
Aug 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0f7d7c9
Adding configuration for hosts_mode
ycombinator May 14, 2020
e40d85d
Only perform master check in HostsModeNode
ycombinator May 14, 2020
407cfba
Only ask the node if it's the master node if we're in HostsModeNode
ycombinator May 14, 2020
991d984
Unpack host_mode string into enum
ycombinator May 14, 2020
0f7dd98
Adding some specific TODOs in node_stats code
ycombinator May 14, 2020
d038410
Updating x-pack/metricbeat reference config
ycombinator May 14, 2020
113af15
Set correct service URI
ycombinator May 14, 2020
a662880
Get master node ID
ycombinator May 14, 2020
6da2d75
Adding CHANGELOG entry
ycombinator May 14, 2020
61dc0bc
Rename hosts_mode => scope
ycombinator May 15, 2020
f0b72dd
Removing stale TODO comment
ycombinator May 15, 2020
b763633
Adding docs
ycombinator May 15, 2020
e80a7f4
Refactoring common code into helper method
ycombinator May 15, 2020
b493312
Do not set service URI up front
ycombinator May 18, 2020
2a89782
Updating documentation per review
ycombinator May 27, 2020
6ebfeae
Remove comments from doc examples
ycombinator Jul 2, 2020
3c84bb3
Adding configuration for hosts_mode
ycombinator May 14, 2020
8e46133
Set correct service URI
ycombinator May 14, 2020
de0defd
Adding CHANGELOG entry
ycombinator May 14, 2020
8f005dd
Rename hosts_mode => scope
ycombinator May 15, 2020
01a51f2
Do not set service URI up front
ycombinator May 18, 2020
baa6da8
Update metricbeat/docs/modules/elasticsearch.asciidoc
ycombinator Jul 2, 2020
9569e6d
Update metricbeat/module/elasticsearch/_meta/docs.asciidoc
ycombinator Jul 2, 2020
54f6c3f
Update reference config
ycombinator Jul 2, 2020
d0343a6
Cleaning up CHANGELOG
ycombinator Aug 3, 2020
2f507a9
Updating generated files
ycombinator Aug 3, 2020
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 CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d
- Added cache and connection_errors metrics to status metricset of MySQL module {issue}16955[16955] {pull}19844[19844]
- Update MySQL dashboard with connection errors and cache metrics {pull}19913[19913] {issue}16955[16955]
- Add cloud.instance.name into aws ec2 metricset. {pull}20077[20077]
- Add `scope` setting for elasticsearch module, allowing it to monitor an Elasticsearch cluster behind a load-balancing proxy. {issue}18539[18539] {pull}18547[18547]

*Packetbeat*

Expand Down
16 changes: 11 additions & 5 deletions metricbeat/docs/modules/elasticsearch.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,16 @@ The `elasticsearch` module collects metrics about {es}.
The `elasticsearch` module works with {es} 6.7.0 and later.

[float]
=== Usage for Stack Monitoring
=== Module-specific configuration notes

Like other {beatname_uc} modules, the `elasticsearch` module accepts a `hosts` configuration setting.
This setting can contain a list of entries. The related `scope` setting determines how each entry in
the `hosts` list is interpreted by the module.
dedemorton marked this conversation as resolved.
Show resolved Hide resolved

* If `scope` is set to `node` (default), each entry in the `hosts` list indicates a distinct node in an
{es} cluster.
* If `scope` is set to `cluster`, each entry in the `hosts` list indicates a single endpoint for a distinct
{es} cluster (for example, a load-balancing proxy fronting the cluster).

The `elasticsearch` module can be used to collect metrics shown in our {stack} {monitor-features}
UI in {kib}. To enable this usage, set `xpack.enabled: true` and remove any `metricsets`
Expand Down Expand Up @@ -45,12 +54,9 @@ metricbeat.modules:
#password: "changeme"
#ssl.certificate_authorities: ["/etc/pki/root/ca.pem"]

# Set to false to fetch all entries
#index_recovery.active_only: true

# Set to true to send data collected by module to X-Pack
# Monitoring instead of metricbeat-* indices.
#xpack.enabled: false
#scope: node
----

This module supports TLS connections when using `ssl` config field, as described in <<configuration-ssl>>.
Expand Down
5 changes: 1 addition & 4 deletions metricbeat/metricbeat.reference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -273,12 +273,9 @@ metricbeat.modules:
#password: "changeme"
#ssl.certificate_authorities: ["/etc/pki/root/ca.pem"]

# Set to false to fetch all entries
#index_recovery.active_only: true

# Set to true to send data collected by module to X-Pack
# Monitoring instead of metricbeat-* indices.
#xpack.enabled: false
#scope: node

#------------------------------ Envoyproxy Module ------------------------------
- module: envoyproxy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@
#password: "changeme"
#ssl.certificate_authorities: ["/etc/pki/root/ca.pem"]

# Set to false to fetch all entries
#index_recovery.active_only: true

# Set to true to send data collected by module to X-Pack
# Monitoring instead of metricbeat-* indices.
#xpack.enabled: false
#scope: node
11 changes: 10 additions & 1 deletion metricbeat/module/elasticsearch/_meta/docs.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,16 @@ The `elasticsearch` module collects metrics about {es}.
The `elasticsearch` module works with {es} 6.7.0 and later.

[float]
=== Usage for Stack Monitoring
=== Module-specific configuration notes

Like other {beatname_uc} modules, the `elasticsearch` module accepts a `hosts` configuration setting.
This setting can contain a list of entries. The related `scope` setting determines how each entry in
the `hosts` list is interpreted by the module.
dedemorton marked this conversation as resolved.
Show resolved Hide resolved

* If `scope` is set to `node` (default), each entry in the `hosts` list indicates a distinct node in an
{es} cluster.
* If `scope` is set to `cluster`, each entry in the `hosts` list indicates a single endpoint for a distinct
{es} cluster (for example, a load-balancing proxy fronting the cluster).

The `elasticsearch` module can be used to collect metrics shown in our {stack} {monitor-features}
UI in {kib}. To enable this usage, set `xpack.enabled: true` and remove any `metricsets`
Expand Down
9 changes: 3 additions & 6 deletions metricbeat/module/elasticsearch/ccr/ccr.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,11 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) {

// Fetch gathers stats for each follower shard from the _ccr/stats API
func (m *MetricSet) Fetch(r mb.ReporterV2) error {
isMaster, err := elasticsearch.IsMaster(m.HTTP, m.GetServiceURI())
shouldSkip, err := m.ShouldSkipFetch()
if err != nil {
return errors.Wrap(err, "error determining if connected Elasticsearch node is master")
return err
}

// Not master, no event sent
if !isMaster {
m.Logger().Debug("trying to fetch ccr stats from a non-master node")
if shouldSkip {
return nil
}

Expand Down
11 changes: 3 additions & 8 deletions metricbeat/module/elasticsearch/cluster_stats/cluster_stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
package cluster_stats

import (
"github.com/pkg/errors"

"github.com/elastic/beats/v7/metricbeat/mb"
"github.com/elastic/beats/v7/metricbeat/module/elasticsearch"
)
Expand Down Expand Up @@ -51,14 +49,11 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) {

// Fetch methods implements the data gathering and data conversion to the right format
func (m *MetricSet) Fetch(r mb.ReporterV2) error {
isMaster, err := elasticsearch.IsMaster(m.HTTP, m.HostData().SanitizedURI+clusterStatsPath)
shouldSkip, err := m.ShouldSkipFetch()
if err != nil {
return errors.Wrap(err, "error determining if connected Elasticsearch node is master")
return err
}

// Not master, no event sent
if !isMaster {
m.Logger().Debug("trying to fetch cluster stats from a non-master node")
if shouldSkip {
return nil
}

Expand Down
22 changes: 22 additions & 0 deletions metricbeat/module/elasticsearch/elasticsearch.go
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,28 @@ func IsMLockAllEnabled(http *helper.HTTP, resetURI, nodeID string) (bool, error)
return false, fmt.Errorf("could not determine if mlockall is enabled on node ID = %v", nodeID)
}

// GetMasterNodeID returns the ID of the Elasticsearch cluster's master node
func GetMasterNodeID(http *helper.HTTP, resetURI string) (string, error) {
content, err := fetchPath(http, resetURI, "_nodes/_master", "filter_path=nodes.*.name")
if err != nil {
return "", err
}

var response struct {
Nodes map[string]interface{} `json:"nodes"`
}

if err := json.Unmarshal(content, &response); err != nil {
return "", err
}

for nodeID, _ := range response.Nodes {
return nodeID, nil
}

return "", errors.New("could not determine master node ID")
}

// PassThruField copies the field at the given path from the given source data object into
// the same path in the given target data object.
func PassThruField(fieldPath string, sourceData, targetData common.MapStr) error {
Expand Down
9 changes: 3 additions & 6 deletions metricbeat/module/elasticsearch/enrich/enrich.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,11 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) {

// Fetch gathers stats for each enrich coordinator node
func (m *MetricSet) Fetch(r mb.ReporterV2) error {
isMaster, err := elasticsearch.IsMaster(m.HTTP, m.GetServiceURI())
shouldSkip, err := m.ShouldSkipFetch()
if err != nil {
return errors.Wrap(err, "error determining if connected Elasticsearch node is master")
return err
}

// Not master, no event sent
if !isMaster {
m.Logger().Debug("trying to fetch enrich stats from a non-master node")
if shouldSkip {
return nil
}

Expand Down
10 changes: 3 additions & 7 deletions metricbeat/module/elasticsearch/index/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,11 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) {

// Fetch gathers stats for each index from the _stats API
func (m *MetricSet) Fetch(r mb.ReporterV2) error {

isMaster, err := elasticsearch.IsMaster(m.HTTP, m.HostData().SanitizedURI+statsPath)
shouldSkip, err := m.ShouldSkipFetch()
if err != nil {
return errors.Wrap(err, "error determining if connected Elasticsearch node is master")
return err
}

// Not master, no event sent
if !isMaster {
m.Logger().Debug("trying to fetch index stats from a non-master node")
if shouldSkip {
return nil
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
package index_recovery

import (
"github.com/pkg/errors"

"github.com/elastic/beats/v7/metricbeat/mb"
"github.com/elastic/beats/v7/metricbeat/module/elasticsearch"
)
Expand Down Expand Up @@ -67,14 +65,11 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) {

// Fetch gathers stats for each index from the _stats API
func (m *MetricSet) Fetch(r mb.ReporterV2) error {
isMaster, err := elasticsearch.IsMaster(m.HTTP, m.GetServiceURI())
shouldSkip, err := m.ShouldSkipFetch()
if err != nil {
return errors.Wrap(err, "error determining if connected Elasticsearch node is master")
return err
}

// Not master, no event sent
if !isMaster {
m.Logger().Debug("trying to fetch index recovery stats from a non-master node")
if shouldSkip {
return nil
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,11 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) {

// Fetch gathers stats for each index from the _stats API
func (m *MetricSet) Fetch(r mb.ReporterV2) error {
isMaster, err := elasticsearch.IsMaster(m.HTTP, m.HostData().SanitizedURI+statsPath)
shouldSkip, err := m.ShouldSkipFetch()
if err != nil {
return errors.Wrap(err, "error determining if connected Elasticsearch node is master")
return err
}

// Not master, no event sent
if !isMaster {
m.Logger().Debug("trying to fetch index summary stats from a non-master node")
if shouldSkip {
return nil
}

Expand Down
54 changes: 53 additions & 1 deletion metricbeat/module/elasticsearch/metricset.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
package elasticsearch

import (
"fmt"

"github.com/pkg/errors"

"github.com/elastic/beats/v7/metricbeat/helper"
"github.com/elastic/beats/v7/metricbeat/mb"
"github.com/elastic/beats/v7/metricbeat/mb/parse"
Expand All @@ -36,13 +40,39 @@ var (
}.Build()
)

type Scope int

const (
// Indicates that each item in the hosts list points to a distinct Elasticsearch node in a
// cluster.
ScopeNode Scope = iota

// Indicates that each item in the hosts lists points to a endpoint for a distinct Elasticsearch
// cluster (e.g. a load-balancing proxy) fronting the cluster.
ScopeCluster
)

func (h *Scope) Unpack(str string) error {
switch str {
case "node":
*h = ScopeNode
case "cluster":
*h = ScopeCluster
default:
return fmt.Errorf("invalid scope: %v", str)
}

return nil
}

// MetricSet can be used to build other metric sets that query RabbitMQ
// management plugin
type MetricSet struct {
mb.BaseMetricSet
servicePath string
*helper.HTTP
XPack bool
Scope Scope
}

// NewMetricSet creates an metric set that can be used to build other metric
Expand All @@ -54,9 +84,11 @@ func NewMetricSet(base mb.BaseMetricSet, servicePath string) (*MetricSet, error)
}

config := struct {
XPack bool `config:"xpack.enabled"`
XPack bool `config:"xpack.enabled"`
Scope Scope `config:"scope"`
}{
XPack: false,
Scope: ScopeNode,
}
if err := base.Module().UnpackConfig(&config); err != nil {
return nil, err
Expand All @@ -67,6 +99,7 @@ func NewMetricSet(base mb.BaseMetricSet, servicePath string) (*MetricSet, error)
servicePath,
http,
config.XPack,
config.Scope,
}

ms.SetServiceURI(servicePath)
Expand All @@ -84,3 +117,22 @@ func (m *MetricSet) SetServiceURI(servicePath string) {
m.servicePath = servicePath
m.HTTP.SetURI(m.GetServiceURI())
}

func (m *MetricSet) ShouldSkipFetch() (bool, error) {
// If we're talking to a set of ES nodes directly, only collect stats from the master node so
// we don't collect the same stats from every node and end up duplicating them.
if m.Scope == ScopeNode {
isMaster, err := IsMaster(m.HTTP, m.GetServiceURI())
if err != nil {
return false, errors.Wrap(err, "error determining if connected Elasticsearch node is master")
}

// Not master, no event sent
if !isMaster {
m.Logger().Debugf("trying to fetch %v stats from a non-master node", m.Name())
return true, nil
}
}

return false, nil
}
12 changes: 3 additions & 9 deletions metricbeat/module/elasticsearch/ml_job/ml_job.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
package ml_job

import (
"github.com/pkg/errors"

"github.com/elastic/beats/v7/metricbeat/mb"
"github.com/elastic/beats/v7/metricbeat/module/elasticsearch"
)
Expand Down Expand Up @@ -54,15 +52,11 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) {

// Fetch methods implements the data gathering and data conversion to the right format
func (m *MetricSet) Fetch(r mb.ReporterV2) error {

isMaster, err := elasticsearch.IsMaster(m.HTTP, m.GetServiceURI())
shouldSkip, err := m.ShouldSkipFetch()
if err != nil {
return errors.Wrap(err, "error determining if connected Elasticsearch node is master")
return err
}

// Not master, no event sent
if !isMaster {
m.Logger().Debug("trying to fetch machine learning job stats from a non-master node")
if shouldSkip {
return nil
}

Expand Down
17 changes: 7 additions & 10 deletions metricbeat/module/elasticsearch/node_stats/data_xpack.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,18 +187,14 @@ func eventsMappingXPack(r mb.ReporterV2, m *MetricSet, info elasticsearch.Info,
return errors.Wrap(err, "failure parsing Elasticsearch Node Stats API response")
}

// Normally the nodeStruct should only contain one node. But if _local is removed
// from the path and Metricbeat is not installed on the same machine as the node
// it will provid the data for multiple nodes. This will mean the detection of the
// master node will not be accurate anymore as often in these cases a proxy is in front
// of ES and it's not know if the request will be routed to the same node as before.
masterNodeID, err := elasticsearch.GetMasterNodeID(m.HTTP, m.HTTP.GetURI())
if err != nil {
return err
}

var errs multierror.Errors
for nodeID, node := range nodesStruct.Nodes {
isMaster, err := elasticsearch.IsMaster(m.HTTP, m.HTTP.GetURI())
if err != nil {
errs = append(errs, errors.Wrap(err, "error determining if connected Elasticsearch node is master"))
continue
}
isMaster := nodeID == masterNodeID

event := mb.Event{}

Expand All @@ -207,6 +203,7 @@ func eventsMappingXPack(r mb.ReporterV2, m *MetricSet, info elasticsearch.Info,
errs = append(errs, errors.Wrap(err, "failure to apply node schema"))
continue
}

nodeData["node_master"] = isMaster
nodeData["node_id"] = nodeID

Expand Down
Loading