You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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对后台进程的输出处理存在限制:

  1. 后台进程的stdout默认采用块缓冲,只有当缓冲区填满或进程退出时才会输出内容,而Terraform在主进程(等待任务停止的循环)结束前不会主动等待后台进程的缓冲刷新,导致日志被滞留。
  2. 即使进程被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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 09:24:51