Terraform local-exec中aws logs tail --follow输出消失问题
Terraform local-exec中AWS日志tail输出丢失问题
需求
- 在Terraform apply阶段触发ECS任务执行
- 使用terraform_data(原null_resource)作为执行载体
- 脚本需实现:启动ECS任务,后台追踪任务日志,等待任务停止后终止日志追踪
问题现象
aws logs tail ... --follow &的日志输出在Terraform apply过程中完全看不到- 脚本内其他echo输出均正常显示
- 移除
--follow参数后,能正常获取所有日志内容 - 给aws logs命令添加
--debug参数,仅能看到stderr的调试信息,主日志仍丢失 - 直接在本地shell运行脚本,日志输出正常,仅在Terraform环境中出现丢失
疑问
日志输出去哪了?是Terraform local-exec的机制问题,还是AWS CLI的输出特性导致?
相关代码
resource "terraform_data" "run_task" { triggers_replace = { task_definition = aws_ecs_task_definition.app.arn } provisioner "local-exec" { command = <<-EOT START="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" TASK_ARN="$(aws ecs run-task --cluster "$CLUSTER_ID" --task-definition "$TASK_DEFINITION_ARN" --network-configuration "$TASK_NETWORK_CONFIGURATION" --launch-type FARGATE --enable-execute-command --region "$AWS_REGION" | jq -r '.tasks[].taskArn')" echo "Watching task: $TASK_ARN" LOG_STREAM_NAME="$NAME/$CONTAINER_NAME/$(echo "$TASK_ARN" | sed "s/^arn:aws:ecs:$AWS_REGION:$AWS_ACCOUNT_ID:task\/$CLUSTER_NAME\///")" echo "Tailing logs: $LOG_STREAM_NAME" aws logs tail "$LOG_GROUP_NAME" --log-stream-name-prefix "$LOG_STREAM_NAME" --follow --format short & LOGS_PID="$!" until aws ecs wait tasks-stopped --cluster "$CLUSTER_ID" --tasks "$TASK_ARN" --region "$AWS_REGION"; do echo "Watching task: $TASK_ARN" done echo "Task has stopped: $TASK_ARN" kill "$LOGS_PID" || true EOT interpreter = ["/bin/sh", "-c"] environment = {...} } }
原因分析与解决方案
问题根源
Terraform的local-exec对后台进程的输出处理存在限制:
- 后台进程的stdout默认采用块缓冲,只有当缓冲区填满或进程退出时才会输出内容,而Terraform在主进程(等待任务停止的循环)结束前不会主动等待后台进程的缓冲刷新,导致日志被滞留。
- 即使进程被kill,此时local-exec可能已经停止捕获输出,缓冲的日志无法被打印。
修复方案
修改脚本,强制AWS CLI无缓冲输出,并确保后台进程的输出被Terraform捕获:
resource "terraform_data" "run_task" { triggers_replace = { task_definition = aws_ecs_task_definition.app.arn } provisioner "local-exec" { command = <<-EOT START="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" TASK_ARN="$(aws ecs run-task --cluster "$CLUSTER_ID" --task-definition "$TASK_DEFINITION_ARN" --network-configuration "$TASK_NETWORK_CONFIGURATION" --launch-type FARGATE --enable-execute-command --region "$AWS_REGION" | jq -r '.tasks[].taskArn')" echo "Watching task: $TASK_ARN" LOG_STREAM_NAME="$NAME/$CONTAINER_NAME/$(echo "$TASK_ARN" | sed "s/^arn:aws:ecs:$AWS_REGION:$AWS_ACCOUNT_ID:task\/$CLUSTER_NAME\///")" echo "Tailing logs: $LOG_STREAM_NAME" # 强制无缓冲输出,禁用分页器,并重定向输出到当前终端 PYTHONUNBUFFERED=1 aws logs tail "$LOG_GROUP_NAME" --log-stream-name-prefix "$LOG_STREAM_NAME" --follow --format short --no-cli-pager >&1 & LOGS_PID="$!" until aws ecs wait tasks-stopped --cluster "$CLUSTER_ID" --tasks "$TASK_ARN" --region "$AWS_REGION"; do sleep 5 echo "Waiting for task to complete: $TASK_ARN" done echo "Task has stopped: $TASK_ARN" kill "$LOGS_PID" || true EOT interpreter = ["/bin/sh", "-c"] environment = {...} } }
关键修改点
PYTHONUNBUFFERED=1:AWS CLI基于Python开发,该环境变量强制Python程序使用无缓冲输出,确保日志实时打印。--no-cli-pager:禁用AWS CLI的分页器,避免输出被拦截。>&1:将后台进程的stdout和stderr显式重定向到当前终端,确保Terraform能捕获到输出流。
备选方案(更稳定)
如果后台进程的方式仍有问题,可以改用循环查询日志的方式,避免后台进程的输出丢失:
# 替换原日志tail部分的代码 NEXT_TOKEN="" while true; do if [ -z "$NEXT_TOKEN" ]; then aws logs get-log-events --log-group-name "$LOG_GROUP_NAME" --log-stream-name "$LOG_STREAM_NAME" --start-time "$(date -d "$START" +%s)000" --output text --query 'events[].message' NEXT_TOKEN=$(aws logs get-log-events --log-group-name "$LOG_GROUP_NAME" --log-stream-name "$LOG_STREAM_NAME" --start-time "$(date -d "$START" +%s)000" --query 'nextForwardToken' --output text) else aws logs get-log-events --log-group-name "$LOG_GROUP_NAME" --log-stream-name "$LOG_STREAM_NAME" --next-token "$NEXT_TOKEN" --output text --query 'events[].message' NEXT_TOKEN=$(aws logs get-log-events --log-group-name "$LOG_GROUP_NAME" --log-stream-name "$LOG_STREAM_NAME" --next-token "$NEXT_TOKEN" --query 'nextForwardToken' --output text) fi # 检查任务状态,退出循环 if aws ecs describe-tasks --cluster "$CLUSTER_ID" --tasks "$TASK_ARN" --region "$AWS_REGION" | jq -e '.tasks[].lastStatus == "STOPPED"' >/dev/null; then break fi sleep 2 done
内容的提问来源于stack exchange,提问作者mcfedr
相关产品推荐
相关产品推荐

