skywalking-agent使⽤emptyDir导致磁盘空间不⾜:运维开发故事 作者:姜总
背景
今天发现好多pod的状态都是Evicted,然后我没有监控的权限,本来想看看grafana上监控图是否出现了特殊情况,⽆奈没权限看。
因为我发现pod出现⼤量的Evicted状态的时候,查看pod所在的node节点,距离当时发⽣Evicted的时间已经是7⼩时之久了。因此可能会存在⼀种原因:发⽣了Evicted的时候的确磁盘已经超过默认的kubelet的资源预留参数了。但是问题发⽣后,触发了阈值,已经回收了磁盘空间,导致我看的时候磁盘空间已经恢复。
在每个 Kubernetes Node节点 上,kubelet 默认根⽬录是 /var/lib/kubelet 和 ⽇志⽬录 /var/log 保存在节点的系统分区上,这个分区同时也会被Pod的 EmptyDir 类型的 volume、容器⽇志、镜像层、容器的可写层所占⽤。ephemeral-storage 便是对系统分区进⾏管理。
为什么会有Evicted状态的pod
根据我的k8s使⽤经验来看,出现了Evicted的状态的pod⼀般都是由于磁盘压⼒导致。通常可以describe pod查看到events中出
现DiskPressure的关键字。表⽰磁盘当前存在压⼒。可能是你的数据盘使⽤量到达85%了。
为什么是百分之85%呢?
当然是有根据的。因为k8s官⽹给出了kubelet的资源预留参数,默认就是15%,所以,当你的磁盘使⽤率⼤搞85%以上kubelet就会启动垃圾回收机制。
查看docker使⽤的根⽬
# 我这⾥的docker⽬录被修改到了/data/docker下,⽽⾮默认的/var/lib/docker
$ docker info
...
Docker Root Dir: /data/docker
...
查看docker的daemon.json⽂件
$ cat /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "1g",
"max-file": "4"
},
"data-root": "/data/docker",
"storage-driver": "overlay2"
}
发现我配置的docker容器的⽇志⽂件是单个⽂件最⼤1G,最多可以轮转4个⽂件也就是4G。那为啥还会导致磁盘使⽤率过⾼呢?想了⼀下,我这边因为最近使⽤了skywalking-agent,并且是⽤initContainers的形式,⽽且挂载到本地宿主机的emptyDir⽬录下。emptyDir ⽬录有⼀个点:pod⼀旦
重启,emptyDir中的数据就会消失,如果你pod⼀直没重启,那么⼀直往emptyDir中写数据的话⽂件就会越来越⼤,磁盘占⽤就越来越多。
磁盘被写满的后果
不能创建 Pod (⼀直 ContainerCreating)
不能删除 Pod (⼀直 Terminating)
⽆法 exec 到容器
于是带着这些问题⼀步步开始排查
⼤量的Evicted状态的pod
$ kubectl get po -A -o wide | grep -v "Running"
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NO MINATED NODE READINESS GATES
nsop account-service-pre-master-6db67f5cc-5nrgf 0/1 Evicted 0
103m <none> node2.pre.ayunw <none> <none>
nsop account-service-pre-master-6db67f5cc-7zbrf 0/1 Evicted 0 103m <none> node2.pre.ayunw <none> <none>
nsop account-service-pre-master-6db67f5cc-h78hv 0/1 Evicted 0 103m <none> node2.pre.ayunw <none> <none>
nsop account-service-pre-master-6db67f5cc-jj4xx 0/1 Evicted 0 103m <none> node2.pre.ayunw <none> <none>
nsop account-service-pre-master-6db67f5cc-jz4cs 0/1 Evicted 0 103m <none> node2.pre.ayunw <none> <none>
nsop account-service-pre-master-6db67f5cc-km2cz 0/1 Evicted 0 103m <none> node2.pre.ayunw <none> <none>
当我们的集中有太多被驱逐的 pod 时,这会导致⽹络负载。因为每个 pod 即使被驱逐是连接到⽹络的,并且在云 Kubernetes 集的情况下,也会阻塞⼀个 IP 地址,这可能导致如果您的集有固定的 IP 地址池,也会耗尽 IP 地址。
此外,当我们有太多处于 Evicted 状态的 Pod 时,通过运⾏kubectl get pod命令来监控 Pod 会变得很困难,因为会存在⾮常多的 Evicted Pod。当然你可以通过grep等⼿段过滤掉Evicted状态的pod。
查看Evicted的pod的任意⼀个node
describe任意⼀个pod查看,发现Warning提⽰DiskPressure,表⽰磁盘存在压⼒了。
$ kubectl describe po account-service-pre-master-6db67f5cc-5nrgf -n nsop
...
QoS Class: Burstable
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
topology.kubernetes.io/env=pre:NoSchedule
topology.kubernetes.io/region=bce-gz:NoSchedule
topology.kubernetes.io/type=appserver:NoSchedule
topology.kubernetes.io/zone:NoSchedule op=Exists
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 100m default-scheduler Successfully assigned nsop/account-service-pre-master-6db67f5cc-5nrgf to node2.pre.ayunw Warning Evicted 100m kubelet, node2.pre.ayunw The node had condition: [DiskPressure].
登录node2.pre.ayunw查看
[root@node2-pre-ayunw ~]# df -Th | egrep -v "overlay2|kubernetes|docker"
Filesystem Type Size Used Avail Use% Mounted on
devtmpfs devtmpfs 32G 0 32G 0% /dev
tmpfs tmpfs 32G 0 32G 0% /dev/shm
tmpfs tmpfs 32G 5.9M 32G 1% /run
tmpfs tmpfs 32G 0 32G 0% /sys/fs/cgroup
/dev/vda1 ext4 50G 7.9G 39G 17% /
/dev/vdb1 xfs 200G 138G 63G 69% /data
tmpfs tmpfs 6.3G 0 6.3G 0% /run/user/0
发现还有69%的磁盘空间,似乎也没有什么问题啊,应该磁盘空间也还很充⾜的。
iostat命令查看磁盘IO
[root@node2-pre-ayunw ~]# iostat -xk 1 3
Linux 5.10.8-1.el8.elrepo.x86_64 (node2-pre-ayunw.c) 08/31/2021 _x86_64_ (32 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
1.86 0.00 1.77 0.03 0.00 96.34
Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %rrqm %wrqm r_await w_await aqu-sz rareq-sz wareq-sz svctm %util
vda 0.02 2.77 0.40 22.43 0.00 2.14 4.57 43.58 1.51 0.75 0.00 24.11 8.09 0.38 0.11
vdb 0.08 126.81 3.31 519.35 0.00 0.54 0.31 0.43 3.20 0.56 0.07 40.29 4.10 0.47 6.01
avg-cpu: %user %nice %system %iowait %steal %idle
3.09 0.00 3.34 0.03 0.00 93.54
Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %rrqm %wrqm r_await w_await aqu-sz rareq-sz wareq-sz svctm %util
vda 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
vdb 0.00 51.00 0.00 168.50 0.00 0.00 0.00 0.00 0.00 0.45 0.02 0.00 3.30 0.55 2.80
avg-cpu: %user %nice %system %iowait %steal %idle
2.74 0.00 2.81 0.00 0.00 94.45
Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %rrqm %wrqm r_await w_await aqu-sz rareq-sz wareq-sz svctm %util
vda 0.00 3.00 0.00 40.00 0.00 7.00 0.00 70.00 0.00 0.67 0.00 0.00 13.33 1.00 0.30
vdb 0.00 62.00 0.00 619.50 0.00 7.00 0.00 10.14 0.00 0.58 0.04 0.00 9.99 0.50 3.10
⽬前似乎也看不到有什么IO压⼒
。但是由于我⼿上没有监控权限,估计也是直接就没有对pod做监控。然后describe的时候看到问题发⽣也是7个⼩时之前的事情了,所以的话这边猜测可能是当时已经触发了kubelet的eviction-hard,然后
磁盘已经有部分空间被回收了,⽽且压⼒可能也已经下去了。但是实际上这⾥和IO压⼒⽆关的,因为后续闻了⼀下有监控权限的⼈员看了之前⼀段时间的监控,说磁盘的IO完全没压⼒的。
查看node上的⽇志
查看节点上kubelet⽇志和message⽇志,并没有任何Evicted的⽇志被发现。
$ tail -500 kubelet.log | grep "Evicted"
$ tail -500 /var/log/messages | grep "Evicted"
那当前情况下就只能临时先处理掉这个Evicted状态的pod了
$ kubectl get po -n nsop --field-selector 'status.phase!=Running' -o json| kubectl delete -f -
因为是磁盘空间的问题,所以想到去检查⼀下是否有持续增长的⽬录。最后排查发现,所有出现Evicted状态的pod所处的节点似乎都有⼀个共性:那就是都启⽤了skywalking,并且以emptyDir的形式写⽇志到本地临时存储中。
⽬前公司将默认的docker⽬录和kubelet⽬录都改到了/data⽬录下,上pod所在的node,到/data/⽬录
exited下通过du -sh ./* | grep G命令去查看了⼀下有好多/data/kubernetes/kubelet/pods/xxx/volumes/kubernetes.io~empty-dir/vol-apm-empty/logs的⽬录下存在skywalking-api.log的⽇志,⽽且都是轮转的⽇志,默认没有设置⽇志保留时间。
skywalking-agent的配置⽂件中默认开启了以下⼏个参数:
$ egrep -v "^$|^#" fig
agent.service_name=${SW_AGENT_NAME:Your_ApplicationName}
collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:127.0.0.1:11800}
logging.file_name=${SW_LOGGING_FILE_NAME:skywalking-api.log}
logging.level=${SW_LOGGING_LEVEL:INFO}
skywalking-agent在emptyDir下的⽇志
[root@node2-pre-ayunw vol-apm-empty]# cd logs/
[root@node2-pre-ayunw logs]# ll
total 4327672
-rw-r--r-- 1 root root 260328481 Aug 31 09:43 skywalking-api.log
-rw-r--r-- 1 root root 314573222 Aug 12 02:56 skywalking-api.log.2021_08_12_02_56_35
-rw-r--r-- 1 root root 314573394 Aug 13 15:01 skywalking-api.log.2021_08_13_15_01_56
-rw-r--r-- 1 root root 314574277 Aug 15 03:12 skywalking-api.log.2021_08_15_03_12_26
-rw-r--r-- 1 root root 314574161 Aug 16 15:21 skywalking-api.log.2021_08_16_15_21_13
-rw-r--r-- 1 root root 314574334 Aug 18 03:31 skywalking-api.log.2021_08_18_03_31_18
-rw-r--r-- 1 root root 314572887 Aug 19 15:40 skywalking-api.log.2021_08_19_15_40_22
-rw-r--r-- 1 root root 314574238 Aug 21 03:44 skywalking-api.log.2021_08_21_03_44_28
-rw-r--r-- 1 root root 314574144 Aug 22 15:49 skywalking-api.log.2021_08_22_15_49_08
-
rw-r--r-- 1 root root 314573963 Aug 24 03:51 skywalking-api.log.2021_08_24_03_51_28
-rw-r--r-- 1 root root 314572991 Aug 25 15:54 skywalking-api.log.2021_08_25_15_54_21
-rw-r--r-- 1 root root 314573321 Aug 27 03:57 skywalking-api.log.2021_08_27_03_57_11
-rw-r--r-- 1 root root 314572890 Aug 28 16:01 skywalking-api.log.2021_08_28_16_01_26
-rw-r--r-- 1 root root 314573311 Aug 30 04:05 skywalking-api.log.2021_08_30_04_05_34
我的docker根⽬录被更改过,不是默认的/var/lib/docker,⽽是/data/docker。我的k8s的kubelet⽬录也是被更改过的,
在/data/kubernetes/kubelet。
临时解决⽇志爆满的两种⽅法
在K8s-master节点查看Evicted的pod调度在哪个节点,然后到/data/kubernetes/kubelet/pods⽬录下去通过du -sh 命令到⽬录占⽤量⼤的pod,然后将截图指出的轮转后(就是带上时间2021_08_17这⼀类)的⽇志⽂件删除
直接重新删除pod,其实只要是pod重启后,EmptyDir⽬录就会被删除掉。
操作步骤
$ cd /data/kubernetes/kubelet/pods
$ du -sh ./* | grep G
1.3G ./02c9511d-0787-49f1-8c59-0db239baee79
1.3G ./079f3ca0-810d-468d-9136-75f3d3235b2d
4.8G ./07fc67f7-d46d-4d0c-8f6c-401e14705ae1
3.0G ./091594a0-b5ac-45c2-8ad9-7dcfc91c9e55
1.8G ./130a1b35-b447-43e1-8802-eb74aefa566c
1.2G ./1b257c27-cbaf-49f8-bca3-ceadc467aad6
2.8G ./2ec50216-f81e-4e83-922d-14316762dee2
7.0G ./321baae6-1efe-4535-8a20-0fdfa6cc3117
8.0G ./46680114-11f7-47af-9ee2-347f56592924
...
我这⾥到了占⽤7.0G⼤⼩的pod,根据⽬录名称到pod名字,然后触发了这个pod的cicd,也就相当于更新了这个pod的
deployment.yaml,然后apply -f重新⽣成了⼀遍这个pod
$ docker ps -a | grep "321baae6-1efe-4535-8a20-0fdfa6cc3117"
a69b2635ba98 registry.ayunw/tsp/msmessagecenter "/startApp.sh" 5 weeks ago Up 5 weeks k8 s_msmessagecenter-perf-dev-v1-0-0_msmessagecenter-perf-dev-v1-0-0-7f746b84bf-wb4g5_tsp_321baae6-1efe-4535-8a20-0fdfa6cc3117_0
c8f2cc0a2737 874552b27b34 "sh -c 'set -ex;mkdi…" 5 weeks ago Exited (0) 5 weeks ago k8s_init-skywalking-agent_msmessagecenter-perf-dev-v1-0-0-7f746b84bf-wb4g5_tsp_321baae6-1efe-4535-8a20-0fdfa6cc3117_0
c415f52e7489 registry.ayunw/io/pause:3.2 "/pause" 5 weeks ago Up 5 weeks k8s_P OD_msmessagecenter-perf-dev-v1-0-0-7f746b84bf-wb4g5_tsp_321baae6-1efe-4535-8a20-0fdfa6cc3117_0
等pod被完全删除后查看这个⽬录已经消失
$ du -sh ./* | grep G
1.3G ./02c9511d-0787-49f1-8c59-0db239baee79
1.3G ./079f3ca0-810d-468d-9136-75f3d3235b2d
4.8G ./07fc67f7-d46d-4d0c-8f6c-401e14705ae1
3.0G ./091594a0-b5ac-45c2-8ad9-7dcfc91c9e55
1.8G ./130a1b35-b447-43e1-8802-eb74aefa566c
1.2G ./1b257c27-cbaf-49f8-bca3-ceadc467aad6
2.8G ./2ec50216-f81e-4e83-922d-14316762dee2
8.0G ./46680114-11f7-47af-9ee2-347f56592924
...
永久解决⽇志保留个数⽅法
直接在Dockerfile打镜像的时候更改参数或者提前写好配置⽂件然后构建镜像的时候COPY进去
我这⾥直接改好fig参数然后Dockerfile中COPY进去了
$ cat Dockerfile
FROM registry.ayunw/library/alpine:3.12.0
ENV LANG=C.UTF-8 \
SKYWLKING_AGENT_VERSION=8.6.0
RUN set -eux && mkdir -p /opt/skywalking/agent \
&& apk add wget \
&& wget /skywalking/${SKYWLKING_AGENT_VERSION}/apache-skywalking-apm-es7-${SKYWLKING_AGENT_VERSION} . -P /tmp/ \
&& cd /tmp && tar zxf apache-skywalking-apm-es7-${SKYWLKING_AGENT_VERSION}. \
&& mv /tmp/apache-skywalking-apm-bin-es7/agent/* /opt/skywalking/agent \
&& rm -f /opt/skywalking/agent/optional-plugins/apm-spring-annotation-plugin-8.6.0.jar /opt/skywalking/agent/plugins/thrift-plugin-8.6.0.jar \
&& mv /opt/skywalking/agent/plugins/thrift-plugin-8.6.0.jar /tmp/thrift-plugin-8.6.0.jar \
&& cp -r /opt/skywalking/agent/optional-plugins/* /opt/skywalking/agent/plugins/ \
&& unset export \
&& rm -rf /tmp/* /opt/skywalking/agent/fig
fig /opt/skywalking/agent/config/
WORKDIR /
$ egrep -v "^$|^#" fig
agent.service_name=${SW_AGENT_NAME:Your_ApplicationName}
collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:127.0.0.1:11800}
logging.file_name=${SW_LOGGING_FILE_NAME:skywalking-api.log}
logging.level=${SW_LOGGING_LEVEL:INFO}
# 以下参数是我更改后的,表⽰⽇志保留个数为3个
logging.max_history_files=${SW_LOGGING_MAX_HISTORY_FILES:3}
其实fig⽂件中是有logging.max_history_files=${SW_LOGGING_MAX_HISTORY_FILES:-1}这⼀⾏的,但是默认被注释掉了。我这⾥将它打开,然后将-1改成了3。这⾏配置是JAVA中的写法,意思是默认是-1表⽰"最⼤历史⽇志⽂件保留个数",⽽-1则表⽰不设置最⼤历史⽇志⽂件保留,也就是⼀直轮转,不会做⽇志清理。参数意思可以参考skywalking官⽹。
然后重新构建这个skywalking-agent镜像,在deployment中引⽤即可。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论