菜单
开源 RSS

在 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 创建集群

bash
aws ecs create-cluster --cluster-name ecs-firelens-cluster

我们还需要一个 IAM 角色来运行容器,让我们创建一个新角色并授权 ECS 承担此角色。

注意

如果您的 AWS 账户中已存在 ecsTaskExecutionRole 角色,则可以跳过此步骤。

bash
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 管理日志。

bash
aws iam attach-role-policy --role-name ecsTaskExecutionRole --policy-arn "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"

创建任务定义

Amazon Firelens 是一个日志路由器(通常是 fluentdfluentbit),您可以将其与您的应用程序容器一起在同一任务定义中运行,以便将其日志路由到 Loki。

在此示例中,我们将使用已安装 fluentbit output pluginfluentbit,但如果您更喜欢 fluentd,请务必查看 fluentd output plugin 文档。

注意

我们建议您使用 fluentbit,因为它比 fluentd 消耗的资源更少。

我们的任务定义将由两个容器组成:用于向 Loki 发送日志的 Firelens 日志路由器(log_router)以及一个用于生成日志的示例应用程序(sample-app)。

让我们下载任务定义,我们将详细介绍最重要的部分。

bash
curl https://raw.githubusercontent.com/grafana/loki/main/docs/sources/send-data/promtail/cloud/ecs/ecs-task.json > ecs-task.json
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 容器,但在测试和配置完成后,您可以随时将其移除。

json
 {
    "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 日志驱动。

请继续,将 HostHTTP_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 配置段:

conf
[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_nameecs_task_definitionsourceecs_cluster,但您可以通过 Labels 选项静态添加更多标签。

注意

如果您想在任务中运行多个容器,所有容器都需要一个 logConfiguration 部分,这让您可以根据不同的容器添加不同的标签。

json
{
    "containerDefinitions": [
     ...
    ],
    "cpu": "256",
    "executionRoleArn": "arn:aws:iam::00000000:role/ecsTaskExecutionRole",
    "family": "loki-fargate-task-definition",
    "memory": "512",
    "networkMode": "awsvpc",
    "requiresCompatibilities": [
        "FARGATE"
    ]
}

最后,您需要将 executionRoleArn 替换为我们在第一节中创建的角色的 ARN

编辑完任务定义后,我们可以运行以下命令来创建任务

bash
aws ecs register-task-definition --region us-east-2 --cli-input-json  file://ecs-task.json

现在让我们创建并启动一个服务。

运行您的服务

要运行服务,您需要提供任务定义名称 loki-fargate-task-definition:1,它是任务族加上任务修订版本 :1 的组合。您还需要您自己的子网和安全组,您可以在下面的命令中分别替换 subnet-306ca97dsg-02c489bbdeffdca1d 并启动您的服务。

bash
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 日志显示出来,如下所示。

grafana logs firelens

使用“日志标签”下拉菜单,您应该能够通过 ECS 元数据发现您的工作负载,展开日志行时也可以看到这些元数据。

就是这样。请务必查阅 LogQL 文档,了解 Loki 强大的查询语言。