Kubernetes 准入控制

news2024/11/16 19:55:06

header-how-to-optimize-kubernetes-for-reliability-and-cos

Author:rab


目录

    • 前言
    • 一、限制范围
    • 二、配置案例
      • 2.1 名称空间 CPU 与内存约束
        • 2.1.1 CPU 约束
        • 2.1.2 内存约束
        • 2.1.3 默认 CPU 申请约束
        • 2.1.4 默认内存申请约束
      • 2.2 名称空间总容量限额约束
    • 总结


前言

LimitRange 是限制命名空间内可为每个适用的对象类别 (例如 Pod 或 PersistentVolumeClaim) 指定的资源分配量(限制和请求)的策略对象。默认情况下, Kubernetes 集群上的容器运行使用的计算资源没有限制。 使用 Kubernetes 资源配额, 管理员可以在一个指定的命名空间内限制集群资源的使用与创建。 在命名空间中,一个 Pod 最多能够使用命名空间的资源配额所定义的 CPU 和内存用量。

一个 LimitRange(限制范围) 对象提供的限制能够做到:

  • 在一个命名空间中实施对每个 Pod 或 Container 最小和最大的资源使用量的限制。
  • 在一个命名空间中实施对每个 PersistentVolumeClaim 能申请的最小和最大的存储空间大小的限制。
  • 在一个命名空间中实施对一种资源的申请值和限制值的比值的控制。
  • 设置一个命名空间中对计算资源的默认申请/限制值,并且自动的在运行时注入到多个 Container 中。

当某命名空间中有一个 LimitRange 对象时,将在该命名空间中实施 LimitRange 限制。

且 LimitRange 的名称必须是合法的 DNS 子域名,这一要求意味着名称必须满足如下规则:

  • 不能超过 253 个字符
  • 只能包含小写字母、数字,以及 ‘-’ 和 ‘.’
  • 必须以字母数字开头
  • 必须以字母数字结尾

官方参考文档:https://kubernetes.io/zh-cn/docs/concepts/policy/limit-range/

一、限制范围

  1. 管理员在一个命名空间内创建一个 LimitRange 对象。

  2. 用户在此命名空间内创建(或尝试创建) Pod 和 PersistentVolumeClaim 等对象。

  3. 首先,LimitRanger 准入控制器对所有没有设置计算资源需求的所有 Pod(及其容器)设置默认请求值与限制值。

  4. 其次,LimitRange 跟踪其使用量以保证没有超出命名空间中存在的任意 LimitRange 所定义的最小、最大资源使用量以及使用量比值。

  5. 若尝试创建或更新的对象(Pod 和 PersistentVolumeClaim)违反了 LimitRange 的约束, 向 API 服务器的请求会失败,并返回 HTTP 状态码 403 Forbidden 以及描述哪一项约束被违反的消息。

  6. 若你在命名空间中添加 LimitRange 启用了对 cpumemory 等计算相关资源的限制, 你必须指定这些值的请求使用量与限制使用量。否则,系统将会拒绝创建 Pod。

  7. LimitRange 的验证仅在 Pod 准入阶段进行,不对正在运行的 Pod 进行验证。 如果你添加或修改 LimitRange,命名空间中已存在的 Pod 将继续不变。

  8. 如果命名空间中存在两个或更多 LimitRange 对象,应用哪个默认值是不确定的。

二、配置案例

2.1 名称空间 CPU 与内存约束

我们可以通过 LimitRange 对象声明 CPU 的最小和最大值. 如果 Pod 不能满足 LimitRange 的限制,就无法在该命名空间中被创建。

2.1.1 CPU 约束

1、创建命名空间

创建一个命名空间,以便本练习中创建的资源和集群的其余部分相隔离。

kubectl create namespace constraints-cpu-example

2、创建 LimitRange

vim cpu-constraints.yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-min-max-demo-lr
spec:
  limits:
  - max:
      cpu: "800m"
    min:
      cpu: "200m"
    type: Container
kubectl apply -f cpu-constraints.yaml -n constraints-cpu-example
kubectl get limitrange cpu-min-max-demo-lr --output=yaml -n constraints-cpu-example

image-20231107143848149

输出结果显示 CPU 的最小和最大限制符合预期。但需要注意的是,尽管你在 LimitRange 的配置文件中你没有声明默认值,默认值也会被自动创建(且默认为你设置的 max 值)。

此时,每当你在 constraints-cpu-example 命名空间中创建 Pod 时,或者某些其他的 Kubernetes API 客户端创建了等价的 Pod 时,Kubernetes 就会执行下面的步骤:

  • 如果 Pod 中的任何容器未声明自己的 CPU 请求和限制,控制面将为该容器设置默认的 CPU 请求和限制。
  • 确保该 Pod 中的每个容器的 CPU 请求至少 200 millicpu(否则创建失败)。
  • 确保该 Pod 中每个容器 CPU 请求不大于 800 millicpu(否则创建失败)。

接下来继续创建 Pod 进行测试。

3、创建 Pod

vim cpu-constraints-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo
spec:
  containers:
  - name: constraints-cpu-demo-ctr
    image: nginx
    resources:
      limits:
        cpu: "800m"
      requests:
        cpu: "500m"
kubectl apply -f cpu-constraints-pod.yaml -n constraints-cpu-example

4、验证 Pod 状态(确保健康)

kubectl get pod constraints-cpu-demo --namespace=constraints-cpu-example

image-20231107144339031

5、查看 Pod 的详情

kubectl get pod constraints-cpu-demo --output=yaml --namespace=constraints-cpu-example

image-20231107144434020

输出结果显示该 Pod 的容器的 CPU 请求为 500 millicpu,CPU 限制为 800 millicpu,这些参数满足 LimitRange 规定的限制范围。

Pod 异常情况分析:

Pod 的 limits 及 requests 的值只要在 LimitRange 的范围内时可正常运行,那如果超过这两个参数值超过 LimitRange 范围(即不再该范围)时又会怎么样呢?这里我们分三种情况来测试一下:超过最大 CPU 限制(limits)不满足最小 CPU 请求(requests)一个没有声明 CPU 请求和 CPU 限制的 Pod

1、超过最大 CPU 限制(limits)的 Pod

这里 Pod 的 limits 值超过 LimitRange 的 max 最大值。

vim cpu-constraints-pod-2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo-2
spec:
  containers:
  - name: constraints-cpu-demo-2-ctr
    image: nginx
    resources:
      limits:
        cpu: "1.5"
      requests:
        cpu: "500m"
kubectl apply -f cpu-constraints-pod-2.yaml --namespace=constraints-cpu-example

输出结果:

image-20231107150029113

Error from server (Forbidden): error when creating "cpu-constraints-pod-2.yaml": pods "constraints-cpu-demo-2" is forbidden: maximum cpu usage per Container is 800m, but limit is 1500m.

输出结果表明 Pod 没有创建成功,因为其中定义了一个无法被接受的容器,该容器之所以无法被接受是因为其中设定了过高的 CPU 限制值(即超过了 LimitRange 指定的 CPU 最大值)。

2、满足最小 CPU 请求(requests)的 Pod

这里 Pod 的 limits 值小于 LimitRange 的 min 最小值。

vim cpu-constraints-pod-3.yaml
apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo-3
spec:
  containers:
  - name: constraints-cpu-demo-3-ctr
    image: nginx
    resources:
      limits:
        cpu: "800m"
      requests:
        cpu: "100m"
kubectl apply -f cpu-constraints-pod-3.yaml --namespace=constraints-cpu-example

image-20231107150918321

输出结果同样显示 Pod 没有创建成功,因为其中定义了一个无法被接受的容器。 该容器无法被接受的原因是其中所设置的 CPU 请求小于最小值的限制(即不满足 LimitRange 指定的 CPU 最小请求值)。

3、创建一个没有声明 CPU 请求和 CPU 限制的 Pod

这里的 Pod 没有设置 limits。

vim cpu-constraints-pod-4.yaml
apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo-4
spec:
  containers:
  - name: constraints-cpu-demo-4-ctr
    image: vish/stress
kubectl apply -f cpu-constraints-pod-4.yaml --namespace=constraints-cpu-example

查看 Pod 的详情:

kubectl get pod constraints-cpu-demo-4 --namespace=constraints-cpu-example --output=yaml

image-20231107152319891

输出结果显示 Pod 的唯一容器的 CPU 请求为 800 millicpu,CPU 限制为 800 millicpu。因此,如果容器没有声明自己的 CPU 请求和限制, 控制面会根据命名空间中配置 LimitRange 设置默认(default)的 CPU 请求和限制。

2.1.2 内存约束

1、创建命名空间

创建一个命名空间,以便本练习中创建的资源和集群的其余部分相隔离。

kubectl create namespace constraints-mem-example

2、创建 LimitRange

vim memory-constraints.yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: mem-min-max-demo-lr
spec:
  limits:
  - max:
      memory: 1Gi
    min:
      memory: 500Mi
    type: Container
kubectl apply -f memory-constraints.yaml --namespace=constraints-mem-example

查看 LimitRange 的详情:

kubectl get limitrange mem-min-max-demo-lr --namespace=constraints-mem-example --output=yaml

image-20231107153154899

输出显示预期的最小和最大内存约束。 但请注意,即使你没有在 LimitRange 的配置文件中指定默认值,默认值也会自动生成(且默认值是你 LimitRange 中的 max 值)。

现在,每当在 constraints-mem-example 命名空间中创建 Pod 时,Kubernetes 就会执行下面的步骤:

  • 如果 Pod 中的任何容器未声明自己的内存请求和限制,控制面将为该容器设置默认的内存请求和限制。
  • 确保该 Pod 中的每个容器的内存请求至少 500 MiB。
  • 确保该 Pod 中每个容器内存请求不大于 1 GiB。

以下为包含一个容器的 Pod 清单。该容器声明了 600 MiB 的内存请求和 800 MiB 的内存限制, 这些满足了 LimitRange 施加的最小和最大内存约束。

案例效果与 CPU 约束基本上是一致的,你就是说你的 Pod 也是不能小于或超过 LimitRange 范围值,否则 Pod 将创建失败,这里就不再演示了,大家可去看官方文档。

2.1.3 默认 CPU 申请约束

前面也提到了,当我们创建 LimitRange 资源并指定 CPU max/min 值时,会自动为我们添加上默认的 CPU 约束值(即 default 和 defaultRequest 值)且是将 max 值作为默认值,如下图:

image-20231107143848149

那这两个值有什么用呢?

我们前面提到,如果创建的 Pod 没有指定约束,那 K8s 控制面板就会将 LimitRange 的 default 和 defaultRequest 值应用到 Pod 上。当然了,前提是你已经配置了 LimitRange,否则默认情况下 K8s 集群对 Pod 的资源(CPU/内存)是没有限制的。

案例也是一样,这里不再重复案例演示。

这里要注意的就是一下几种情况:

1、没有声明容器的限制(limits)和请求(requests)

此时 Pod 的 limits 和 requests 值就是 LimitRange 的 default 和 defaultRequest 值。

2、只声明容器的限制(limits)而不声明请求(requests)

此时 Pod 的 limits 和 requests 值就是你当前声明的值 limits。

3、只声明容器的请求(requests)而不声明限制(limits)

此时 Pod 的 limits 值为你 LimitRange 的 default 值,requests 值就是你当前声明的 requests 值。

因此不难总结出 LimitRange 的 limits 的作用:

  • default:在 Pod 没有指定 limits 约束时的 limits 值;

  • defaultRequest:在 Pod 没有指定 requests 约束时的 requests 值;

  • max:在 Pod 指定了 limits 约束时不能超过的最大值;

  • min:在 Pod 指定了 requests 约束时不能小于的最最值;

而且也要记住,你指定的 Pod 值也是不能超过 LimitRange 默认值范围。

2.1.4 默认内存申请约束

前面也提到了,当我们创建 LimitRange 资源并指定内存 max/min 值时,会自动为我们添加上默认的内存约束值(即 default 和 defaultRequest 值)且是将 max 值作为默认值,如下图:

image-20231107153154899

那这两个值有什么用呢?

同理,当创建的 Pod 没有指定约束时,那 K8s 控制面板就会将 LimitRange 的 default 和 defaultRequest 值应用到 Pod 上。

案例也是一样,这里不再重复案例演示。

这里要注意的就是一下几种情况:

1、没有声明容器的限制(limits)和请求(requests)

此时 Pod 的 limits 和 requests 值就是 LimitRange 的 default 和 defaultRequest 值。

2、只声明容器的限制(limits)而不声明请求(requests)

此时 Pod 的 limits 和 requests 值就是你当前声明的值 limits。

3、只声明容器的请求(requests)而不声明限制(limits)

此时 Pod 的 limits 值为你 LimitRange 的 default 值,requests 值就是你当前声明的 requests 值。

因此不难总结出 LimitRange 的 limits 的作用:

  • default:在 Pod 没有指定 limits 约束时的 limits 值;

  • defaultRequest:在 Pod 没有指定 requests 约束时的 requests 值;

  • max:在 Pod 指定了 limits 约束时不能超过的最大值;

  • min:在 Pod 指定了 requests 约束时不能小于的最最值;

而且也要记住,你指定的 Pod 值也是不能超过 LimitRange 默认值范围。

2.2 名称空间总容量限额约束

以上的案例都是使用 LimitRange 对 namespace 中某个 Pod 的 CPU、内存资源约束,当我们希望控制单个名字空间总的可以消耗多少存储空间、CPU、内存以控制成本时,我们就可以使用名称空间存储容量约束-StorageQuota。

而存储约束无非就这几个方面:

  1. 名字空间中持久卷申领(persistent volume claims)的数量
  2. 每个申领(claim)可以请求的存储量
  3. 名字空间可以具有的累计存储量

1、创建命名空间

创建一个命名空间,以便本练习中创建的资源和集群的其余部分相隔离。

kubectl create namespace quota-mem-cpu-example

2、创建 LimitRange

vim quota-mem-cpu.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: mem-cpu-demo
spec:
  hard:
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi
kubectl apply -f quota-mem-cpu.yaml --namespace=quota-mem-cpu-example

查看 ResourceQuota 详情:

kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example --output=yaml

image-20231107165339685

ResourceQuota 在 quota-mem-cpu-example 命名空间中设置了如下要求:

  • 在该命名空间中的每个 Pod 的所有容器都必须要有内存请求和限制,以及 CPU 请求和限制。
  • 在该命名空间中所有 Pod 的内存请求总和不能超过 1 GiB。
  • 在该命名空间中所有 Pod 的内存限制总和不能超过 2 GiB。
  • 在该命名空间中所有 Pod 的 CPU 请求总和不能超过 1 cpu。
  • 在该命名空间中所有 Pod 的 CPU 限制总和不能超过 2 cpu。

3、创建 Pod

vim quota-mem-cpu-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: quota-mem-cpu-demo
spec:
  containers:
  - name: quota-mem-cpu-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
        cpu: "800m"
      requests:
        memory: "600Mi"
        cpu: "400m"
kubectl apply -f quota-mem-cpu-pod.yaml --namespace=quota-mem-cpu-example

查看 Pod 状态:

kubectl get pod quota-mem-cpu-demo --namespace=quota-mem-cpu-example

image-20231107165734288

再次查看 ResourceQuota 的详情:

kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example --output=yaml

image-20231107165825218

输出结果显示了配额以及有多少配额已经被使用,你可以看到 Pod 的内存和 CPU 请求值及限制值没有超过配额。

如果我们再继续创建一个 Pod,如下:

vim quota-mem-cpu-pod-2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: quota-mem-cpu-demo-2
spec:
  containers:
  - name: quota-mem-cpu-demo-2-ctr
    image: redis
    resources:
      limits:
        memory: "1Gi"
        cpu: "800m"
      requests:
        memory: "700Mi"
        cpu: "400m"
kubectl apply -f quota-mem-cpu-pod-2.yaml --namespace=quota-mem-cpu-example

image-20231107170437885

报错了,为什呢?因为在 quota-mem-cpu-example 名称空间中,内存请求与已经使用的内存请求之和超过了内存请求的配额:600 MiB + 700 MiB > 1 GiB

总结

1、LimitRange

  • 用于对 namespace 中的 Pod 的 CPU、内存约束;
  • 默认情况下 K8s 集群对 Pod 是没有约束的(即没有 LimitRange);
  • 当对 namespace 进行准入控制时,在该 namespace 中的 Pod 需遵循 namespace 中的约束。

2、StorageQuota

  • 用于限制某个 namespace 总的限额(即该 namespace 中的所有的 Pod 资源的 CPU、内存、存储不能超过指定的限额)。

—END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1181239.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于51单片机的停车场管理系统仿真电路设计

**单片机设计介绍,基于51单片机的停车场管理系统仿真电路设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 停车场管理系统仿真电路设计介绍 停车场管理系统主要用于自动化管理和控制停车场,以提高停车…

Elasticsearch内存分析

文章目录 Elasticsearch JVM内存由哪些部分组成Indexing BufferNode Query CacheShard Request CacheField Data CacheSegments Cache查询 非堆内存内存压力mat分析es的jvm缓存监控 Elasticsearch JVM内存由哪些部分组成 官方建议Elasticsearch设置堆内存为32G,因为…

第三章:人工智能深度学习教程-人工智能与机器学习与深度学习之间的区别

人工智能基本上是通过一组规则(算法)将人类智能融入机器的机制。人工智能是两个词的组合:“人工”是指由人类或非自然物体制造的东西,“智能”是指相应地理解或思考的能力。另一个定义可能是“人工智能基本上是训练机器&#xff0…

Starward(米家游戏启动器)支持米哈游旗下的所有桌面端游戏

Starward应用名的由来 Starward 出自星穹铁道开服前的宣传语———愿此行,终抵群星 (May This Journey Lead Us Starward),虽然这不是一个正确的英文单词,但是很适合拿来用作应用名。 Starward 是一个米家游戏启动器,支持米哈游旗…

web3通过antd 在React dapp中构建订单组件基本结构

上文web3 dapp React项目引入 antd 对 balance 用户token信息组件进行样式改造 中 我们导入 antd组件 算是比较完整的编写了用户资产组件 那么 今天开始 我们就要说订单组件了 这个就会比之前的复杂很多 我们还是先开环境 ganache 终端执行 ganache -d然后 将合约 发布到区块链…

Django+Celery框架自动化定时任务开发

本章介绍使用DjCelery即DjangoCelery框架开发定时任务功能,在Autotestplat平台上实现单一接口自动化测试脚本、业务场景接口自动化测试脚本、App自动化测试脚本、Web自动化测试脚本等任务的定时执行、调度、管理等,从而取代Jenkins上的定时执行脚本和发送…

趋动云云端部署ChatGLM3-6B

趋动云端部署ChatGLM3-6B 文章目录 趋动云端部署ChatGLM3-6B1.项目创建2.模型部署3.总结参考 本部分主要内容:1.熟悉趋动云项目创建流程2.动手部署ChatGLM3-6B模型 1.项目创建 首先是趋动云的项目的创建,其主要以项目为载体,一个项目内可以进…

深度学习连接

全连接批量归一化 目的是:通过归一化,让所有的 x i x_i xi​具有一样的分布,学习率是一个值,每个参数 w i w_i wi​梯度的值大致相当实现是:实际上是在全连接中增加了两个节点 γ \gamma γ, β \beta β

设置区块链节点输出等级为警告级,并把日志存储阈值位100MB并验证;

题目 获取指定区块链节点输出等级为警告级,并设置日志存储阈值位100MB并验证; 操作步骤 1.切换目录 cd nodes/127.0.0.1/node0 2.打开配置文件并修改 vim config.ini warn:警告

拒绝一次性芯片,新技术:无线升级芯片

其便捷性和兼容性拥有4年经验的职场人都上手试用! 就算把产品寄到国外出问题了或需要升级 2.4G射频芯片帮您实现Mcu Ota无线升级产品0.3就能换一次救命的机会,相当于给产品买个保险! 问宇凡微拿了规格书和样品,经过几天的摸索研…

怎么更改文件创建日期?

怎么更改文件创建日期?如今科技发展日新月异,人们对于信息和数据的依赖程度日益加深。在这个高度信息化的时代,文件处理已经成为数字化办公不可或缺的环节。无论是个人还是企业,都需要通过数字化的方式来处理和管理大量的文档、表…

聊聊低代码技术

目录 一、什么是低代码开发? 二、为什么需要低代码开发,具备哪些优势? 三、低代码开发在实际工作中的作用 四、是不是有了低代码,就能不关注“质量”呢? 五、引迈旗下低代码开发平台--JNPF初体验 一、什么是低代码开发…

大厂硬性要求的性能优化,如何做到极致?可从7个方向切入

关于Android开发中的性能优化也是老生常谈的技术了。在许多大厂的招聘条件中就必须要求这项技术;因为Android 开发越来越规范, 国内工程师的素质,以及用户对产品的要求也越来越高。所以这也间接导致我们对研发项目的质量要求到了近乎苛刻的地…

『MySQL快速上手』-③-库的操作

文章目录 1.创建数据库2.创建数据库案例3.字符集和校验规则3.2 校验规则对数据库的影响3.2.1 进行查询3.2.2 进行排序 4.字符集和检验规则的作用5.操纵数据库5.1 查看数据库5.2 显示创建语句5.3 修改数据库5.4 数据库删除 6.备份与恢复6.1 备份6.2 还原6.3 注意事项 7.查看数据…

2023年最热门的五大编程技术趋势

2023年最热门的五大编程技术趋势 摘要:本文将介绍2023年最热门的五大编程技术趋势,包括人工智能、区块链、WebAssembly、5G和边缘计算以及自动化。我们将详细讨论这些趋势的当前状态、未来展望以及如何利用这些技术来提高您的编程技能。 一、前言 随着…

【NI-DAQmx入门】数据流盘

1.NI-DAQmx高速数据流盘 1.1什么是TDMS格式 TDMS文件格式是NI推荐易于交换、固有结构化、具有高速流传输能力的文件格式,用于将基于时间的测量数据保存到磁盘,以实现高性能、可用性和复用性。借助NI-DAQmx驱动,可以实现将数据快速传输到磁盘…

【vue3】引入高德地图并初始化

npm安装amap/amap-jsapi-loader包 <script setup> import AMapLoader from amap/amap-jsapi-loader; /*在Vue3中使用时,需要引入Vue3中的shallowRef方法 (使用shallowRef进行非深度监听, 因为在Vue3中所使用的Proxy拦截操作会改变JSAPI原生对象 否则会出现问题,建议JSAP…

学习笔记:利用CANOE Panel和CAPL脚本模拟主节点发送LIN通信指令

前一篇文章已经对CANOE如何模拟主节点和从节点进行LIN通信做了简单的记录&#xff0c;修改主节点发送的指令需要修改LIN ISC模块里的Frames帧对应的signal。这样改起来比较麻烦且不直观&#xff0c;幸好CANOE提供了Panel designer这样的工具&#xff0c;我们可以利用它设计自己…

谷歌浏览器配置允许跨域

1、在谷歌浏览器导航栏搜索chrome://flags 2、搜索Block insecure private network requests 3、修改状态

代码随想录打卡第六十三天|84.柱状图中最大的矩形

84.柱状图中最大的矩形 题目&#xff1a;给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 提示&#xff1a; 1 < heights.length <105 0 < h…