在 Docker 中拖尾命名管道以写入标准输出

Tailing named pipe in Docker to write to stdout

我的Docker文件:

FROM php:7.0-fpm

# Install dependencies, etc

RUN \
    && mkfifo /tmp/stdout \
    && chmod 777 /tmp/stdout

ADD docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh

ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]

如您所见,我正在 /tmp/stdout 创建一个命名管道。还有我的 docker-entrypoint.sh:

#!/usr/bin/env bash

# Some run-time configuration stuff...

exec "./my-app" "$@" | tail -f /tmp/stdout

我的 PHP 应用程序(名为 my-app 的可执行文件)将其应用程序日志写入 /tmp/stdout。我希望这些日志随后被 Docker 捕获,以便我可以执行 docker logs <container_id> 并查看应用程序写入 /tmp/stdout 的日志。我正在尝试通过 运行 执行 my-app 命令然后拖尾 /tmp/stdout,然后将日志输出到 stdout.

我看到的情况是,当我 运行 我的应用程序写入第一条日志消息时挂起。我相信发生这种情况是因为没有从命名管道中“读取”任何内容,并且写入命名管道块直到从中读取内容。如果我执行 docker exec -it <container_id> bash,然后在容器内自己执行 tail -f /tmp/stdout,则可以确认这一点。一旦我这样做,容器立即退出,因为应用程序已将其日志写入命名管道。

由于我不想让这个 post 膨胀的原因,my-app 本身不可能将日志写入 stdout。它必须使用 /tmp/stdout.

谁能告诉我为什么这不起作用,我需要更改什么?我希望我必须更改 docker-entrypoint.sh 中的 exec 调用,但我不确定如何更改。谢谢!

What I'm seeing happen is that when I run my application, it hangs when it writes the first log message. I believe this happens because there is nothing "reading" from the named pipe, and writing to a named pipe blocks until something reads from it.

这是正确的,参见 fifo(7)。但是使用您的示例代码

exec "./my-app" "$@" | tail -f /tmp/stdout

这实际上应该有效,因为管道将同时启动 ./my-apptail,因此 /tmp/stdout 读取的内容。

但这里的一个问题是 tail -f 永远不会自行终止,因此您的 docker-entrypoint.sh/容器也不会。您可以通过以下方式解决此问题:

tail --pid=$$ -f /tmp/stdout &
exec "./my-app" "$@"

tail --pid 将在 id 提供的进程终止后立即终止,其中 $$bash 进程的 pid(通过 exec 之后的 pid ./my-app).


For reasons that I won't bloat this post with, it's not possible for my-app itself to write logs to stdout. It has to use /tmp/stdout.

这是否意味着它必须写入文件系统 路径 还是路径 /tmp/stdout 是硬编码的?

如果您可以使用任何路径,您可以使用 /dev/stdout / /proc/self/fd/1 / /proc/$$/fd/1 作为日志记录路径,让您的应用程序写入 stdout.

如果 /tmp/stdout 是硬编码的,请尝试将其符号链接到 /dev/stdout:

ln -s /dev/stdout /tmp/stdout