阅读 243

k8s节点日志,镜像磁盘爆满,记一次解决方案

背景

项目使用敏捷开发,jenkins负责持续集成,构建新镜像push到harbor,最后k8s节点从harbor仓库拉取新镜像更新集群。

暴露的问题

jenkins

githup上的项目代码一更新,触发webhook,jenkins开始构建项目的新镜像,长此以往,jenkins节点本地无用的镜像会越来越多,占用磁盘空间也会越来越大。

harbor

jenkins构建的新镜像会push到harbor仓库,长此以往,harbor节点本地无用的镜像就会越来越多,占用的磁盘空间也就越来越大。

k8s

  • 每次更新k8s,将要从harbor仓库pull新的镜像到worker节点本地,随着频繁更新,worker节点的无用镜像也会越来越多
  • k8s运行着多个docker,docker会产生日志,日志也是占用磁盘主要渠道。docker日志分为俩种,一种为容器的本身的标准输出日志,另一种是容器内部卷形式的文件日志,随着docker的运行,日志也会慢慢变大,占用磁盘空间的很大部分。

解决方案

jenkins

我们知道jenkins属于pipeline,我们的目的是想删除jenkins节点的无用镜像,我们只要在pipeline里加一段stage即可。

harbor

harbor就更简单了,有tag保留策略。


image.png

k8s

偶尔看到这篇文章记一次k8s集群节点镜像存储容量报警问题,很有感触,其实也是每个人应该思考的。

自从我们的kubernetes集群部署到生产环境后,将流量从原有的服务器上切过来之后,部分节点出现挂载目录容量爆满的情况。

运维的同事报给我们之后,我们首先想到的是节点镜像过多,于是我们提供一个命令用于清理当前节点上无用的、报错的、镜像和docker资源文件

docker system prune 命令可以用于清理磁盘,删除关闭的容器、无用的数据卷和网络,以及dangling镜像(即无tag的镜像)

docker system prune -a 命令清理得更加彻底,可以将没有容器使用Docker镜像都删掉。

待运维执行之后,目录存储资源释放了一些,我们本以为这就告一段落了。然而,事与愿违,没过多久,再次容量报警。。。

我们开始重视起来,开始检视节点上工作的容器,发现在日志爆炸的节点上运行了定时任务,开发人员将定时任务的日志输出到控制台,于是我们回到节点docker的工作目录,通过du -sh *方式查看每个文件夹大小,发现docker目录下containers目录占用空间巨大,进去看原来是每个运行的容器存放日志的目录,我们找出占用空间最大的日志目录,发现容器日志特别的大

我们可使用如下命令查看各个日志的文件大小

ls -lh $(find /var/lib/docker/containers/ -name *-json.log)

那我们如何清理日志呢,如果docker容器正在运行,那么使用rm -rf 方式删除日志后,通过df -h会发现磁盘空间并没有释放

原因:在Linux或者Unix系统中,通过rm或者文件管理器删除文件将会从文件系统的目录结构上解除链接(unlink).然而如果文件是被打开的(有一个进程正在使用),那么进程将仍然可以读取该文件,磁盘空间也一直被占用

我们通过cat /dev/null > *-json.log来清理相应的日志,然后重启

systemctl daemon-reload

systemctl restart docker

然而,我思考,不能每次满的时候找运维清理日志啊,这多麻烦,难道docker没有相应的机制应付输出到控制台的日志吗?答案是:当然不会

在新版的docker中我们可以通过设置vim /etc/docker/daemon.json 来限制docker的日志量

"log-driver":"json-file","log-opts":{ "max-size" :"200m","max-file":"5"}

顾名思义max-size就是每个日志文件大小,max-file是最多生成的文件数,如上我设置成功后,每个容器运行的日志最多有五份每份200M大小,这样就基本限制了容器的日志大小。

然后你觉得结束了吗??并不!!

容器日志我们是限制完了,本以为高枕无忧,不用担心出现日志爆满的情况了,但是事与愿违,过几天硬盘容量又满了。。。

我们究其原因,发现在docker的运行目录下overlay这个文件夹里存放着所有的容器挂载目录,也就是容器的系统文件在这里放着,在容器中跑着的服务产生日志很可能并不是输出到控制台,而是保存到本地,容器内的日志文件也是会占用磁盘空间的,这就让我们犯愁了,这个不好限制开发团队不存日志或者规定团队存放目录啊,对于一个成熟的容器平台来说,海纳百川那是必须的~



下面我来总结以下:

docker日志分为俩种,一种是容器的标准输出日志,也就是docker启动后控制台输出的日志,另一种是容器内的文件日志,通常是我们项目通过卷的形式挂载到宿主机上的。

k8s中卷volume支持很多形式,常见的如下

  • emptyDir
  • hostPath
  • nfs
  • glusterfs

还支持很多很多类型,如果挂载日志的方式volume选择emptyDir,则是直接在pod所在node上存放相关日志,只要pod一直运行在该node上,emptyDir就会一直在。如果pod迁移到别的pod或者node宕机,日志都会消失,所以一般存储日志不会选择该方式。

hostPath方式是 允许Node上的文件系统挂载到pod里去,如果pod里需要node上的文件,可以使用该种方式。比如pod需要node节点上的docker命令,kubectl命令等。该种方式如果pod迁移或者node宕机日志也会消失,所以也推荐使用该方式。

nfs和glusterfs这些文件存储系统, 看到的效果是把pod内的日志存储到他们自己的文件存储系统内,但本质是pod内的日志以emptyDir的方式先存储到node节点的某个目录上,然后nfs和glusterfs到node的这些文件目录中copy日志到自己文件存储系统中。

说这么多,想要表达的意思就是不管你以哪种方式在存储日志,k8s的worker节点都存在日志爆满的风险。

针对这个问题,我们可以查看我的另一篇文章https://www.jianshu.com/p/a06d79334426,有详细解释k8s的驱逐策略。

我们需要注意
kubelet 只支持两种文件系统分区。

nodefs 文件系统,kubelet 将其用于卷和守护程序日志等。
imagefs 文件系统,容器运行时用于保存镜像和容器可写层。

针对这俩种文件系统,k8s有4种相关的驱逐信号

驱逐信号 描述
nodefs.available node.stats.fs.available
nodefs.inodesFree node.stats.fs.inodesFree
imagefs.available node.stats.runtime.imagefs.available
imagefs.inodesFree node.stats.runtime.imagefs.inodesFree

也就是说你的驱逐信号中如果包含nodefs相关的,触发阀值后kubelet会删除卷和守护程序日志,本质就是驱逐pod了。删除卷就是项目内的文件日志,删除守护程序日志就是容器的标准日志。

驱逐信号中如果包含imagefs相关的,触发阀值后kubelet会删除没有使用的镜像。

到此,已经知道了k8s节点因镜像或者日志占用磁盘空间占用过大的解决方案。


执行vim /etc/systemd/system/kubelet.service.d/10-kubeadm.conf

在里面插入

Environment="KUBELET_OTHER_ARGS=

--eviction-hard=memory.available<2Gi,nodefs.available<5Gi,imagefs.available<5Gi

--eviction-minimum-reclaim=memory.available=500Mi,nodefs.available=5Gi,imagefs.available=5Gi

--node-status-update-frequency=10s

--eviction-pressure-transition-period=30s"

作者:PENG先森_晓宇

原文链接:https://www.jianshu.com/p/7e2c6bd3424f

文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐