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

Add bodysize limit for metric scraping. #1467

Merged
merged 1 commit into from
May 18, 2022

Conversation

raptorsun
Copy link
Contributor

@raptorsun raptorsun commented Nov 5, 2021

  • I added CHANGELOG entry for this change.
  • No user-facing changes, so no entry in CHANGELOG was needed.

This PR adds an option to CMO config map to activate bodysize limit on metrics scraping, which can prevent potential OOM problems when scraping metric endpoints responding with an oversized HTTP body.

The dependency upgrade PR is PR #1468 . This functionality requires Prometheus-Operator 0.51+. So I upgrade it to 0.52.

Here is the JIRA ticket.

FIeld prometheusK8s.enforcedBodySizeLimit to CMO ConfigMap, accepting the size format from Prometheus:

  • Empty value and 0 mean no limit
  • string "automatic" for automatically set the limit according the cluster pod capacity.
  • [0-9]+[A-Z][a-z]*B for a customized size.

@openshift-ci openshift-ci bot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. labels Nov 5, 2021
@openshift-ci openshift-ci bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Nov 5, 2021
@openshift-ci openshift-ci bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Nov 5, 2021
@raptorsun
Copy link
Contributor Author

/retest-required

@raptorsun
Copy link
Contributor Author

/test e2e-agnostic-upgrade

@openshift-ci openshift-ci bot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Nov 14, 2021
@openshift-ci openshift-ci bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Nov 22, 2021
@raptorsun raptorsun changed the title WIP [MON-1838] Add bodysize limit for metric scraping. [MON-1838] Add bodysize limit for metric scraping. Nov 23, 2021
@openshift-ci openshift-ci bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Nov 23, 2021
@raptorsun raptorsun changed the title [MON-1838] Add bodysize limit for metric scraping. Add bodysize limit for metric scraping. Nov 23, 2021
@raptorsun
Copy link
Contributor Author

Tested with clusterbot it is well set to 2MB body size limit when adding the option limitScrapeBodySize: true in CMO config map.

$k exec -it prometheus-k8s-0 -n openshift-monitoring -- cat /etc/prometheus/config_out/prometheus.env.yaml  | grep body_size
  body_size_limit: 2MB
  body_size_limit: 2MB
  body_size_limit: 2MB

With the default value 2MB limit all targets are correctly scraped.

When setting the body size limit to 500KB part of the targets shows an error of exceeding the limit as shown in the screenshot below. (CMO running locally)
image

@jan--f
Copy link
Contributor

jan--f commented Nov 24, 2021

Nice, this looks good to me. Is the scenario of hitting a scrape limit already covered by an existing alert?

I think it should definitely be alerted upon, as otherwise user might not get relevant metrics without being aware.

@raptorsun
Copy link
Contributor Author

Nice, this looks good to me. Is the scenario of hitting a scrape limit already covered by an existing alert?

I think it should definitely be alerted upon, as otherwise user might not get relevant metrics without being aware.

Thanks for pointing out the alert. I almost forget it 😀
There is a metric prometheus_target_scrapes_exceeded_body_size_limit_total in prometheus to trigger alert when scrape body exceeds the size limit. I complete the PR with a commit adding this alert.

@@ -326,6 +326,26 @@ local patchedRules = [
},
];

local addedRules = [
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this be defined in upstream prometheus mixin?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I will submit a change to upstream prometheus mixins later. Once it is fit into upstream, we can clean it from CMO.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

PR created in Prometheus to add this alert to mixins: prometheus/prometheus#9873
Hope it can be merged someday :)

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add a TODO so we don't forget?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

of course, when the PR is merged, we can safely removed this alert patch.

Copy link
Contributor

Choose a reason for hiding this comment

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

let remove the patched alert and rely on upstream.

@openshift-ci openshift-ci bot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Nov 25, 2021
@openshift-ci openshift-ci bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Nov 25, 2021
@raptorsun
Copy link
Contributor Author

A new version is pushed :)
limitScrapeBodySize is a string now accepting the following 3 values:

  1. a size in Prometheus config format such as "10MB"
  2. a string "default" to use the default value we provided "2MB"
  3. empty. no limit will be set.

@@ -315,19 +325,18 @@ func (cfg *TelemeterClientConfig) IsEnabled() bool {
return true
}

func NewConfig(content io.Reader) (*Config, error) {
func NewConfig(content io.Reader) (res *Config, err error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not a big fan of named return values as I find that they make the code less readable. Maybe this is only me but I don't feel either that this change is required here.

}

func (c *Config) LoadEnforcedBodySizeLimit(pcr PodCapacityReader, ctx context.Context) error {
if c.ClusterMonitoringConfiguration.PrometheusK8sConfig.EnforcedBodySizeLimit == "automatic" {
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe define a const for "automatic".

TelemetryMatches []string `json:"-"`
AlertmanagerConfigs []AdditionalAlertmanagerConfig `json:"additionalAlertmanagerConfigs"`
QueryLogFile string `json:"queryLogFile"`
EnforcedBodySizeLimit string `json:"enforcedBodySizeLimit,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add a comment about how using the parameter?

  • an empty value means no enforcement
  • "automatic" means that CMO picks up a value based on the cluster capacity
  • A fixed size can be defined too.

}

func (c *Config) LoadEnforcedBodySizeLimit(pcr PodCapacityReader, ctx context.Context) error {
if c.ClusterMonitoringConfiguration.PrometheusK8sConfig.EnforcedBodySizeLimit == "automatic" {
Copy link
Contributor

Choose a reason for hiding this comment

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

you can handle the "return-early" case first.

Suggested change
if c.ClusterMonitoringConfiguration.PrometheusK8sConfig.EnforcedBodySizeLimit == "automatic" {
if c.ClusterMonitoringConfiguration.PrometheusK8sConfig.EnforcedBodySizeLimit == "" {
return nil
}
if c.ClusterMonitoringConfiguration.PrometheusK8sConfig.EnforcedBodySizeLimit == "automatic" {

return nil
}

func (c *Config) UseMinimalEnforcedBodySizeLimit() {
Copy link
Contributor

Choose a reason for hiding this comment

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

this doesn't seem to be used but should be incorporated in calculateBodySizeLimit() I believe.

func calculateBodySizeLimit(podCapacity int) string {
const samplesPerPod = 400 // 400 samples per pod
const sizePerSample = 200 // 200 Bytes
const loadFactorPercentage = 60 // assume 80% of the maximum pods capacity per node is used
Copy link
Contributor

Choose a reason for hiding this comment

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

I would assume that the full capacity can be used.

bodySize := loadFactorPercentage * podCapacity / 100 * samplesPerPod * sizePerSample
if bodySize < minimalSizeLimit {
bodySize = minimalSizeLimit
klog.Infof("Calculated scrape body size limit is too small, using default value %v instead", minimalSizeLimit)
Copy link
Contributor

Choose a reason for hiding this comment

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

we could log both values (e.g. "calculated body size limit = ... is too small, using ... instead")

err = c.LoadEnforcedBodySizeLimit(o.client, ctx)
if err != nil {
c.ClusterMonitoringConfiguration.PrometheusK8sConfig.EnforcedBodySizeLimit = ""
klog.Warningf("Error loading enforced body size limit, no body size limit will be enforced. Error: %v", err)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
klog.Warningf("Error loading enforced body size limit, no body size limit will be enforced. Error: %v", err)
klog.Warningf("Error loading enforced body size limit, no body size limit will be enforced: %v", err)

f.MustCreateOrUpdateConfigMap(t, configMapWithData(t, data))

f.PrometheusK8sClient.WaitForQueryReturn(
t, 5*time.Minute, `ceil(sum(increase(prometheus_target_scrapes_exceeded_body_size_limit_total{job="prometheus-k8s"}[5m])))`,
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
t, 5*time.Minute, `ceil(sum(increase(prometheus_target_scrapes_exceeded_body_size_limit_total{job="prometheus-k8s"}[5m])))`,
t, 5*time.Minute, `sum(increase(prometheus_target_scrapes_exceeded_body_size_limit_total{job="prometheus-k8s"}[5m]))`,

Copy link
Contributor Author

Choose a reason for hiding this comment

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

need to keep the ceil function to round the result to an integer as WaitForQueryReturn requires.

@openshift-ci openshift-ci bot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Apr 22, 2022
@openshift-ci openshift-ci bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Apr 22, 2022
@raptorsun
Copy link
Contributor Author

/test e2e-agnostic-upgrade

Comment on lines 521 to 523
const loadFactorPercentage = 100 // assume 100% of the maximum pods capacity per node is used

bodySize := loadFactorPercentage * podCapacity / 100 * samplesPerPod * sizePerSample
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can get rid of loadFactorPercentage.

Suggested change
const loadFactorPercentage = 100 // assume 100% of the maximum pods capacity per node is used
bodySize := loadFactorPercentage * podCapacity / 100 * samplesPerPod * sizePerSample
bodySize := podCapacity * samplesPerPod * sizePerSample

// Limit the body size from scrape queries
// Assumptions: one node has in average 110 pods, each pod exposes 400 metrics, each metric is expressed by on average 250 bytes.
// 1.5x the size for a safe margin, it rounds to 16MB (16,500,000 Bytes).
minimalSizeLimit = 1.5 * 110 * 400 * 250
Copy link
Contributor

Choose a reason for hiding this comment

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

We probably need a "safe" lower-bound value for the "automatically computed" value but I'm not sure that this value is accurate. I would take a typical CI cluster, load it with as many pods/secret/configmaps as possible and measure the body size returned by kube-state-metrics /metrics.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Having tested with 100k secrets and 100k config maps with kube-burner, KSM scrape target gives us ~32MB body size.
So this lower bound is tripled to 48MB in new commit. It should be large enough I suppose :)

Screenshot from 2022-05-10 12-09-02

@openshift-ci openshift-ci bot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label May 10, 2022
bodysize when scraping metric.
Empty value or 0 means bodysize limit. "automatic" for automatically
deduced bodysize limit.
@openshift-ci openshift-ci bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label May 10, 2022
@raptorsun
Copy link
Contributor Author

/test e2e-agnostic-operator

1 similar comment
@raptorsun
Copy link
Contributor Author

/test e2e-agnostic-operator

Copy link
Contributor

@jan--f jan--f left a comment

Choose a reason for hiding this comment

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

This lgtm

Copy link
Contributor

@simonpasquier simonpasquier left a comment

Choose a reason for hiding this comment

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

/lgtm

@simonpasquier
Copy link
Contributor

/hold cancel

@openshift-ci openshift-ci bot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label May 18, 2022
@openshift-ci openshift-ci bot added the lgtm Indicates that a PR is ready to be merged. label May 18, 2022
@openshift-ci
Copy link
Contributor

openshift-ci bot commented May 18, 2022

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: jan--f, raptorsun, simonpasquier

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:
  • OWNERS [jan--f,raptorsun,simonpasquier]

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-bot
Copy link
Contributor

/retest-required

Please review the full test history for this PR and help us cut down flakes.

4 similar comments
@openshift-bot
Copy link
Contributor

/retest-required

Please review the full test history for this PR and help us cut down flakes.

@openshift-bot
Copy link
Contributor

/retest-required

Please review the full test history for this PR and help us cut down flakes.

@openshift-bot
Copy link
Contributor

/retest-required

Please review the full test history for this PR and help us cut down flakes.

@openshift-bot
Copy link
Contributor

/retest-required

Please review the full test history for this PR and help us cut down flakes.

@openshift-ci
Copy link
Contributor

openshift-ci bot commented May 18, 2022

@raptorsun: all tests passed!

Full PR test history. Your PR dashboard.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here.

@openshift-merge-robot openshift-merge-robot merged commit 36c38fa into openshift:master May 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. docs-approved Signifies that Docs has signed off on this PR lgtm Indicates that a PR is ready to be merged. px-approved Signifies that Product Support has signed off on this PR qe-approved Signifies that QE has signed off on this PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants