3

Kubernetes上生产环境后,99%会遇到这两个故障 - 运维 - dbaplus社群:围绕Data、Bloc...

 3 weeks ago
source link: https://dbaplus.cn/news-134-5988-1.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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 年证书的,这个我没有尝试,不过在初始化集群的时候也是一个方法。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK