kube-apiserver内存溢出问题调查及go tool pprof工具的使用


本文摘自网络,作者,侵删。

问题描述

测试集群三台master,每个master上面的kube-apiserver都频繁的重启。登录其中一台master,发现kube-apiserver的内存占用特别高,每次重启完后内存很快就飙到了20G左右,而且还有继续增长的趋势。因为默认kube-apiserver的静态pod是没有设置memeory limit的,最终api-server会吃光机器的所有内存,导致master机器运行异常。

查看容器内存占用的命令:

docker stats --no-stream |grep kube-apiserver

临时方案

出现问题后,没有找到问题根源,所以先修改了kube-apiserver的静态pod yaml文件,位于/etc/kubernetes/manifests/kube-apiserver.yaml,添加resources.limits.memory 为32g(本机内存为64g)。这样现在kube-apiserver的内存,使它消耗内存到32g的时候就oom kill自动重启,不会影响宿主机的性能

问题分析

api-server内存持续升高,肯定是有资源在堆内存里申请了空间但是没有释放。首先考虑是否是因为建立了太多的链接导致的,使用如下指令查询kube-apiserver链接数:

netstat -nat | grep -i "6443" | wc -l

发现链接数在100多并不算多。

继续分析只能考虑导出kube-apiserver的heap文件来查看其详细的内存分布。这种方式需要使用go语言包的pprof工具,下面详细讲解go tool pprof 工具的使用以及kube-apiserver的配置。

go tool pprof 工具使用

kube-apiserver集成了pprof工具,可以通过链接/debug/prof/heap的url来获得heap文件。在kubernetes 1.18这个功能是默认打开的,1.18之前需要修改kube-apiserver的启动参数加上--profile true。测试集群kube-apiserver是1.17.3,所以需要修改启动参数,修改过程如下,profiling 改为true

vi /etc/kubernetes/manifests/kube-apiserver.yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    component: kube-apiserver
    tier: control-plane
  name: kube-apiserver
  namespace: kube-system
spec:
  containers:
  - command:
    - kube-apiserver
    - --advertise-address=10.250.7.21
    - --allow-privileged=true
    - --anonymous-auth=True
    - --apiserver-count=3
    - --authorization-mode=Node,RBAC
    - --bind-address=0.0.0.0
    - --client-ca-file=/etc/kubernetes/ssl/ca.crt
    - --enable-admission-plugins=NodeRestriction
    - --enable-aggregator-routing=False
    - --enable-bootstrap-token-auth=true
    - --endpoint-reconciler-type=lease
    - --etcd-cafile=/etc/ssl/etcd/ssl/ca.pem
    - --etcd-certfile=/etc/ssl/etcd/ssl/node-t-paas-k8s-0-master-0.pem
    - --etcd-keyfile=/etc/ssl/etcd/ssl/node-t-paas-k8s-0-master-0-key.pem
    - --etcd-servers=https://10.250.7.21:2379,https://10.250.7.22:2379,https://10.250.7.23:2379
    - --feature-gates=CSINodeInfo=true,VolumeSnapshotDataSource=true,ExpandCSIVolumes=true,RotateKubeletClientCertificate=true
    - --insecure-port=0
    - --kubelet-client-certificate=/etc/kubernetes/ssl/apiserver-kubelet-client.crt
    - --kubelet-client-key=/etc/kubernetes/ssl/apiserver-kubelet-client.key
    - --kubelet-preferred-address-types=InternalDNS,InternalIP,Hostname,ExternalDNS,ExternalIP
    - --profiling=true

修改完保存,kube-apiserver是静态pod修改完yaml文件后,kubelete会自动重启kube-apiserver的容器。pprof工具是go tool里的工具,所以需要下载go语言包到宿主机。为了方便链接(不用指定证书),使用kubectl proxy 在宿主机127.0.0.1:8001启动一个代理。下面是详细操作记录:

wget https://studygolang.com/dl/golang/go1.15.6.linux-amd64.tar.gz
tar xzvf go1.15.6.linux-amd64.tar.gz

#启动proxy
kubectl proxy
Starting to serve on 127.0.0.1:8001

#新开一个terminal,执行如下指令,进入交互界面
./go tool pprof http://127.0.0.1:8001/debug/pprof/heap

#进入交互界面后,输入top 20查看内存使用前20的函数调用
Fetching profile over HTTP from http://127.0.0.1:8001/debug/pprof/heap
Saved profile in /root/pprof/pprof.kube-apiserver.alloc_objects.alloc_space.inuse_objects.inuse_space.004.pb.gz
File: kube-apiserver
Type: inuse_space
Time: Dec 10, 2020 at 2:46pm (CST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)top 20

#得到如下输出
Showing nodes accounting for 11123.22MB, 95.76% of 11616.18MB total
Dropped 890 nodes (cum <= 58.08MB)
Showing top 20 nodes out of 113
     flat  flat%   sum%        cum   cum%
9226.15MB 79.43% 79.43%  9226.15MB 79.43%  bytes.makeSlice
1122.61MB  9.66% 89.09%  1243.65MB 10.71%  k8s.io/kubernetes/vendor/k8s.io/api/core/v1.(*ConfigMap).Unmarshal
 139.55MB  1.20% 90.29%   153.05MB  1.32%  k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/apis/meta/v1.(*ObjectMeta).Unmarshal
 120.53MB  1.04% 91.33%   165.03MB  1.42%  encoding/json.(*decodeState).objectInterface
 117.29MB  1.01% 92.34%   117.29MB  1.01%  reflect.unsafe_NewArray
 108.03MB  0.93% 93.27%   108.03MB  0.93%  reflect.mapassign
  66.51MB  0.57% 93.84%    66.51MB  0.57%  k8s.io/kubernetes/vendor/github.com/json-iterator/go.(*Iterator).ReadString
  62.51MB  0.54% 94.38%    62.51MB  0.54%  k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/registry/generic.ObjectMetaFieldsSet
  61.51MB  0.53% 94.91%    61.51MB  0.53%  k8s.io/kubernetes/vendor/k8s.io/apiextensions-apiserver/pkg/registry/customresource.objectMetaFieldsSet
  43.01MB  0.37% 95.28%   137.03MB  1.18%  k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/runtime.structToUnstructured
     18MB  0.15% 95.43%   183.61MB  1.58%  k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/storage/cacher.(*watchCache).Replace
  13.50MB  0.12% 95.55%   137.03MB  1.18%  k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/runtime.toUnstructured
      9MB 0.077% 95.63%   237.54MB  2.04%  k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured.unstructuredJSONScheme.decodeToUnstructured
   7.50MB 0.065% 95.69%   144.53MB  1.24%  k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/runtime.(*unstructuredConverter).ToUnstructured
      6MB 0.052% 95.74%   123.29MB  1.06%  reflect.MakeSlice
   1.50MB 0.013% 95.76%  8944.02MB 77.00%  encoding/json.mapEncoder.encode

通过heap文件输出,可以看到占用内存最多的是makeSlice函数占用了9G多的内存。它的调用者是v1.(*ConfigMap).Unmarshal,configmap是kubernetes的一种资源,Unmarshal是json和struct做转换的函数。所以怀疑与configmap资源有关。

阅读剩余部分

相关阅读 >>

Go time an online Golang date format tool

Golang文件复制

海康/大华sdk协议easycvr如何通过Go语言读取csv文件内容?

Go + websocket 快速实现一个 chat 服务

24 Goroutine channel实现并发和并行(一)

Golang sync.once 实现的思考

(一)Gof 通过epoll模型管理连接

使用 Go-randgen 测试 join 查询

Golang 类似php中 http_build_query 方法

Golang 中的 nil

更多相关阅读请进入《Go》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...