Kubernetes上生产环境后,99%会遇到这两个故障

知了乎 2024-04-23 11:34:21
随着微服务的不断推进,使用 k8s 集群越来越多,越来越深入,随之而来会遇到一系列的问题,本文向大家介绍实际使用 k8s 遇到的一些问题以及解决方法。

 

问题一:修复 K8S 内存泄露问题

 

 
1.问题描述

 

1)当 k8s 集群运行日久以后,有的 node 无法再新建 pod,并且出现如下错误

 

当重启服务器之后,才可以恢复正常使用。查看 pod 状态的时候会出现以下报错。

 

  •  
  •  
applying cgroup … caused: mkdir …no space left on device或者在describe pod的时候出现cannot allocate memory

 

这时候你的 k8s 集群可能就存在内存泄露的问题了,当创建的 pod 越多的时候内存会泄露的越多,越快。

 

2)具体查看是否存在内存泄露

 

  •  
  •  
  •  
  •  
  •  
at /sys/fs/cgroup/memory/kubepods/memory.kmem.slabinfo当出现cat: /sys/fs/cgroup/memory/kubepods/memory.kmem.slabinfo: Input/output error则说明不存在内存泄露的情况如果存在内存泄露会出现slabinfo - version: 2.1# name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>

 

 
2.解决方案

 

1)解决方法思路

 

关闭 runc 和 kubelet 的 kmem,因为升级内核的方案改动较大,此处不采用。

 

2)kmem 导致内存泄露的原因

 

内核对于每个 cgroup 子系统的的条目数是有限制的,限制的大小定义在 kernel/cgroup.c #L139,当正常在 cgroup 创建一个 group 的目录时,条目数就加 1。我们遇到的情况就是因为开启了 kmem accounting 功能,虽然 cgroup 的目录删除了,但是条目没有回收。这样后面就无法创建 65535 个 cgroup 了。也就是说,在当前内核版本下,开启了 kmem accounting 功能,会导致 memory cgroup 的条目泄漏无法回收。

 

 
3.具体实现

 

1)编译 runc

 

  • 配置 go 语言环境

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
wget https://dl.google.com/go/go1.12.9.linux-amd64.tar.gztar xf go1.12.9.linux-amd64.tar.gz -C /usr/local/
写入bashrcvim ~/.bashrc export GOPATH="/data/Documents"export GOROOT="/usr/local/go"export PATH="$GOROOT/bin:$GOPATH/bin:$PATH"export GO111MODULE=off
验证source ~/.bashrc go env

 

  • 下载 runc 源码

 

  •  
  •  
  •  
  •  
  •  
mkdir -p /data/Documents/src/github.com/opencontainers/cd /data/Documents/src/github.com/opencontainers/git clone https://github.com/opencontainers/runccd runc/git checkout v1.0.0-rc9  # 切到v1.0.0-rc9 tag

 

  • 编译

 

  •  
  •  
  •  
  •  
安装编译组件sudo yum install libseccomp-develmake BUILDTAGS='seccomp nokmem'编译完成之后会在当前目录下看到一个runc的可执行文件,等kubelet编译完成之后会将其替换

 

2)编译 kubelet

 

  • 下载 kubernetes 源码

 

  •  
  •  
  •  
  •  
  •  
mkdir -p /root/k8s/cd /root/k8s/git clone https://github.com/kubernetes/kubernetescd kubernetes/git checkout v1.15.3

 

  • 制作编译环境的镜像(Dockerfile 如下)

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
FROM centos:centos7.3.1611
ENV GOROOT /usr/local/goENV GOPATH /usr/local/gopathENV PATH /usr/local/go/bin:$PATH
RUN yum install rpm-build which where rsync gcc gcc-c++ automake autoconf libtool make -y \    && curl -L https://studygolang.com/dl/golang/go1.12.9.linux-amd64.tar.gz | tar zxvf - -C /usr/local

 

  • 在制作好的 go 环境镜像中来进行编译 kubelet

 

  •  
  •  
  •  
  •  
docker run  -it --rm   -v /root/k8s/kubernetes:/usr/local/gopath/src/k8s.io/kubernetes   build-k8s:centos-7.3-go-1.12.9-k8s-1.15.3   bashcd /usr/local/gopath/src/k8s.io/kubernetes#编译GO111MODULE=off KUBE_GIT_TREE_STATE=clean KUBE_GIT_VERSION=v1.15.3 make kubelet GOFLAGS="-tags=nokmem"

 

3)替换原有的 runc 和 kubelet

 

  • 将原有 runc 和 kubelet 备份

 

  •  
  •  
mv /usr/bin/kubelet /home/kubeletmv /usr/bin/docker-runc /home/docker-runc

 

  • 停止 docker 和 kubelet

 

  •  
  •  
systemctl stop dockersystemctl stop kubelet

 

  • 将编译好的 runc 和 kubelet 进行替换

 

  •  
  •  
  •  
cp kubelet /usr/bin/kubeletcp kubelet /usr/local/bin/kubeletcp runc /usr/bin/docker-runc

 

  • 检查 kmem 是否关闭前需要将此节点的 pod 杀掉重启或者重启服务器,当结果为 0 时成功

 

  •  
cat /sys/fs/cgroup/memory/kubepods/burstable/memory.kmem.usage_in_bytes

 

  • 检查是否还存在内存泄露的情况

 

  •  
cat /sys/fs/cgroup/memory/kubepods/memory.kmem.slabinfo

 

问题二:k8s 证书过期问题的两种处理方法

 

 
1.前情提要

 

公司测试环境的 k8s 集群使用已经很长时间了,突然有一天开发联系我说 k8s 集群无法访问,开始以为是测试环境的机器磁盘空间不够了,导致组件异常或者把开发使用的镜像自动清理掉了,但是当登上机器去查验的时候发现不是这个原因。当时觉得也很疑惑。因为开发环境使用人数较少,不应该会出问题,所以就去查验 log 的相关报错信息。

 

 
2.问题现象

 

出现 k8s api 无法调取的现象,使用 kubectl 命令获取资源均返回如下报错:

 

  •  
Unable to connect to the server: x509: certificate has expired or is not yet valid

 

经网上搜索之后发现应该是 k8s 集群的证书过期了,使用命令排查证书的过期时间。

 

  •  
kubeadm alpha certs check-expiration

发现确实是证书过期了。

 

 
3.相关介绍以及问题解决

 

因为我们是使用 kubeadm 部署的 k8s 集群,所以更新起证书也是比较方便的,默认的证书时间有效期是一年,我们集群的 k8s 版本是 1.15.3 版本是可以使用以下命令来更新证书的,但是一年之后还是会到期,这样就很麻烦,所以我们需要了解一下 k8s 的证书,然后我们来生成一个时间很长的证书,这样我们就可以不用去总更新证书了。

 

  •  
  •  
  •  
kubeadm alpha certs renew all --config=kubeadm.yamlsystemctl restart kubeletkubeadm init phase kubeconfig all --config kubeadm.yaml

 

然后将生成的配置文件替换,重启 kube-apiserver、kube-controller、kube-scheduler、etcd 这4个容器即可。

 

另外 kubeadm 会在控制面板升级的时候自动更新所有证书,所以使用 kubeadm 搭建的集群最佳的做法是经常升级集群,这样可以确保你的集群保持最新状态并保持合理的安全性。但是对于实际的生产环境我们可能并不会去频繁的升级集群,所以这个时候我们就需要去手动更新证书。

 

下面我们通过调用 k8s 的 api 来实现更新一个 10 年的证书。

 

首先在 /etc/kubernetes/manifests/kube-controller-manager.yaml 文件加入配置。

  •  
  •  
  •  
  •  
  •  
  •  
  •  
spec:  containers:  - command:    - kube-controller-manager    # 设置证书有效期为 10年    - --experimental-cluster-signing-duration=87600h    - --client-ca-file=/etc/kubernetes/pki/ca.crt

 

修改完成后 kube-controller-manager 会自动重启生效。然后我们需要使用下面的命令为 Kubernetes 证书 API 创建一个证书签名请求。如果您设置例如 cert-manager 等外部签名者,则会自动批准证书签名请求(CSRs)。否则,您必须使用 kubectl certificate 命令手动批准证书。以下 kubeadm 命令输出要批准的证书名称,然后等待批准发生:

 

  •  
kubeadm alpha certs renew all --use-api --config kubeadm.yaml &

 

需要将全部 pending 的证书全部批准。

 

我们还不能直接重启控制面板的几个组件,这是因为使用 kubeadm 安装的集群对应的 etcd 默认是使用的 /etc/kubernetes/pki/etcd/ca.crt 这个证书进行前面的,而上面我们用命令 kubectl certificate approve 批准过后的证书是使用的默认的 /etc/kubernetes/pki/ca.crt 证书进行签发的,所以我们需要替换 etcd 中的 ca 机构证书:

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
# 先拷贝静态 Pod 资源清单cp -r /etc/kubernetes/manifests/ /etc/kubernetes/manifests.bakvi /etc/kubernetes/manifests/etcd.yaml......spec:  containers:  - command:    - etcd    # 修改为 CA 文件    - --peer-trusted-ca-file=/etc/kubernetes/pki/ca.crt    - --trusted-ca-file=/etc/kubernetes/pki/ca.crt......    volumeMounts:    - mountPath: /var/lib/etcd      name: etcd-data    - mountPath: /etc/kubernetes/pki  # 更改证书目录      name: etcd-certs  volumes:  - hostPath:      path: /etc/kubernetes/pki  # 将 pki 目录挂载到 etcd 中去      type: DirectoryOrCreate    name: etcd-certs  - hostPath:      path: /var/lib/etcd      type: DirectoryOrCreate    name: etcd-data......

 

由于 kube-apiserver 要连接 etcd 集群,所以也需要重新修改对应的 etcd ca 文件:

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
vi /etc/kubernetes/manifests/kube-apiserver.yaml......spec:  containers:  - command:    - kube-apiserver    # 将etcd ca文件修改为默认的ca.crt文件    - --etcd-cafile=/etc/kubernetes/pki/ca.crt......

 

除此之外还需要替换 requestheader-client-ca-file 文件,默认是 /etc/kubernetes/pki/front-proxy-ca.crt 文件,现在也需要替换成默认的 CA 文件,否则使用聚合 API,比如安装了 metrics-server 后执行 kubectl top 命令就会报错:

 

  •  
  •  
cp /etc/kubernetes/pki/ca.crt /etc/kubernetes/pki/front-proxy-ca.crtcp /etc/kubernetes/pki/ca.key /etc/kubernetes/pki/front-proxy-ca.key

 

这样我们就得到了一个 10 年证书的 k8s 集群,还可以通过重新编译 kubeadm 来实现一个 10 年证书的,这个我没有尝试,不过在初始化集群的时候也是一个方法。

 

作者丨知了乎
来源丨zhuanlan.zhihu.com/p/343031257
dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn
最新评论
访客 2024年04月08日

如果字段的最大可能长度超过255字节,那么长度值可能…

访客 2024年03月04日

只能说作者太用心了,优秀

访客 2024年02月23日

感谢详解

访客 2024年02月20日

一般干个7-8年(即30岁左右),能做到年入40w-50w;有…

访客 2023年08月20日

230721

活动预告