5

两个常见的Kubernetes问题以及解决方法

 2 years ago
source link: http://dockone.io/article/2434554
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集群越来越多,越来越深入,随之而来会遇到一系列的问题,本文向大家介绍实际使用Kubernetes遇到的一些问题以及解决方法。

问题一:报错cannot allocate memory或者no space left on device,修复Kubernetes内存泄露问题

一、当Kubernetes集群运行日久以后,有的Node无法再新建Pod,并且出现如下错误,当重启服务器之后,才可以恢复正常使用。查看Pod状态的时候会出现以下报错。
applying cgroup … caused: mkdir …no space left on device
或者在describe pod的时候出现cannot allocate memory

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

二、具体查看是否存在内存泄露
cat /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>

一、解决方法思路:关闭runc和kubelet的kmem,因为升级内核的方案改动较大,此处不采用。

二、kmem导致内存泄露的原因:

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

1、配置go语言环境
wget https://dl.google.com/go/go1.12.9.linux-amd64.tar.gz
tar xf go1.12.9.linux-amd64.tar.gz -C /usr/local/

写入bashrc
vim ~/.bashrc 
export GOPATH="/data/Documents"
export GOROOT="/usr/local/go"
export PATH="$GOROOT/bin:$GOPATH/bin:$PATH"
export GO111MODULE=off

验证
source ~/.bashrc 
go env

2、下载runc源码
mkdir -p /data/Documents/src/github.com/opencontainers/
cd /data/Documents/src/github.com/opencontainers/
git clone https://github.com/opencontainers/runc
cd runc/
git checkout v1.0.0-rc9  # 切到v1.0.0-rc9 tag

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

二、编译kubelet

1、下载Kubernetes源码
mkdir -p /root/k8s/
cd /root/k8s/
git clone https://github.com/kubernetes/kubernetes
cd kubernetes/
git checkout v1.15.3

2、制作编译环境的镜像(Dockerfile如下)
FROM centos:centos7.3.1611

ENV GOROOT /usr/local/go
ENV GOPATH /usr/local/gopath
ENV 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

3、在制作好的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   bash
cd /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"

三、替换原有的runc和kubelet

1、将原有runc和kubelet备份
mv /usr/bin/kubelet /home/kubelet
mv /usr/bin/docker-runc /home/docker-runc

2、停止Docker和kubelet
systemctl stop docker
systemctl stop kubelet

3、将编译好的runc和kubelet进行替换
cp kubelet /usr/bin/kubelet
cp kubelet /usr/local/bin/kubelet
cp runc /usr/bin/docker-runc

4、检查kmem是否关闭前需要将此节点的Pod杀掉重启或者重启服务器,当结果为0时成功
cat /sys/fs/cgroup/memory/kubepods/burstable/memory.kmem.usage_in_bytes

5、是否还存在内存泄露的情况
cat /sys/fs/cgroup/memory/kubepods/memory.kmem.slabinfo

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

公司测试环境的Kubernetes集群使用已经很长时间了,突然有一天开发联系我说Kubernetes集群无法访问,开始以为是测试环境的机器磁盘空间不够了,导致组件异常或者把开发使用的镜像自动清理掉了,但是当登上机器去查验的时候发现不是这个原因。当时觉得也很疑惑。因为开发环境使用人数较少,不应该会出问题,所以就去查验log的相关报错信息。
出现Kubernetes API无法调取的现象,使用kubectl命令获取资源均返回如下报错:
Unable to connect to the server: x509: certificate has expired or is not yet valid

经网上搜索之后发现。应该是Kubernetes集群的证书过期了,使用命令排查证书的过期时间
kubeadm alpha certs check-expiration

发现确实是证书过期了。

相关介绍以及问题解决

因为我们是使用kubeadm部署的Kubernetes集群,所以更新起证书也是比较方便的,默认的证书时间有效期是一年,我们集群的Kubernetes版本是1.15.3版本是可以使用以下命令来更新证书的,但是一年之后还是会到期,这样就很麻烦,所以我们需要了解一下Kubernetes的证书,然后我们来生成一个时间很长的证书,这样我们就可以不用去总更新证书了。
kubeadm alpha certs renew all --config=kubeadm.yaml
systemctl restart kubelet
kubeadm init phase kubeconfig all --config kubeadm.yaml
然后将生成的配置文件替换,重启kube-apiserver、kube-controller、kube-scheduler、etcd这4个容器即可

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

下面我们通过调用Kubernetes的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.bak
vi /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.crt
cp /etc/kubernetes/pki/ca.key /etc/kubernetes/pki/front-proxy-ca.key

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

原文链接:https://zhuanlan.zhihu.com/p/343031257

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK