View a markdown version of this page

配置 Amazon ECS 日志,以实现高吞吐量 - Amazon Elastic Container Service

配置 Amazon ECS 日志,以实现高吞吐量

对于高日志吞吐量场景,我们建议将 awsfirelens 日志驱动程序与 FireLens 和 Fluent Bit 一起使用。Fluent Bit 是一款轻量级日志处理器,资源利用效率高,可以处理数百万条日志记录。但是,要大规模实现最佳性能,需要调整其配置。

本部分介绍高级 Fluent Bit 优化技术,用于处理高日志吞吐量,同时保持系统稳定性并确保不丢失数据。

有关如何使用 FireLens 自定义配置文件的信息,请参阅 使用自定义配置文件。有关其他示例,请参阅 GitHub 上的 Amazon ECS FireLens 示例

注意

本部分中的某些配置选项(例如 workersthreaded)需要适用于 Fluent Bit 版本 3 或更高版本的 AWS。有关可用版本的信息,请参阅适用于 Fluent Bit 的 AWS 版本

了解分块

Fluent Bit 以称为分块的单元处理数据。当 INPUT 插件收到数据时,引擎会创建一个分块,该分块在发送到 OUTPUT 目标之前存储在内存或文件系统中。

缓冲行为取决于 INPUT 部分中的 storage.type 设置。默认情况下,Fluent Bit 使用内存缓冲。对于高吞吐量或生产场景,文件系统缓冲可提供更好的韧性。

有关更多信息,请参阅 Fluent Bit 文档中的分块和 AWS for Fluent Bit 示例存储库中的什么是分块?

内存缓冲(默认)

默认情况下,Fluent Bit 使用内存缓冲(storage.type memory)。您可以使用 Mem_Buf_Limit 参数限制每个 INPUT 插件的内存使用量。

以下示例显示了一个内存缓冲的输入配置:

[INPUT] Name tcp Tag ApplicationLogs Port 5170 storage.type memory Mem_Buf_Limit 5MB
重要

当一个插件超出 Mem_Buf_Limit 时,Fluent Bit 将暂停输入并且会丢失新记录。这可能会导致背压并减慢应用程序的速度。Fluent Bit 日志中会出现以下警告:

[input] tcp.1 paused (mem buf overlimit)

内存缓冲适用于日志吞吐量低至中等的简单应用场景。对于需要高吞吐量或用于生产环境且存在数据丢失风险的情况,应采用文件系统缓冲机制。

有关更多信息,请参阅 Fluent Bit 文档中的缓冲和内存以及 AWS for Fluent Bit 示例存储库中的仅限内存缓冲

文件系统缓冲

对于高吞吐量场景,我们建议使用文件系统缓冲。有关 Fluent Bit 如何管理缓冲和存储的更多信息,请参阅 Fluent Bit 文档中的缓冲和存储

文件系统缓冲提供以下优势:

  • 更大的缓冲容量 – 磁盘空间通常比内存更充足。

  • 持久性 – 缓冲的数据在 Fluent Bit 重新启动后仍能存活下来。

  • 正常降级 – 在输出失败期间,数据会积聚在磁盘上,而不是导致内存耗尽。

要启用文件系统缓冲,请提供自定义 Fluent Bit 配置文件。以下示例展示了建议的配置:

[SERVICE] # Flush logs every 1 second Flush 1 # Wait 120 seconds during shutdown to flush remaining logs Grace 120 # Directory for filesystem buffering storage.path /var/log/flb-storage/ # Limit chunks stored 'up' in memory (reduce for memory-constrained environments) storage.max_chunks_up 32 # Flush backlog chunks to destinations during shutdown (prevents log loss) storage.backlog.flush_on_shutdown On [INPUT] Name forward unix_path /var/run/fluent.sock # Run input in separate thread to prevent blocking threaded true # Enable filesystem buffering for persistence storage.type filesystem [OUTPUT] Name cloudwatch_logs Match * region us-west-2 log_group_name /aws/ecs/my-app log_stream_name $(ecs_task_id) # Use multiple workers for parallel processing workers 2 # Retry failed flushes up to 15 times retry_limit 15 # Maximum disk space for buffered data for this output storage.total_limit_size 10G

关键配置参数:

storage.path

Fluent Bit 在磁盘上存储缓冲块的目录。

storage.backlog.flush_on_shutdown

启用后,Fluent Bit 会尝试在停机期间将所有积压文件系统块刷新到目标。这有助于确保在 Fluent Bit 停止之前传输数据,但可能会增加停机时间。

storage.max_chunks_up

保留在内存中的块的数量。默认值为 128 个块,这会消耗 500 MB 以上的内存,因为每个块最多可以使用 4-5 MB 的内存。在内存受限的环境中,请降低此值。例如,如果您有 50 MB 可于缓冲,请将其设置为 8-10 个块。

storage.type filesystem

为输入插件启用文件系统存储。尽管名称如此,但 Fluent Bit 会使用 mmap 将块映射到内存和磁盘,从而在不牺牲性能的情况下提供持久性。

storage.total_limit_size

特定 OUTPUT 插件的缓冲数据的最大磁盘空间。当达到此限制时,该输出的最旧记录将被删除。有关大小的更多信息,请参阅 了解 storage.total_limit_size

threaded true

在自己的线程中运行输入,与 Fluent Bit 的主事件循环分开。这样可以防止慢速输入阻塞整个管道。

有关更多信息,请参阅 Fluent Bit 文档中的文件系统缓冲以及 AWS for Fluent Bit 示例存储库中的文件系统和内存缓冲

了解 storage.total_limit_size

每个 OUTPUT 插件上的 storage.total_limit_size 参数控制该输出的缓冲数据的最大磁盘空间。当达到此限制时,该输出的最旧记录将被删除以为新数据腾出空间。当磁盘空间完全耗尽时,Fluent Bit 无法将记录加入队列,这些记录会丢失。

根据您的日志速率和所需的恢复窗口,使用以下公式计算适当的 storage.total_limit_size

If log rate is in KB/s, convert to MB/s first: log_rate (MB/s) = log_rate (KB/s) / 1000 storage.total_limit_size (GB) = log_rate (MB/s) × duration (hours) × 3600 (seconds/hour) / 1000 (MB to GB)

下表显示了常见日志速率和恢复窗口的计算示例:

日志速率 1 小时 6 小时 12 小时 24 小时
0.25 MB/s 0.9 GB 5.4 GB 10.8GB 21.6GB
0.5 MB/s 1.8 GB 10.8GB 21.6GB 43.2 GB
1 MB/s 3.6 GB 21.6GB 43.2 GB 86.4GB
5 MB/s 18 GB 108 GB 216 GB 432 GB
10 MB/s 36 GB 216 GB 432 GB 864 GB

要观测峰值吞吐量并选择合适的缓冲区大小,请使用 measure-throughput FireLens 样本

使用公式、示例计算和基准测试来选择合适的 storage.total_limit_size,在故障发生时为尽力恢复提供足够的空间。

Amazon ECS 任务存储要求

将 OUTPUT 部分的所有 storage.total_limit_size 值相加,并为开销部分预留缓冲空间。这个总数决定了您在 Amazon ECS 任务定义中所需的存储空间大小。例如,3 个输出 × 每个 10GB = 30GB + 缓冲区 (5—10GB) = 总共需要 35—40GB。如果总存储量超过可用存储空间,则 Fluent Bit 可能无法将记录加入队列,这些记录会丢失。

提供以下存储选项:

绑定挂载(临时存储)
  • 对于 AWS Fargate,默认值为 20GB 的临时存储空间(最大 200GB)。在任务定义中使用 ephemeralStorage 进行配置。有关更多信息,请参阅《AWS CloudFormation 用户指南》中的 EphemeralStorage

  • 对于 EC2,使用 Amazon ECS 优化的 AMI(在操作系统和 Docker 之间共享)时,默认值为 30GB。通过更改根卷大小来增加。

Amazon EBS 卷
Amazon EFS 卷

有关数据卷的更多信息,请参阅 Amazon ECS 任务的存储选项

优化输出配置

网络问题、服务中断和目标节流可能会导致无法传送日志。正确的输出配置可确保韧性而不会丢失数据。

当输出刷新失败时,Fluent Bit 可以重试该操作。以下参数控制重试行为:

retry_limit

在首次尝试后放弃记录之前的最大重试次数。默认 为 1。例如,retry_limit 3 表示总尝试次数 4 次(1 次初始尝试 + 3 次重试)。对于生产环境,我们建议设置为 15 或更高,这可以通过指数回退覆盖几分钟的中断时间。

设置为 no_limitsFalse 以进行无限重试:

  • 使用内存缓冲时,无限次重试会导致输入插件在达到内存限制时暂停。

  • 使用文件系统缓冲时,最旧的记录在到达 storage.total_limit_size 时会被丢弃。

重要

用尽所有重试次数(1 次初始尝试 + retry_limit 次重试)后,记录将被删除。带有 auto_retry_requests true 的 AWS 插件(默认)在 Fluent Bit 的重试机制之前提供了额外的重试层。有关更多信息,请参阅 Fluent Bit 文档中的配置重试

例如,使用默认设置(scheduler.base 5scheduler.cap 2000net.connect_timeout 10s)的 retry_limit 3 可提供大约 70 秒的调度程序等待时间(10s + 20s + 40s)、40 秒的网络连接超时(4 次尝试 × 10s)以及 AWS 插件重试次数,总计大约 2-10 分钟,具体取决于网络条件和操作系统 TCP 超时。

scheduler.base

两次重试之间的基本秒数(默认值:5)。我们建议 10 秒。

scheduler.cap

两次重试之间的最大秒数(默认值:2000)。我们建议 60 秒。

两次重试之间的等待时间使用带有抖动的指数回退:

wait_time = random(base, min(base × 2^retry_number, cap))

例如,使用 scheduler.base 10scheduler.cap 60

  • 第一次重试:在 10–20 秒之间随机等待

  • 第二次重试:在 10–40 秒之间随机等待

  • 第三次重试及以后:在 10–60 秒之间随机等待(上限)

有关更多信息,请参阅 Fluent Bit 文档中的配置重试等待时间联网

workers

并行输出处理的线程数。多个 Worker 允许并发刷新,从而提高处理多个块时的吞吐量。

auto_retry_requests

一种特定于 AWS 插件的设置,在 Fluent Bit 的内置重试机制之前提供了额外的重试层。默认值为 true。启用后,AWS 输出插件会在内部重试失败的请求,然后请求才会被视为刷新失败并受 retry_limit 配置的约束。

[SERVICE] 部分中的 Grace 参数设置 Fluent Bit 在停机期间等待刷新缓冲数据的时间。Grace 期限必须与容器的 stopTimeout 相协调。确保 stopTimeout 超过 Grace 期限,进而允许 Fluent Bit 在接收 SIGKILL 之前完成刷新。例如,如果 Grace 为 120 秒,则将 stopTimeout 设置为 150 秒。

以下示例展示了针对高吞吐量场景的完整 Fluent Bit 配置,其中包含所有推荐设置:

[SERVICE] # Flush logs every 1 second Flush 1 # Wait 120 seconds during shutdown to flush remaining logs Grace 120 # Directory for filesystem buffering storage.path /var/log/flb-storage/ # Limit chunks stored 'up' in memory (reduce for memory-constrained environments) storage.max_chunks_up 32 # Flush backlog chunks to destinations during shutdown (prevents log loss) storage.backlog.flush_on_shutdown On # Minimum seconds between retries scheduler.base 10 # Maximum seconds between retries (exponential backoff cap) scheduler.cap 60 [INPUT] Name forward unix_path /var/run/fluent.sock # Run input in separate thread to prevent blocking threaded true # Enable filesystem buffering for persistence storage.type filesystem [OUTPUT] Name cloudwatch_logs Match * region us-west-2 log_group_name /aws/ecs/my-app log_stream_name $(ecs_task_id) # Use multiple workers for parallel processing workers 2 # Retry failed flushes up to 15 times retry_limit 15 # Maximum disk space for buffered data for this output storage.total_limit_size 10G

了解数据丢失情况

在长时间故障或输出目标出现问题的情况下,记录可能会丢失。本指南中的配置建议是尽力减少数据丢失,但不能保证长时间故障期间零丢失。了解这些场景有助于您配置 Fluent Bit 以最大限度地提高韧性。

记录可能会以两种方式丢失:当存储空间满时,最旧的记录会被删除;或者当系统无法再接收更多数据时,最新的记录会被拒绝。

最旧的记录已删除

当重试尝试用尽或 storage.total_limit_size 已满且需要为新数据腾出空间时,最旧的缓冲记录将被丢弃。

超出了重试限制

发生在 AWS 插件重试(如果 auto_retry_requests true)加上 1 次初始 Fluent Bit 尝试加上 retry_limit 重试次数之后。为了缓解风险,为每个 OUTPUT 插件设置 retry_limit no_limits 以进行无限次重试:

[OUTPUT] Name cloudwatch_logs Match ApplicationLogs retry_limit no_limits auto_retry_requests true
重要

无限重试可防止由于重试耗尽而丢弃记录,但可能会导致 storage.total_limit_size 被填满。

已达到存储限制(文件系统缓冲)

在输出目标不可用的时间超过您所配置的 storage.total_limit_size 可以缓冲的时间时,就会出现这种情况。例如,日志速率为 1 MB/s 的 10GB 缓冲区可提供大约 2.7 小时的缓冲时间。为了缓解风险,请增加每个 OUTPUT 插件的 storage.total_limit_size 并预置足够的 Amazon ECS 任务存储空间:

[OUTPUT] Name cloudwatch_logs Match ApplicationLogs storage.total_limit_size 10G

最新的记录被拒绝

当磁盘空间耗尽或由于 Mem_Buf_Limit 暂停输入时,最新的记录将被丢弃。

磁盘空间已耗尽(文件系统缓冲)

当磁盘空间完全耗尽时发生。Fluent Bit 无法将新记录列队且它们会丢失。为了缓解风险,将所有的 storage.total_limit_size 值相加并预置足够的 Amazon ECS 任务存储空间。有关更多信息,请参阅 Amazon ECS 任务存储要求

已达到内存限制(内存缓冲)

在输出目标不可用且内存缓冲区已满时发生。暂停的输入插件将停止接受新记录。要缓解风险,请使用 storage.type filesystem 提高韧性,或增加 Mem_Buf_Limit

最大限度减少数据丢失的最佳实践

考虑以下最佳实践来最大限度减少数据丢失:

  • 使用文件系统缓冲 – 设置 storage.type filesystem 以在中断期间提高韧性。

  • 适当调整存储空间大小 – 根据日志速率和所需的恢复窗口计算 storage.total_limit_size

  • 预置足够的磁盘 – 确保 Amazon ECS 任务有足够的临时存储、Amazon EBS 或 Amazon EFS。

  • 配置重试行为 – 在 retry_limit(用尽重试后丢弃记录)和 no_limits(无限次重试但可能会填满存储空间)之间保持平衡。

使用多目标日志记录实现可靠性

将日志发送到多个目标可以消除单点故障。例如,如果 CloudWatch Logs 出现中断,日志仍会到达 Amazon S3。

多目标日志记录提供以下优势:Amazon S3 输出插件还支持 gzip 和 Parquet 格式等压缩选项,这可以降低存储成本。有关更多信息,请参阅 Fluent Bit 文档中的 S3 压缩

多目标日志记录可以提供以下优势:

  • 冗余 – 如果一个目标出现故障,日志仍会到达另一个目标。

  • 恢复 – 重建一个系统与另一个系统之间的差距。

  • 持久性 – 将日志归档到 Amazon S3 实现长期保留。

  • 成本优化 – 将最近的日志保存在诸如 CloudWatch Logs 之类的快速查询服务中,保留时间较短,而将所有日志归档到成本较低的 Amazon S3 存储可以实现长期保留。

以下 Fluent Bit 配置将日志发送到 CloudWatch Logs 和 Amazon S3:

[OUTPUT] Name cloudwatch_logs Match * region us-west-2 log_group_name /aws/ecs/my-app log_stream_name $(ecs_task_id) workers 2 retry_limit 15 [OUTPUT] Name s3 Match * bucket my-logs-bucket region us-west-2 total_file_size 100M s3_key_format /fluent-bit-logs/$(ecs_task_id)/%Y%m%d/%H/%M/$UUID upload_timeout 10m # Maximum disk space for buffered data for this output storage.total_limit_size 5G

两个输出都使用相同的 Match * 模式,因此所有记录都独立发送到两个目标。在一个目标中断期间,日志继续流向另一个目标,而失败的刷新会积聚在文件系统缓冲区中供以后重试。

通过 tail 输入插件使用基于文件的日志记录

对于日志丢失是一个关键问题的高吞吐量场景,可以使用另一种方法:让应用程序将日志写入磁盘上的文件,然后使用 tail 输入插件将 Fluent Bit 配置为读取日志。这种方法完全绕过了 Docker 日志记录驱动程序层。

通过 tail 插件进行基于文件的日志记录提供以下优势:

  • 偏移跟踪 – Tail 插件可以将文件偏移存储在数据库文件中(使用 DB 选项),从而在 Fluent Bit 重新启动后提供持久性。这有助于防止在容器重新启动期间丢失日志。

  • 输入级缓冲 – 可以使用 Mem_Buf_Limit 直接在输入插件上配置内存缓冲区限制,从而更精细地控制内存使用情况。

  • 避免 Docker 开销 – 日志直接从文件转到 Fluent Bit,无需通过 Docker 的日志缓冲区。

要使用这种方法,您的应用程序必须将日志写入文件中,而不是 stdout。应用程序容器和 Fluent Bit 容器都挂载一个存储日志文件的共享卷。

以下示例展示了具有最佳实践的 tail 输入配置:

[INPUT] Name tail # File path or glob pattern to tail Path /var/log/app.log # Database file for storing file offsets (enables resuming after restart) DB /var/log/flb_tail.db # when true, controls that only fluent-bit will access the database (improves performance) DB.locking true # Skip long lines instead of skipping the entire file Skip_Long_Lines On # How often (in seconds) to check for new files matching the glob pattern Refresh_Interval 10 # Extra seconds to monitor a file after rotation to account for pending flush Rotate_Wait 30 # Maximum size of the buffer for a single line Buffer_Max_Size 10MB # Initial allocation size for reading file data Buffer_Chunk_Size 1MB # Maximum memory buffer size (tail pauses when full) Mem_Buf_Limit 75MB

使用 tail 输入插件时,请考虑以下几点:

  • 对应用程序日志实施日志轮换,以防止磁盘耗尽。监控基础卷指标以衡量性能。

  • 根据您的日志格式考虑诸如 Ignore_OlderRead_from_Head 和多行解析器之类的设置。

有关更多信息,请参阅 Fluent Bit 文档中的 Tail。有关最佳实践,请参阅适用于 Fluent Bit 的 AWS 故障排除指南中的 Tail 配置和最佳实践

直接记录到 FireLens

在任务定义中指定 awsfirelens 日志驱动程序时,Amazon ECS 容器 代理会将以下环境变量注入容器中:

FLUENT_HOST

分配给 FirelLens 容器的 IP 地址。

注意

如果在 bridge 网络模式下使用 EC2,则在重启 FireLens 日志路由器容器(容器定义中包含 firelensConfiguration 对象的容器)后,应用程序容器中的 FLUENT_HOST 环境变量可能会变得不准确。这是因为 FLUENT_HOST 是一个动态 IP 地址,重启后就会改变。地址改变后,直接从应用程序容器登录到 FLUENT_HOST IP 地址可能会失败。有关重启单个容器的更多信息,请参阅 使用容器重启策略重启 Amazon ECS 任务中的单个容器

FLUENT_PORT

Fluent Forward 协议正在侦听的端口。

您可以使用这些环境变量,使用 Fluent Forward 协议从应用程序代码直接记录到 Fluent Bit 日志路由器,而不是写入 stdout。这种方法绕过了 Docker 日志记录驱动程序层,提供以下优势:

  • 更低的延迟 – 日志无需通过 Docker 的日志记录基础设施即可直接转到 Fluent Bit。

  • 结构化日志记录 – 以原生方式发送结构化日志数据,无需 JSON 编码开销。

  • 更好的控制 – 您的应用程序可以实现自己的缓冲和错误处理逻辑。

以下 Fluent 记录器库支持 Fluent Forward 协议,可用于将日志直接发送到 Fluent Bit:

配置 Docker 缓冲区限制

创建任务定义时,您可以通过在 log-driver-buffer-limit 中指定值来指定将在内存中缓冲的日志行数。这会控制 Docker 和 Fluent Bit 之间的缓冲区。有关更多信息,请参阅 Docker 文档中的 Fluentd 日志记录驱动程序

当吞吐量高时使用此选项,因为 Docker 可能会耗尽缓冲区内存并丢弃缓冲区消息,以便添加新消息。

使用此选项时,请考虑以下几点:

  • 平台版本为 1.4.0 或更高版本的 EC2 和 Fargate 类型支持此选项。

  • 该选项仅在 logDriver 设置为 awsfirelens 时有效。

  • 默认缓冲区限制为 1048576 个日志行。

  • 缓冲区限制必须大于或等于 0 且小于 536870912 个日志行。

  • 用于此缓冲区的最大内存量是每个日志行的大小与缓冲区大小的乘积。例如,假设应用程序的日志行大小平均为 2 KiB,则缓冲区限制为 4096 时最多只能使用 8 MiB。除了日志驱动程序的内存缓冲区外,在任务级别分配的内存总量必须大于为所有容器分配的内存量。

以下任务定义展示了如何配置 log-driver-buffer-limit

{ "containerDefinitions": [ { "name": "my_service_log_router", "image": "public.ecr.aws/aws-observability/aws-for-fluent-bit:3", "cpu": 0, "memoryReservation": 51, "essential": true, "firelensConfiguration": { "type": "fluentbit" } }, { "essential": true, "image": "public.ecr.aws/docker/library/httpd:latest", "name": "app", "logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "firehose", "region": "us-west-2", "delivery_stream": "my-stream", "log-driver-buffer-limit": "52428800" } }, "dependsOn": [ { "containerName": "my_service_log_router", "condition": "START" } ], "memoryReservation": 100 } ] }