k8s_day03_03
容器资源限制
resource 相关 request、limit
一个宿主机的内核之上可能管理了一组硬件,包括cpu、内存、一个宿主机上可能运行了多个容器,这些容器将共享底层的同一个内核,很显然 ,硬件是属于内核的,因此任何一个容器内的进程默认可以请求占有内核管理的所有硬件资源。
尤其是多租户的情况当中,有人恶意的运行了一个容器,容器中的进程会尽可能多的占用CPU内存,进而会导致其他用户也无法运行了,容器间的隔离默认情况下 (或者使用docker默认启动时),在内核的名称空间级别进行了隔离,但是在进程运行时所用到的资源范围没有做太多的隔离操作
因此,我们应该为每个应用设定其内部的运行时做 为资源的最小保证量和最大保证量。为了应用正常运行,系统应该预留一定范围的资源。 整个节点上运行的pod 会越来越多,直到最后,很可能因为其他Pod 所占用资源后,剩余量 虽然确保核心容器启动够了,但是随着后期的运行,需要用到更多内存,而节点无法分配时 就尴尬了。为了避免这种情况 ,就使用request :下阈值, 确保节点至少为Pod 或者容器配备的资源最少量; 当创建pod 时,如果节点无法满足 pod的request 资源请求,就不会调度到该节点,如果被调度到了满足request 的节点时,无论pod 用或不用申请的最小资源限制,pod要求预留的量, 节点必须预留。而且节点就会假设那么多预留的量已经被使用了。
limit :限制,上阈值,是用来表示 pod 请求分配资源的上限,最大无边界
节点上重要的三资源: CPU 、Memory、 存储空间【因为用的是存储卷,先不讨论】
cpu:
如何分配? cpu是可以压缩型资源,分配到的cpu不够,顶多是运行 比较慢而已,cpu 是支持抢占的 ,分给你用了,但是还可以拿回来
Memory:
而内存是不可以压缩型, 意味着 应用本来现在是用的2G内存 ,现在给减少1G ,那应用就会崩溃.如果应用现在就占用了1G的内存 ,现在要扩展到2G,如果节点上没有物理内存资源了, 会使用交换内存 ,但是k8默认禁用swap . 这时候系统就会OOM ,就会触发内核中oom killer 把占用系统最多的内存(或者内存频繁分配)进程kill了
Mem ,CPU 的分配单位
Mem :1ki 1Mi i表示换算关系是1024 而不是1000
cpu: 毫核,1核=1000m核 【m表示毫核,100m 表示占用一个cpu十分之一的运行时间】
例子:
[root@master01 chapter4]# cat resource-requests-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: stress-pod
spec:
containers:
- name: stress
image: ikubernetes/stress-ng
command: ["/usr/bin/stress-ng", "-c 1", "-m 1", "--metrics-brief"]
resources:
requests:
memory: "128Mi"
cpu: "200m"
limits:
memory: "512Mi"
cpu: "400m"
正常情况下 , resources 可以 定义在pod上, 也可以定义在container上 定义在pod 就会对pod内所有容器生效,一般而言是定义在每一个资源上的,因为不同的容器 需求可能不一样。
验证cpu限制
[root@node01 ~]# kubectl exec po/stress-pod -- top
Mem: 1848196K used, 2013324K free, 25120K shrd, 2248K buff, 748276K cached
CPU: 19% usr 2% sys 0% nic 78% idle 0% io 0% irq 0% sirq
Load average: 0.47 0.42 0.38 4/427 28
PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND
6 1 root R 6888 0% 1 10% {stress-ng-cpu} /usr/bin/stress-ng
8 7 root R 262m 7% 0 10% {stress-ng-vm} /usr/bin/stress-ng
1 0 root S 6244 0% 0 0% /usr/bin/stress-ng -c 1 -m 1 --met
7 1 root S 6244 0% 1 0% {stress-ng-vm} /usr/bin/stress-ng
9 0 root S 1504 0% 0 0% top
14 0 root S 1500 0% 0 0% top
19 0 root S 1500 0% 0 0% top
24 0 root R 1500 0% 0 0% top
物理机的是2个 。200m cpu 代表占用cpu 百分比是200/2000 =10% ,400/2000 =20% 所以最低用10%,最多20%。
stress-ng 是一个应用程序, 专门做压力测试的 “-c 1” 表示会运行一个cpu 压力进程 “-m 1” 会运行一个内存压力进程, “-- metrics-brief” 表示 简要输出指标信息。
可能因为程序是压测程序,导致压测内存的程序变成2个就不测了
eg2 : 容器内存泄露示例
[root@master01 chapter4]# cat memleak-demo.yaml
# Maintainer: MageEdu <mage@magedu.com>
# URL: http://www.magedu.com
# ---
apiVersion: v1
kind: Pod
metadata:
name: memleak-pod
spec:
containers:
- name: simmemleak
image: ikubernetes/simmemleak
resources:
requests:
memory: "64Mi"
cpu: "1"
limits:
memory: "64Mi"
cpu: "1"
[root@master01 chapter4]# kubectl apply -f memleak-demo.yaml
pod/memleak-pod created
[root@master01 chapter4]# kubectl get po/memleak-pod -w
NAME READY STATUS RESTARTS AGE
memleak-pod 0/1 OOMKilled 2 30s
memleak-pod 0/1 CrashLoopBackOff 2 41s
memleak-pod 0/1 OOMKilled 3 59s
memleak-pod 0/1 CrashLoopBackOff 3 71s
memleak-pod 0/1 OOMKilled 4 110s
memleak-pod 0/1 CrashLoopBackOff 4 111s
memleak-pod 1/1 Running 5 3m26s
memleak-pod 0/1 OOMKilled 5 3m27s
memleak-pod 0/1 CrashLoopBackOff 5 3m39s
memleak-pod 0/1 OOMKilled 6 6m23s
memleak-pod 0/1 CrashLoopBackOff 6 6m24s
memleak-pod 0/1 OOMKilled 7 11m
memleak-pod 0/1 CrashLoopBackOff 7 11m
memleak-pod 0/1 OOMKilled 8 16m
memleak-pod 0/1 CrashLoopBackOff 8 16m
memleak-pod 0/1 CrashLoopBackOff 9 26m
memleak-pod 0/1 OOMKilled 10 26m
memleak-pod 0/1 CrashLoopBackOff 10 27m
memleak-pod 0/1 OOMKilled 11 32m
memleak-pod 0/1 CrashLoopBackOff 11 32m
这个memleak-pod 会吃掉所有内存,所以限制多少内存都没有用。因为默认restartpolicy是always , 所以一旦发生oom ,就会oom kill 或者触发 CrashLoopBackOff 。 pod就会被重启,然后陷入内存耗尽、重启循环
k8s为了避免这种无谓的反复重启,他有一个退避算法。比如pod因为故障被重启:第一次重启 ,一般是立即重启,第二次可能还是立即,如果第三次也是立即重启还没有解决问题,第4次重启就会延长,比如等1秒重启,第五次就等5秒钟重启 ,第6次就20秒重启,直到到达最大次数结束还没有解决问题。如果重启容器仍然不成功,k8s就会将pod置于CrashLoopBackOff状态。
事实上k8s 对退避算法是这样实现的:
第1次,0秒 第2次,10秒 第3次,20秒 第4次,80秒 第5次,160秒 第7次,300秒,第8次之后都是5分钟。只要重启不成功就置于CrashLoopBackOff状态。
因为重启的速度太快,所以频繁出现CrashLoopBackOff和 OOMKilled交叉,具体的状态值取决于容器退出的原因
容器可见资源
即便容器做了资源限制, 但是容器中可见的资源量仍然是节点级别可用总量。这就有问题了,比如通常java程序设置堆内存是默认是按照主机内的内存总量比例 来的,这样就会让java容器很快oom
解决方法就是将 download api 将limits 定义的资源暴露给容器
现象如下
[root@node01 ~]# kubectl exec po/stress-pod -- free -m
total used free shared buffers cached
Mem: 3771 1842 1928 0 2 732
-/+ buffers/cache: 1107 2663
Swap: 0 0 0
[root@node01 ~]#
[root@node01 ~]# kubectl exec po/stress-pod -- cat /proc/cpuinfo |grep "^processor"
processor : 0
processor : 1
容器内可以看见节点总内存和cpu个数 ,而不是limit 限制的数量
Pod 服务质量类别
节点无法承载过多pod,资源不够的情况下,那种次序终止pod 就成问题
QoS Class:服务质量类别,代表了Pod的资源被优先满足的类别
Guaranteed:
Pod内的每个容器都分别设定了CPU和Memroy资源需求和资源限制,CPU的需求与限制相等,而且Memory的需求与限制也相等; 【保证】
Bustable:随便设置一个限制条件就行但是不能满足Guaranteed条件【二者之间】
BestEffort:未为任何一个容器设定任何需求或限制; 【尽力满足,就是不想满足呗】
综合应用案例
[root@master01 chapter4]# cat all-in-one.yaml
# Maintainer: MageEdu <mage@magedu.com>
# URL: http://www.magedu.com
apiVersion: v1
kind: Pod
metadata:
name: all-in-one
namespace: default
spec:
initContainers:
- name: iptables-init
image: ikubernetes/admin-box:latest
imagePullPolicy: IfNotPresent
command: ['/bin/sh','-c']
args: ['iptables -t nat -A PREROUTING -p tcp --dport 8080 -j REDIRECT --to-port 80']
securityContext:
capabilities:
add:
- NET_ADMIN
containers:
- name: sidecar-proxy
image: envoyproxy/envoy-alpine:v1.13.1
command: ['/bin/sh','-c']
args: ['sleep 3 && envoy -c /etc/envoy/envoy.yaml']
lifecycle:
postStart:
exec:
command: ['/bin/sh','-c','wget -O /etc/envoy/envoy.yaml http://ilinux.io/envoy.yaml']
livenessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 5
readinessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 5
- name: demo
image: ikubernetes/demoapp:v1.0
imagePullPolicy: IfNotPresent
env:
- name: PORT
value: '8080'
livenessProbe:
httpGet:
path: '/livez'
port: 8080
initialDelaySeconds: 5
readinessProbe:
httpGet:
path: '/readyz'
port: 8080
initialDelaySeconds: 15
securityContext:
runAsUser: 1001
runAsGroup: 1001
resources:
requests:
cpu: 0.5
memory: "64Mi"
limits:
cpu: 2
memory: "1024Mi"
securityContext:
supplementalGroups: [1002, 1003]
fsGroup: 2000
[root@master01 chapter4]#