gateway pod oomkilled排查


公有云vali gateway服务容器重启问题排查

目录

[一、问题描述... 2](#_Toc54605147)

[二、问题复现与排查... 2](#_Toc54605148)

[三、问题分析... 8](#_Toc54605149)

[四,意外情况... 10](#_Toc54605150)

[五、两种内存策略对比压测与复现... 11](#_Toc54605151)

[六、总结与解决方案... 12](#_Toc54605152)

[七、参考... 12](#_Toc54605153) 

一、问题描述
------

gateway服务容器发现oom重启

时间:2023/10/10 22点16分

可用区:华南/华东

 图(一)

 图(二)

二、问题复现与排查
---------

1,生产环境复现压测

内存监控图

 图(三)

gateway是go语言开发的

生产环境压测结果存在内存泄露的可能性,使用go tool pprof排查泄露问题

go程序存在泄露的点主要有

Heap

Goroutine

全局变量

Http 未关闭的响应主体

配置go pprof

 import _ "net/http/pprof"

然后就可以在浏览器中使用http://localhost:port/debug/pprof/ 直接看到当前web服务的状态,包括CPU占用情况和内存使用情况等。

当然,非WEB的也可以用下面方式启动WEB。

在 main 方法中增加

func main() {

 go func() {

 http.ListenAndServe("localhost:7777", nil)

 }()

pprof可以使用图像方式查看,如下:

 图(四)

由于生产环境不能修改程序,在UAT环境重现压测

收集heap/goroutine数据,收集压测前和压测冷却后的方面对比,如果存在

 curl -s -v [http://192.168.1.19:7777/debug/pprof/heap](http://192.168.1.19:7777/debug/pprof/heap) > heap1140.out

curl -s -v [http://192.168.1.19:7777/debug/pprof/goroutine](http://192.168.1.19:7777/debug/pprof/goroutine) > goroutine1140.out

 curl -s -v [http://192.168.1.19:7777/debug/pprof/heap](http://192.168.1.19:7777/debug/pprof/heap) > heap1549.out

curl -s -v [http://192.168.1.19:7777/debug/pprof/goroutine](http://192.168.1.19:7777/debug/pprof/goroutine) > goroutine1549.out

比较前后的数据

#go tool pprof -base goroutine1350.out goroutine1549.out

go tool pprof -base goroutine1350.out goroutine1549.out

File: vali_gateway

Build ID: b94e9e6eab7c528b19eda2124cd449cac03b6335

Type: goroutine

Time: Oct 15, 2019 at 1:51pm (CST)

Entering interactive mode (type "help" for commands, "o" for options)

(pprof) top

Showing nodes accounting for 0, 0% of 2 total

 flat flat% sum% cum cum%

 1 50.00% 50.00% 0 0% net/http.(*connReader).backgroundRead

 -1 50.00% 0% -1 50.00% runtime.gopark

 0 0% 0% -1 50.00% internal/poll.(*FD).Read

 0 0% 0% -1 50.00% internal/poll.(*pollDesc).wait

 0 0% 0% -1 50.00% internal/poll.(*pollDesc).waitRead

 0 0% 0% -1 50.00% internal/poll.runtime_pollWait

 0 0% 0% -1 50.00% net.(*conn).Read

 0 0% 0% -1 50.00% net.(*netFD).Read

 0 0% 0% -1 50.00% runtime.netpollblock

#go tool pprof -base heap1350.out heap1549.out

go tool pprof -base heap1350.out heap1549.out

File: vali_gateway

Build ID: b94e9e6eab7c528b19eda2124cd449cac03b6335

Type: inuse_space

Time: Oct 15, 2019 at 1:51pm (CST)

No samples were found with the d华东ult sample value type.

Try "sample_index" command to analyze different sample values.

Entering interactive mode (type "help" for commands, "o" for options)

(pprof) top

Showing nodes accounting for 0, 0% of 0 total

 flat flat% sum% cum cum%

从heap和goroutine对比看不出有泄露

由于UAT环境没有监控

查看pod使用内存的情况

压测前

/opt/vali # ps aux -o pid,vsz,rss,comm

PID VSZ RSS COMMAND

 1 8168 676 run-vali-service

 7 1.2g 33m vali_gateway

 78 8168 916 sh

 96 8168 916 sh

 125 8168 908 sh

 154 8168 912 sh

 164 8168 672 ps

压测中

/opt/vali # ps aux -o pid,vsz,rss,comm

PID VSZ RSS COMMAND

 1 8168 676 run-vali-service

 7 1.2g 63m vali_gateway

 78 8168 916 sh

 96 8168 916 sh

 125 8168 908 sh

 143 8168 912 sh

 153 8168 672 ps

压测冷却后

/opt/vali # ps aux -o pid,vsz,rss,comm

PID VSZ RSS COMMAND

 1 8168 676 run-vali-service

 7 1.2g 33m vali_gateway

 78 8168 916 sh

 96 8168 916 sh

 125 8168 908 sh

 154 8168 912 sh

 161 8168 672 ps

从以上数据看出内存没有增长的现象,基本排除泄露的可能性

三、问题分析
------

为什么prd会内存一直增长而uat没有,环境的差异入手

操作系统的内核版本不一样

UAT是centos7.2,PRD 是centos7.6

从网络搜索有类似的案例,gateway使用go的版本是1.13

一直以来 go 的 runtime 在释放内存返回到内核时,在 Linux 上使用的是 MADV_DONTNEED,虽然效率比较低,但是会让 RSS(resident set size 常驻内存集)数量下降得很快。不过在 go 1.12 里专门针对这个做了优化,runtime 在释放内存时,使用了更加高效的 MADV_FREE 而不是之前的 MADV_DONTNEED

这样带来的好处是,一次 GC 后的内存分配延迟得以改善,runtime 也会更加积极地将释放的内存归还给操作系统,以应对大块内存分配无法重用已存在的堆空间的问题。不过也会带来一个副作用:RSS 不会立刻下降,而是要等到系统有内存压力了,才会延迟下降。需要注意的是,`MADV_FREE ` 需要 4.5 以及以上内核,否则 runtime 会继续使用原先的 `MADV_DONTNEED` 方式。

但是centos7.6内核版本是

# uname -r

3.10.0-957.el7.x86_64

不过centos发行版会使用backport,高版本中的新特性移植到发行版的低版本中

采用strace查看程序的内存分配(需要在压测时查看,空闲状态没有内存活动)

[root@CLAM400137 ~]# ps aux | grep gateway

root 27723 0.0 0.3 1137740 30508 ? Sl Oct14 1:04 /opt/vali/vali_gateway --server_address=100.71.64.39:0 --registry=consul api --handler=http --namespace=vali.v1.portal

root 28731 3.5 0.4 1285908 33644 ? Sl 11:31 8:53 /opt/vali/vali_gateway --server_address=192.168.1.19:0 --registry=consul api --handler=http --namespace=vali.v1.portal

root 31738 0.0 0.0 112644 948 pts/29 S+ 15:43 0:00 grep --color=auto gateway

[root@CLAM400137 ~]# strace -q -e trace=memory -p 28731

madvise(0xc0028dc000, 8192, MADV_DONTNEED) = 0

madvise(0xc0028d4000, 8192, MADV_DONTNEED) = 0

madvise(0xc0028c8000, 16384, MADV_DONTNEED) = 0

madvise(0xc0028ba000, 16384, MADV_DONTNEED) = 0

madvise(0xc0028aa000, 8192, MADV_DONTNEED) = 0

madvise(0xc0028a0000, 8192, MADV_DONTNEED) = 0

madvise(0xc00289a000, 8192, MADV_DONTNEED) = 0

madvise(0xc002896000, 8192, MADV_DONTNEED) = 0

madvise(0xc00288e000, 24576, MADV_DONTNEED) = 0

madvise(0xc00288a000, 8192, MADV_DONTNEED) = 0

madvise(0xc002884000, 8192, MADV_DONTNEED) = 0

madvise(0xc002880000, 8192, MADV_DONTNEED) = 0

madvise(0xc00286e000, 24576, MADV_DONTNEED) = 0

madvise(0xc002868000, 8192, MADV_DONTNEED) = 0

madvise(0xc002862000, 8192, MADV_DONTNEED) = 0

madvise(0xc002854000, 32768, MADV_DONTNEED) = 0

[root@华东m11672 11759]# ps aux | grep gate

root 603 0.0 0.0 112724 988 pts/1 S+ 18:11 0:00 grep --color=auto gate

root 11759 6.7 0.3 1987748 49732 ? Sl 10月11 456:33 /opt/vali/vali_gateway --server_address=192.168.1.116:0 --api_address=192.168.1.116:443 --registry=consul api --handler=http --namespace=vali.v1.portal

[root@华东m11672 11759]# strace -q -e trace=memory -p 11759

madvise(0xc0010d6000, 8192, MADV_FREE) = 0

madvise(0xc0010cc000, 24576, MADV_FREE) = 0

madvise(0xc0010c6000, 8192, MADV_FREE) = 0

madvise(0xc0010c0000, 16384, MADV_FREE) = 0

madvise(0xc0010ba000, 16384, MADV_FREE) = 0

madvise(0xc0010b4000, 16384, MADV_FREE) = 0

madvise(0xc0010aa000, 16384, MADV_FREE) = 0

madvise(0xc0010a6000, 8192, MADV_FREE) = 0

madvise(0xc00109a000, 16384, MADV_FREE) = 0

madvise(0xc001096000, 8192, MADV_FREE) = 0

madvise(0xc00108a000, 16384, MADV_FREE) = 0

madvise(0xc001086000, 8192, MADV_FREE) = 0

madvise(0xc001082000, 8192, MADV_FREE) = 0

madvise(0xc00107c000, 16384, MADV_FREE) = 0

发现在centos7.6中已经支持MADV_FREE策略,所以导致prd的程序内存占用不断增加,另外docker是共享内核模式,内核回收内存的压力是取决于节点内核空闲情况而非容器。由于采用k8s+docker容器化部署,设置了pod 内存limit为1G,当累积到1G时出现oom

四,意外情况
------

在生产压测gateway的过程,导致consul 实例OOM,经过分析,consul实例的内存limits设置为512M,偏小。另外压测时,gateway收到服务请求会consul查询服务健康状况,所以consul的压力也随着增加。为了保证后续的压测顺利,调整consul的内存limits为2000Mi。

对比复现压测时的资源消耗监控图(五),consul实例消耗内存大于800Mi。

图(五)

五、两种内存策略对比压测与复现
---------------

对比压测思路,根据两种内存策略的特性,MADV_FREE倾向内存页使用后不回收,MADV_DONTNEED倾向回收(可以从图(六)监控图看两种策略的差异)。同时为了加速复现效果,设置内存limit为100M,压力尽可能接近oom,但不能oom,随着时间的增加,MADV_FREE策略的实例内存页由于倾向不加收而不断累积,最终导致OOM,而MADV_DONTNEED策略的实例则不会被OOM。由压力没办法精确控制,从长时间看应该是MADV_FREE策略的实例被OOM的次数应该远大小后者。

 图(六)

比对压测结果:

经过三天测试,MADV_FREE策略的实例被OOM 14次,MADV_DONTNEED策略的实例被OOM1次,符合压测预期结果。

六、总结与解决方案
---------

1,结论重启非内存泄露,由内存MADV_REE策略与资源limit设置太小引起的。

2,回退MADV_REE策略为 MADV_DONTNEED,设置 GODEBUG=madvdontneed=1,避免。

3,调整服务实例内存limits,gateway与consul内存limits为2000Mi,其他设置为1500Mi

4,标准化一致的环境才能更好的提供排错复现条件,UAT环境需要与生产环境保持一致。

 七、参考
-----

[https://ms2008.github.io/2019/06/30/golang-madvfree/](https://ms2008.github.io/2019/06/30/golang-madvfree/)

[https://www.cnblogs.com/ExMan/p/12175312.html](https://www.cnblogs.com/ExMan/p/12175312.html)

[https://www.v2ex.com/t/666257](https://www.v2ex.com/t/666257)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值