首页
学习
活动
专区
圈层
工具
发布
50 篇文章
1
kubernetes与velero的第一次尝试
2
在Kubernetes中如何针对Namespace进行资源限制?
3
kubernetes之metrics-server安装与配置
4
kubernetes部署metrics-server
5
Kubernetes1.20.9摘掉一个master节点再重新加入(ETCD需要注意的)
6
Kubernetes 1.17.17升级到1.18.20
7
Kubernetes 1.18.20升级到1.19.12
8
Kubernetes 1.19.12升级到1.20.9(强调一下selfLink)
9
Kubernetes 1.16.15升级到1.17.17
10
使用 kainstall 工具一键部署 kubernetes 高可用集群
11
附034.Kubernetes_v1.21.0高可用部署架构二
12
附016.Kubernetes_v1.17.4高可用部署
13
附022.Kubernetes_v1.18.3高可用部署架构一
14
附024.Kubernetes_v1.18.3高可用部署架构二
15
使用 StatefulSet 部署 etcd 集群
16
Kubernetes 稳定性保障手册 -- 极简版
17
Linux(centos7)离现安装kubernetes1.19.2和docker——组件部分
18
docker register 私有仓库部署 - http模式
19
KubeSphere 开源 KubeEye:Kubernetes 集群自动巡检工具
20
K8S 中的 CPUThrottlingHigh 到底是个什么鬼?
21
全链路分布式跟踪系统 Apache SkyWalking 入门教程
22
pod Evicted的状态究竟是何人所为
23
使用 ezctl 工具部署和管理 Kubernetes 集群
24
Kubernetes部署策略详解
25
kubernetes容器探针检测
26
使用Spring Boot实现动态健康检查HealthChecks
27
真一文搞定 ingress-nginx 的使用
28
K8S备份、恢复、迁移神器 Velero
29
一次关于k8s kubectl top 和 contained ps 不一致的问题探究
30
kubernetes备份恢复之velero
31
使用 Velero 进行集群备份与迁移
32
TKE集群中nginx-ingress使用实践
33
使用velero进行kubernetes灾备
34
Kubernetes 映射外部服务
35
运维体系建设套路
36
k8s解决pod调度不均衡的问题
37
ingress中虚拟路径解决方案
38
容器下的两地三中心建设
39
k8s集群外的主机访问pod的解决方案
40
k8s基础-健康检查机制
41
k8s基础-标签使用
42
ingress-nginx请求改写
43
nginx ingress server alias 多域名多证书问题
44
JAVA | Java 解决跨域问题 花式解决跨域问题
45
如何通过ingress-nginx实现应用灰度发布?
46
在Kubernetes(k8s)中使用GPU
47
使用 Prometheus-Operator 监控 Calico
48
使用Kubespray部署Kubernetes集群
49
云原生下的CI/CD:Argo CD 详解,手把手教你入门
50
Pod的健康检查机制
清单首页k8s文章详情

一次关于k8s kubectl top 和 contained ps 不一致的问题探究

k8s kubectl top命令和contained内部 ps 看到的进程内存占用不一致。下午的时候,我被这个问题问倒了。具体如图

kubectltop-vmtop-vm

网上搜索了下,难得看到有认真研判问题的IT文章了。这篇帖子推荐给大家。

  • 一、问题背景
  • 二、Buffer & cache原理
  • 三、缓存测试
  • 四、生产环境内存飙升解决方案的建议

目录

时间线:

  • 在上 Kubernetes 的前半年,只是用 Kubernetes,开发没有权限,业务服务极少,忙着写新业务,风平浪静。
  • 在上 Kubernetes 的后半年,业务服务较少,偶尔会阶段性被运维唤醒,问之 “为什么你们的服务内存占用这么高,赶紧查”。此时大家还在为新业务冲刺,猜测也许是业务代码问题,但没有调整代码去尝试解决。
  • 再后面,出过几次OOM的问题,普遍增加了容器限额 Limits,出现了好几个业务服务是内存小怪兽,因此如果不限制的话,服务过度占用会导致驱逐,因此反馈语也就变成了:“为什么你们的服务内存占用这么高,老被 OOM Kill,赶紧查”。

一、问题背景

kubernetes 运行的java应用,内存占用持续在增长。思路如下:

代码语言:javascript
复制
kubectl exec -it pod -n xxx /bin/bash

执行 top 命令查看下当前 pod 正在运行的进程,发现在容器里面有一个 7 号进程 VSZ 占用 6522m

应用内存占用 17 G之多,那很显然,并不是这个进程在捣鬼,但整个容器里面确实就只有这个进程在运行着,并且该 Java 进程还设置了分配内存的限制,最大不会超过 4g,可是内存还是一直在涨。

容器内部ps

而且容器里面执行 top 看到的信息很少,我们对比下实际操作系统的 top 命令执行结果多了很多列,例如RES、 %MEM 等等。

top命令

小TIPS: RSSVSZ指标相关的参数含义:

  • RSS是Resident Set Size(常驻内存大小)的缩写,用于表示进程使用了多少内存(RAM中的物理内存),RSS不包含已经被换出的内存。RSS包含了它所链接的动态库并且被加载到物理内存中的内存。RSS还包含栈内存和堆内存。
  • VSZ是Virtual Memory Size(虚拟内存大小)的缩写。它包含了进程所能访问的所有内存,包含了被换出的内存,被分配但是还没有被使用的内存,以及动态库中的内存。

使用top 需注意:

  • 虚拟内存通常并不会全部分配给物理内存;
  • 共享内存 SHR 并不一定是共享,例如程序的代码段、非共享的动态链接库,也都算在 SHR 里。其中,SHR 也包括了进程间真正共享的内存。 因此,计算多个进程的内存使用时,不建议把所有进程的 SHR 直接相加得出结果。

所以只从 top 看是不准确的,/proc/pid/status会更精准显示进程内存占用:

代码语言:javascript
复制
cat /proc/7/status

查看当前 pid 的状态,其中有一个字段VmRSS 表示当前进程所使用的内存,然而我们发现用户当前进程所占用的内存才2.3G 左右。

proc实际内存显示

但事实是 kubectl top pod` 查看 pod 的内存占用 确实发现该 pod 内存占用确实高达 17 G ,推断并不是容器内进程内存泄露导致的问题,那这就奇怪了,是什么原因导致占用这么多内存呢?

二、Buffer & cache原理

要继续排查这个问题,我们就需要先看看容器的内存统计是如何计算的了。

众所周知,操作系统系统的内存设计,有二级,三级物理缓存。为加快运算速度,缓存被大量应用,常用数据会被buffercache之类占用,在 Linux 操作系统中会把这部分内存算到已使用。

对于容器来讲,也会把某容器引发的cache占用算到容器占用的内存上,要验证这个问题,我们可以启动一个容器, dd 创建一个大文件观察下内存变化。

代码语言:javascript
复制
[root@8e3715641c31 /]# dd if=/dev/zero of=my_new_file count=1024000 bs=3024

1024000+0 records in
1024000+0 records out
3096576000 bytes (3.1 GB, 2.9 GiB) copied, 28.7933 s, 108 MB/s

你会发现,系统的 buff/cache 这一列会不断的增大。

代码语言:javascript
复制
[root@8e3715641c31 /]# free -h
              total        used        free      shared  buff/cache   available
Mem:          3.7Gi       281Mi       347Mi       193Mi       3.1Gi       3.0Gi
Swap:            0B          0B          0B

三、缓存测试

回归上述问题,会不会是 Java 程序在不停的往磁盘写文件,导致 cache 不断的增大呢?

代码语言:javascript
复制
kubectl logs -f pod-name -n namespace-name 

查看,发现整屏幕不断的输出 debug 日志。然后回到开头的图,我们会发现cache 空间高达 20g 左右。

topinfo

我们尝试把 cache 清掉下看看内存是否会下降,通过配置 drop_caches强行清楚系统缓存。

代码语言:javascript
复制
sync; echo 3 > /proc/sys/vm/drop_caches
sync; echo 1 > /proc/sys/vm/drop_caches
sync; echo 2 > /proc/sys/vm/drop_caches  

当执行完如上命令后,该 pod 的内存瞬间变小,同时磁盘 I/O 持续飙升,印证是 cache 问题导致的。告知开发把日志级别从 debug 改成 info,内存问题得到解决。

TIPS /proc/sys是虚拟文件系统,是与kernel实体间进行通信的桥梁。通过修改/proc中的文件,来对当前kernel的行为做出调整。通过调整/proc/sys/vm/drop_caches来释放内存。其默认数值为0。

  • 1 表示仅清除页面缓存(PageCache):
  • 2 表示清除目录项和inode
  • 3 //表示清空所有缓存(pagecache、dentries 和 inodes

四、生产环境内存飙升解决方案的建议

  • 建议1

合理的规划资源,对每个 Pod 配置Limit,限制资源使用。kubernetes 提供了针对 pod 级别的资源限制功能,但默认没有 CPU 和内存的限额。这意味着系统中的任何 Pod 将能够像执行该 Pod 所在的节点一样,消耗足够多的 CPU 和内存。这容易导致node节点的资源被无限占用。k8s官方也并不推荐该方式。

建议方案:通过 limits 来限制 Pod 的内存和 CPU ,这样一来一旦内存达到使用限制,pod 会自动重启,而不会影响到其他 pod。

代码语言:javascript
复制
resources:
      requests:
        cpu: "200m"
        memory: "128Mi"
      limits:
        cpu: "500m"
        memory: "512Mi"
  • 建议2

针对应用本身也需要加上资源使用限制,例如 Java 程序可以限制堆内存和非堆内存的使用:

堆内存分配:

  • JVM 最大分配的内存由**-Xmx** 指定,默认是物理内存的 1/4;
  • JVM 初始分配的内存由**-Xms** 指定,默认是物理内存的 1/64;
  • 默认空余堆内存小于 40% 时,JVM 就会增大堆直到-Xmx 的最大限制;空余堆内存大于 70% 时,JVM 会减少堆直到 -Xms 的最小限制; 因此,服务器的推荐设置是:-Xms、-Xmx 相等以避免在每次 GC 后调整堆的大小。对象的堆内存由称为垃圾回收器的自动内存管理系统回收。

非堆内存分配:

  • 由 XX:MaxPermSize 设置最大非堆内存的大小,默认是物理内存的 1/4;
  • JVM 使用**-XX:PermSize** 设置非堆内存初始值,默认是物理内存的 1/64;
  • -Xmn2G:设置年轻代大小为 2G;
  • -XX:SurvivorRatio,设置年轻代中 Eden 区与 Survivor 区的比值。
  • 建议3

应用本身优化,如本案例中所类似的问题要尽量避免在生产环境发生,即在生产环境开 debug 模式。即有安全风险,又会因为频繁的写日志会把 cache 打的非常高。建议将日志收集到专业的日志管理工具中,例如 ELK或SLS

  • 建议4

监控非常重要,针对应用本身的资源使用情况和系统的各项监控指标要完善,便于及时发现问题。

参考:https://blog.csdn.net/yucaifu1989/article/details/108084086

下一篇
举报
领券