Kubernetes实战系列(4)

这一篇主要讲讲K8S集群中的资源相关的内容,包括使用资源配额quota,还有资源紧张的时候使用优先级类PriorityClass,使用动态缩放器(HPA和VPA)来让pod在繁忙的时候能够应对。


资源配额Quota

准确来说,资源配额是用来限制命名空间内pod等工作负载对资源的使用的,可以限制pod的数量,cpu的总使用量,内存的总使用量等。

这里限制默认的命名空间只能够使用10个pod,cpu设置为1000m,也就是一个逻辑处理器。一个逻辑处理器也就是1000m,至于内存的话,注意使用Mi而不是MiB。

可以通过kubectl get quota来查看默认命名空间的资源配额的使用情况,可以通过kubectl describe quota myquota来查看详细信息,这里就不概述了。

可以尝试创建10个以上的pod试试,然后查看资源配额的使用情况,发现还是3个。这是因为deployment的复制集指定了要10个pod,但是不能满足,所以整个deployment都不能调度。

如下是通过describe查看到的具体信息,可以看到创建失败这个复制集,描述的是整个复制集的状态。这就是资源配额能够起到的作用。

Pod资源调度

上面的quota是站在命名空间的角度来看的,那么对于pod来说能否设置资源的需求呢?答案是可以的,并且设置pod的资源需求会影响pod本身的调度,因为调度器会将能够满足pod需求的节点上。

准确来说是在spec.resources.requests字段和spec.resources.limits字段来设置cpu和memory的使用量,节点需要满足requests的请求才能够调度该pod(该requests数值×容器数量),至于limits是限制pod内容器使用过多资源,当超过limits的时候就会杀死对应的容器。

不过这个Pod的资源还是和资源配额quota有关系的,上面我们只通过--hard参数指定了cpu和memory的使用量,实际上这默认是requests的限制,如果要设置limits的限制,需要使用--hard=limits.cpu=来设置。

前后的区别就是多一列,仔细看是多了LIMIT这一列。如果总的Pod的requests或者limits资源(pod数×数值×容器数)已经超过了资源配额的限制,则会创建失败。

优先级类

虽然上面确实设置了命名空间或者pod的资源占用情况,但是还是有时候资源会不足。这时候集群里面的各个工作负载就会出现竞争情况。可能是pod过度调度导致的,也有可能是pod实际使用的资源比它声明的要多。

反正就是资源紧张的时候就会出现竞争,那么肯定不能当核心业务受损呀。所以给核心业务一个更高的优先级,而且在k8s中默认是采用抢占式的,优先级高的pod能够抢占别人的资源。不过可以通过设置--preemption-policy=Nerver来禁止抢占。

我们通过预定义优先级类,然后让pod使用该优先级达到效果,通过spec.priorityClassName指定。优先级需要指定value,范围是0~2的32次方-1,也就是差不多10亿以上。

自动缩放

主要指的是Pod的自动缩放,包括水平自动缩放和垂直自动缩放。前者通过增加pod的数量,后者则是增加pod的资源。两者都需要metrics server服务,不过VPA更加复杂,还需要安装三个组件。

使用kubectl autoscale deployment name --cpu-percent=50 --min=1 --max=5就可以创建一个HPV了,这比较简单。默认是达到条件(cpu繁忙)立刻增加pod数量,但是cpu不繁忙的时候要等五分钟才能够看到pod减少。

configMap和Secret

使用命令行创建configMap也简单,主要是通过--from-literal和--from-file两个参数来设置的。不过注意的是,通过describe查看是明文的,意味着不适合存储敏感数据。

不过使用secret则可以很好的解决这个问题,创建secret的方法和configMap类似,第一个docker-registry用于存储docker仓库的认证信息,generic则是通用型secret,而tls则是存储tsl的公私钥和证书。其中只有generic是自定义字段,可以通过--from-file和--from-literal来设置。

复制代码
kubectl create secret (docker-registry | generic | tls) [options]

并且通过describe查看的时候就看不到数据了,不过实际上数据并不是加密的,而是通过base64编码的。

configMap和secret一般作为环境变量和挂载卷给Pod使用,如何作为环境变量呢?是通过spec.containers.env.valueFrom字段设置的(指定configMap的名称和键名),然后在spec.containers.env.name设置键名。

至于挂载,则是在spec.volumes.configMap字段设置,指定名称就好了。不过说到挂载,可以提一提emptyDir,是通过spec.volumes.emptyDir指定,value可以是{},也可以是sizeLimit: 2Gi这样的键值对。不过它实质上是hostPath类型,只不过用的是临时目录。

配置持久存储

虽然主要是使用PV和PVC,但是hostPath类型也能够实现持久存储,只不过挂载目录在pod所在的节点上,当pod迁移的时候就会很麻烦。

可以通过spec.volumes.hostPath指定path和type指定,type主要是用于校验path的,确保file是file,目录是目录,错误则会报错。如果type为空,则可能会导致安全问题。

但是重点是如何创建PV和PVC,不过这两个都不能够通过命令行创建,并且这属于集群资源。使用PVC的时候不但可以通过spec.volumeName指定PV,还可以通过spec.storageClassName指定存储类,这样Kubernetes 会检查该 PV 的容量和访问模式是否满足 PVC 的要求。如果不满足,绑定会失败,并 fallback 到存储类动态供给。

复制代码
apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-static-pv
spec:
  storageClassName: fast-ssd # 手动指定该PV属于'fast-ssd'存储类
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  nfs:
    server: nfs-server.example.com
    path: /exports/data

要注意的是如果PV指定了存储类,PVC也要指定同样的存储类,否则无法绑定。或者说都不指定存储类,不过不利于分类。

存储类不一定需要有动态供给能力,可以是自定义的。如下是一个虚拟的 provisioner 名称​​,实际不存在对应的驱动。Kubernetes 看到这个 provisioner 时,​​​​不会尝试动态创建 PV​​,但会允许 PVC 绑定到手动创建的、同名的 PV。

复制代码
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: my-nfs-class
provisioner: example.com/no-provisioner  # 关键:使用一个假的后端驱动
reclaimPolicy: Retain                   # 统一回收策略
volumeBindingMode: Immediate            # 绑定模式
相关推荐
专注代码七年5 小时前
Docker 本地开发环境搭建(MySQL5.7 + Redis7 + Nginx + 达梦8)- Windows11 版 2.0
nginx·docker·容器
我真的是大笨蛋5 小时前
K8S-Pod(上)
java·云原生·容器·kubernetes
野生技术架构师6 小时前
开发微服务的9个最佳实践
微服务·云原生·架构
脚大江山稳10 小时前
docker使用nginxWebUI配置
java·docker·容器
iiYcyk12 小时前
服务器线程高占用定位方法
容器
哈里谢顿12 小时前
Kubernetes 服务高可用详解
kubernetes
A-刘晨阳12 小时前
从全球视角到K8s落地的Apache IoTDB实战
kubernetes·apache·iotdb
曾经的三心草13 小时前
微服务的编程测评系统22-项目部署结束
微服务·云原生·架构
Delphi菜鸟1 天前
docker 部署RustDesk服务
运维·docker·容器