如何将脚本的输出写入旋转日志文件和标准输出?

如何将脚本的输出写入旋转日志文件和标准输出?

我们在容器内以 K8s Pod 的形式运行微服务。为了确保我们的应用程序收到发送到容器的任何信号(特别是 pod 驱逐时的 SIGTERM),我们通常exec在启动脚本的最后使用,以便启动脚本 bash 进程(PID 为 1)有效“变成”我们的应用程序的过程。所以我们的启动脚本通常以

exec <ourCommand>

为了方便起见,我们希望它的日志输出(stdout 和 stderr)在 的输出中可见kubectl logs,但为了事后分析,我们还希望输出最终出现在一个文件中(是​​的,该文件最终出现在它存在的地方) Pod 重新启动:-))。为了避免让文件增长太多,应该旋转它。对于日志轮转,我们对 Apache 的rotatelogs工具有很好的经验。它的“-e”选项将“回显日志到标准输出”——为我们提供文件和标准输出上的记录输出。最明显的方法是通过管道传输脚本输出,如下所示:

exec <ourCommand> | rotatelogs -e -n 10 stdout.log 10M

这似乎工作正常,但我们随后注意到,这有效地阻止了exec 预期的行为,并且启动脚本将仍然是根进程,例如,pstree -p 将显示以下结构:

start.sh(1)-+-<ourCommand>(17)
        `-rotatelogs(18)

所以我们的信号处理被破坏了,应用程序不会收到 SIGTERM 并且不会正常退出,相反,pod 将在终止宽限期结束后被杀死。

经过大量的试验和错误,我们使用流程替换得出了这个解决方案: exec <ourCommand> &> >( rotatelogs -e -n 10 stdout.log 10M)

结果是所需的过程结构和再次工作的信号处理,pstree -p现在将显示此结构:

<ourCommand>(1)---start.sh(17)---rotatelogs(18)

然而,我们随后反复注意到 Pod 崩溃的情况,但有时没有可见的错误消息kubectl logs(但它曾是在日志文件中可见,但没有人查看那里,期望它具有与 相同的内容kubectl logs,所以“何必麻烦”)。有趣的是,这种情况在生产环境(EKS 和 AKS 集群)中发生得更频繁,而在尝试在本地环境(minikube)中重现问题时则不太可靠。

我们最好的猜测是,rotatelogs 并不总是刷新其输出缓冲区,因此特别是脚本输出的最后一行(通常是有用的错误消息......)可能最终会丢失。由于rotatelogs没有提供强制刷新每一行的设置,所以现在的想法是使用这种方法(我们称之为“双进程替换”):

exec <ourCommand> &> >(tee >(rotatelogs -n 10 stdout.log 10M ))

这个想法是让 tee 进行“流分割”,并且由于它不存在旋转日志所-e具有的刷新问题,因此效果很好 - 崩溃应用程序的“著名的遗言”将可靠地显示在日志文件中,并且在kubectl logs

然后有人想到了一个绝妙的主意,通过 ts 过滤来为所有日志行添加时间戳:

exec <ourCommand> &> >(ts | tee >(rotatelogs -n 10 stdout.log 10M ))

这又导致了“著名的遗言”经常不会出现在kubectl logs。现在的流程结构如下所示:

<ourCommand>(1)---start.sh(17)-+-tee(19)---start.sh(20)---rotatelogs(21)
                               `-ts(18)

是的,我们确实尝试了“三重进程替换”:-D,但这并没有解决问题。只有删除 ts 才会。

对 bash 有更深入了解的任何人都可以尝试解释这里发生的事情(并且最好能提出解决方案)吗?

谢谢

答案1

ts期望在添加时间戳并打印之前收到整行。因此,如果您的流程的“遗言”不以 - 结尾\n,它们可能会丢失。

如何修复它?自己写一个ts,可能是这样的:

#!/bin/perl
$foundLF = 1;

while($ch=getc) {
    if ($foundLF == 1) {
        $datestring = gmtime();
        print "$datestring ";
        $foundLF = 0;
    }

    print $ch;

    $foundLF = 1 if (ord($ch) == 10);
}

已经ts写好了perl,大家可以参考一下。

如果您tee将该rotatelog功能包含到您自己的ts.这很容易做到perl

相关内容