为什么CloudWatch Logs Group的某些流不完整(即ECS任务的Docker Container成功退出,但日志突然停止更新)?几乎在所有日志组中都间歇性地看到这一点,但并不是在每个日志流/任务运行时都如此。
Dockerfile
使用CMD
命令运行node.js或Python脚本。
这些不是服务器/长时间运行的进程,我的用例要求容器在任务完成时退出。
示例Dockerfile:
FROM node:6
WORKDIR /path/to/app/
COPY package*.json ./
RUN npm install
COPY . .
CMD [ "node", "run-this-script.js" ]
使用docker run
在本地终端上运行此命令时,所有日志均正确打印到我的终端的stdout / stderr。
要在Fargate上将其作为ECS任务运行,请从CloudFormation模板中将其日志驱动程序设置为awslogs
。
...
LogConfiguration:
LogDriver: 'awslogs'
Options:
awslogs-group: !Sub '/ecs/ecs-task-tasks-${TaskName}'
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: ecs
...
看到有时cloduwatch日志输出不完整。我已经运行测试并检查了CW Logs Limits中的每个限制,并且确定问题不存在。
最初,我认为这是节点js在刷新console.log()
之前异步退出的问题,或者该进程退出得太早了,但是通过添加睡眠计时器导致代码延迟并没有起作用。
此外,Python容器中也会出现相同的错误,这使我相信这不是代码问题,而是Cloudwatch的问题。
可能是由于docker容器在任务完成后立即退出,因此日志没有足够的时间写到CWLogs,但是必须有一种方法来确保不会发生这种情况?
样本日志: 流不完整:
{ "message": "configs to run", "data": {"dailyConfigs":"filename.json"]}}
running for filename
完整的日志流:
{ "message": "configs to run", "data": {"dailyConfigs":"filename.json"]}}
running for filename
stdout: entered query_script
... <more log lines>
stderr:
real 0m23.394s
user 0m0.008s
sys 0m0.004s
(node:1) DeprecationWarning: PG.end is deprecated - please see the upgrade guide at https://node-postgres.com/guides/upgrading
答案 0 :(得分:2)
使用ECS Fargate容器运行Python脚本时,我已经看到了相同的行为-并且产生了同样的挫败感!
我认为这是由于CloudWatch Logs Agent批量发布日志事件所致:
如何批处理日志事件?
批处理已满,并且满足以下任一条件时将发布:
自添加第一个日志事件以来已过去
buffer_duration
的时间。已累积少于
batch_size
个日志事件,但添加新的日志事件超过了batch_size
。日志事件数已达到
batch_count
。批处理中的日志事件跨度不超过24小时,但是添加新的日志事件超过了24小时约束。
(参考:https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AgentReference.html)
因此,可能的解释是,日志事件由代理缓冲,但在ECS任务停止时尚未发布。 (如果是这样,这似乎是一个ECS问题-任何AWS ECS工程师是否愿意对此发表看法??)
似乎没有直接的方法可以确保日志已发布,但是它确实建议人们可以等待至少buffer_duration
秒(默认为5秒),并且任何先前的日志都应该发布
我将在下面描述一些测试,这是我采用的解决方法。 Shell脚本run_then_wait.sh
包装命令以触发Python脚本,并在脚本完成后添加睡眠。
Dockerfile
FROM python:3.7-alpine
ADD run_then_wait.sh .
ADD main.py .
# The original command
# ENTRYPOINT ["python", "main.py"]
# To run the original command and then wait
ENTRYPOINT ["sh", "run_then_wait.sh", "python", "main.py"]
run_then_wait.sh
#!/bin/sh
set -e
# Run the given command
"$@"
command_status="$?"
echo "Waiting for logs to flush to CloudWatch Logs..."
sleep 10 # twice the `buffer_duration` default of 5 seconds
exit "$command_status"
main.py
import logging
import time
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger()
if __name__ == "__main__":
# After testing some random values, had most luck to induce the
# issue by sleeping 9 seconds here; would occur ~30% of the time
time.sleep(9)
logger.info("Hello world")
希望该方法可以适应您的情况。您也可以在脚本中实现睡眠,但是要确保它不管终止如何发生都可能比较棘手。
很难证明建议的解释是正确的,因此我使用上面的代码测试了解决方法是否有效。测试是原始命令与run_then_wait.sh
的对比,每次运行30次。结果是,分别在30%的时间和0%的时间观察到此问题。希望这对您同样有效!
答案 1 :(得分:1)
只需就此问题联系AWS支持,以下是他们的回复:
...
基于这种情况,我可以看到这种情况发生在 Fargate在输出到stdout / stderr后快速退出的任务。它 似乎与awslogs驱动程序的工作方式以及Docker in Fargate与CW端点通信。
看看我们的内部票,我可以看到我们的 服务团队仍在为此寻求永久解决方案 报告的错误。不幸的是,修复程序没有共享的ETA 将被部署。但是,我借此机会添加了这个 将内部票箱告知类似情况并尝试 加快过程
同时,可以通过延长使用寿命来避免这种情况。 通过在记录之间添加延迟(〜> 10秒)来退出容器 应用程序的输出和流程的退出(退出 容器)。
...
答案 2 :(得分:0)
我也观察到了这一点。它一定是ECS错误吗?
我的解决方法(Python 3.7):
import atexit
from time import sleep
atexit.register(finalizer)
def finalizer():
logger.info("All tasks have finished. Exiting.")
# Workaround:
# Fargate will exit and final batch of CloudWatch logs will be lost
sleep(10)
答案 3 :(得分:0)
我在将日志刷新到CloudWatch
时遇到了同样的问题。
在回答asavoy's之后,我从exec形式切换为ENTRYPOINT
的shell形式,并在最后添加了10秒的睡眠时间。
之前:
ENTRYPOINT ["java","-jar","/app.jar"]
之后:
ENTRYPOINT java -jar /app.jar; sleep 10