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
┆凉┆暖┆降┆等┆幸┆我┆我┆里┆将┆ ┆可┆有┆谦┆戮┆那┆ ┆大┆始┆ ┆然┆
┆薄┆一┆临┆你┆的┆还┆没┆ ┆来┆ ┆是┆来┆逊┆没┆些┆ ┆雁┆终┆ ┆而┆
┆ ┆暖┆ ┆如┆地┆站┆有┆ ┆也┆ ┆我┆ ┆的┆有┆精┆ ┆也┆没┆ ┆你┆
┆ ┆这┆ ┆试┆方┆在┆逃┆ ┆会┆ ┆在┆ ┆清┆来┆准┆ ┆没┆有┆ ┆没┆
┆ ┆生┆ ┆探┆ ┆最┆避┆ ┆在┆ ┆这┆ ┆晨┆ ┆的┆ ┆有┆来┆ ┆有┆
┆ ┆之┆ ┆般┆ ┆不┆ ┆ ┆这┆ ┆里┆ ┆没┆ ┆杀┆ ┆来┆ ┆ ┆来┆