Lambda Promtail 客户端
Grafana Loki 包括 Terraform 和 CloudFormation,用于通过 lambda 函数将 Cloudwatch、Cloudtrail、VPC Flow Logs 和负载均衡器日志发送到 Loki。这是通过 lambda-promtail 完成的,它处理 Cloudwatch 事件,并通过 push-api scrape 配置将其传播到 Loki(或 Promtail 实例)。
部署
lambda-promtail 可以通过提供的 Terraform 和 CloudFormation 文件轻松部署。Terraform 部署还会拉取 variables.tf 中定义的变量值。
对于这两种部署类型,必须定义几个值
- 写入地址,一个与 Loki Write API 兼容的端点(Loki 或 Promtail)
- 如果写入地址是 Loki 端点且需要认证,则为 basic auth 用户名/密码
- lambda-promtail 镜像,完整的 ECR repo 路径:tag
Terraform 部署还接受日志组和存储桶名称的数组,并且可以接受 VPC 子网和安全组的数组。
还有一个标志用于在从 Cloudwatch 传播日志时保留日志流标签,该标志默认为 false。当基数过大时(例如每个 lambda 调用一个日志流),这会很有帮助。
此外,可以配置一个环境变量以向 lambda-promtail 流式传输的日志添加额外标签。这些额外标签将采用 __extra_<name>=<value>
的形式。
可以配置一个可选的环境变量以向 lambda-promtail 流式传输的日志添加租户 ID。
为了尽可能简化 lambda-promtail 的部署,我们创建了一个 公共 ECR repo 来发布我们的 lambda-promtail 构建版本。用户可以克隆此 repo,对 Go 代码进行自己的修改,并将自己的镜像上传到自己的 ECR repo。
示例
Terraform
## use cloudwatch log group
terraform apply -var "lambda_promtail_image=<repo:tag>" -var "write_address=https://logs-prod-us-central1.grafana.net/loki/api/v1/push" -var "password=<password>" -var "username=<user>" -var 'log_group_names=["/aws/lambda/log-group-1", "/aws/lambda/log-group-2"]' -var 'bucket_names=["bucket-a", "bucket-b"]' -var 'batch_size=131072'
## use kinesis data stream
terraform apply -var "<ecr-repo>:<tag>" -var "write_address=https://your-loki-url/loki/api/v1/push" -var "password=<basic-auth-pw>" -var "username=<basic-auth-username>" -var 'kinesis_stream_name=["kinesis-stream-01", "kinesis-stream-02"]' -var 'extra_labels="name1,value1,name2,value2"' -var "tenant_id=<value>"
main.tf
的前几行定义了部署到的 AWS 区域。您可以根据需要修改,或者删除并部署到
provider "aws" {
region = "us-east-2"
}
要保留日志组标签,请添加 -var "keep_stream=true"
。
要添加额外标签,请添加 -var 'extra_labels="name1,value1,name2,value2"'
。
要添加租户 ID,请添加 -var "tenant_id=value"
。
请注意,提供的 Terraform 文件中创建 Cloudwatch 订阅过滤器只接受日志组名称的数组。它不接受通过订阅过滤器对日志内容进行 regex 过滤的字符串。我们建议扩展 Terraform 文件以实现此功能。或者,让 lambda-promtail 写入 Promtail 并使用 pipeline 阶段。
CloudFormation
aws cloudformation create-stack --stack-name lambda-promtail --template-body file://template.yaml --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM --region us-east-2 --parameters ParameterKey=WriteAddress,ParameterValue=https://logs-prod-us-central1.grafana.net/loki/api/v1/push ParameterKey=Username,ParameterValue=<user> ParameterKey=Password,ParameterValue=<password> ParameterKey=LambdaPromtailImage,ParameterValue=<repo:tag>
在 CloudFormation 模板文件中,根据需要为每个日志组复制、粘贴和修改订阅过滤器部分
MainLambdaPromtailSubscriptionFilter:
Type: AWS::Logs::SubscriptionFilter
Properties:
DestinationArn: !GetAtt LambdaPromtailFunction.Arn
FilterPattern: ""
LogGroupName: "/aws/lambda/some-lamda-log-group"
要保留日志组标签,请添加 ParameterKey=KeepStream,ParameterValue=true
。
要添加额外标签,请包含 ParameterKey=ExtraLabels,ParameterValue="name1,value1,name2,value2"
。
要添加租户 ID,请添加 ParameterKey=TenantID,ParameterValue=value
。
要修改现有的 CloudFormation 栈,请使用 update-stack。
如果使用 CloudFormation 编写基础设施代码,您应该考虑基于 EventBridge 的解决方案,以便更轻松地部署。
用途
临时作业
此工作流旨在成为一种有效的方法,用于监测临时作业,例如在 AWS Lambda 上运行的那些作业,否则很难/不可能通过其他 Loki 客户端之一进行监测。
临时作业很容易违反基数最佳实践。在高请求负载期间,AWS lambda 函数可能会并行膨胀,在 Cloudwatch 中创建许多日志流。因此,lambda-promtail 默认在将日志传播到 Loki 时不保留日志流值作为标签。这之所以成为可能,是因为新版本的 Loki 不再对单个流内的日志施加摄取顺序约束。
概念验证 Loki 部署
对于那些使用 Cloudwatch 并希望以低风险方式测试 Loki 的用户,此工作流允许将 Cloudwatch 日志传输到 Loki,无论事件源是什么(EC2、Kubernetes、Lambda、ECS 等),而无需在其基础设施上设置一组 Promtail 守护程序。然而,从长远来看,将 Promtail 作为守护程序在您的基础设施上运行是实现灵活性、可靠性、性能和成本的最佳实践部署策略。
注意
将日志从 Cloudwatch 传播到 Loki 意味着您仍然需要为 Cloudwatch 付费。
VPC 流日志
此工作流允许从 S3 摄取 AWS VPC 流日志。
需要注意的一点是,默认流日志格式没有时间戳,因此日志时间戳将设置为 lambda 开始处理日志文件的时间。
负载均衡器日志
此工作流允许将存储在 S3 上的 AWS Application/Network Load Balancer 日志摄取到 Loki。
Cloudtrail 日志
此工作流允许将存储在 S3 上的 AWS Cloudtrail 日志摄取到 Loki。
Cloudfront 日志
Cloudfront 日志可以批量处理或实时流式传输到 Loki
- 可以在 Cloudfront distribution 上激活日志记录,将 S3 存储桶作为目标。在这种情况下,工作流与其他服务(VPC 流日志、负载均衡器日志、Cloudtrail 日志)相同。
- Cloudfront 实时日志可以发送到 Kinesis 数据流。数据流可以映射为 lambda-promtail 的事件源,以将日志发送到 Loki。
通过 SQS 触发 Lambda-Promtail
对于支持向 SQS 发送消息的 AWS 服务(例如,通过 S3 Notification 到 SQS 的 S3),可以使用 lambda 触发器通过 SQS 队列处理事件,而不是直接配置源服务触发 lambda。Lambda-promtail 将从 SQS 消息体中检索嵌套事件,并像它们直接来自源服务一样处理它们。
使用 SQS 进行失败日志恢复
通过 SQS 触发 lambda-promtail 允许使用辅助 SQS 队列作为死信队列 (DLQ) 处理失败日志的恢复。您可以配置 lambda,以便将未成功处理的消息发送到 DLQ。解决问题后,操作员将能够使用 SQS DLQ redrive 功能将消息从 DLQ 发送回源队列,从而重新处理这些消息。
基于 S3 的日志记录和 CloudFormation
Lambda-promtail 允许您从使用 S3 作为日志目标的不同服务(ALB、VPC Flow、CloudFront access logs 等)发送日志。为此,您需要配置 S3 存储桶通知来触发 lambda-promtail 部署。然而,当使用 CloudFormation 编码基础设施时,在同一栈中配置 AWS::S3::BucketNotification
和将由通知触发的资源时存在已知问题。
为了解决这个问题,AWS 推出了基于 Event Bridge 的 S3 事件通知。当在 S3 存储桶中创建对象时,会向 EventBridge bus 发送事件,您可以创建规则将这些事件发送到 Lambda-promtail。
下图显示了通知日志如何从源服务写入 S3 存储桶。从那里开始,S3 存储桶将向 EventBridge default
bus 发送 Object created
通知,我们可以在其中配置规则来触发 Lambda Promtail。

template-eventbridge.yaml
CloudFormation 模板使用 EventBridge 配置 Lambda-promtail,以解决此已知问题。要部署模板,请使用下面的代码片段,适当完成 ParameterValue
参数。
aws cloudformation create-stack \
--stack-name lambda-promtail-stack \
--template-body file://template-eventbridge.yaml \
--capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
--region us-east-2 \
--parameters ParameterKey=WriteAddress,ParameterValue=https://your-loki-url/loki/api/v1/push ParameterKey=Username,ParameterValue=<basic-auth-username> ParameterKey=Password,ParameterValue=<basic-auth-pw> ParameterKey=BearerToken,ParameterValue=<bearer-token> ParameterKey=LambdaPromtailImage,ParameterValue=<ecr-repo>:<tag> ParameterKey=ExtraLabels,ParameterValue="name1,value1,name2,value2" ParameterKey=TenantID,ParameterValue=<value> ParameterKey=SkipTlsVerify,ParameterValue="false" ParameterKey=EventSourceS3Bucket,ParameterValue=<S3 where target logs are stored>
传播的标签
传入日志可以分配七个特殊标签,可在 relabeling 或 Promtail pipeline 的后续阶段使用
__aws_log_type
:此日志的来源(Cloudwatch、Kinesis 或 S3)。__aws_cloudwatch_log_group
:此日志关联的 Cloudwatch Log Group。__aws_cloudwatch_log_stream
:此日志关联的 Cloudwatch Log Stream(如果KEEP_STREAM=true
)。__aws_cloudwatch_owner
:此事件所有者的 AWS ID。__aws_kinesis_event_source_arn
:Kinesis 事件源 ARN。__aws_s3_log_lb
:负载均衡器名称。__aws_s3_log_lb_owner
:负载均衡器所有者的账户 ID。
Relabeling 配置
Lambda-promtail 通过 RELABEL_CONFIGS
环境变量支持 Prometheus 风格的 relabeling。这允许您在将日志发送到 Loki 之前修改、保留或丢弃标签。配置以 relabel 配置的 JSON 数组形式提供。relabeling 功能遵循与 Prometheus relabeling 相同的原理——关于 relabeling 工作原理的详细解释,请参阅 Prometheus relabeling 工作原理。
配置示例
- 重命名标签并捕获 regex 组
{
"RELABEL_CONFIGS": [
{
"source_labels": ["__aws_log_type"],
"target_label": "log_type",
"action": "replace",
"regex": "(.*)",
"replacement": "${1}"
}
]
}
- 仅保留特定日志类型(有助于过滤)
{
"RELABEL_CONFIGS": [
{
"source_labels": ["__aws_log_type"],
"regex": "s3_.*",
"action": "keep"
}
]
}
- 丢弃内部 AWS 标签(清理)
{
"RELABEL_CONFIGS": [
{
"regex": "__aws_.*",
"action": "labeldrop"
}
]
}
- 多个 relabeling 规则(组合不同操作)
{
"RELABEL_CONFIGS": [
{
"source_labels": ["__aws_log_type"],
"target_label": "log_type",
"action": "replace",
"regex": "(.*)",
"replacement": "${1}"
},
{
"source_labels": ["__aws_s3_log_lb"],
"target_label": "loadbalancer",
"action": "replace"
},
{
"regex": "__aws_.*",
"action": "labeldrop"
}
]
}
支持的操作
支持以下操作,与 Prometheus relabeling 功能相匹配
replace
:使用 regex 捕获组替换标签值keep
:保留标签与 regex 匹配的条目(有助于过滤)drop
:丢弃标签与 regex 匹配的条目(有助于排除)hashmod
:将标签设置为标签哈希值的模(有助于分片)labelmap
:根据 regex 匹配将标签复制到其他标签labeldrop
:移除与 regex 模式匹配的标签labelkeep
:仅保留与 regex 模式匹配的标签lowercase
:将标签值转换为小写uppercase
:将标签值转换为大写
配置字段
每个 relabeling 配置支持这些字段(除 action
外,所有字段均为可选)
source_labels
:用作操作输入的标签名称列表separator
:用于连接源标签值的字符串(默认值:“;”)target_label
:要修改的标签(replace 和 hashmod 操作必需)regex
:要匹配的正则表达式(大多数操作默认为“(.+)”)replacement
:匹配到的 regex 的替换模式,支持 ${1}、${2} 等用于捕获组modulus
:hashmod 操作的模数action
:上述支持的操作之一
重要说明
- Relabeling 在合并额外标签和丢弃
DROP_LABELS
指定的标签后应用。 - 如果在 relabeling 后移除了所有标签,则日志条目将被完全丢弃。
- relabeling 配置遵循与 Prometheus 的
relabel_configs
相同的格式,这使得 Prometheus 用户熟悉它。 - Relabeling 规则按顺序处理,每个规则可以影响后续规则的输入。
regex
字段中的正则表达式支持完整的 RE2 语法。- 对于
replace
操作,如果regex
不匹配,则目标标签保持不变。
有关 relabeling 工作原理和高级用例的更多详细信息,请参阅Prometheus relabeling 博客文章。
限制
Promtail 标签
注意
本节适用于在 lambda-promtail 和最终 Loki 部署之间运行 Promtail 的情况,并且在 v2.4 Loki 发布之前用于规避乱序问题,该版本移除了排序限制。
如前所述,此工作流将最坏情况下的流基数从 number_of_log_streams 转移到 number_of_log_groups * number_of_promtails。因此,每个 Promtail 必须附加一个唯一的标签到其处理的日志(理想情况下通过 --client.external-labels=promtail=${HOSTNAME}
之类的参数),并且建议根据您的吞吐量和冗余需求在负载均衡器后面运行少量 Promtail 实例。
当您有大量日志流但希望按日志组聚合它们时,这种权衡非常有效。这在 AWS Lambda 中非常常见,其中日志组是“应用”,而日志流是单个应用容器,它们会随意启动和停止,可能只是为了单个函数调用。
数据持久性
可用性
为了解决可用性问题,请在负载均衡器后面运行一组 Promtail 实例。
批量处理
如果 lambda-promtail 配置为写入 Promtail,则此项相关。由于 Promtail 为提高性能而批量写入 Loki,因此 Promtail 可能会接收日志,为写入发出成功的 204
http 状态码,然后在稍后写入上游 Loki 之前被终止。这种情况应该很少见,但这是此工作流的一个缺点。
当批处理大小达到默认值 131072
(128KB) 时,此 lambda 将刷新日志,这可以通过 BATCH_SIZE
环境变量更改,该变量设置为要使用的字节数。
模板/部署
当前的 CloudFormation 模板是基础的。如果您需要添加 vpc 配置、额外要监测的日志组、子网声明等,您需要手动编辑模板。如果您需要订阅多个 Cloudwatch Log Group,您还需要为每个组复制粘贴模板的该部分。
Terraform 文件更为完善,可以配置为接受日志组和存储桶名称的数组以及 vpc 配置。
提供的 Terraform 和 CloudFormation 文件旨在涵盖默认用例,更复杂的部署可能需要对提供的文件进行一些修改和扩展。
Promtail 配置示例
注意
这应与附加的 Promtail 特定标签结合运行,理想情况下通过
--client.external-labels=promtail=${HOSTNAME}
等 flag 参数。它将通过 push-api 在端口3500
(http) 和3600
(grpc) 上接收写入。
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://ip_or_hostname_where_Loki_run:3100/loki/api/v1/push
scrape_configs:
- job_name: push1
loki_push_api:
server:
http_listen_port: 3500
grpc_listen_port: 3600
labels:
# Adds a label on all streams indicating it was processed by the lambda-promtail workflow.
promtail: 'lambda-promtail'
relabel_configs:
- source_labels: ['__aws_log_type']
target_label: 'log_type'
# Maps the cloudwatch log group into a label called `log_group` for use in Loki.
- source_labels: ['__aws_cloudwatch_log_group']
target_label: 'log_group'
# Maps the loadbalancer name into a label called `loadbalancer_name` for use in Loki.
- source_labels: ['__aws_s3_log_lb']
target_label: 'loadbalancer_name'
多个 Promtail 部署
免责声明:以下部分仅适用于无法接受乱序日志的旧版本 Loki。
然而,这些可能只会活跃很短时间。这给在 Loki 中组合这些短暂日志流带来了问题,因为时间戳在多个日志流之间可能不会严格递增。另一个显而易见的途径是基于日志流创建标签,这也不可取,因为它会通过许多低吞吐量日志流导致基数问题。
相反,我们可以将 Cloudwatch 日志通过 pipeline 传输到一组 Promtail 实例,这可以通过两种方式缓解这些问题
- 结合使用 Promtail 的 push api 和
use_incoming_timestamp: false
配置,我们让 Promtail 根据摄取日志的时间而不是 Cloudwatch 分配的时间戳来确定时间戳。显然,这意味着我们失去了原始时间戳,因为现在由 Promtail 分配,但在这种实时摄取系统中,这是一个相对较小的差异。 - 结合 (1),Promtail 可以合并跨 Cloudwatch 日志流的日志,因为它在组合多个源(lambda 调用)时不再容易出现乱序错误。
在使用负载均衡器后面的一组 Promtail 实例运行时,需要记住一个重要方面,那就是我们将基数问题有效地从日志流数量转移到 Promtail 数量。如果您尚未配置 Loki 接受乱序写入,则需要在每个 Promtail 上分配一个 Promtail 特定标签,以避免当 Promtail 实例为同一日志组向 Loki 发送数据时遇到乱序错误。这可以通过传递给 Promtail 的 --client.external-labels=promtail=${HOSTNAME}
之类的配置轻松完成。