Philipp KahrNathan Smith

在 Elastic Observability 中使用 APM 元数据优化云资源和成本

使用 Elastic APM 优化云成本。了解如何利用云元数据、计算定价,并做出更明智的决策以获得更好的性能。

Optimizing cloud resources and cost with APM metadata in Elastic Observability

应用程序性能监控 (APM) 不仅仅是捕获和跟踪错误和堆栈跟踪。如今,基于云的企业将应用程序部署在各个区域甚至云提供商之间。因此,利用 Elastic APM 代理提供的元数据变得至关重要。利用元数据(包括云区域、提供商和机器类型等关键信息)使我们能够跨应用程序堆栈跟踪成本。在这篇博文中,我们将探讨如何使用云元数据来帮助企业做出更明智、更具成本效益的决策,同时提高资源利用率和用户体验。

首先,我们需要一个示例应用程序,使我们能够有效地监控基础设施变化。我们使用带有 Elastic Python APM 代理的 Python Flask 应用程序。该应用程序是一个简单的计算器,将数字作为 REST 请求接收。我们利用 Locust(一个简单的负载测试工具)来评估不同工作负载下的性能。

下一步包括获取与云服务相关的定价信息。每个云提供商都不同。他们中的大多数都提供通过 API 检索定价的选项。但是今天,我们将重点关注 Google Cloud,并将利用其定价计算器来检索相关的成本信息。

计算器和 Google Cloud 定价

要进行成本分析,我们需要知道所用机器的成本。Google 提供了用于以编程方式获取必要数据的计费 API客户端库。在本博客中,我们不介绍 API 方法。相反,Google Cloud 定价计算器就足够了。在计算器中选择机器类型和区域,并将计数设置为 1 个实例。然后它将报告该机器的总估计成本。对 e2-standard-4 机器类型执行此操作,得出 730 小时运行时间的成本为 107.7071784 美元。

现在,让我们转到我们的 Kibana®,在其中创建 Dev Tools 内的新索引。由于我们不想分析文本,我们将告诉 Elasticsearch® 将每个文本都视为关键字。索引名称为 cloud-billing。我可能想对 Azure 和 AWS 执行相同的操作,然后可以将其附加到同一索引。

PUT cloud-billing
{
  "mappings": {
    "dynamic_templates": [
      {
        "stringsaskeywords": {
          "match": "*",
          "match_mapping_type": "string",
          "mapping": {
            "type": "keyword"
          }
        }
      }
    ]
  }
}

接下来是制作我们的账单文档

POST cloud-billing/_doc/e2-standard-4_europe-west4
{
  "machine": {
    "enrichment": "e2-standard-4_europe-west4"
  },
  "cloud": {
    "machine": {
       "type": "e2-standard-4"
    },
    "region": "europe-west4",
    "provider": "google"
  },
  "stats": {
    "cpu": 4,
    "memory": 8
  },
  "price": {
    "minute": 0.002459068,
    "hour": 0.14754408,
    "month": 107.7071784
  }
}

我们创建一个文档并设置一个自定义 ID。此 ID 与实例名称和区域匹配,因为机器的成本在每个区域中可能不同。自动 ID 可能会有问题,因为我可能想定期更新机器的成本。我可以使用时间戳索引来做到这一点,并且只使用最新匹配的文档。但是通过这种方式,我可以进行更新而不必担心它。我还将价格计算为每分钟和每小时的价格。最重要的是 machine.enrichment 字段,它与 ID 相同。同一实例类型可以存在于多个区域中,但是我们的富集处理器仅限于匹配或范围。我们创建一个匹配的名称,该名称可以显式匹配,例如 e2-standard-4_europe-west4。是否要在其中添加云提供商并将其设为 google_e2-standard-4_europ-west-4 由您决定。

计算成本

在 Elastic Stack 中有多种方法可以实现此目的。在本例中,我们将使用富集策略、提取管道和转换。

富集策略的设置相当简单

PUT _enrich/policy/cloud-billing
{
  "match": {
    "indices": "cloud-billing",
    "match_field": "machine.enrichment",
    "enrich_fields": ["price.minute", "price.hour", "price.month"]
  }
}

POST _enrich/policy/cloud-billing/_execute

不要忘记在末尾运行 _execute。这对于在提取管道中生成富集使用的内部索引是必需的。提取管道非常简单 — 它调用富集并重命名一个字段。这就是我们的 machine.enrichment 字段的用武之地。关于富集的一个注意事项是,当您向 cloud-billing 索引添加新文档时,您需要重新运行 _execute 语句。最后一部分计算总成本,其中包含所看到的唯一机器的数量。

PUT _ingest/pipeline/cloud-billing
{
  "processors": [
    {
      "set": {
        "field": "_temp.machine_type",
        "value": "{{cloud.machine.type}}_{{cloud.region}}"
      }
    },
    {
      "enrich": {
        "policy_name": "cloud-billing",
        "field": "_temp.machine_type",
        "target_field": "enrichment"
      }
    },
    {
      "rename": {
        "field": "enrichment.price",
        "target_field": "price"
      }
    },
    {
      "remove": {
        "field": [
          "_temp",
          "enrichment"
        ]
      }
    },
    {
      "script": {
        "source": "ctx.total_price=ctx.count_machines*ctx.price.hour"
      }
    }
  ]
}

由于现在所有这些都已配置完成,我们已为转换做好准备。为此,我们需要一个与 APM data_streams 匹配的数据视图。这是 traces-apm*、metrics-apm.*、logs-apm.*。对于转换,请转到 Kibana 中的“转换”UI 并按以下方式进行配置

我们正在进行每小时细分,因此,我每个服务、每小时、每种机器类型都会获得一个文档。有趣的是聚合。我想查看平均 CPU 使用率以及第 75、95、99 个百分位数,以按小时查看 CPU 使用率。这使我能够识别一个小时内的 CPU 使用率。在底部,为转换命名并选择索引 cloud-costs 并选择 cloud-billing 提取管道。

这是整个转换为 JSON 文档

PUT _transform/cloud-billing
{
  "source": {
    "index": [
      "traces-apm*",
      "metrics-apm.*",
      "logs-apm.*"
    ],
    "query": {
      "bool": {
        "filter": [
          {
            "bool": {
              "should": [
                {
                  "exists": {
                    "field": "cloud.provider"
                  }
                }
              ],
              "minimum_should_match": 1
            }
          }
        ]
      }
    }
  },
  "pivot": {
    "group_by": {
      "@timestamp": {
        "date_histogram": {
          "field": "@timestamp",
          "calendar_interval": "1h"
        }
      },
      "cloud.provider": {
        "terms": {
          "field": "cloud.provider"
        }
      },
      "cloud.region": {
        "terms": {
          "field": "cloud.region"
        }
      },
      "cloud.machine.type": {
        "terms": {
          "field": "cloud.machine.type"
        }
      },
      "service.name": {
        "terms": {
          "field": "service.name"
        }
      }
    },
    "aggregations": {
      "avg_cpu": {
        "avg": {
          "field": "system.cpu.total.norm.pct"
        }
      },
      "percentiles_cpu": {
        "percentiles": {
          "field": "system.cpu.total.norm.pct",
          "percents": [
            75,
            95,
            99
          ]
        }
      },
      "avg_transaction_duration": {
        "avg": {
          "field": "transaction.duration.us"
        }
      },
      "percentiles_transaction_duration": {
        "percentiles": {
          "field": "transaction.duration.us",
          "percents": [
            75,
            95,
            99
          ]
        }
      },
      "count_machines": {
        "cardinality": {
          "field": "cloud.instance.id"
        }
      }
    }
  },
  "dest": {
    "index": "cloud-costs",
    "pipeline": "cloud-costs"
  },
  "sync": {
    "time": {
      "delay": "120s",
      "field": "@timestamp"
    }
  },
  "settings": {
    "max_page_search_size": 1000
  }
}

创建并运行转换后,我们需要一个索引为 cloud-costs 的 Kibana 数据视图。对于事务,请在 Kibana 中使用自定义格式化程序,并将其格式设置为“微秒”中的“持续时间”。

这样,一切都安排好了,可以开始了。

观察基础设施变化

下面我创建了一个仪表板,使我们能够识别

  • 某个服务产生多少成本
  • CPU 使用率
  • 内存使用率
  • 事务持续时间
  • 识别节省成本的潜力

从左到右,我们希望关注第一个图表。我们看到柱状图表示 CPU,其中绿色表示平均值,蓝色表示顶部的第 95 个百分位数。它从 0 到 100% 不等,并且已归一化,这意味着即使有 8 个 CPU 内核,它仍然会读取 100% 的使用率,而不是 800%。折线图表示事务持续时间,其中红色表示平均值,紫色表示第 95 个百分位数。最后,我们在底部有一个橙色区域,它是该主机上的平均内存使用率。

我们立即意识到我们的计算器不需要大量的内存。将鼠标悬停在图表上会显示 2.89% 的内存使用率。我们正在使用的 e2-standard-8 机器有 32 GB 的内存。在第 95 个百分位数时,我们偶尔会将 CPU 使用率飙升至 100%。当这种情况发生时,我们看到平均事务持续时间飙升至 2.5 毫秒。但是,这台机器每小时花费我们大约 30 美分。利用这些信息,我们现在可以缩小规模以更好地适应。平均 CPU 使用率约为 11-13%,而第 95 个百分位数并没有那么远。

因为我们正在使用 8 个 CPU,所以现在可以说 12.5% 代表一个完整的核心,但这只是纸上谈兵的假设。尽管如此,我们知道有很大的余地,我们可以大幅缩减规模。在这种情况下,我决定使用 2 个 CPU 和 2 GB 的 RAM,即 e2-highcpu2。这应该更适合我的计算器应用程序。我们几乎没有触及 RAM,32GB 中的 2.89% 大约是 1GB 的使用量。在更改并重启计算器机器后,我启动了相同的 Locust 测试,以确定我的 CPU 使用率,更重要的是,我的事务是否变慢,如果变慢,则变慢多少。最终,我想决定 1 毫秒的额外延迟是否值得每小时多支付 10 美分。我将此更改作为 Lens 中的注释添加。

在运行一段时间后,我们现在可以确定较小主机的影响。在这种情况下,我们可以看到平均值没有变化。但是,第 95 个百分位数(即 95% 的事务都低于此值)确实飙升了。再次,乍一看很糟糕,但检查一下,它从 ~1.5 毫秒增加到 ~2.10 毫秒,增加了 ~0.6 毫秒。现在,您可以决定这 0.6 毫秒的增加是否值得每月多支付约 180 美元,或者当前的延迟是否足够好。

结论

可观测性不仅仅是收集日志、指标和跟踪。将用户体验与云成本联系起来,可以让您的企业识别出可以省钱的领域。拥有合适的工具可以帮助您快速生成这些见解。就如何优化云成本并最终改善用户体验做出明智的决策是最终目标。

仪表板和数据视图可以在我的 GitHub 存储库中找到。您可以下载 .ndjson 文件,并使用 Kibana 中 Stack Management 内的 Saved Objects 导入它。

注意事项

定价仅适用于不包含任何磁盘信息、静态公共 IP 地址以及任何其他额外成本(例如操作系统许可)的基础机器。此外,它不包括竞价定价、折扣或免费信用额度。此外,服务之间的数据传输成本也不包括在内。我们仅根据服务运行的分钟费率进行计算 — 我们不检查 Google Cloud 的计费间隔。在我们的例子中,无论 Google Cloud 有什么,我们都会按分钟计费。使用唯一 instance.ids 的计数按预期工作。但是,如果一台机器只运行一分钟,我们将根据小时费率进行计算。因此,一台运行一分钟的机器,其成本与运行 50 分钟的机器相同 — 至少我们是这样计算的。转换使用日历小时间隔;因此,它是上午 8 点到上午 9 点,上午 9 点到上午 10 点,依此类推。

本帖子中描述的任何功能或特性的发布和时间安排仍由 Elastic 自行决定。任何当前不可用的功能或特性都可能无法按时交付,甚至根本无法交付。

分享这篇文章