英文:
How can a container running in k8s read its own logs?
问题
我有一个设计为在K8s应用程序中运行的应用程序,并且它导入了一些我不拥有的依赖项,这些依赖项运行exec.Cmd
。这没问题,但是我想捕获这些日志。但是当我这样做时,会抛出一个错误:
r := bufio.NewReader(os.Stdout)
...
line, err := r.ReadString('\n')
错误显示/dev/stdout
是一个bad file descriptor
。这是怎么回事?难道它不是用于控制台输出的标准本地目标吗?
kubectl logs
似乎能够捕获输出,而且更具体地说,我们的中央日志转发器也能够捕获它。但是尝试从实际生成这些日志的容器内的kube API服务器捕获日志似乎有点愚蠢...有没有更好的方法来做到这一点?
英文:
I have an application designed to run as a K8s application, and it imports some dependencies (that I don't own) that run exec.Cmd
s. This is fine, except I want to capture those logs. For some reason, when I do:
r := bufio.NewReader(os.Stdout)
...
line, err := r.ReadString('\n')
An error is thrown saying that /dev/stdout
is a bad file descriptor
. How can this be? Isn't that the standard local destination for console output?
kubectl logs
seems to be able to capture the output, and more specifically, our central log forwarder is able to capture it as well. But trying to capture logs from the kube API server inside the container that's actually generating those logs seems kinda silly... Is there a better way to do this?
答案1
得分: 1
通常情况下,stdin
是一个只读流,用于获取写入程序的输入,而stdout
是一个只写流,用于发送程序写入的输出。换句话说,除了Chuck Norris之外,没有人可以从/dev/stdout读取。
默认情况下,stdout
指向你的终端。但是可以将stdout
从终端重定向到文件中。这个重定向是在启动程序之前设置的。
通常发生的情况是:容器运行时将容器进程的stdout
重定向到运行容器的节点上的一个文件中(例如/var/log/containers/<container-name>-<container-id>.log
)。当你使用kubectl logs
请求日志时,kubectl连接到kube-apiserver,kube-apiserver连接到运行容器的节点上的kubelet,并要求kubelet将日志文件的内容发送回来。
还可以参考https://kubernetes.io/docs/concepts/cluster-administration/logging/,该文档解释了各种日志记录设计方法。
从安全性和可移植性的角度来看,一个解决方案是在容器中添加一个hostPath
挂载,将节点上的/var/log/containers
目录挂载到容器中,并直接访问容器日志。
一个正确的解决方案可能是更改镜像的命令,并将输出写入容器的stdout
以及容器内的一个本地文件。可以使用tee
命令来实现这一点。然后,你的应用程序可以从该文件中读取日志。但要记住,如果没有适当的轮换,日志文件将会增长,直到容器被终止。
apiVersion: v1
kind: Pod
metadata:
name: log-to-stdout-and-file
spec:
containers:
- image: bash:latest
name: log-to-stdout-and-file
command:
- bash
- -c
- '(while true; do date; sleep 10; done) | tee /tmp/test.log';
一个稍微复杂一些的解决方案是,使用mkfifo
创建一个命名管道文件,将容器中的日志文件替换为该命名管道文件。这样可以避免文件大小增长的问题(只要你的应用程序不断从命名管道文件中读取日志)。
apiVersion: v1
kind: Pod
metadata:
name: log-to-stdout-and-file
spec:
# init容器在一个空目录挂载中创建fifo
initContainers:
- image: bash:latest
name: create-fifo
command:
- bash
- -c
- mkfifo /var/log/myapp/log
volumeMounts:
- name: ed
mountPath: /var/log/myapp
# 实际的应用程序使用tee将日志写入stdout和fifo
containers:
- image: bash:latest
name: log-to-stdout-and-fifo
command:
- bash
- -c
- '(while true; do date; sleep 10; done) | tee /var/log/myapp/log'
volumeMounts:
- name: ed
mountPath: /var/log/myapp
# 这个sidecar容器仅用于测试目的,它读取写入fifo的内容(通常由应用程序自己完成)
#- image: bash:latest
# name: log-reader
# command:
# - bash
# - -c
# - cat /var/log/myapp/log
# volumeMounts:
# - name: ed
# mountPath: /var/log/myapp
volumes:
- name: ed
emptyDir: {}
希望这些信息对你有所帮助!
英文:
Generally, stdin
is a read-only stream for retrieving input written to your program, while stdout
is a write-only stream for sending output written by your program. In other words, nobody can read from /dev/stdout, except Chuck Norris.
By default, stdout
is "pointing" to your terminal. But it is possible to redirect stdout
from your terminal to a file. This redirection is set up before your program is started.
What usually happens, is the following: The container runtime redirects stdout
of the process of your container to a file on the node where your container is running (e.g., /var/log/containers/<container-name>-<container-id>.log
). When you request logs with kubectl logs
, kubectl connects to kube-apiserver, which connects to the kubelet on the node running your container and asks it to send back the content from the log file.
Also take a look at https://kubernetes.io/docs/concepts/cluster-administration/logging/ which explains the various logging design approaches.
A solution, which from a security and portability perspective you would definitely NOT implement, is to add a hostPath
mount in your container mounting the /var/log/containers
directory of your node and to access the container log directly.
A proper solution might be to change the command of your image and to write output to stdout
of your container and also to a local file within your container. This can be achieved using the tee
command. Your application can then read back the log from this file. But keep in mind, that without proper rotation, the log file will grow until your container is terminated.
apiVersion: v1
kind: Pod
metadata:
name: log-to-stdout-and-file
spec:
containers:
- image: bash:latest
name: log-to-stdout-and-file
command:
- bash
- -c
- '(while true; do date; sleep 10; done) | tee /tmp/test.log'
A little more complex solution would be, to replace the log file in the container with a named pipe file created with mkfifo
. This avoids the growing file size problem (as long as your application is continuously reading the log from the named pipe file).
apiVersion: v1
kind: Pod
metadata:
name: log-to-stdout-and-file
spec:
# the init container creates the fifo in an empty dir mount
initContainers:
- image: bash:latest
name: create-fifo
command:
- bash
- -c
- mkfifo /var/log/myapp/log
volumeMounts:
- name: ed
mountPath: /var/log/myapp
# the actual app uses tee to write the log to stdout and to the fifo
containers:
- image: bash:latest
name: log-to-stdout-and-fifo
command:
- bash
- -c
- '(while true; do date; sleep 10; done) | tee /var/log/myapp/log'
volumeMounts:
- name: ed
mountPath: /var/log/myapp
# this sidecar container is only for testing purposes, it reads the
# content written to the fifo (this is usually done by the app itself)
#- image: bash:latest
# name: log-reader
# command:
# - bash
# - -c
# - cat /var/log/myapp/log
# volumeMounts:
# - name: ed
# mountPath: /var/log/myapp
volumes:
- name: ed
emptyDir: {}
答案2
得分: 0
你应该考虑使用多容器 Pod,其中包括一个主要/应用容器和一个作为 sidecar 容器的日志容器,用于读取主要/应用容器的日志。可以参考下面链接中的示例,了解如何从主要容器中获取日志。
https://learnk8s.io/sidecar-containers-patterns
英文:
You should consider multi container pod with main/app container and log container as sidecar container reading the logs from main/app container. conside r the example from the below link showing how to tail logs from main container
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论