配置 Amazon ECS 日志,以实现高吞吐量 - Amazon Elastic Container Service
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

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

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

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

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

注意

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

使用文件系统缓冲

默认情况下,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 将块映射到内存和磁盘,从而在不牺牲性能的情况下提供持久性。

threaded true

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

优化输出配置

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

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

retry_limit

删除记录之前的最大重试尝试次数。默认 为 1。对于生产环境,我们建议设置为 15 或更高,这可以通过指数回退覆盖几分钟的中断时间。

scheduler.base

两次重试之间的最短秒数。我们建议 10 秒。

scheduler.cap

使用指数回退时两次重试之间的最长秒数。我们建议 60 秒。

workers

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

[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

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

将日志发送到多个目标可以消除单点故障。例如,如果 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 的 Amazon 故障排除指南中的 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 } ] }