使用ECS上的CloudWatch Agent将Prometheus指标聚合到CloudWatch Metrics中
我试图收集Prometheus(OpenMetrics)格式的指标公开端点的指标,并将收集到的指标值汇总到CloudWatch Metrics中,以便在仪表板上进行可视化和设置警报。
由于参考AWS文档等仍然很难想象各个设置的作用,并且很少有其他参考资料可用,因此我自己一直在努力理解和实施到我所设想的操作。
这样做似乎是在说它会动,但是我不知道这个设置具体起什么作用,也不知道该如何进行调试,所以出于“少再次遇到麻烦更好”的考虑,我写下了它的运作方式、设置方法以及设置选项的解释说明。
建筑设计(可能是这样的感觉)
我正在尝试创建一个配置,从WildFly(Java EE服务器)内置的指标终端收集数据,然后使用CloudWatch Agent将收集的指标值写入CloudWatch Logs。

CloudWatch代理的操作顺序图
-
- 根据 CloudWatch Agent 的配置,使用 AWSCLI 确认要收集 ECS Task 信息的指标。
-
- 收集指标:根据第一步中确认的 ECS Task 信息(如 IP 地址等),访问指标的端点来收集指标。
- 指标汇总:将收集的指标信息转换为能够写入 CloudWatch Metrics 的「嵌入式格式」,并将其放置在 CloudWatchLogs 上。
设定的步骤
-
- CloudWatch Agent を ECS Service としてデプロイする
-
- 収集したいメトリクスのエンドポイントを自動ディスカバリする設定をする
- 収集したメトリクスを CloudWatch Metrics に収集できる形にする
以ECS Service的形式部署CloudWatch Agent。
目标:以ECS Service的形式部署CloudWatch Agent容器(无需进行指标收集)。
任务滚动设置
为了实现服务发现(体系结构序列1),必须将以下任何IAMPolicy设置为任务角色。
-
- AWS管理策:CloudWatchAgentServerPolicy
允许以下自定义管理策略的操作:
ec2:DescribeInstances
ecs:ListTasks
ecs:ListServices
ecs:DescribeContainerInstances
ecs:DescribeServices
ecs:DescribeTasks
ecs:DescribeTaskDefinition
创建ECS任务定义
-
- DockerHub上也有容器映像,但免费使用时可能有拉取次数限制或带宽限制,因此建议使用ECR Public Repositories上公开的映像。
使用ECR Public的CloudWatch Agent容器映像: public.ecr.aws/cloudwatch-agent/cloudwatch-agent:1.247350.0b251780
空的CloudWatch Agent配置
将以下配置(JSON格式)作为环境变量CWAGENT_CONFIG加载。
CWAGENT_CONFIG
{
“agent”: {
“debug”: true // CloudWatch Agent将输出调试级别的日志(输出到标准输出)。
}
}
创建ECS服务
尝试将所需计数(起动任务数)设定为1,以创建仅仅启动(持续运行)CloudWatch代理任务的状态。
进行设置,以发现要收集的指标的终端节点。
在任务定义的地方,将CWAGENT_CONFIG中的JSON配置文件添加上用于发现Prometheus端点的设置。
{
"agent": {
"debug": true
},
"logs": {
"metrics_collected": {
"prometheus": {
// Prometheus の設定情報をファイル、または環境変数から読み出す設定
// ここでは、PROMETHEUS_CONFIG_CONTENT という環境変数から読み出している。設定の内容は後述。
"prometheus_config_path": "env:PROMETHEUS_CONFIG_CONTENT",
// ECS 上にデプロイされているメトリクス
"ecs_service_discovery": {
// CWAgent がディスカバリを行う間隔です (1m となっていれば、 1分毎に収集対象を更新してくれます)
"sd_frequency": "1m",
// ディスカバリした結果が設定ファイルとしてこのパスに出力される。
// 特に編集したりすることはないですが、どんなメトリクスエンドポイントがディスカバリされたかなどを確認するときはこのファイルを参照すると良いでしょう。
"sd_result_file": "/tmp/cwagent_ecs_auto_sd.yaml",
// ECSサービス名が正規表現パターンに一致したものに紐づくタスクを列挙してくれる設定です。他にも タスク定義のARNベースや、Dockerラベルベースで検出してくれるものもある。
"service_name_list_for_tasks": [
{
// メトリクスエンドポイントが公開されているポート番号
"sd_metrics_ports": "9990",
// ECSサービス名の正規表現パターン。このパターンにマッチしたものがすべて列挙される。(この場合は、全ての ECS Service がディスカバリ対象となります)
// ※ディスカバリ対象のECSクラスタ名を ecs_service_discovery の sd_target_cluster に書いていない場合は、デプロイされたクラスタが対象となる。
"sd_service_name_pattern": ".*",
// メトリクスのエンドポイントを公開しているパス
// /metrics の場合に限りこの設定を省略することもできるようです。
"sd_metrics_path": "/metrics"
}
]
}
}
}
}
}
需要在CWAGENT_CONFIG中设置PROMETHEUS_CONFIG_CONTENT环境变量的配置选项。
这是Prometheus的配置本身,CloudWatch Agent将读取并使用此配置。
global:
# メトリクスエンドポイントをスクレイピングする間隔
# 1m となっている場合は、 1分毎に メトリクスエンドポイント、今回の場合は /metrics をスクレイピングしてくれる
scrape_interval: 1m
# スクレイピングするときのタイムアウト設定
# 10s となっている場合は、1つのメトリクスエンドポイントに対してのスクレイピングの開始から10秒以内に応答しない場合はタイムアウトしてメトリクスの収集を中断する
scrape_timeout: 10s
scrape_configs:
# スクレイピングする設定をジョブ名といったキーで管理する
- job_name: wildfly-metrics
# 1つのメトリクスエンドポイントから収集するメトリクスの最大数
sample_limit: 10000
将上述的环境变量设置为任务定义后,启用CloudWatch Agent,将执行服务发现,并将从发现的指标端点收集的指标输出到CloudWatch Logs日志组/aws/ecs/containerinsights/{CLUSTER_NAME}/prometheus。请注意,这是一个不同于CWAgent任务本身日志驱动程序(如awslogs)传输的日志组,仅写入收集的指标信息的日志组。默认情况下,它将使用上述日志组名称,但据说也可以使用CWAGENT_CONFIG指定日志组。
如果在这个日志组中没有输出,那么表示未能收集到指标数据,需要按以下顺序检查是否存在问题。
-
- 使用 AWSCLI 搜索 CloudWatch Agent 任务日志,并且如果成功检测到,应该在旁边记录一个大于等于1的数字。
-
- 如果不大于1,则请确认设置在 sd_metrics_ports 上的端口是否被发布。
- 如果大于等于1,则请确认无法访问到指标端点的端口的访问权限是否受到已发现任务的安全组等限制。
将收集到的度量指标整理成可以汇总到CloudWatch Metrics的形式。
在之前的步骤中,我们只是收集指标并将其直接流向日志组,而不会被记录到 CloudWatch Metrics 中。因此,我们需要在 CWAGENT_CONFIG 中添加配置,并将要记录的命名空间和维度写入 emf_processor 部分。
假设在指标终端点上公开了如下所示的指标。
# HELP base_gc_total Displays the total number of collections that have occurred. This attribute lists -1 if the collection count is undefined for this collector.
# TYPE base_gc_total counter
base_gc_total{name="G1 Old Generation"} 0.0
base_gc_total{name="G1 Young Generation"} 34.0
目前,关于上述指标的度量信息被输出到了输出日志组中,其值如下所示。
{
"ClusterName": "demo",
"LaunchType": "FARGATE",
"ServiceName": "wildfly-demo",
"StartedBy": "ecs-svc/123456789",
"TaskClusterName": "demo",
"TaskDefinitionFamily": "wildfly-demo",
"TaskGroup": "service:wildfly-demo",
"TaskId": "xxxxxx",
"TaskRevision": "2",
"Timestamp": "1648540305048",
"Version": "0",
"base_gc_total": 4,
"container_name": "wildfly",
"instance": "10.123.123.123:9990",
"job": "demo",
"name": "G1 Old Generation",
"prom_metric_type": "counter"
}
将上述的指标信息配置到 CloudWatch Metrics 中以便写入的 emf_processor 进行设置。
{
"agent": {
"debug": true
},
"logs": {
"metrics_collected": {
"prometheus": {
"prometheus_config_path": "env:PROMETHEUS_CONFIG_CONTENT",
"ecs_service_discovery": { /** 省略 */ }
},
"emf_processor": {
"metric_declaration_dedup": true,
"metric_declaration": [
{
"source_labels": [
"^base_gc" // base_gc から始まるメトリクス名があるものについて下記の設定に従い、
],
// 下記のディメンションで記録させる
"dimensions": [
[
"ClusterName", // 上記メトリクスログにおける ClusterName の値をディメンションに設定
"ServiceName", // 上記メトリクスログにおける ServiceName の値をディメンションに設定
"GC" // メトリクスログに GC という項目 がないが、name の値を GC として記録したい。(設定方法は後述)
]
],
// メトリクス値として記録する値のメトリクス名
"metric_selectors": [
"^base_gc"
]
},
]
}
}
}
}
在 PROMETHEUS_CONFIG_CONTENT 的 metric_relabel_configs 中添加一个设置,将日志输出的 name 的值重命名为 GC。
global:
scrape_interval: 1m
scrape_timeout: 10s
scrape_configs:
- job_name: demo
sample_limit: 10000
file_sd_configs:
- files: ["/tmp/cwagent_ecs_auto_sd.yaml"]
metric_relabel_configs:
- source_labels: ["name"] # name の項目を
target_label: GC # GC に置き変える (source_labels は ["name", base_gc_total"] としたほうがいい可能性がある。replacement: $1 という設定が省略されているが、 source_labels の第一引数の値を置換えてくれる模様。
通过此设置,将生成以下输出,并在 CloudWatch Metrics 中使用上述设定的维度进行记录。
{
"CloudWatchMetrics": [
{
"Metrics": [
{
"Name": "base_gc_total"
}
],
"Dimensions": [
[
"ClusterName",
"GC",
"ServiceName"
]
],
"Namespace": "ECS/ContainerInsights/Prometheus" // Namespace は特別設定しないとこの名前になる
}
],
"ClusterName": "demo",
"GC": "G1 Old Generation", // この項目が name と同じ内容で追加されている
"LaunchType": "FARGATE",
"ServiceName": "wildfly-demo",
"StartedBy": "ecs-svc/123456789",
"TaskClusterName": "demo",
"TaskDefinitionFamily": "wildfly-demo",
"TaskGroup": "service:wildfly-demo",
"TaskId": "xxxxxx",
"TaskRevision": "2",
"Timestamp": "1648540305048",
"Version": "0",
"base_gc_total": 4,
"container_name": "wildfly",
"instance": "10.123.123.123:9990",
"job": "demo",
"name": "G1 Old Generation",
"prom_metric_type": "counter"
}