LimitRange
kubernetes创建pod时,默认不指定资源请求和限制。如果namespace设置了配额,那么创建不指定资源请求和资源限制的pod是不允许的。为了在设定配额的namespace中使用pod,namespace还需要为pod资源请求设定默认范围。
LimitRange 资源,也称为limits,定义了单个pod的资源请求和资源限制default、minimum、maximum值。pod的资源请求是其中所有容器请求的总和。
LimitRange 资源用于限定特定 namespace。
namespace设定了LimitRange,创建资源规则:
- 如果项目中请求一个未提供计算资源的对象,那么此时namespace将使用limit范围default值创建该对象。
- 如果项目中请求一个计算资源的对象,请求的资源小于limit最小值,那么该资源**无法创建**。
- 如果项目中请求一个计算资源的对象,请求的资源大于limit最大值,那么该资源**无法创建**。
LimitRange 示例
root@master30:~# vim limits.yamlapiVersion:v1kind:LimitRangemetadata:name:mylimitspec:limits:-type:Containermax:memory:1024Micpu:1min:memory:128Micpu:100mdefault:memory:512Micpu:500mdefaultRequest:memory:256Micpu:200m说明:
name:只能使用小写字母,数字, ‘-’ 和 ‘.’,而且只能是数字或字母开头和结尾。default:即该namespace配置resourceQuota时,创建container的默认limit上限defaultRequest:即该namespace配置resourceQuota时,创建container的默认request上限max:即该namespace下创建container的资源最大值min:即该namespace下创建container的资源最小值
其中:min <= defaultRequest <= default <= max
root@master30:~# kubectl apply -f limits.yamlroot@master30:~# kubectl get limitrangesNAME CREATED AT mylimit2021-09-09T04:09:15Z root@master30:~# kubectl describe limitranges mylimitName: mylimit Namespace:quotaType Resource Min Max Default Request Default Limit Max Limit/Request Ratio ---- -------- --- --- --------------- ------------- ----------------------- Container cpu 100m1200m 500m - Container memory 128Mi 1Gi 256Mi 512Mi -未指定 resources
示例1:
root@master30:~# vim pod-without-limits.yamlapiVersion:v1kind:Podmetadata:name:stressspec:containers:-name:stressimage:hub.gsb.cloud/progrium/stressimagePullPolicy:IfNotPresentargs:['-c','1']**结论:**创建出来的pod的resources 与 limitranage 指定的相关默认值一致。
root@master30:~# kubectl apply -f pod-without-limits.yamlroot@master30:~# kubectl top podsNAME CPU(cores)MEMORY(bytes)stress 501m 0Mi root@master30:~# kubectl get pod stress -o yaml......spec:containers:image:hub.gsb.cloud/progrium/stressimagePullPolicy:IfNotPresentname:stressresources:limits:cpu:500mmemory:512Mirequests:cpu:200mmemory:256Mi......# 清理资源root@master30:~# kubectl delete limitranges mylimitroot@master30:~# kubectl delete pod web --force只指定 limit 值
示例 2-1:limit 值大于 max 值
apiVersion:v1kind:Podmetadata:name:webspec:containers:-name:webimage:nginxresources:limits:cpu:1.1memory:1100Miroot@master30:~# kubectl apply -f limit.ymlError from server(Forbidden): error when creating"limit.yml":pods"web"is forbidden:[maximum cpu usage per Container is1, but limit is 1100m, maximum memory usage per Container is 1Gi, but limit is 1181116006400m]示例 2-2:limit 值小于 min 值
apiVersion:v1kind:Podmetadata:name:webspec:containers:-name:webimage:nginxresources:limits:cpu:60mmemory:60Miroot@master30:~# kubectl apply -f limit.ymlError from server(Forbidden): error when creating"limit.yml":pods"web"is forbidden:[minimum cpu usage per Container is 100m, but request is 60m, minimum memory usage per Container is 128Mi, but request is 60Mi]示例 2-3:min 值< limit 值< max 值
apiVersion:v1kind:Podmetadata:name:webspec:containers:-name:webimage:nginxresources:limits:cpu:600mmemory:600Mi结论:
创建的容器limits值必须满足条件:min值<指定的limit值<max值
当只指定limits值时,requests值与limits值保持一致,而不是default request。
root@master30:~# kubectl get pod web -o yaml......spec:containers:-image:nginximagePullPolicy:Alwaysname:webresources:limits:cpu:600mmemory:600Mirequests:cpu:600mmemory:600Mi......
只指定 requests
示例 3-1:requests 大于 max 值
apiVersion:v1kind:Podmetadata:name:webspec:containers:-name:webimage:nginxresources:requests:cpu:1600mmemory:600Miroot@master30:~# kubectl apply -f limit4.ymlThe Pod"web"is invalid: * spec.containers[0].resources.requests: Invalid value:"1600m":must belessthan or equal to cpu limit * spec.containers[0].resources.requests: Invalid value:"1600Mi":must belessthan or equal to memory limit示例 3-2:requests 小于 min 值
apiVersion:v1kind:Podmetadata:name:webspec:containers:-name:webimage:nginxresources:requests:cpu:60mmemory:60Miroot@master30:~# kubectl apply -f limit.ymlError from server(Forbidden): error when creating"limit.yml":pods"web"is forbidden:[minimum cpu usage per Container is 100m, but request is 60m, minimum memory usage per Container is 128Mi, but request is 60Mi]示例 3-3:min 值< request 值< max 值
apiVersion:v1kind:Podmetadata:name:webspec:containers:-name:webimage:nginxresources:requests:cpu:400mmemory:400Mi结论:
创建的容器requests值必须满足条件:min值<requests值<limits值
当只指定requests值时,limits值与default值保持一致。
root@master30:~# kubectl get pod web -o yaml......spec:containers:-image:nginximagePullPolicy:Alwaysname:webresources:limits:cpu:500mmemory:512Mirequests:cpu:400mmemory:400Mi......
限定资源类型
LimitRange 资源可以限定如下资源:
| Type | Resource Name | Description |
|---|---|---|
| container | cpu、memory | 限定容器 cpu、memroy |
| Pod | cpu、memory | 限定 Pod 中所有容器cpu、memroy的总和 |
| PVC | storage | 限定PVC申请的存储空间大小 |
LimitRange for PVC 示例:
apiVersion:v1kind:LimitRangemetadata:name:storagelimitsspec:limits:-type:PersistentVolumeClaimmax:storage:2Gimin:storage:1Gi环境清理
root@master30:~# kubectl delete ns quotaKubernetes Health Check
学习参考:配置存活、就绪和启动探针
环境准备
root@master30:~# kubectl create ns healthroot@master30:~# kubectl config set-context --current --namespace healthHealth Check
应用可能会因为各种问题,变的unhealthy,例如临时连接断开,配置错误,应用本身错误。
kubelet 使用probes(探针),周期性地监控容器中应用是否为healthy状态,进一步决定什么时候要重启容器。 例如,当存活探针可以探测到应用死锁(应用在运行,但是无法继续执行后面的步骤)情况,进而重启pod,有助于提高应用的可用性,即使其中存在缺陷。
没有探测的情况,看一个例子:
# 创建一个普通 podroot@master30:~# kubectl run web --image=httpd --image-pull-policy=IfNotPresent[root@gsb20 health]# kubectl describe pod web|grep '^IP:'IP:10.224.73.16 root@master30:~# curl 10.224.73.16<html><body><h1>It works!</h1></body></html># 删除主页文件,即使pod中应用数据丢失,pod状态依然为Runningroot@master30:~# kubectl exec web -- rm -f htdocs/index.html# 查看主页内容root@master30:~# curl 10.224.73.16<!DOCTYPE HTML PUBLIC"-//W3C//DTD HTML 3.2 Final//EN"><html><head><title>Index of /</title></head><body><h1>Index of /</h1><ul></ul></body></html>root@master30:~# kubectl get podNAME READY STATUS RESTARTS AGE web1/1 Running051s# 清理环境root@master30:~# kubectl delete pod web --forceProbe Type
kubelet 使用启动探针来了解应用容器何时启动。 如果配置了这类探针,存活探针和就绪探针成功之前不会重启,确保这些探针不会影响应用的启动。 启动探针可以用于对慢启动容器进行存活性检测,避免它们在启动运行之前就被杀掉。
LivenessProbe:用于确定pod中应用是否处于healthy状态。如果liveness probe检测的状态为unhealthy,则控制器将重新启动pod。
ReadinessProbe:用于确定pod中应用是否可以提供服务。如果返回失败状态,则**服务将从endpoints 中删除容器ip地址。**即使容器处于运行状态,也不接受代理发过来的请求。
StartupProbe:用于确定pod是否成功初始化。 如果指定,则在成功完成之前不会执行其他探测。如果此探测失败,Pod 将重新启动,就像 livenessProbe 失败一样。 这可用于在 Pod 生命周期开始时提供不同的探测参数,此时加载数据或预热缓存可能需要比稳态操作期间更长的时间。 这无法更新。
我们这里不深入讨论StartupProbe。
Checking Methods
探针检查容器有四种不同的方法:
- httpGet,对容器的 IP 地址上指定端口和路径执行 HTTP
GET请求。如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的。 - exec,在容器内执行指定命令。如果命令退出时返回码为 0,则认为诊断成功。
- tcpSocket,对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开,则诊断被认为是成功的。 如果远程系统(容器)在打开连接后立即将其关闭,这算作是健康的。
- grpc,使用 gRPC 执行一个远程过程调用。 目标应该实现 gRPC 健康检查。 如果响应的状态是 “SERVING”,则认为诊断成功。
我们这里不讨论grpc方法。
HTTP Checks-httpGet
当使用HTTP Checks,控制器使用webhoook判定容器健康情况。如果HTTP的响应码在200-399之间,判定check成功。适应范围:可以返回HTTP状态码应用。
livenessProbe
root@master30:~# vim deploy-httpGet-liveness.yamlapiVersion:apps/v1kind:Deploymentmetadata:labels:app:webname:webspec:replicas:1selector:matchLabels:app:webtemplate:metadata:labels:app:webspec:containers:-image:httpdimagePullPolicy:IfNotPresentname:httpd# 添加livenessProbe部分livenessProbe:failureThreshold:3initialDelaySeconds:5periodSeconds:5successThreshold:1timeoutSeconds:10httpGet:path:/index.html# port填写时间web端口port:80# scheme指定协议,HTTP或者HTTPSscheme:HTTPprobe选项说明:
- initialDelaySeconds:必选。容器启动后多长时间,probe开始生效。
- timeoutSeconds:必选。probe需要多长时间完成。如果超过该值,控制器判定probe失败。默认值1s,最小值是1秒。
- periodSeconds:可选。检查频率。默认值10s,最小值是1秒。
- successThreshold:可选,连续成功最少次数后判定probe成功。默认值1,最小值是1。
- failureThreshold:可选。连续失败最少次数后判定probe失败。默认值3,最小值是1。
root@master30:~# kubectl apply -f deploy-httpGet-liveness.yamlroot@master30:~# kubectl get podNAME READY STATUS RESTARTS AGE web-85c6ff748f-qwszz1/1 Running012m root@master30:~# kubectl describe pod web-85c6ff748f-j92jn|grep '^IP:'IP:10.98.146.216# 删除主页文件root@master30:~# kubectl exec web-85c6ff748f-qwszz -- bash -c 'rm htdocs/index.html'# 观察pod状态,RESTARTS次数变位1,再次访问root@master30:~# kubectl get podNAME READY STATUS RESTARTS AGE web-85c6ff748f-qwszz1/1 Running113m# 容器删除需要一些时间,由参数terminationGracePeriodSeconds设定,默认值为30s。# 只有等容器删除,并创建完成后才会继续检测root@master30:~# curl 10.98.146.216<html><body><h1>It works!</h1></body></html># 清理环境root@master30:~# kubectl delete deployments.apps webreadinessProbe
root@master30:~# vim deploy-httpGet-readiness.yamlapiVersion:apps/v1kind:Deploymentmetadata:labels:app:webname:webspec:replicas:3selector:matchLabels:app:webtemplate:metadata:labels:app:webspec:containers:-image:httpdimagePullPolicy:IfNotPresentname:httpd# 添加readinessProbe部分readinessProbe:failureThreshold:3initialDelaySeconds:5periodSeconds:5successThreshold:1timeoutSeconds:10httpGet:path:/index.htmlport:80scheme:HTTP# 创建应用root@master30:~# kubectl apply -f deploy-httpGet-readiness.yamlroot@master30:~# kubectl expose deployment web --port=80 --target-port=80root@master30:~# kubectl get svcNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)AGE web ClusterIP10.98.146.216<none>80/TCP 4m5s root@master30:~# kubectl get podNAME READY STATUS RESTARTS AGE web-9479dc55c-6bpg71/1 Running02m34s web-9479dc55c-d2gbn1/1 Running02m34s web-9479dc55c-hqh8q1/1 Running02m34s# 准备3个pod主页文件root@master30:~# for pod in $(kubectl get pods -o name|awk -F / '{print $2}'); do kubectl exec $pod -- bash -c "echo $pod > htdocs/index.html"; doneroot@master30:~# for i in {1..90};do curl -s 10.96.180.30;done|sort |uniq -c24web-9479dc55c-6bpg733web-9479dc55c-d2gbn33web-9479dc55c-hqh8q root@master30:~# kubectl get endpoints webNAME ENDPOINTS AGE web10.224.73.15:80,10.224.73.34:80,10.224.73.35:80 21m# 删除 web-9479dc55c-6bpg7主页文件root@master30:~# kubectl exec -it web-9479dc55c-6bpg7 -- rm -f htdocs/index.html# web 服务的后端没有pod的iproot@master30:~# kubectl get endpoints webNAME ENDPOINTS AGE web10.224.73.15:80,10.224.73.34:80 23m# 访问svc,后端无法看到 web-9479dc55c-6bpg7root@master30:~# for i in {1..90};do curl -s 10.96.180.30;done|sort |uniq -c50web240web3# 观察web1状态,READY为0,RESTARTS数量为0root@master30:~# kubectl get podNAME READY STATUS RESTARTS AGE web-9479dc55c-6bpg70/1 Running08m49s web-9479dc55c-d2gbn1/1 Running08m49s web-9479dc55c-hqh8q1/1 Running08m49s# 清理环境root@master30:~# kubectl delete deployments.apps webExecution Checks-exec
当使用容器执行检测,kubelet代理将在容器内执行命令。返回值是0,代表check成功。
示例1:检测容器自带文件
root@master30:~# vim deploy-exec-liveness.yamlapiVersion:apps/v1kind:Deploymentmetadata:labels:app:webname:webspec:replicas:1selector:matchLabels:app:webtemplate:metadata:labels:app:webspec:containers:-image:httpdimagePullPolicy:IfNotPresentname:httpd# 添加livenessProbe部分livenessProbe:failureThreshold:3initialDelaySeconds:5periodSeconds:5successThreshold:1timeoutSeconds:10exec:command:-cat-/usr/local/apache2/htdocs/index.html# 创建应用root@master30:~# kubectl apply -f deploy-exec-liveness.yamlroot@master30:~# kubectl get podsNAME READY STATUS RESTARTS AGE web-8c9ff9b76-nm6s21/1 Running1(2s ago)18s# 删除主页文件root@master30:~# kubectl exec web-8c9ff9b76-nm6s2 -- bash -c 'rm htdocs/index.html'# 观察pod状态,RESTARTS次数变位1root@master30:~# kubectl get podNAME READY STATUS RESTARTS AGE web1/1 Running14m5s示例2:检测自定义文件
root@master30:~# kubectl run busybox --image=busybox --image-pull-policy=IfNotPresent -o yaml --dry-run=client > busybox.ymlroot@master30:~# vim deploy-exec-busybox.ymlapiVersion:v1kind:Podmetadata:creationTimestamp:nulllabels:run:busyboxname:busyboxspec:containers:-image:busyboximagePullPolicy:IfNotPresentname:busybox# 添加args参数args:-/bin/sh--c-touch /tmp/healthy; sleep 10; rm-rf /tmp/healthy; sleep 100#添加livenessProbe参数livenessProbe:failureThreshold:3initialDelaySeconds:5periodSeconds:5successThreshold:1timeoutSeconds:10exec:command:-ls-/tmp/healthydnsPolicy:ClusterFirstrestartPolicy:AlwaysTCP Socket Checks-tcpSocket
当使用TCP socket checks,kubelet代理尝试打开容器socket。如果check可以建立连接,判定check成功。
示例:liveness probe使用TCP Socket check
root@master30:~# vim deploy-tcpSocket-liveness.yamlapiVersion:apps/v1kind:Deploymentmetadata:labels:app:webname:webspec:replicas:1selector:matchLabels:app:webtemplate:metadata:labels:app:webspec:containers:-image:httpdimagePullPolicy:IfNotPresentname:httpd# 添加livenessProbe部分livenessProbe:failureThreshold:3initialDelaySeconds:5periodSeconds:5successThreshold:1timeoutSeconds:10tcpSocket:port:80Health Check Case
Health Check 在 Scale Up 中的应用
对于多副本应用, 当执行Scale Up操作时, 新副本会作为backend被添加到Service的负载均衡中, 与已有副本一起处理客户的请求。考虑到应用启动通常都需要一个准备阶段, 比如加载缓存数据、 连接数据库等, 从容器启动到真正能够提供服务是需要一段时间的。 我们可以通过Readiness探测判断容器是否就绪, 避免将请求发送到还没有准备好的backend。
Health Check 在滚动更新中的应用
Health Check另一个重要的应用场景是Rolling Update。 试想一下, 现有一个正常运行的多副本应用, 接下来对应用进行更新(比如使用更高版本的image) , Kubernetes会启动新副本, 然后发生了如下事件:
- 正常情况下新副本需要10秒钟完成准备工作, 在此之前无法响应业务请求。
- 由于人为配置错误, 副本始终无法完成准备工作(比如无法连接后端数据库)。
如果没有配置Health Check, 会出现怎样的情况?
因为新副本本身没有异常退出, 默认的Health Check机制会认为容器已经就绪, 进而会逐步用新副本替换现有副本, 其结果就是: 当所有旧副本都被替换后, 整个应用将无法处理请求, 无法对外提供服务。 如果这是发生在重要的生产系统上, 后果会非常严重。
如果正确配置了Health Check, 新副本只有通过了探测才会被添加到Service; 如果没有通过探测, 现有副本不会被全部替换, 业务仍然正常进行。
环境清理
root@master30:~# kubectl delete ns health