在 AWS ECS 上运行 Promtail 客户端
ECS 是 Amazon 提供的全托管容器编排服务。结合 Fargate,您无需预置自己的计算资源即可运行容器工作负载。在本教程中,我们将介绍如何利用 AWS 日志路由器 Firelens 将所有日志和工作负载元数据转发到 Grafana Loki 实例。
注意
Promtail 已被废弃,并将在 2026 年 2 月 28 日前处于长期支持 (LTS) 阶段。Promtail 将在 2026 年 3 月 2 日达到生命周期结束 (EOL)。您可以在此处找到迁移资源。
在本教程之后,您将能够使用 Grafana 在一个地方查询所有日志。
要求
在我们开始之前,您需要
- 已配置 AWS CLI(运行
aws configure
)。 - 已配置 Loki 数据源的 Grafana 实例,您可以使用 GrafanaCloud 免费试用版。
- VPC 中可从互联网访问的子网。(如果需要创建,请按照这些说明操作)。
- 为您的容器选择一个安全组。(如果需要创建,请按照这些说明操作)。
为了简单起见,我们将使用 GrafanaCloud 的 Loki 和 Grafana 实例,您可以在我们的网站上获得本教程的免费帐户,但如果您运行的是自己的开源版 Loki 和 Grafana 实例,所有步骤都是相同的。
设置 ECS 集群
要在 ECS 上运行容器,您需要一个ECS 集群,我们将使用 Fargate 集群,但如果您更喜欢使用 EC2 集群,则给定的所有步骤仍然适用。
让我们使用 awscli 创建集群
aws ecs create-cluster --cluster-name ecs-firelens-cluster
我们还需要一个 IAM 角色来运行容器,让我们创建一个新角色并授权 ECS 承担此角色。
注意
如果您的 AWS 账户中已存在
ecsTaskExecutionRole
角色,则可以跳过此步骤。
curl https://raw.githubusercontent.com/grafana/loki/main/docs/sources/send-data/promtail/cloud/ecs/ecs-role.json > ecs-role.json
aws iam create-role --role-name ecsTaskExecutionRole --assume-role-policy-document file://ecs-role.json
{
"Role": {
"Path": "/",
"RoleName": "ecsTaskExecutionRole",
"RoleId": "AROA5FW5RZWLXFPU656SQ",
"Arn": "arn:aws:iam::0000000000:role/ecsTaskExecutionRole",
"CreateDate": "2020-07-09T14:51:49+00:00",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"ecs-tasks.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
}
}
记下此新角色的 ARN,稍后我们将使用它来创建 ECS 任务。
最后,我们将 ECS 任务执行策略 AmazonECSTaskExecutionRolePolicy
赋予创建的角色,这将使我们能够使用 Firelens 管理日志。
aws iam attach-role-policy --role-name ecsTaskExecutionRole --policy-arn "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
创建任务定义
Amazon Firelens 是一个日志路由器(通常是 fluentd
或 fluentbit
),您可以将其与您的应用程序容器一起在同一任务定义中运行,以便将其日志路由到 Loki。
在此示例中,我们将使用已安装 fluentbit output plugin 的 fluentbit,但如果您更喜欢 fluentd,请务必查看 fluentd output plugin 文档。
注意
我们建议您使用 fluentbit,因为它比 fluentd 消耗的资源更少。
我们的任务定义将由两个容器组成:用于向 Loki 发送日志的 Firelens 日志路由器(log_router
)以及一个用于生成日志的示例应用程序(sample-app
)。
让我们下载任务定义,我们将详细介绍最重要的部分。
curl https://raw.githubusercontent.com/grafana/loki/main/docs/sources/send-data/promtail/cloud/ecs/ecs-task.json > ecs-task.json
{
"essential": true,
"image": "grafana/fluent-bit-plugin-loki:2.9.3-amd64",
"name": "log_router",
"firelensConfiguration": {
"type": "fluentbit",
"options": {
"enable-ecs-log-metadata": "true"
}
},
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "firelens-container",
"awslogs-region": "us-east-2",
"awslogs-create-group": "true",
"awslogs-stream-prefix": "firelens"
}
},
"memoryReservation": 50
},
log_router
容器镜像是已预装 Loki 插件的 Fluent bit Loki docker 镜像。如您所见,firelensConfiguration
类型设置为 fluentbit
,并且我们还添加了 options
来启用 ECS 日志元数据。这在使用 Loki LogQL label matchers 查询日志时将非常有用。
注意
logConfiguration
主要用于调试 fluent-bit 容器,但在测试和配置完成后,您可以随时将其移除。
{
"command": [
"/bin/sh -c \"while true; do sleep 15 ;echo hello_world; done\""
],
"entryPoint": ["sh","-c"],
"essential": true,
"image": "alpine:3.13",
"logConfiguration": {
"logDriver": "awsfirelens",
"options": {
"Name": "loki",
"Host": "<grafanacloud host>",
"Http_User": "<userid>",
"Labels": "{job=\"firelens\"}",
"RemoveKeys": "container_id,ecs_task_arn",
"LabelKeys": "container_name,ecs_task_definition,source,ecs_cluster",
"LineFormat": "key_value"
},
"secretOptions": [{
"name": "Http_Passwd",
"valueFrom": "data.aws_secretsmanager_secret.grafana_cloud_loki_http_password.id"
}]
},
"name": "sample-app"
}
第二个容器是我们的 sample-app
,这是一个简单的 alpine 容器,会向标准输出打印欢迎消息。为了将这些日志发送到 Loki,我们将配置此容器使用 awsfirelens
日志驱动。
请继续,将 Host
和 HTTP_User
属性替换为您的 GrafanaCloud 凭据,您可以在您的账户中 Loki 实例页面找到它们。如果您运行的是自己的 Loki 实例,请完全替换 URL(例如,http://my-loki.com:3100/loki/api/v1/push
)。
为了简单起见,我们在 options
中包含了明文凭据。然而,这会暴露您 ECS 任务定义和任何版本控制配置中的凭据。通过使用像 AWS Secrets Manager 这样的密钥存储,并结合 secretOptions 配置选项来在日志配置中注入敏感数据,可以缓解此问题。
logConfiguration
的所有 options
都将自动转换为 fluentbit 的 output
配置。例如,上述 options
将生成以下 fluent bit OUTPUT
配置段:
[OUTPUT]
Name grafana-loki
Match awsfirelens*
Url https://<userid>:<grafancloud apikey>@logs-prod-us-central1.grafana.net/loki/api/v1/push
Labels {job="firelens"}
RemoveKeys container_id,ecs_task_arn
LabelKeys container_name,ecs_task_definition,source,ecs_cluster
LineFormat key_value
此 OUTPUT
配置会将日志转发到 GrafanaCloud Loki,要了解有关这些 options
的更多信息,请务必阅读 fluentbit output plugin 文档。我们保留了一些有趣且有用的标签,例如 container_name
、ecs_task_definition
、source
和 ecs_cluster
,但您可以通过 Labels
选项静态添加更多标签。
注意
如果您想在任务中运行多个容器,所有容器都需要一个
logConfiguration
部分,这让您可以根据不同的容器添加不同的标签。
{
"containerDefinitions": [
...
],
"cpu": "256",
"executionRoleArn": "arn:aws:iam::00000000:role/ecsTaskExecutionRole",
"family": "loki-fargate-task-definition",
"memory": "512",
"networkMode": "awsvpc",
"requiresCompatibilities": [
"FARGATE"
]
}
最后,您需要将 executionRoleArn
替换为我们在第一节中创建的角色的 ARN。
编辑完任务定义后,我们可以运行以下命令来创建任务
aws ecs register-task-definition --region us-east-2 --cli-input-json file://ecs-task.json
现在让我们创建并启动一个服务。
运行您的服务
要运行服务,您需要提供任务定义名称 loki-fargate-task-definition:1
,它是任务族加上任务修订版本 :1
的组合。您还需要您自己的子网和安全组,您可以在下面的命令中分别替换 subnet-306ca97d
和 sg-02c489bbdeffdca1d
并启动您的服务。
aws ecs create-service --cluster ecs-firelens-cluster \
--service-name firelens-loki-fargate \
--task-definition loki-fargate-task-definition:1 \
--desired-count 1 --region us-east-2 --launch-type "FARGATE" \
--network-configuration "awsvpcConfiguration={subnets=[subnet-306ca97d],securityGroups=[sg-02c489bbdeffdca1d],assignPublicIp=ENABLED}"
注意
确保公共 IP(
assignPublicIp
)已启用,否则 ECS 将无法连接到互联网,您将无法拉取外部 Docker 镜像。
您现在可以访问 ECS 控制台,应该会看到您的任务正在运行。现在让我们打开 Grafana,使用 Loki 数据源的 Explore 功能来查看我们的任务日志。输入查询 {job="firelens"}
,您应该会看到我们的 sample-app
日志显示出来,如下所示。

使用“日志标签”下拉菜单,您应该能够通过 ECS 元数据发现您的工作负载,展开日志行时也可以看到这些元数据。
就是这样。请务必查阅 LogQL 文档,了解 Loki 强大的查询语言。