【问题标题】:Timeout when writing custom metric data to CloudWatch with AWS lambda使用 AWS lambda 将自定义指标数据写入 CloudWatch 时超时
【发布时间】:2020-10-21 06:15:26
【问题描述】:

我正在运行一个普通的 AWS lambda 函数来计算我的 RabbitMQ 任务队列中的消息数量:

import boto3
from botocore.vendored import requests


cloudwatch_client = boto3.client('cloudwatch')


def get_queue_count(user="user", password="password", domain="<my domain>/api/queues"):
    url = f"https://{user}:{password}@{domain}"
    res = requests.get(url)
    message_count = 0
    for queue in res.json():
        message_count += queue["messages"]
    return message_count


def lambda_handler(event, context):
    metric_data = [{'MetricName': 'RabbitMQQueueLength', "Unit": "None", 'Value': get_queue_count()}]
    print(metric_data)
    response = cloudwatch_client.put_metric_data(MetricData=metric_data, Namespace="RabbitMQ")
    print(response)

在测试运行时返回以下输出:

Response:
{
  "errorMessage": "2020-06-30T19:50:50.175Z d3945a14-82e5-42e5-b03d-3fc07d5c5148 Task timed out after 15.02 seconds"
}

Request ID:
"d3945a14-82e5-42e5-b03d-3fc07d5c5148"

Function logs:
START RequestId: d3945a14-82e5-42e5-b03d-3fc07d5c5148 Version: $LATEST
/var/runtime/botocore/vendored/requests/api.py:72: DeprecationWarning: You are using the get() function from 'botocore.vendored.requests'.  This dependency was removed from Botocore and will be removed from Lambda after 2021/01/30. https://aws.amazon.com/blogs/developer/removing-the-vendored-version-of-requests-from-botocore/. Install the requests package, 'import requests' directly, and use the requests.get() function instead.
  DeprecationWarning
[{'MetricName': 'RabbitMQQueueLength', 'Value': 295}]
END RequestId: d3945a14-82e5-42e5-b03d-3fc07d5c5148

您可以看到我能够很好地与 RabbitMQ API 交互 - 尝试发布指标时该函数挂起。

lambda 函数使用 IAM 角色 put-custom-metric,该角色使用策略 recommended here 以及 CloudWatchFullAccess 进行良好衡量。

我的 RabbitMQ 服务器所在的内部负载均衡器上的资源受 VPN 保护,因此我有必要将此功能与适当的 VPC/安全组相关联。这是它现在的设置方式(我知道这是有效的,否则与 RabbitMQ 的通信将失败): 我阅读了this post,其中多个贡献者建议增加函数内存和超时设置。这两个我都做了,超时依然存在。

我可以在 5 秒内在 CloudWatch 上毫无问题地在本地运行该指标。

【问题讨论】:

  • 是否有理由使用 Lambda 将 RMQ 指标导出到 CloudWatch?您可以简单地使用 plugin 或简单地在 RMQ 节点上运行 cron 作业。
  • @noxdafox 啊,这很酷。因此,不是在 AWS 端进行黑客攻击,而是说我可以将 AWS 凭证放在我的 RMQ 服务器上并直接写入 cloudwatch?这绝对看起来是更清洁的解决方案。我在专用 VPC 端点方面取得了一些进展,但我可能会改变方向。我的应用程序是 dockerized,我使用 rabbitmq:3-management-alpine 进行 RMQ。有关在 docker 中实施您的方法的任何提示?我的第一个想法是编写一个使用 alpine 映像的自定义 Dockerfile,然后执行其余的配置。

标签: python-3.x amazon-web-services aws-lambda rabbitmq


【解决方案1】:

您可能想要使用 Lambda 函数来实现目标的唯一原因是您不拥有 RabbitMQ 集群。您的逻辑在通信期间挂起的事实表明存在网络问题,主要是由于安全组配置错误。

如果您可以更改集群配置,我建议您安装和配置 CloudWatch metrics exporter 插件,它可以为您完成大部分繁重的工作。

如果您的集群在 Docker 上运行,我相信自定义 Docker 文件是最佳解决方案。如果您通过 ECS/Fargate 在 AWS 中运行 Docker 实例,插件应该能够自动从 Task RoleExAws 推断凭证。否则,只需按照 README 说明自行设置凭据即可。

【讨论】:

  • 这看起来是正确的答案。我现在不在城里,但是当我周一回来时,我会试一试,并给你一个当之无愧的支持,假设这一切都解决了。
  • 问题是连接到您的 VPC 的 lambda 函数无法访问 AWS 云资源,包括 S3。解决方法似乎是 VPC 端点,但您的解决方案是 100% 更好。一个
  • 仍然没有启动并生活在 prod 中,但我现在正在使用已激活您的插件的容器运行本地测试,一切看起来都很好。使用 Docker 进行此操作有点痛苦——我在构建过程中尝试了从源代码安装,但遇到了各种 erlang 依赖问题——在复制和粘贴最新版本上所有 .es 文件的链接之前(这似乎是你推荐的)。用我的方法向您发送 PR,以防对其他人有帮助:github.com/noxdafox/rabbitmq-cloudwatch-exporter/compare/…
【解决方案2】:

@noxdafox 编写了一个出色的插件,让我大部分时间都成功了,但最终我最终还是选择了一个纯粹的基于 lambda 的解决方案。让云监控插件与 docker 一起运行非常棘手,而且在我遇到容器关闭其服务并停止处理消息队列的问题之后。此外,我希望能够通过我的 ECS 集群中的工作服务数量来标准化队列计数,因此无论如何我都需要从我的 VPC 中连接到至少一个 AWS 资源。我想最好把所有东西都简单地放在同一个地方。

import os
import boto3
from botocore.vendored import requests

USER = os.getenv("RMQ_USER")
PASSWORD = os.getenv("RMQ_PASSWORD")

cloudwatch_client = boto3.client(service_name='cloudwatch', endpoint_url="https://MYCLOUDWATCHURL.monitoring.us-east-1.vpce.amazonaws.com")
ecs_client = boto3.client(service_name='ecs', endpoint_url="https://vpce-MYECSURL.ecs.us-east-1.vpce.amazonaws.com")


def get_message_count(user=USER, password=PASSWORD, domain="rabbitmq.stockbets.io/api/queues"):
    url = f"https://{user}:{password}@{domain}"
    res = requests.get(url)
    message_count = 0
    for queue in res.json():
        message_count += queue["messages"]
    print(f"message count: {message_count}")
    return message_count


def get_worker_count():
    worker_data = ecs_client.describe_services(cluster="prod", services=["worker"])
    worker_count = worker_data["services"][0]["runningCount"]
    print(f"worker_count count: {worker_count}")
    return worker_count


def lambda_handler(event, context):
    message_count = get_message_count()
    worker_count = get_worker_count()
    print(f"msgs per worker: {message_count / worker_count}")
    metric_data = [
        {'MetricName': 'MessagesPerWorker', "Unit": "Count", 'Value': message_count / worker_count},
        {'MetricName': 'NTasks', "Unit": "Count", 'Value': worker_count}
    ]
    cloudwatch_client.put_metric_data(MetricData=metric_data, Namespace="RabbitMQ")

创建 VPC 端点比我想象的要容易。对于 Cloudwatch,您希望在创建步骤期间搜索“监控”VPC 端点(而不是“cloudwatch”或“日志”。搜索“ecs”可以获得 ECS 连接所需的内容。

一旦您的 lambda 是我们,您需要配置指标和伴随的警报,然后将它们与自动缩放策略相关联,但这可能超出了本文的范围。如果您对我的解决方法有任何疑问,请发表评论。

【讨论】:

    猜你喜欢
    • 2017-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-01
    • 1970-01-01
    • 2020-08-05
    • 1970-01-01
    相关资源
    最近更新 更多