k8s subPathExpr stat no such file or directory 及挂载后找不到文件的问题

在 k8s 集群、云基础架构或是网络设备上我们常常需要用 fluent bit、fluentd 之类的工具来收集日志。其中一种架构是将收集日志的 agent 运行在宿主机上,我们自己的服务写日志,agent 收集日志转发到 elastic search 之类的处理后端上。

如果 agent 和我们自己的服务都是以 pod 的形式运行在 k8s 集群上,我们就需要让他们一个读一个写同一个文件,就都需要挂载同一个目录。而当我们有多个 pod 可能有相同的日志路径时,我们就要保证能区别出不同的 pod 的日志。

挂载时映射到不同路径

一种方法是直接写日志时,写到包含 $POD_NAME 这类环境变量的路径下。但我想在挂载目录时就映射到宿主机包含 $POD_NAME 的目录下,于是就考虑 SubPathExpr,这个是 Kubernetes 1.17 后有的功能。

大概的用法如下

apiVersion: apps/v1
kind: Deployment
...
spec:
    spec:
      containers:
      - name: asr
        ...
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name
        volumeMounts:
          - name: log
            mountPath: /log
            subPathExpr: $(POD_NAME)
      volumes:
        - name: log
          hostPath:
            path: /tmp/log

然而执行

kubectl apply -f deployment.yaml

后,并没有成功运行起来,

kubectl describe pod my-pod-xxx -n mynamespace

发现报错

Error: stat /tmp/log: no such file or directory

非常地不解,也只好先加上 type:

      volumes:
        - name: log
          hostPath:
            path: /tmp/log
            type: DirectoryOrCreate

这下是运行起来了,但是本地怎么就不见 /tmp/log 里有新的文件夹呢?

到容器里执行

mount | grep "/tmp/log"

得到

overlay on /tmp/log type overlay (rw,relatime,lowerdir=/data00/docker/lib/overlay2/l/CCC:....省略...:/data00/docker/lib/overlay2/l/NNN,upperdir=/data00/docker/lib/overlay2/eee/diff,workdir=/data00/docker/lib/overlay2/eee/work)

而其它 hostPath 挂载的长这样

/dev/vda1 on /opt/tmp/xxx type ext4 (rw,relatime,errors=remount-ro,data=ordered)

后来找到了这个 issue:

https://github.com/kubernetes/kubernetes/issues/61456

(实际上先搜到的是一个翻译文 ddeevv.com/question/kubernetes-kubernetes-61456.html

原来是因为早期 k8s 不会对 subPath 做检查,于是就存在一个漏洞,用户可以搞一个软链接,让容器可以访问任何宿主机上的目录,后来修复了这个漏洞 https://kubernetes.io/blog/2018/04/04/fixing-subpath-volume-vulnerability/,

就导致容器方式(containerized)运行的 kubelet,用 subPath (或 subPathExpr)后创建的目录就跑到 kubelet 的容器里了。

那要怎么办呢,如果 kubelet 是你自己部署的,那可以把 hostPath 对应的路径给挂载到 kubelet 的容器里,不然就没办法了

其实还有办法,就是不用 subPath(subPathExpr 同),而是搞个 initContainer 来创建目录。

修改写日志的路径

或者绕过去,修改写日志的路径,由于我们有多个日志要写,统一用配置文件来配置这些日志写的路径,所以就可以搞一个 configmap 来存配置文件。

kind: ConfigMap
apiVersion: v1
metadata:
  name: log-config
  namespace: development
data:
  log.conf: >+
    xxxxxx
    xxxxxx.File=/log/${POD_NAME}/xxxx.log
    xxxxxx

然后

kubectl apply -f configmap.yml

还要编辑 deployment:

          - name: log
            mountPath: /log/
            # subPathExpr: $(POD_NAME)
          - name: log-config
            mountPath: /conf
            readOnly: true
      volumes:
        - name: log
          hostPath:
            path: /my/log
        - name: log-config
          configMap:
            name: log-config

kubectl apply -f development.yml
posted @ 2022-01-29 21:35  水郁  阅读(1910)  评论(0编辑  收藏  举报
……