一.Pod是什么

二.Pod管理方式

2.1 自助式Pod

2.2 控制器Pod

三.Pod运行应用

3.1 创建Pod应用

3.2 Pod运行阶段

3.3 容器运行阶段

3.4 阶段状态实践

四.Pod运行应用对应字段

4.1 容器镜像拉取策略

4.2 获取私有仓库镜像

4.3 自定义容器环境变量

4.4 自定义容器命令与参数

4.5 自定义容器端口

五.Pod重启策略

5.1 Always

5.2 Never

5.3 OnFailure

六.Pod生命周期流程

6.1 什么是生命周期

6.2 生命周期流程

6.3 生命周期总结

七.Init Container

7.1 基本概念

7.2 应用场景

7.3 场景实践

八. Pod Hook

8.1 两种钩子

8.2 钩子实例

8.3 场景实践

九.Pod检测探针

9.1 为何需要探针

9.2 探针类型

9.3 探针检查机制

9.4 探针配置格式

十.startupProbe

10.1 exec命令探针

10.2 httpGet http状态码探针

10.3 tcpSocket 端口存活谭政

十一.livenessProbe

11.1 exec

11.2 httpGet

11.3 tcpSocket

十二.readinessProbe

12.1 exec

12.2 httpGet

12.3 tcpSocket

十三.Pod资源限制

13.1 什么是资源限制

13.2 如何实现资源相纸

13.3 资源限制的目的与意义

十四.资源限制单位换算

14.1 CPU限制单位

14.2 内存分配单位

十五. CPU资源限制实践

15.1 设置容器的CPU请求和限制

15.2 设置超过节点的CPU请求

15.3 如果不指定CPU的limits

十六. 内存资源限制

16.1 设置容器的内存请求和限制

16.2 运行超过容器内存限制的应用

16.3 超过节点的内存分配

16.4 如果没有指定内存限制

十七. Pod服务质量Qos

17.1 什么是QOS

17.2 QOS类别

17.3 创建Guaranteed的Pod

17.4 创建Burstable的pod

17.5 创建BestEffort的Pod

17.6 创建多容器Pod

十八. Downward API

18.1 什么是Downward API

18.2 可注入的元数据信息

18.3 环境变量方式注入元数据

18.4 存储卷方式注入元数据

18.5 为注册服务注入Pod名称

18.6 为Tomcat注入堆内存相知

==============================================================================================================================

一.Pod是什么

podk8s里能够运行的最小的逻辑端元 一个pod代表集群运行的一个进程!

1个pod里面可以运行单个或者多个容器 他们共享 Network(网络)PID(进程)IPC(进程间通讯)HostName(主机名称)Volume(卷)

Pod中的容器共享IP HostName IPC 他们可以通过localhost 互相发现 他们之间可以通过IPC通讯

Pod中的容器也有访问共享volume的权限 这些volume也会被定义成Pod的一部分被挂载到pod的容器中应用成文件系统

可以把Pod理解成豌豆荚 容器为豆粒

一个pod部署两个容器_一个pod部署两个容器


一个pod里运行多个容器 又叫边车(SideCar)模式

边车模式可以很好的保证原有业务不变的情况,通便边车模式添加新的容器来匹配现在的环境,例如日志手机 IAAS平台环境收集

二.Pod管理方式

2.1自助式Pod

kubernetes中,我们部署POD的时候,基本上都是使用控制器管理,如果不使用控制器,也可以直接定义一个Pod资源,那么就是Pod自己去控制自己,这样的Pod称为自主式Pod!

  • 如果Pod被删除,那就是真的被删除,不会重新运行新的Pod
  • 如果Pod所在的节点需要维护,那么节点会先执行驱逐,如果是自主式Pod,驱逐后不会被重建。
  • 如果Pod期望部署多个副本,这个也能实现,但如果想持续维护副本数量,则需要认为参与,过于繁琐。
    1.创建一个自主式Pod
cat nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
    - name: nginx-container
      image: nginx:1.16
  ports:
  - containerPort: 80

2.测试删除Pod,验证是否能被彻底删除

kubectl delete pod nginx-pod

3.测试节点故障,当Pod所运行的节点故障,那么改Pod会被删除,不会重新运行起来

kubectl drain node01 --ignore-daemonsets --force #驱逐
kubectl uncordon node01

2.2 控制器Pod

kubernetes通过controller控制器来创建管理Pod实例。Controller可以创建和管理多个Pod,提供副本副本管理、滚动升级和集群级别的自愈呢能力。

Pod的标签能匹配Controller控制器的标签,当运行Pod的节点发生故障时,控制器会把Pod调度到其他节点,Pod被删除,控制器会根据清单上Pod的数量来运行,Controller控制器能控制pod的副本(水平扩展)、滚动升级、故障自愈。

一个pod部署两个容器_fish_02


1.创建控制器管理的Pod

cat nginx-dp.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-dp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx-container
        image: nginx:1.16

2.测试删除Pod,会发现Pod被删除后,立即又启动一个相同的Pod实例。

#查看
[root@k8s-240 pod]# kubectl get pod -o wide
NAME                        READY   STATUS    RESTARTS            AGE   IP               NODE        NOMINATED NODE   READINESS GATES
nginx-dp-776b5f86cc-5t46z   1/1     Running   0                   22s   10.244.232.223   flyfish81   <none>           <none>
nginx-dp-776b5f86cc-kd8s7   1/1     Running   0                   22s   10.244.157.160   flyfish83   <none>           <none>
nginx-dp-776b5f86cc-ngnvw   1/1     Running   0                   22s   10.244.149.155   flyfish82   <none>           <none>
#删除Pod
[root@k8s-240 pod]# kubectl delete pod nginx-dp-776b5f86cc-5t46z 
pod "nginx-dp-776b5f86cc-5t46z" deleted
#验证
[root@k8s-240 pod]# kubectl get pod -o wide
NAME                        READY   STATUS    RESTARTS            AGE   IP               NODE        NOMINATED NODE   READINESS GATES
nginx-dp-776b5f86cc-4xj5b   1/1     Running   0                   7s    10.244.232.224   flyfish81   <none>           <none>
nginx-dp-776b5f86cc-kd8s7   1/1     Running   0                   37s   10.244.157.160   flyfish83   <none>           <none>
nginx-dp-776b5f86cc-ngnvw   1/1     Running   0                   37s   10.244.149.155   flyfish82   <none>           <none>

三.Pod运行应用

3.1 创建应用

cat nginx-pod.yaml
apiVersion: apps/v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
    - name: nginx-container
      image: nginx:1.16

3.2 Pod运行阶段

Pod创建后,起始为 Pending 阶段,如果其中至少又一个主要容器正常启动,则进入Running,之后的状态取决于Pod中是否有容器运行失败或被管理员停止运行,从而会进入 Succeeded 或者 Failed 阶段。

一个pod部署两个容器_fish_03

  • Pending: Pod 已被 Kubernetes 系统接受,但有一个或者多个容器尚未创建亦未运行。此阶段包括等待 Pod 被调度的时间和通过网络下载镜像的时间。
  • Running: Pod已经绑定至某个节点,同时Pod中所有的容器都已创建。至少有一个容器在运行,或处于启动、重启状态。
  • Succeeded: Pod中的所有容器都已成功终止,并且不会再重启
  • Failed: Pod中的所有容器都已终止,并且至少有一个容器是因为失败终止。也就是说,容器以非0状态退出或被系统终止
  • Unknown: 因为某些原因无法取得 Pod 的状态。这种情况通常是因为与 Pod 所在主机通信失败。

1.可以通过 kubectl describe pod <pod名称> 查看podStatus 对象中的Status,查看当前Pod所处的阶段

[root@k8s-240 pod]# kubectl describe pod nginx-dp-776b5f86cc-4xj5b 
Name:             nginx-dp-776b5f86cc-4xj5b
Namespace:        default
Priority:         0
Service Account:  default
Node:             flyfish81/192.168.1.240
Start Time:       Wed, 24 May 2023 16:39:23 +0800
Status:           Running
#如果某节点死掉或者与集群中其他节点失恋,`Kubernetes`会实施一种策略,将失去节点上运行的`Pod`的`phase`设置为`Failed`

2.在PodStatus对象中还包含了一组Conditions,它主要描述造成当前Status的具体原因。

[root@k8s-240 pod]# kubectl describe pod nginx-dp-776b5f86cc-4xj5b
Conditions:
  Type              Status
  Initialized       True    #pod中所有的init容器都已成功完成;
  Ready             True    #pod可以对外提供服务,并可以加入对应的负载均衡;
  ContainersReady   True    #pod中的所有容器都处于就绪状态;
  PodScheduled      True    #pod已经成功被调度到莫个节点上;

3.3 容器运行阶段

kubernetes会跟踪Pod中每个容器的状态,就像跟踪Pod阶段一样。Pod中运行的容器状态与Pod阶段是存在关联关系的,所以当Pod出现故障时,讲Pod的阶段状态和Pod中的容器状态结合起来查看,更容易定位具体问题。

一旦调度器讲Pod分配给莫格节点,kubelet就通过容器运行时开始为Pod创建容器。容器的状态有三种:Waiting(等待)、Running(运行中)和Terminated(已终止)。容器状态官方站点

一个pod部署两个容器_一个pod部署两个容器_04


可以通过

kubectl describe pod <pod名称> 查看pod种的容器状态

[root@k8s-240 pod]# kubectl describe pod nginx-dp-776b5f86cc-4xj5b 
Containers:
  nginx-container:
    Container ID:   containerd://7410087425f295554a14e42f5cc2927c3f738005dc3381dc0e8536f24b6a4fa9
    Image:          nginx:1.16
    Image ID:       docker.io/library/nginx@sha256:d20aa6d1cae56fd17cd458f4807e0de462caf2336f0b70b5eeb69fcaaf30dd9c
    Port:           <none>
    Host Port:      <none>
    State:          Running  #当前状态除了Running,还有Waiting Terminated
      Started:      Wed, 24 May 2023 16:39:26 +0800
    Ready:          True     #容器是否已经就绪
    Restart Count:  0        #容器重启的次数
    Environment:    <none>

四. Pod运行容器对应字段

4.1 容器镜像拉取策略

imagePullPolicy 容器的镜像拉取策略

  • IfNotPresent: 本地有镜像则使用本地镜像,本地不存在则拉取镜像。
  • Always: 每次都会尝试拉取新镜像
  • Never: 永不拉取镜像!如果镜像已经存在本地,kubelet会尝试启动容器,否则会启动失败。
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-dp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx-container
        image: nginx:1.16
        imagePullPolicy: Always #指定镜像的拉取策略 每次pod启动都拉取最新版

没有设置imagePullPolicy字段,容器镜像的标签是latest,imagePullPolicy会自动设置为Always.如果你省略了imagePullPolicy字段,并且没有指定容器镜像的标签,imagePullPolicy会自动设置为Always.如果省略了imagePullPolicy字段,并且tag为非 latest, imagePullPolicy就会自动设置为IfNotPresent.

4.2 容器拉取私有仓库镜像

ImagePullSecrets拉取私有仓库中的镜像
1.创建一个Pod资源,拉取一个私有仓库镜像,在不添加ImagePullSectets时,验证是否正常拉取镜像

apiVersion: v1
kind: Pod
metadata:
  name: pod-image-secret
spec:
  containers:
  - name: pod-image-secret
    image: registry.cn-huhehaote.aliyuncs.com/oldxu3957/nginxdemo:latest
    imagePullPolicy: Always

2.检查Pod状态,发信啊Pod状态为ErrImagePull,通过describe描述,发现需要docker login 后才能拉取镜像

[root@k8s-240 pod]# kubectl apply -f imagepull.yaml 
pod/pod-image-secret created
[root@k8s-240 pod]# kubectl get pod
NAME                        READY   STATUS         RESTARTS      AGE
pod-image-secret            0/1     ErrImagePull   0             2s
[root@k8s-240 pod]# kubectl describe pod pod-image-secret
Events:
  Type     Reason     Age                 From               Message
  ----     ------     ----                ----               -------
  Normal   Scheduled  2m6s                default-scheduler  Successfully assigned default/pod-image-secret to flyfish83
  Normal   Pulling    36s (x4 over 2m6s)  kubelet            Pulling image "registry.cn-huhehaote.aliyuncs.com/oldxu3957/nginxdemo:latest"
  Warning  Failed     35s (x4 over 2m5s)  kubelet            Failed to pull image "registry.cn-huhehaote.aliyuncs.com/oldxu3957/nginxdemo:latest": rpc error: code = Unknown desc = failed to pull and unpack image "registry.cn-huhehaote.aliyuncs.com/oldxu3957/nginxdemo:latest": failed to resolve reference "registry.cn-huhehaote.aliyuncs.com/oldxu3957/nginxdemo:latest": pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed
  Warning  Failed     35s (x4 over 2m5s)  kubelet            Error: ErrImagePull
  Warning  Failed     23s (x6 over 2m5s)  kubelet            Error: ImagePullBackOff
  Normal   BackOff    8s (x7 over 2m5s)   kubelet            Back-off pulling image "registry.cn-huhehaote.aliyuncs.com/oldxu3957/nginxdemo:latest"

3.创建一个名为aliyun的secret资源,然后配置对应仓库的用户名称及密码

[root@k8s-240 pod]# kubect create secret docker-registry aliyun --docker-username=552408925@qg.com -docker-password=123456 --docker-server registry.cn-huhehaote.aliyuncs .com

4.修改Pod资源清单,添加ImagePullSecrets传入对应的Secrets资源名称

apiVersion: v1
kind: Pod
metadata:
  name: pod-image-secret
spec:
  imagePullSecrets:  #增加imagePullSecrets字段,传入对应资源的名称
  - name: aliyun

  containers:
  - name: pod-image-secret
    image: registry.cn-huhehaote.aliyuncs.com/oldxu3957/nginxdemo:latest
    imagePullPolicy: Always

4.3 容器环境变量自定义

使用 env 控制容器环境变量
1.准备一个mysql镜像,然后通过env传入对应的登录密码,同事创建一个默认的k8s数据库

apiVersion: v1
kind: Pod
metadata:
  name: pod-mysql-env
spec:
  containers:
  - name: mysql
    image: mysql:5.7.37
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: "oldxu3957"
    - name: MYSQL_DATABASE
      value: "k8s"

2.查看mysql-pod容器详情

[root@k8s-240 pod]# kubectl get pod -o wide
NAME               READY   STATUS             RESTARTS      AGE   IP               NODE        NOMINATED NODE   READINESS GATES
pod-mysql-env      1/1     Running            0             94s   10.244.149.156   flyfish82   <none>           <none>

3.通过本机的 mysql client 尝试连接服务,验证密码是否传入成功数据库是否建立

[root@k8s-240 pod]# mysql -h 10.244.149.156 -uroot -p
MySQL [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| k8s                |
| mysql              |
| performance_schema |
| sys                |
+--------------------+

4.4 容器启动命令与参数

  • command: 为容器指定启动命令,会覆盖容器启动的默认命令,不指定则默认容器的启动命令
  • args: 为命令提供选项或参数
    1.准备一个nginx镜像演示command与args的作用
container:
  - images; nginx
    name: nginx
  command:
    - /bin/bash  #参数
    - -C
    - "echo $(msg);sleep 600:"
  env:
    - name : msg
      value: "Hello 0ldxu"

4.5 容器自定义端口

ports 用于暴露pod对外访问的端口,如不指定,则无法通过 PodIP + PodPort 访问该应用

  • containerPort: 填写Pod对外暴露的端口(0~65535)
  • name: 为端口指定一个名称,当服务存在多个端口,可以通过名称区分
  • protocol: 指定端口对应的协议,有tcp udp sctp 默认不写为tcp
    示例文件
apiVersion: v1
kind: Pod
metadata:
  name: redis-port
spec:
  containers:
    - name: redis-port
      image: redis
      ports:
        - name: port
          containerPort: 6379
          protocol: TCP

五.Pod重启策略

Podspec 中包含一个 restartPolicy 字段,用来设置 Pod 中所有容器的重启策略,取值有ALways0nFailureNever。默认值是ATways

  • ALways: 当容器出现异常退出时,kubelet 会尝试重启该容器已恢复正常状态; (默认策略)
  • Never: 当容器退出时,kubelet 永远不会尝试重启该容器 (适合Job类一次性任务)
  • 0nFailure: 当容器异常退出 (且退出状态码非0时) ,kubelet会尝试重启容器 (适合Job类一次性任务)
    注意: 通过 kubelet 重新启动的容器,后续如果还出现异常退出,则会以指数增加延迟 (10s,20s,40s...) 来进行容器的重新创建和启动,其最长延迟为 5 分钟。一旦容器执行了 10 分钟并且没有出现问题,kubelet 对该容器的重启计时器进行重置为初始状态

5.1 Always

1.编写Pod的yaml文件

apiVersion: v1
kind: Pod
metadata:
  name: pod-always
spec:
  restartPolicy: Always
  containers:
  - name: pod-always
    image: nginx
    ports:
    - containerPort: 80

2.检查Pod的运行状态,可以看到Pod正常运行,RESTARTS(重启次数)字段为0

[root@k8s-240 pod]# kubectl get pod 
NAME         READY   STATUS    RESTARTS   AGE
pod-always   1/1     Running   0          17m

3.正常停止容器应用,可以看到容器被重启了一次,然后Pod又恢复正常状态了。

[root@k8s-240 pod]# kubectl exec -it pod-always  -- /bin/bash -c 'nginx -s quit'
2023/05/25 08:07:31 [notice] 33#33: signal process started
[root@k8s-240 pod]# kubectl get pod
NAME         READY   STATUS    RESTARTS     AGE
pod-always   1/1     Running   1 (4s ago)   28m

4.非正常停止容器应用,发现容器又被重启了一下,然后Pod又恢复正常状态了,并且重启次数再次增加1次;

[root@k8s-240 pod]# kubectl exec -it pod-always  -- /bin/bash -c 'kill 1'
[root@k8s-240 pod]# kubectl get pod
NAME         READY   STATUS    RESTARTS      AGE
pod-always   1/1     Running   2 (55s ago)   53m

重启侧罗Always,在创建单个Pod的情况下,不管Pod中的容器是否正常停止,最终都会恢复。

5.2 Never

1.编写Pod的yaml文件

apiVersion: v1
kind: Pod
metadata:
  name: pod-never
spec:
  restartPolicy: Never
  containers:
  - name: pod-never
    image: nginx
    ports:
    - containerPort: 80

2.检查Pod的运行状态,可以看到Pod正常运行,RESTARTS(重启次数)字段为0.

[root@k8s-240 pod]# kubectl exec -it pod-never  -- /bin/bash -c 'nginx -s quit'
2023/05/25 08:50:35 [notice] 33#33: signal process started
[root@k8s-240 pod]# kubectl get pod
NAME         READY   STATUS      RESTARTS      AGE
pod-always   1/1     Running     2 (18m ago)   71m
pod-never    0/1     Completed   0             29s

5.3 OnFailure

1.编写Pod的yaml文件

apiVersion: v1
kind: Pod
metadata:
  name: pod-onfailure
spec:
  restartPolicy: OnFailure
  containers:
  - name: pod-onfailure
    image: nginx
    ports:
    - containerPort: 80

2.检查Pod的运行状态,可以看到Pod正常运行,RESTARTS(重启次数)字段为0.

[root@k8s-240 pod]# kubectl get pod 
NAME            READY   STATUS    RESTARTS   AGE
pod-onfailure   1/1     Running   0          19s
[root@k8s-240 pod]# kubectl exec -it pod-onfailure -- /bin/bash -c 'kill 1'
error: cannot exec into a container in a completed pod; current phase is Succeeded
[root@k8s-240 pod]# kubectl get pod 
NAME            READY   STATUS      RESTARTS   AGE
pod-onfailure   0/1     Completed   0          61s                      #没有成功

6.Pod生命周期

6.1 什么是Pod生命周期

Pod对象从创建开始至终止退出之间的时间称其为生命周期

6.2 生命周期流程

下图展示了一个Pod的完整生命周期过程,其中包含Init Container、Pod Hook、健康检查 三个主要部分,接下来我们就来分别介绍影响Pod生命周期的部分

一个pod部署两个容器_一个pod部署两个容器_05

6.3 生命周期总结

当一个Pod对象被创建时,其生命周期的运行步骤如下:
1. 创建pause基础容器:在启动任何其他容器之前,首先创建一个pause容器。该容器用于初始化Pod的环境,并提供共享的名称空间给后续加入的容器使用。
2. 运行初始化容器:按照定义顺序依次运行用户定义的初始化容器。这些初始化容器以串行方式运行,用于完成Pod环境的初始化。如果任何一个初始化容器运行失败,将导致Pod创建失败。根据restartPolicy的策略进行处理,默认为重启。
3. 启动程序容器:在所有初始化容器成功完成后,启动主要的程序容器。如果Pod中有多个容器,它们将并行启动,并各自维护自己的生命周期。同时,主容器上定义的Poststart钩子函数会在容器启动时运行。如果该步骤失败,相关容器将被重启。
4. 运行健康状态监测:在容器启动后,执行容器启动健康状态的监测(startupProbe)。如果检测失败,根据restartPolicy中定义的策略进行处理。如果没有定义策略,默认状态为成功(Success)。
5. 运行存活状态和就绪状态监测:容器在启动成功后,定期进行存活状态监测(Liveness)和就绪状态监测(Readiness)。如果存活状态监测失败,将导致容器重启;而就绪状态监测失败将使容器从负载均衡中被移除。
6. 终止Pod:在终止Pod时,先运行preStop钩子函数。在经过宽限期(terminationGracePeriodSeconds)后,终止容器。宽限期默认为30秒。
以上是Pod对象生命周期的运行步骤。

7.Init Container

7.1 基本概念

Init Container是用来做初始化工作的容器。可以有一个或多个,如果多个按照定义的顺序依次执行,只有所有的执行完后,主容器才启动由于一个Pod里的存储卷是共享的,所以 Init Container 里产生的数据可以被主容器使用到,但它仅仅是在Pod启动时,在主容器启动前执行,做初始化工作,如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init 容器成功为止。如果 Pod 对应的restartPolicy 值为 Never,Kubernetes 不会重新启动 Pod。

7.2 应用场景

1、app容器依赖MySQL的数据交互,所以可以启动一个初始化容器检查MySOL服务是否正常,如果正常则启动主容器;
2、在启动主容器之前,使用初始化容器对系统内核参数进行调优.然后共享给主容器使用;
3、获取集群成员节点地址,为主容器生成对应配置信息,这样主容器启动后,可以通过配置信息加入集群环境;

7.3 场景实践

1.编写yaml,使用初始化容器对MySQL端口进行检查,如果存活则运行Pod,否则一直重启尝试

apiVersion: v1
kind: Pod
metadata:
  name: init-check-mysql
spec:
  initcontainers:
    - name: check-mysql
      image: oldxu3957/tools
      command: ["sh", "-c", "nc -z 10.0.0.206 3306"]
      securityContext:
        privileged: true          #以特权模式运行容器,否则无法修改内核参数
  Containers:                     #主容器
    - name: app-mysql
      image: nginx
      ports:
        - containerPort: 80

2.当MySQL服务没有启动完毕,则改Pod会出现初始化失败,然后触发重启

[root@master ~]# kubectl get pod init-systctl-nginx
NAME          READYSTATUS      RESTARTS                      AGE
init-check-mysql 0/1         Init:CrashLoopBack0ff(5s ago)   43s

3.安装MySQL服务,确保3306对外监听

yum install mariadb-server
systemctl start mariadb

4.检查Pod,发现已经检查到MySQL运行,所以会启动主容器

[root@master ~] kubectl get pod init-systctl-nginx
NAME                READY   STATUS   RESTARTS   AGE
init-check-mysql    1/1]   Running    0         103s

场景实践二 优化内核参数
1.编写yaml,使用初始化容器对内核参数进行优化

[root@master pod-init]# cat init-sysctl-nginx.yaml
apiVersion: v1
kind: Pod
metadata:
  name: init-sysctl-nginx
spec:
  initContainers: #初始化容器,必须运行完就结束,否则后续无法正常运行主容器
  - name: set-sysctl
    image: alpine:3.13
    command: ["sh","-c","sysctl -wnet.core.somaxconn=32769; sysctl -wnet.ipv4.ip_local_port_range='1024 65000"]
    securityContext:
      privileged: true# 以特权模式运行容器否则无法修改内核参数
  containers:   #主容器
  - name: app-sysctl
    image: nginx
    ports:
    - containerPort: 80

2.运行Pod,检查Pod的内核参数,发现已经优化了(建议使用镜像查看此前的内核参数)

[root@master ~]# kubectl get pod
NAME                  READY    STATUS     RESTARTS     AGE
init-sysctl-nginx      0/1     Init:0/1     0           4S
[root@master ~]# ubectl exec -it init-systctl-nginx-c "app-sysctl" -- /bin/bash -c "cat/proc/sys/net/core/somaxconn"
32769

八.Pod Hook

容器生命周期钩子(Container Lifecycle Hooks)监听容器生命周期的特定实践,并在事件发生时执行已注册的回调函数。
kubernetes支持 poststart和prestop事件。当一个容器启动后,kubernetes将立即发送poststart事件;在容器被终结之前,kubernetes将发送一个prestop事件。容器可以为每个事件指定一个处理程序。

8.1 两种钩子

postStart: 容器创建后立即执行,由于是异步执行,它无法保证一定在容器之前运行。如果失败,容器会被杀死,并根据RestartPolicy决定是否重启。
preStop: 容器终止前执行。用于:释放占用的资源,清理注册过的信息,优雅的关闭进程。在其完成之前会阻塞删除容器的操作,默认等待时间为30s,可以通过terminationGracePeriodSecpnds宽限时间。

8.2 钩子示例

postStart示例:

#通过postStart设定端口重定向,将请求本机的8080调度到本机的80端口
lifecycle:
  postStart:
    exec:
      command:
        - "/bin/bash"
        - "-c"
        - "iptables -t nat -A PREROUTING -p tcp --dport 8080 -j REDIRECT --to-ports 80"

preStop

# runner主要用来编译打包提高CI效率。启动后悔注册到gitlab上,后续不需要可以删除Pod,然后清理注册信息
#通过preStop清理runner注册信息
lifecycle:
  preStop:
    exec:
      command:
        - /bin/bash
        - -c
        - /usr/bin/gitlab-runner unregister -n $RUNNER_NAME

8.3 场景实践

postStart 命令在容器的 /usr/share/nginx/html/index.html自定义一段内容 preStop负载优雅的终止nginx服务。
terminationGracePeriodSecpnds: 宽限期,如果超过宽限期pod还没终止,则会由SIGKILL强制关闭信号介入。

apiVersion: v1
kind: Pod
metadata:
  name: Tifecycle-nginx
spec:
  containers:
    - name: Tifecycle-demo-container
      image: nginx
      lifecycle:
        postStart:
          exec:
            command:
              - "/bin/sh"
              - "-c"
              - "echo 'Hello from the postStart handler' > /usr/share/nginx/html/index.html"
        preStop:
          exec:
            command:
              - "/bin/sh"
              - "-c"
              - "nginx -s stop"

场景实践二
postStart命令负责将默认页面,拷贝至/usr/local/tomcat/webapps preStop负责给容器发送 SIGERM信号,从而优雅的终止tomcat服务。
terminationGracePeriodSecpnds: 宽限期,如果超过宽限期pod还没终止,则会有SIGKILL强制关闭信号介入。

apiVersion: v1
kind: Pod
metadata:
  name: Tifecycle-tomcat-hook
  labels:
    web: tomcat
spec:
  terminationGracePeriodSeconds: 120
  containers:
    - name: tomcat
      image: tomcat
      ports:
        - containerPort: 8080
      lifecycle:
        postStart:
          exec:
            command:
              - "/bin/bash"
              - "-c"
              - "cp -rf /usr/local/tomcat/webapps.dist/* /usr/local/tomcat/webapps"
        preStop:
          exec:
            command:
              - "/bin/bash"
              - "-c"
              - "sleep 10; /usr/local/tomcat/bin/shutdown.sh"

九.Pod检测探针

9.1 为何需要探针

当容器进程运行时如果出现了异常退出,Kubernetes则会认为容器发生故障,会尝试进行重启解决该问题。但有不少情况是发生了故障,但进程并没有退出。比如访问Web服务器时出现了500的内错误,可能是系统超载,也可能是资源死锁,但nginx进程并没有异常退出,在这种情况下重启容器可能是最佳的方法。那如何来实现这个检测呢;
Kubernetes 使用探针 (probe) 的方式来保障容器正常运行,实现零宕机;它通过 kubelet 定期对容器进行健康检查 (exec、tcp.http) ,当探针检测到容器状态异常时,会通过重启策略来进行重启或重建完成修复。修复后继续进行探针检测,已确保容器稳定运行。

9.2 探针探测类型

针对运行中的容器,kubelet 可以选择以下三种探针来探测容器的状态

  • startupProbe 启动探针
    用于检测容器中的应用是否已经正常启动。如果使用了启动探针,则所有其他探针都会被禁用,需要等待启动探针检测成功之后才可以执行。如果启动探针探测失败,则 kubelet 会将容器杀死,而容器依其重启策略 进行重启。如果容器没有提供启动探测,则默认状态为Success。
  • LivenessProbe 存活探针
    用于检测容器是否存活,如果存活探测检测失败,kubeLet会杀死容器,然后根据容器重启策略,决定是否重启该容器。如果容器不提供存活探针,则默认状态为Success。
  • readinessProbe 就绪探针
    指容器是否准备好接收网络请求,如果就绪探测失败,则将容器设定为未就绪状态,然后将其从负载均衡列表中移除,这样就不会有请求会调度到该Pod上;如果容器不提供就绪态探针,则默认状态为Success.

9.3 探针检查机制

使用探针来检查容器有如下集中方式

  • exec : 在容器内执行指定命令。如果命令退出时返回码为 则认为诊断成功。
  • httpGet: 对指定的IP、端口,执行HTTP请求。如果响应的状态码大于等于200且小于400,则诊断被认为是成功的。
  • tcpSocket: 对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开,则诊断被认为是成功的。
    每次探测都将获得以下三种结果之一
  • Success (成功) : 容器通过了诊断Success
  • Failure (失败) :容器未通过诊断,可能会触发重启操作
  • Unknown (未知) : 诊断失败,因此不会采取任何行动.

9.4 探针配置格式

apiVersion: v1
kind: Pod
metadata :
  name : probe
spec :
  containers:
  - name :
    image:
    livenessProbe:
      exec:
      httpGet:
      tcpSocket:
      initialDelaySeconds:
      timeoutseconds:
      PeriodSeconds:
      successThreshold:
      failureThreshold:

十.startupProbe

10.1 exec

apiVersion: v1
kind: Pod
metadata:
  name: pod-startup-exec
spec:
  containers:
  - name: pod-startup-exec
    image: oldxu3957/demoapp:v1.8
    ports:
    - containerPort: 80
    startupProbe:
      exec:
        command:
        - "/bin/sh"
        - "-c"
        - "ps aux | grep demo.py"
      initialDelaySeconds: 10            #容器启动多久后开始探测,默认0
      periodSeconds: 10                  #探测频率,10s探测一次
      timeoutSeconds: 10                 #探测超时时间     
      successThreshold: 1                #成功多少次则为成功,默认1次
      failureThreshold: 3                #失败多少次则为失败,默认3次

PS 第一次探测失败多久会重启

initialDelaySeconds + (periodSeconds +timeoutSeconds) * faiureThreshold

PS 程序启动完成后: 此时不需要计入initiaLDelaySeconds.

(periodSeconds + timeoutSeconds) * failureThreshold

10.2 httpGet

apiVersion: v1
kind: Pod
metadata:
  name: pod-startup-http
spec:
  containers:
  - name: pod-startup-http
    image: oldxu3957/demoapp:v1.0
    ports:
    - containerPort: 80
    startupProbe:
      httpGet:
        path: /
        port: 80
      initialDelaySeconds: 10   #容器启动多久后开始探测,默认0
      periodSeconds: 10         #探测频率,10s探测一次
      timeoutSeconds: 10        #探测超时时长
      successThreshold: 1       #成功多少次则为成功,默认1次
      failureThreshold: 3       #失败多少次则为失败默认3次

10.3 tcpSocket

apiVersion: v1
kind: Pod
metadata:
  name: pod-startup-tcp
spec:
  containers:
  - name: pod-startup-tcp
    image: oldxu3957/demoapp:v1.0
    ports:
    - containerPort: 80
    startupProbe:
      tcpSocket:
        port: 80
      initialDelaySeconds: 10           #启动多久开始探测
      periodSeconds: 10                 #探测频率 10s一次
      timeoutSeconds: 10                #探测超时时长
      successThreshold: 1               #成功多少次为成功 默认1次
      failureThreshold: 3               #失败多少次为失败 默认3次

十一. livenessProbe

11.1 exec

apiVersion: v1
kind: Pod
metadata:
  name: pod-liveness-exec
spec:
  containers:
  - name: pod-liveness-exec
    image: oldxu3957/demoapp:v1.0
    ports:
    - containerPort: 80
    livenessProbe:
      exec:
        command:
        - "/bin/sh"
        - "-c"
        - '["$(curl -s 127.0.0.1/livez)" = "OK"]'
      initialDelaySeconds: 10           #容器启动后多久开始探测,默认0
      periodSeconds: 10                 #探测时间间隔 10s探测一次
      timeoutSeconds: 10                #探测的超时时间
      successThreshold: 1               #成功多少次为成功 默认1次
      failureThreshold: 3               #失败多少次为失败 默认3次

2、为了测试存活状态监测效果,可以手动将/Livez接口的响应内容修改为任意值。

[root@master ~]# kubectl exec -it pod-liveness-exec- curl -s -X POST -d 'livez=error' http://127.0.0.1/livez

3、会发现容器等待60s之后,会触发重启操作

[root@master ~]# kubectl describe pod pod-liveness-exec
...
Warning   Unhealthy 31s (x3 over 51s) kubelet Liveness probe failed:
Normal    Killing   31s               kubelet Container pod-liveness-exec failed livenessprobe, will be restarted

11.2 httpGet

apiVersion: v1
kind: Pod
metadata:
  name: pod-liveness-http
spec:
  containers:
  - name: pod-liveness-http
    image: oldxu3957/demoapp:v1.0
    ports:
    - containerPort: 80
    livenessProbe:
      httpGet:
        path: '/livez'
        port: 80
        scheme: HTTP
      initialDelaySeconds: 10           #容器启动后多久开始探测,默认0
      periodSeconds: 10                 #探测时间间隔 10s探测一次
      timeoutSeconds: 10                #探测的超时时间
      successThreshold: 1               #成功多少次为成功 默认1次
      failureThreshold: 3               #失败多少次为失败 默认3次

2、镜像中定义的默认响应是以200状态码响应,存活状态会成功完成为了测试存活状态监测效果,可以手动将/Livez接口的响应内容修改为任意值。

[root@master ~]# kubectl exec -it pod-liveness-http-- curl -s -X POST -d 'livez=error' 127.0.0.1/ivez

3、等待3个监测周期,容器会因健康监测失败而被重启,重启后/ivez响应内容会被重置为ok,后续存活状态监测不会出现错误

[root@master ~]# kubectl describe pod pod-liveness-http
Warning Unhealthy 30s (x6 over 7m30s) kubeletLiveness probe failed: HTTP probe failedwith statuscode: 506
Normal Killing30s (x2 over 7m10s) kubeletContainer pod-liveness-http failed livenessprobe, will be restarted

11.3 tcpSocket

apiVersion: v1
kind: Pod
metadata:
  name: pod-liveness-tcp
spec:
  containers:
  - name: pod-liveness-tcp
    image: oldxu3957/demoapp:v1.0
    ports:
    - containerPort: 80
    livenessProbe:
      tcpSocket:
        port: 80
      initialDelaySeconds: 10           #容器启动后多久开始探测,默认0
      periodSeconds: 10                 #探测时间间隔 10s探测一次
      timeoutSeconds: 10                #探测的超时时间
      successThreshold: 1               #成功多少次为成功 默认1次
      failureThreshold: 3               #失败多少次为失败 默认3次

十二.readinessProbe

指容器是否准备好接收网络请求,如果就绪探测失败,则将容器设定为未就绪状态,然后将其从负载均衡列表中移除,这样就不会有请求会调度到该Pod上;如果容器不提供就绪态探针,则默认状态为Success。
有些程序启动后需要加载配置或数据,甚至有些程序需要运行预热的过程,需要一定的时间。所以需要避免Pod启动成功后立即让其处理客户端请求,而应该让其初始化完成后转为就绪状态,在对外提供服务。此类应用就需要使用readinessProbe探针。

12.1 exec

apiVersion: v1
kind: Pod
metadata:
  name: pod-readiness-exec
  labels:
    app: readiness
spec:
  containers:
  - name: pod-readiness-exec
    image: oldxu3957/demoapp:v1.0
    ports:
    - containerPort: 80
    readinessProbe:
      exec:
        command:
        - "/bin/sh"
        - "-c"
        - '[ "$(curl -s 127.0.0.1/readyz)" = "OK" ]'
      initialDelaySeconds: 10           #容器启动后多久开始探测,默认0
      periodSeconds: 10                 #探测时间间隔 10s探测一次
      timeoutSeconds: 10                #探测的超时时间
      successThreshold: 1               #成功多少次为成功 默认1次
      failureThreshold: 3               #失败多少次为失败 默认3次

12.2 httpGet

apiVersion: v1
kind: Pod
metadata:
  name: pod-readiness-http
  labels:
    app: readiness
spec:
  containers:
  - name: pod-readiness-http
    image: oldxu3957/demoapp:v1.0
    ports:
    - containerPort: 80
    readinessProbe:
      httpGet:
        path: '/readyz'
        port: 80
        scheme: HTTP
      initialDelaySeconds: 10           #容器启动后多久开始探测,默认0
      periodSeconds: 10                 #探测时间间隔 10s探测一次
      timeoutSeconds: 10                #探测的超时时间
      successThreshold: 1               #成功多少次为成功 默认1次
      failureThreshold: 3               #失败多少次为失败 默认3次

2.为了测试就绪状态监测效果,将/readyz修改为非0K

[root@master ~]# kubectl exec -it pod-readiness-http -- curl -s -X POST -d 'readyz=error' http://127.0.0.1/readyz

3.由于pod未就绪,所以会将该节点从Service负载均衡中准为未就绪状态 (需要事先创建好负载均衡,否则难以观察效果)

[root@master ~]# kubectl get pod pod-readiness-http
NAME                 READY   STATUS   RESTARTS  AGE
pod-readiness-http    0/1    Running     0      6m
[root@master ~]# kubectl describe endpoints pod-readiness-exec
Name:   pod-readiness-exec
Subsets:
   Addresses:          <none>
   NotReadyAddresses: 192.168.3.38

12.3 tcpSocket

apiVersion: v1
kind: Pod
metadata:
  name: pod-readiness-tcp
  labels:
    app: readiness
spec:
  containers:
  - name: pod-readiness-tcp
    image: oldxu3957/demoapp:v1.0
    ports:
    - containerPort: 80
    readinessProbe:
      tcpSocket:
        port: 80
      initialDelaySeconds: 10           #容器启动后多久开始探测,默认0
      periodSeconds: 10                 #探测时间间隔 10s探测一次
      timeoutSeconds: 10                #探测的超时时间
      successThreshold: 1               #成功多少次为成功 默认1次
      failureThreshold: 3               #失败多少次为失败 默认3次

十三.Pod资源限制

13.1 什么是资源限制

在kubernetes集群中,某Pod发生异常,不断的使用系统资源。会对kubernetes中所有Pod稳定性产生影响,所以需对运行的Pod进行资源限制。

13.2 如何实现资源限制

kubernetes 通过 requests 和 limits 字段阿里实现对pod的资源进行限制

  • requests:启动Pod时申请分配的资源大小;(Pod在调度的时候requests比较重要)
  • limits: 限制Pod运行时最大可用的资源大小;(Pod在运行时limits比较重要)
spec.containers[].resources .request.cpu
spec.containers[].resources.request.memory
spec.containers[].resources.limits.cpu
spec.containers[].resources.limits.memory

13.3 资源限制的目的与意义

CPU: 为集群中运行的容器配置CPU请求和限制,可以有效利用集群上可用的CPU资源。

  • 设置Pod CPU请求 设定在较低的数值,可以使Pod更有机会被调度。
  • 设置CPU limit大于CPU request,可以完成如下两件事:
* 当Pod碰到一些突发负载时,它可以合理利用可用的CPU资源
 * 当Pod在突发流量期间,可使用的CPU被限制为合理的数值,从而可以避免影响其他Pod的正常运行

Memory: 为集群中运行的容器配置内存请求和限制,可以有效利用集群节点上可用的内存资源。

  • 通过将POD的内存请求设定在脚底的数值,可以使Pod更有机会被调度。
  • 通过让内存限制大于内存请求,可以完成如下两件事:
  • 当Pod碰到一些突发负载时,可以更好的利用其主机上的可用内存。
  • 当Pod在突发负载期间可使用的内存被限制为合理的数值,从而可以避免影响其他的Pod的运行。

十四.资源限制单位换算

14.1 CPU限制单位

1核CPU等于1000毫核,当定义容器为0.5时,所能用到的CPU资源时1核心CPU的一半,对于 CPU 资源单位,表达式 .1 等价于表达式100m,可以看作 1 millicpu.

1 核心 = 100 millicpu(1 Core = 1000m)
0.5 核 = 50日 millicpu(0.5 Core = 500m)

举例:当我们有1个物理CPU,16核心,如果某个Pod最多使用一半的核心数,则表达式可以写入如下两种:

  • limits.cpu: 8
  • Timits.cpu: 8000m 计算公式: (160*.5=8000m)

注意: Kubernetes不允许设置精度小于 1m 的CPU资源。因此当CPU 单位小于1时,只能使用毫核来表示。
例如: 期望使用1个CPU的0.5%,应该写 5m 而不是 .5

14.2 内存限制单位

内存的基本单位是字节数(Bytes),也可以加上国际单位,十进制的E、P、T、G、M,K、m,或二进制的 Ei、Pi、Ti、Gi、Mi、Ki.

1MB = 1000 KB = 100000 Bytes
1Mi = 1024 KB = 1048576 bytes

十五.CPU资源限制实践

metrics-server

15.1 设置容器的CPU请求和限制

1.创建一个具有一个容器的Pod。容器将请求0.5个CPU,最多限制使用1个CPU

apiVersion: v1
kind: Pod
metadata:
  name: cpu-demo
spec:
  containers:
  - name: cpu-demo-ctr
    image: vish/stress
    args:              #容器启动命令,容器尝试使用2核CPU
    - "-cpus"
    - "2"
    resources:
      requests:        #显示启动Pod时最多申请0.5核的CPU
        cpu: "500m"
      limits:          #限制运行中的Pod最多使用1核CPU
        cpu: "1000m"

2.查看Pod详细信息,输出显示Pod中的一个容器的CPU请求为500 milli CPU,并且 CPU限制为1个CPU.

[root@master ~]# kubectl get pod cpu-demo -o yaml
    resources:
      Limits:
        cpu:"1"
      requests:
        cpu: 500m

3.检查资源限制情况,容器配置为尝试使用2个CPU,但是容器只被允许最大使用1个CPU,所以容器的CPU用量受到限制。

[root@master ~]# kubectl apply -f https://linux.oldxu .net/metrics-server.yaml
[root@master ~]# kubectl top pod cpu-demo
NAME        CPU(cores)         MEMORY(bytes)
cpu-demo     913m                 0Mi

15.2 设置超过节点的CPU请求

1.创建一个 Pod,设置该Pod中容器的请求为100核,这个值会大于集群中的任何一个节点。

apiVersion: V1
kind: Pod
metadata:
  name: cpu-demo-2
spec:
  containers:  
  - name: cpu-demo-ctr-2
    image: vish/stress
    args:
    - -cpus
    - "2"
    resources:
      requests:
        cpu: "100"
      limits:
        cpu:"100"

2.查看该Pod的状态,输出显示Pod状态为 pending.也就是说 Pod未被调度到任何节点上运行。

[root@master pod-request]# kubectl get pod
NAME           READY        STATUS      RESTARTS     AGE
cpu-demo-2     0/1         Pending        0          3s

3.查看该Pod的详细信息及事件,输出显示由于节点上的CPU资源不足,无法调度容器

[rootmaster ~]# kubectl describe pod cpu-demo-2
Events:
   Type     Reason     Age     From     Message

-----------------------------------------------
Warning FailedScheduling 49s (x2 over 116s)default-scheduler /3 nodes are available: 1node(s) had taint {node-role .kubernetes .io/master:}, that the pod didn't tolerate, 2 Insufficient cpu.

4.删除Pod

[root@master ~]# kubectl delete pod cpu-demo-2
pod "cpu-demo-2" deleted

15.3 如果不指定CPU的limits

如果没有为容器指定 CPU 限制,那么容器在可以使用的 CPU 资源是没有上限。因而可以使用所在节点上所有的可用 CPU 资源,这样可能会造成某一个Pod占用了大量的CPU时间,可能会影响其他的Pod正常运行从而造成业务的不稳定性。
这个也不用担心,在Kubernetes中,可以通过 LimitRange 自动为容器设定,所使用的CPU资源和内存资源最大最小值。

十六. 内存资源限制实践

16.1 设置容器的内存请求和限制

1.创建一个拥有一个容器的Pod。容器将会请求100MiB内存,并且内存会被限制在200MiB 以内

apiVersion: v1
kind: Pod
metadata:
  name: memory- demo
spec:
  containers:
  - name: memory-demo-ctr
    image: polinux/stress
    command: ["stress"]
    args: ["--vm","1","--vm-bytes","150M","--vm-hang”,"1”] # 告知容器尝试分配 150 MB 内存
    resources:
      requests:
        memory: "100Mi'
      limits:
        memory: "200Mi"

2.检查Pod,结果显示该Pod中容器的内存请求为 100MiB,内存限制为 200MiB

[root@master ~]# kubectl get pod memory-demo -o yaml
    resources :
      limits:
        memory: 200Mi
      requests:
        memory: 100Mi

3.获取该Pod的指标数据: 输出结果显示Pod正在只用的内存为150MiB.这大于Pod请求的100MiB,但又在Pod限制的200MiB之内。

[root@master ~]# kubectl top pod memory-demo
NAME            CPU(cores)       MEMORY(bytes)
mmemory-demo    46m              150Mi

4.删除pod

[root@master ~]# kubectl delete pod memory-demo
pod "memory-demo" deleted

16.2 运行超过容器内存限制的应用

当节点拥有足够的可用内存时,容器可以使用其请求的内存。但是,容器不允许使用超过其限制的内存。如果容器分配的内存超过其限制,该容器会成为被终止的候选容器。如果容器继续消耗超出其限制的内存,则终止容器。如果终止的容器可以被重启,则 kubelet 会重新启动它。
1.创建一个 Pod,其拥有一个容器,该容器的内存请求为 100MiB,内存限制为 200MiB,尝试分配超出其限制的内存。

[root@master ~]# cat memory-request-limit-2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: memory- demo-2
spec:
  containers:
  - name: memory-demo-2-ctr
    image: polinux/stress
    command: ["stress"]# 容器会尝试分配 250 MiB 内存,这远高于 100 MiB 的限制。
    args: ["--vm","1","--vm-bytes","250M","-- vm-hang","1” ] # 模拟1个进程产生250M内存
    resources:
      requests:
        memory:"100Mi"
      limits:
        memory: "200Mi"

2.查看Pod,此时,容器可能正在运行或被杀死。重复前面的命令,直到容器被杀掉:

[root@master ~]# kubectl get pod
NAME           READY    STATUS     RESTARTS    AGE
memory-demo-2   0/1      00MKilled   0         13s

3.查看容器更详细的信息,其输出结果为,内存溢出 (00M) ,容器已被杀掉:

[root@master ~]# kubectl get pod memory-demo-2 -o yaml
      lastState: 
        terminated:
          containerID:docker://43d2278dacdac6cea191c6c04f2147025128ce45c961ab0c2d366840dobc5e40
          exitCode: 1
          finishedAt: "2022-04-11T15:45:17Z
          reason: 00MKilTed
          startedAt: "2022-04-11T15:45:17Z"

4.删除Pod

[root@master ~]# kubectl delete pod memory-demo-2
pod "memory-demo-2" deleted

16.3 超过节点的内存分配

Pod 的调度基于请求。只有当节点拥有足够满足 Pod 内存请求的内存时,才会将 Pod 调度至节点上运行。
1.创建一个 Pod,其拥有一个请求 1000GiB 内存的容器,这应该超过了集群中任何一台节点所拥有的内存。

[root@master pod-request]# cat memory-request-limit-3.yaml
apiVersion: V1
kind: Pod
metadata:
  name: memory- demo-3
spec:
  containers:
  - name: memory-demo-3-ctr
    image: polinux/stress
    command: ["stress"]
    args: ["--vm","1","--vm-bytes","25M","--vm-hang","1”] # 告知容器尝试分配 25 MiB 内存
    resources:
      requests:
        memory: "100Gi"
      limits:
        memory:"200Gi"

2.查看 Pod 状态,发现处于 PENDING 状态。 这意味着,该 Pod 没有被调度至任何节点上运行

[root@master ~]# kubectl get pod
NAME            READY    STATUS     RESTARTS   AGE
memory-demo-3    0/1      Pending     0        46s

3.查看Pod详情,输出结果显示,由于节点内存不足,该容器无法被调度:

[root@master ~]# kubectl describe pod memory-demo-3
Events:
   Type           Reason        Age     From
   Message
------------------------------------------------
WarningFailedScheduling 78s default-scheduler0/3 nodes are available: 1 node(s) had taint inoderole.kubernetes .io/master: ], that the pod didn'ttolerate,2 Insufficient memory.
iang

4.删除Pod

[root@master ~]# kubectl delete pod memory-demo-3
pod "memory-demo-3" deleted

16.4 如果没有指定内存限制

如果没有为容器指定内存限制,容器可无限制地使用其所在节点的所有可用内存,进而可能导致该节点调用 00M KiLLer。 此外,如果发生 00MKi飞,没有配置资源限制的容器将被杀掉的可行性更大。
不用担心,在Kubernetes中,可以通过[LimitRange]自动为其容器设定,所使用的内存资源最大最小值。

十七. Pod服务质量QoS

17.1 设么是QoS

QoS (Quality of Service) ,可译为 "服务质量等级",或者译作"服务质量保证",是作用在 Pod 上的一个配置,当 Kubernetes 创建一个 Pod 时,它就会给这个 Pod 分配一个 QoS 等级。
在Kubernetes的环境中,Kubernetes允许节点的Pod过载使用资源这意味着节点无法同时满足所有Pod以过载的方式运行。因此在内存资源紧缺的情况下,Kubernetes需要借助Pod对象的服务质量和优先级等完成判定,进而挑选对应的Pod杀死。Kubernetes根据pod的Requests和Limits属性,把Pod对象归类为三类 BestEffort、BurStable、Guaranteed

17.2 QoS类别

  • Guaranteed: Pod对象为每个容器都设置了CPU资源需求和资源限制,且两者的值相同;还同时为每个容器设置了内存需求与内存限制,并且两者的值相同。这类Pod对象具有最高级别服务质量。
  • Burstable: 至少有一个容器设置了CPU或内存资源Requests属性,但不满足Guaranteed,这类Pod具有中级服务质量。
  • BestEffort:没有为任何容器设置Requests和Limits属性,这类Pod对象服务质量是最低级别。
    当 Kubernetes 集群内存资源紧缺,优先杀死BestEffort类别的容器,因为系统不为该类资源提供任何服务保证,但此类资源最大的好处就是能够尽可能的使用资源
    如果系统中没有BestEffort类别的容器,接下来就轮到Burstable类别的容器,如果有多个Burstable类别的容器,就看谁的内存资源占用多,就优先干掉谁。比如A容器申请1G内存资源,实际使用了95%,而B容器申请了2G内存资源,实际使用了80%,但任然会优先干掉A容器,虽然A容器的用量少,但与自身的Requests值相比,它的占比要大于B容器.
    对于Guaranteed类别的容器拥有最高优先级,它们不会被杀死,除非其内存资源需求超限,或者00M时没有其他更低优先级的Pod对象存在,才会干掉Guaranteed类容器。

17.3 创建Guaranteed的Pod

对于 QoS 类为 Guaranteed 的 Pod:

  • Pod 中的每个容器都必须指定内存请求和内存限制,且Pod中每个容器内存请求必须等于内存限制。
  • Pod 中的每个容器都必须指定CPU请求和CPU限制,且Pod中每个容器CPU请求必须等于CPU限制。
    1.创建一个Pod,容器设置了内存请求和内存限制,值都是200MiB。容器设置了CPU请求和CPU限制,值都是70 milLiCPU
[root@master ~]# cat pod-gos-guaranteed.yaml
apiVersion: V1
kind: Pod
metadata:
  name: pod-qos-guarantee
spec:
  containers:
  - name: qos-demo
    image: nginx
    resources: 
      requests:
        cpu:"700m"
        memory: "200Mi"
      limits:
        cpu: "700m"
        memory: "200Mi"

2.查看Pod详情

spec:
  containers:
    Limits:
      cpu:  700m
      memory: 200Mi 
    Requests:
      cpu: 700m 
      memory: 200Mi
status:
  qosClass: Guaranteed

17.4 创建Burstable的Pod

如果满足下面条件,将会指定 Pod 的 QoS 类为 Burstable。

  • Pod 不符合 Guaranteed QoS 类的标准。
  • Pod 中至少一个容器指定了,内存或 CPU 的请求或限制
    1.创建一个Pod,容器设置了内存请求 100 MiB,以及内存限制 200MiB。
[root@master ~]# cat pod-qos-burstable.yaml
apiVersion: V1
kind: Pod
metadata:
  name: pod-gos-burstable
spec:
  containers:
  - name: qos-demo
    image: nginx
    resources:
      requests:
        memory: "100Mi"
      limits:
        memory: "200Mi"

2.查看Pod详情

spec :
  containers:
    Limits:
      memory: 200Mi
    Requests:
      memory : 100Mi
status:
   qosClass: Burstable

17.5 创建BestEffort的Pod

对于 QoS 类为 BestEffort 的 Pod,Pod 中的容器必须没有设置内存和 CPU 限制或请求。
1.创建一个Pod,容器没有设置内存和 CPU 限制或请求

[root@master tmp]# cat pod-qos-besteffort.yaml
apiVersion: 1
kind: Pod
metadata:
  name: pod-qos-besteffort
spec:
  containers:
  - name: qos-demo
    image: nginx

2.查看 Pod 详情

spec:
  containers:
     ...
    resources: {}
   ...
status:
  qosClass: BestEffort

17.6 创建多容器Pod

1.创建一个Pod,一个容器指定了内存请求 200 MiB。 另外一个容器没有指定任何请求和限制。此 Pod 满足 Burstable QoS 类的标准但它不满足 Guaranteed QoS 类标准,因为它的一个容器设有内存请求。

[root@master ~]# cat pod-qos-mutil.yaml
apiVersion: V1
kind: Pod
metadata:
  name: pod-qos-mutil
spec:
  containers:
  - name: gos-demo
    image: nginx
    resources :
      requests:
        memory: "100Mi"

  - name: qos-demo2
    image: redis

2.查看Pod

spec :
  containers:
    name: qos-demo
    resources :
      Requests:
        memory: 100Mi
......
    name: qos-demo2
    resources: {}
    ....
status:
  qosClass:Burstable

十八. Downward API

18.1 什么是DownwardAPI

DownwardAPI可以让容器获取Pod的相关元数据信息,比如Pod名称Pod的IP,Pod的资源限制等,获取后通过env、volume的方式将相关的环境信息注入到容器中,从而让容器通过这些信息,来设定容器的运行特性。

  • 例如: Nginx进程根据节点的CPU核心数量自动设定要启动的worker进程数。
  • 例如: JVM虚拟根据Pod的内存资源限制,来设定对应容器的堆内存大小。
  • 例如: 获取Pod名称,以Pod名称注册到某个服务,当Pod结束后调用prestop清理对应名称的注册信息。

18.2 可注入的元数据信息

使用 pod.spec.containers.env.valueFrom.fieldRef 可以注入的字段有:

  • metadata.name: Pod对象的名称
  • metadata.namespace: Pod对象隶属的名称空间
  • metadata.uid: Pod对象的UID
  • metadata.Labels[']: 获取Label指定KEY对应的值
  • metadata.annotations[']: 获取Annotations对应KEY的值
  • status.podIP: Pod对象的IP地址
  • status.hostIP: 节点IP
  • status.nodeName: 节点名称
  • spec.serviceAccountName: Pod对象使用的ServiceAccount资源名称
    使用
    pod.spec.containers.env.valueFrom .resourceFieldRef 可以注入的字段有:
  • requests.cpu
  • requests.memory
  • limits.cpu
  • limits.memory

18.3环境变量方式注入元数据

1.创建Pod容器,将Pod相关环境变量注入到容器中,比如 (pod名称命名空间、标签、以及cpu、内存的请求和限制)

apiVersion: v1
kind: Pod
metadata:
  name: pod-downward
  labels:
    app: pod-app
spec:
  containers:
  - name: pod-downward
    image: nginx
    command: ["/bin/sh", "-c", "env"]
    resources:
      requests:
        cpu: "200m"
        memory: "32Mi"
      limits:
        cpu: "200m"
        memory: "64Mi"
    env:
    - name: THIS_POD_NAME
      valueFrom:
        fieldRef:
          fieldPath: metadata.name
    - name: THIS_POD_NAMESPACE
      valueFrom:
        fieldRef:
          fieldPath: metadata.namespace
    - name: THIS_POD_APP_LABEL
      valueFrom:
        fieldRef:
          fieldPath: metadata.labels['app']
    - name: THIS_CPU_LIMITS
      valueFrom:
        resourceFieldRef:
          resource: limits.cpu
    - name: THIS_MEMORY_REQUEST
      valueFrom:
        resourceFieldRef:
          resource: requests.memory
          #divisor: 1Mi         #默认显示为字节,通过divisor调整显示单位为兆

2.通过控制台日志获取注入的环境变量

[root@master tmp]# kubectl Togs pod-downward |grep ^THIS

THIS_CPU_LIMITS=1    # 200毫核,不足1核,则进行取整。
THIS_POD_APP_LABEL=pod-app
THIS_POD_NAME=pod-downward
THIS_MEMORY_REQUEST=32  # 单位为兆,默认字节
THIS_POD_NAMESPACE=default

18.4 存储卷方式注入元数据

apiVersion: v1
kind: Pod
metadata:
  name: pod-downward-volumes
  labels:
    app: pod-app
    zone: beijing
    role: backend
spec:
  containers:
  - name: pod-downward
    image: nginx
    resources:
      requests:
        cpu: "200m"
        memory: "32Mi"
      limits:
        cpu: "200m"
        memory: "54Mi"
    volumeMounts:         #将环境变量挂载到/etc/podinfo目录中,每注入一条元数据都会产生一个文件
    - name: podinfo
      mountPath: /etc/podinfo
  volumes:
  - name: podinfo
    downwardAPI:
      items:
      - path: podname
        fieldRef:
          fieldPath: metadata.name

      - path: podlabels
        fieldRef:
          fieldPath: metadata.labels

      - path: podnamespace
        fieldRef:
          fieldPath: metadata.namespace

      - path: mem_limits
        resourceFieldRef:
          containerName: pod-downward
          resource: limits.memory
          #divisor: 1Mi      #默认显示为字节,通过divisor调整显示单位为兆
      - path: mem_requests
        resourceFieldRef:
          containerName: pod-downward
          resource: requests.memory
          #divisor: 1Mi      #默认显示为字节,通过divisor调整显示单位为兆

18.4 为注册服务注入Pod名称

使用DownwardAPI实现注册与卸载

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-mysql-register
spec:
  replicas: 10
  selector:
    matchLabels:
      app: tools
  template:
    metadata:
      labels:
        app: tools
    spec:
      containers:
      - name: register
        image: oldxu3957/tools:latest
        imagePullPolicy: IfNotPresent
        command:
        - "/bin/bash"
        - "-c"
        - |
         mysql -h 10.8.0.286 -u oldxu -p123 -e "create database ${POD_NAME//-/_}"
         sleep 9999999
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        lifecycle:
          preStop:
            exec:
              command:
              - "/bin/bash"
              - "-c"
              - "mysql -h 10.8.0.286 -u oldxu -p123 -e 'drop database ${POD_NAME//-/_}'"

18.5 为tomcat注入堆内存限制

默认Tomcat应用会使用Pod所在的物理节点内存,初始堆内存为1/64,最大堆内存为1/4.

[root@master tmp]# cat 11-pod-lifecycle-2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-tomcat
spec:
  containers:
  - name: tomcat-web
    image: tomcat:9.0.63
    ports:
    - containerPort: 8080
    env:
    - name: JAVA_OPTS
      value: -server -Xms${JVM_XMS} -Xmx${JVM_XMX} -XX:+UseConcMarkSweepGC
    # downward API
    - name: JVM_XMS
      valueFrom:
        resourceFieldRef:
          resource: requests.memory
    - mame: JVM_XMX
      valueFrom:
        resourceFieldRef:
           resource: limits.memory
    resources:
      requests:
        memory: "250Mi"
      limits:
        memory: "500Mi"