亚马逊AWS官方博客

使用CloudWatch收集ECS Task自定义Prometheus指标

概述

在当今云原生应用程序的快速发展中,容器化已成为现代应用部署的标准方式。随着企业将越来越多的工作负载迁移到容器环境,尤其是在 Amazon ECS (Elastic Container Service) 这样的容器编排平台上,有效监控这些分布式系统的能力变得至关重要。企业在采用容器技术的同时,面临着前所未有的可观测性挑战,比如如何收集JVM/Nginx 等 Prometheus特定指标。在AWS 有两种收集Prometheus 指标的方式,一种是通过ADOT(AWS Distro for OpenTelemetry),ADOT支持service 的方式运行在ECS 集群,这种方式不支持收集应用内部的指标比如JVM,具体详见链接1;ADOT 支持另外一种SideCar 的部署方式,此方式支持收集JVM 等应用指标,但是部署方式对应用有一定的侵入性。另外一种方案是通过Amazon CloudWatch Agent来收集应用内部的指标,CloudWatch Agent 支持service 的方式运行,能与Container Insights和其他AWS 生态系统深度集成,可以实现:

  1. 全面掌握容器健康状况:从基础设施到应用层面的端到端可见性,包括 CPU、内存利用率、网络流量等基础指标,以及应用特定的自定义指标。
  2. 简化多服务架构监控:在微服务架构中,一个应用可能由数十甚至数百个服务组成,每个服务都需要单独监控。通过 Prometheus 集成,可以统一收集和分析这些数据。
  3. 实现主动式问题排查:通过设置基于 Prometheus 指标的告警,运维团队可以在问题影响用户体验之前发现并解决它们。

预构建某些工作负载的dashboards

对于EKS ,EC2 和ECS 都要预构建好的指标和dashboard,比如以下的ECS dashboard:

  • AWS App Mesh (服务网格)
  • Java/JMX Java/JMX应用
  • NGINX(Nginx应用)
  • NGINX Plus (Nginx Plus应用)

但是通常客户有自定义指标的需求,比如jvm_gc_collection 等指标,这时候就需要配置CloudWatch agent 的自定义指标收集,接下来我们将通过一个例子来演示配置过程。

工作原理

Amazon ECS Container Insights 集成 Prometheus 监控,通过 CloudWatch Agent 自动发现和采集容器化应用的 Prometheus 指标。

  1. 应用侧需要通过prometheus 各种exporter 来把指标主动暴露出来,比如接下来我们将使用的JMX_exporter
  2. 我们安装好CloudWatch agent service,并授予适当的CloudWatch agent ECS task role使其实现ECS cluster的自动服务发现
  3. 准备好‘PROMETHEUS_CONFIG_CONTENT’和‘CloudWatch_CONFIG_CONTENT’配置,用于指标抓取和过滤
  4. CloudWatch agent把抓取过来的EMF 格式存储在CloudWatch logs,然后再转换成prometheus 的metric 展示在CloudWatch 面板

Container Insights 支持 Prometheus 指标的以下启动类型和网络模式组合:

Amazon ECS launch type Network modes supported
EC2 (Linux) bridge, host, and awsvpc
Fargate awsvpc

实施过程

通过Cloudformation 安装CloudWatch agent

# 设置环境变量
export AWS_PROFILE=your_aws_config_profile_eg_default
export AWS_DEFAULT_REGION=your_aws_region_eg_ap-southeast-1
export ECS_CLUSTER_NAME=your_ec2_ecs_cluster_name
export ECS_LAUNCH_TYPE=FARGATE
export CREATE_IAM_ROLES=True
export ECS_CLUSTER_SECURITY_GROUP=your_security_group_eg_sg-xxxxxxxxxx
export ECS_CLUSTER_SUBNET=your_subnet_eg_subnet-xxxxxxxxxx
export ECS_TASK_ROLE_NAME=ecsTaskExecutionRole
export ECS_EXECUTION_ROLE_NAME=ecsExecutionRole   

# 下载模板
curl -O https://raw.githubusercontent.com/aws-samples/amazon-cloudwatch-container-insights/latest/ecs-task-definition-templates/deployment-mode/replica-service/cwagent-prometheus/cloudformation-quickstart/cwagent-ecs-prometheus-metric-for-awsvpc.yaml

# 创建 CloudFormation 堆栈
aws cloudformation create-stack \
    --stack-name CWAgent-Prometheus-ECS-${ECS_CLUSTER_NAME}-${ECS_LAUNCH_TYPE}-awsvpc \
    --template-body file://cwagent-ecs-prometheus-metric-for-awsvpc.yaml \
    --parameters \
        ParameterKey=ECSClusterName,ParameterValue=${ECS_CLUSTER_NAME} \
        ParameterKey=CreateIAMRoles,ParameterValue=${CREATE_IAM_ROLES} \
        ParameterKey=ECSLaunchType,ParameterValue=${ECS_LAUNCH_TYPE} \
        ParameterKey=SecurityGroupID,ParameterValue=${ECS_CLUSTER_SECURITY_GROUP} \
        ParameterKey=SubnetID,ParameterValue=${ECS_CLUSTER_SUBNET} \
        ParameterKey=TaskRoleName,ParameterValue=${ECS_TASK_ROLE_NAME} \
        ParameterKey=ExecutionRoleName,ParameterValue=${ECS_EXECUTION_ROLE_NAME} \
    --capabilities CAPABILITY_NAMED_IAM \
    --region ${AWS_DEFAULT_REGION}

部署完后会创建两个Role

CloudWatch agent自动发现ECS cluster

Prometheus 社区有很多自动发现的方法,详见<scrape_config>, 但是对于ECS cluster,我们需要CloudWatch agent 定期访问ECS cluster 的控制平面API 实现Task 的自动发现,他支持3种服务发现的模式:

  • Container docker label-based 自动发现
  • ECS task definition ARN 正则表达式自动发现
  • ECS service name 正则表达式自动发现

可以同时配置3种模式,CloudWatch Agent 可以开启de-duplicates 做指标的去重

去重机制: 基于 {private_ip}:{port}/{metrics_path}

在控制台Systems Manager > Parameter Store 创建 prometheus-config 的参数:

global:
  scrape_interval: 1m
  scrape_timeout: 10s
scrape_configs:
  - job_name: cwagent-ecs-file-sd-config //名字需要跟cwagent-config mapping
    sample_limit: 10000
    file_sd_configs:
      - files: [ "/tmp/cwagent_ecs_auto_sd.yaml" ] //路径需要跟cwagent-config mapping

配置CloudWatch Agent yaml

在控制台Systems Manager > Parameter Store 创建 cwagent-config 的参数:

{
  "agent": {
    "debug": true
  },
  "logs": {
    "metrics_collected": {
      "prometheus": {
        "cluster_name": "testecs", //必须要有,用于过滤
        "log_group_name": "/aws/ecs/containerinsights/testecs/prometheus",//必须要有,用于过滤
        "prometheus_config_path": "env:PROMETHEUS_CONFIG_CONTENT",
        "ecs_service_discovery": {
          "sd_frequency": "1m",
          "sd_result_file": "/tmp/cwagent_ecs_auto_sd.yaml", //名字跟上面的files匹配
          "task_definition_list": [
            {
              "sd_task_definition_arn_pattern": ".*:task-definition/*"  //使用task definition ARN 的正则匹配,可以根据业务要求做task 的过滤
            }
          ]
        },
        "emf_processor": {
          "metric_declaration_dedup": true, //启用自动去重
          "metric_declaration": [
            {
              "source_labels": ["job"],  //这个job 就是job_name 
              "label_matcher": "cwagent-ecs-file-sd-config",  //名字需要跟job_name 匹配
              "dimensions": [["ClusterName","TaskDefinitionFamily"]], //不同指标dimension不同
              "metric_selectors": [   //这里添加自定义指标,名字需要跟jmx_exporter暴露的名字一致
                "^jvm_threads_current$",
                "^jvm_threads_peak$",
               "^jvm_gc_collection_seconds_count$",
               "^jvm_gc_collection_seconds_sum$",
               "^jvm_classes_currently_loaded$",
               "^jvm_memory_max_bytes$",
               "^jvm_memory_used_bytes$"
               
              ]
            },
            {
              "source_labels": ["job"],
              "label_matcher": "cwagent-ecs-file-sd-config",
              "dimensions": [["ClusterName","TaskDefinitionFamily","pool"]],
              "metric_selectors": [
                "^jvm_memory_pool_max_bytes$",
                "^jvm_memory_pool_used_bytes$",
                "^jvm_memory_pool_committed_bytes$",
              ]
            }
          ]
        }
      }
    },
    "force_flush_interval": 5
  }
}

准备测试用的Java 应用

从Github repo下载相关文件,Docker file 如下, 其中9404 端口用于prometheus 指标暴露:

# 使用OpenJDK 17作为基础镜像
FROM openjdk:17-jre-slim

LABEL description="Simple Java Web Application with JMX Exporter for ECS"

# 创建应用目录
RUN mkdir -p /opt/app

# 设置工作目录
WORKDIR /opt/app

# 复制JMX Exporter jar文件
COPY jmx_prometheus_javaagent-0.20.0.jar /opt/app/

# 复制Web应用程序jar文件(使用80端口版本)
COPY simple-web-app.jar /opt/app/

# 复制JMX配置文件
COPY config.yaml /opt/app/

# 安装curl用于健康检查
RUN apt-get update && \
apt-get install -y curl && \
rm -rf /var/lib/apt/lists/* && \
apt-get clean

# 创建非root用户
RUN groupadd -r appuser && useradd -r -g appuser appuser
RUN chown -R appuser:appuser /opt/app
USER appuser

# 暴露端口
EXPOSE 80 9404

# 设置健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD curl -f http://localhost/health || exit 1

# 启动命令
CMD ["java", "-javaagent:jmx_prometheus_javaagent-0.20.0.jar=9404:config.yaml", "-jar", "simple-web-app.jar"]

其中config.yaml ,当前是暴露所有的指标(pattern: “.*”),如果需要做指标rename 和 过滤,可以适当修改

jmx_prometheus_javaagent-0.20.0.jar 可以从prometheus 官网下载,不同版本可能指标名字会不一样

镜像推送到AWS ECR

# 登录ECR
aws ecr get-login-password --region ap-eastsouth-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.ap-southeast-1.amazonaws.com
# 创建repo
aws ecr create-repository --repository-name my-application --region ap-southeast-1
# 构建镜像
docker build -t my-application:latest .
# 镜像打tag
docker tag my-application:latest 123456789012.dkr.ecr.ap-southeast-1.amazonaws.com/my-application:latest
# push 镜像
docker push 123456789012.dkr.ecr.ap-southeast-1.amazonaws.com/my-application:latest

创建Task definition 并运行Task

{
    "taskDefinitionArn": "arn:aws:ecs:ap-southeast-1:your-id:task-definition/simple-web-app-task:3",
    "containerDefinitions": [
        {
            "name": "jmxsample-container",
            "image": "your-id.dkr.ecr.ap-southeast-1.amazonaws.com/simple-web-app",
            "cpu": 0,
            "portMappings": [
                {
                    "containerPort": 80,
                    "hostPort": 80,
                    "protocol": "tcp"
                },
                {
                    "containerPort": 9404,
                    "hostPort": 9404,
                    "protocol": "tcp"
                }
            ],
            "essential": true,
            "environment": [],
            "mountPoints": [],
            "volumesFrom": [],
            "dockerLabels": {
                "Java_EMF_Metrics": "true"
            },
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "/ecs/simple-web-app",
                    "awslogs-region": "ap-southeast-1",
                    "awslogs-stream-prefix": "ecs"
                }
            },
            "systemControls": []
        }
    ],
    "family": "simple-web-app-task",
    "executionRoleArn": "arn:aws:iam::your-id:role/ecsTaskExecutionRole",
    "networkMode": "awsvpc",
    "revision": 3,
    "volumes": [],
    "status": "ACTIVE",
    "requiresAttributes": [
        {
            "name": "com.amazonaws.ecs.capability.logging-driver.awslogs"
        },
        {
            "name": "ecs.capability.execution-role-awslogs"
        },
        {
            "name": "com.amazonaws.ecs.capability.ecr-auth"
        },
        {
            "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19"
        },
        {
            "name": "ecs.capability.execution-role-ecr-pull"
        },
        {
            "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18"
        },
        {
            "name": "ecs.capability.task-eni"
        }
    ],
    "placementConstraints": [],
    "compatibilities": [
        "EC2",
        "FARGATE"
    ],
    "requiresCompatibilities": [
        "FARGATE"
    ],
    "cpu": "256",
    "memory": "512",
    "registeredAt": "2025-07-15T12:38:39.844Z",
    "registeredBy": "arn:aws:iam::your-id:user/iamuser",
    "tags": []
}

Task 安全组需要放开80 和 9404端口

运行Task

等Task 运行后,在控制台找到Task 的私网IP

测试指标是否能暴露

CloudWatch 验证metric 查看

可以在控制台看到有新的ECS/ContainerInsights/Prometheus namespace

点击进去不同的维度,可以看到指标已经收集上来

费用

自定义指标的费用,详情参考官网链接

总结

本文讲述了在AWS ECS 收集prometheus 指标的方法和原理,AWS Container Insights 可以收集预定义的部分prometheus指标,当需要收集自定义指标的时候需要手动配置CloudWatch agent,本文通过一个简单的Java Web 应用演示如何通过jmx exporter 收集JMX 自定义指标。基于此方案, 合作伙伴成功帮助制造业头部客户‘雅马哈发动机’在Amazon ECS 上实现了prometheus 指标监控方案的优化。

参考链接:

https://aws-otel.github.io/docs/getting-started/collector/sidecar-vs-service

https://github.com/aws-samples/prometheus-for-ecs

https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/ContainerInsights-Prometheus-install-ECS.html

https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/ContainerInsights-Prometheus-Setup-configure-ECS.html

https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/ContainerInsights-Prometheus-metrics.html#ContainerInsights-Prometheus-metrics-jmx

*前述特定亚马逊云科技生成式人工智能相关的服务目前在亚马逊云科技海外区域可用。亚马逊云科技中国区域相关云服务由西云数据和光环新网运营,具体信息以中国区域官网为准。

本篇作者

林展望

雅马哈发动机(厦门)信息系统有限公司-创新中心部门,资深Java研发工程师,负责公司开发平台架构设计和开发,最近研究AWS云端环境相关开发、部署和监控。

林国彬

厦门汉为软件技术有限公司资深解决方案架构师。专注于为客户提供基于AWS的云技术咨询服务,在云解决方案设计和成本优化方面拥有丰富经验,能够帮助企业高效利用AWS资源,实现业务目标的同时控制云支出。

张振威

AWS APN 解决方案架构师,主要负责合作伙伴架构咨询和方案设计,同时致力于 AWS 云服务和生成式AI 在国内的应用及推广。