在k8s中,服务日志除了标准输出,还有写入日志文件,若要对这些日志文件进行持久化存储,无论是通过网络文件存储还是hostpath,都会面临一个问题,多个pod会往同一个存储目录的同一个文件进行写入,导致日志写入异常。
解决方法:在存储上先以pod hostname 建个目录,再往里写日志,使用sidercar pod 或者修改启动脚本的方式,但是都不太便捷和浪费资源。从kubernetes 1.15版本后默认启用一个功能VolumeSubpathEnvExpansion。使用 subPathExpr 字段从 Downward API 环境变量构造 subPath 目录名。 subpathexpr 这个参数,支持带拓展的环境变量.
subpathexpr 用法说明
在这个示例中,Pod 基于 Downward API 中的 Pod 名称,使用 subPathExpr 在 hostPath 卷 /data 中创建目录 pod1。 主机目录 /data/pod1 挂载到了容器的 /logs 中。
apiVersion: v1
kind: Pod
metadata:
name: pod1
spec:
containers:
- name: container1
env:
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
image: busybox
ImagePullPolicy: IfNotPresent
command: [ "sh", "-c", "while [ true ]; do echo 'Hello'; sleep 10; done | tee -a /logs/hello.txt" ]
volumeMounts:
- name: workdir1
mountPath: /logs
subPathExpr: $(POD_NAME) #POD_NAME 的值获取,使用到了downwardapi,通过这个特性可以获取到pod的 name,namespace, uid, podIP, nodeName 等; 以及cpu,mem的request 和limit, 这个在一些java应用中经常会用到。还可以把 label annotations 以文件的方式挂载到容器内。
restartPolicy: Never
volumes:
- name: workdir1
hostPath:
path: /data
创建容器:
[root@k8s ~]# kubectl apply -f pod1.yaml
pod/pod1 created
[root@k8s ~]# ls /data/
pod1
[root@k8s ~]# ls /data/pod1/
hello.txt
[root@k8s ~]# kubectl exec -it pod1 sh
/ # ls /logs/
hello.txt
deployment 示例
在目录里把namespace 加上,通过一个deployment 测试。
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
run: subpath
name: subpath
spec:
replicas: 2
selector:
matchLabels:
run: subpath
template:
metadata:
labels:
run: subpath
spec:
containers:
- name: container1
env:
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
image: busybox
command: [ "sh", "-c", "while [ true ]; do echo 'Hello'; sleep 10; done | tee -a /logs/hello.txt" ]
volumeMounts:
- name: workdir1
mountPath: /logs
subPathExpr: $(POD_NAMESPACE)/$(POD_NAME)
volumes:
- name: workdir1
hostPath:
path: /data
通过文件创建deployment
[root@k8s ~]# kubectl apply -f subpath-deploy.yaml
[root@k8s ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
subpath-79dd95caa-0adjs 1/1 Running 0 23m
subpath-79dd95caa-9adfa 1/1 Running 0 24m
[root@k8s ~]# tree /data/
/data/
└── default
├── subpath-79dd95caa-0adjs
│ └── hello.txt
└── subpath-79dd95caa-9adfa
└── hello.txt
3 directories, 2 files
可以看到存储目录 /data下,首先是namespace ,然后是pod name, 每个pod写入的文件都在各自目录,不会出现多个pod 写入同一文件的情况。