K8S-资源清单

179

Pod 资源清单

k8s 中所有的内容都抽象为资源,资源实例化之后,叫做对象

集群资源分类:

一、集群资源分类

1. 名称空间级别:仅存此名称空间下生效

  • 工作负载型资源(workload):
    • Pod: k8s中最小单元
    • ReplicaSet:调度器,通过标签控制 pod 的副本数目
    • Deployment:控制器,通过控制 rs 的创建来创建 pod
    • StatefulSet:为有状态服务创建的管理器
    • DaemonSet:可以在每个节点运行 pod 主键
    • Job:为批处理而生
    • CronJob(ReplicationController)在v1.11版本被废弃:为批处理而生
  • 服务发现及负载均衡型资源(ServiceDiscoveryLoadBalance):Service、Ingress、...
  • 配置与存储型资源:Volume(存储卷)、CSI(容器存储接口,可以扩展各种各样的第三方存储卷)
  • 特殊类型的存储卷:
    • ConfigMap(当配置中心来使用的资源类型):通过他可以创建一些配置文件,达到热更新
    • Secret(保存敏感数据):加密方案存储数据,可以用它存储一些秘钥等
    • DownwardAPI(把外部环境中的信息输出给容器):下载文件的接口,可以下载、上传

2. 集群级别:所有名称空间都可以使用

  • Namespace:名称空间
  • Node:工作节点
  • Role
  • ClusterRole
  • RoleBinding
  • ClusterRoleBinding

3. 元数据型资源

  • HPA
  • PodTemplate(pod模板)
  • LimitRange(资源限制)

二、Pod 资源清单常用字段解释

也可以使用命令:kubectl explain 来查看详细属性

例如:

kubectl explain pod

kubectl explain pod.spec

1. 必须存在的属性(必须写)

参数名字段类型说明默认值
apiVersionString这里是指的是K8S API的版本,目前基本上是v1,可以用 kubectl api-versions 或者 kubectl explain pod 命令查询
kindString这里指的是yaml文件定义的资源类型和角色,比如: Pod
metadataObject元数据对象,固定值就写metadata
`metadata.nameString`元数据对象的名字,这里由我们编写,比如命名Pod的名字
metadata.namespaceString元数据对象的命名空间,由我们自身定义default
metadata.labelsmap[string]string键值数据,常被用作挑选条件
specObject详细定义对象,固定值就写Spec
`spec.containers[]List`这里是Spec对象的容器列表定义,是个列表
spec.containers[].nameString这里定义容器的名字
spec.containers[].imageString这里定义要用到的镜像名称,如果镜像的标签是 latest,每次使用该镜像都会从远程下载

2. 主要对象

参数名字段类型说明默认值
spec.containers[].nameString定义容器的名字随机指定
spec.containers[].imageString定义要用到的镜像名称,如果镜像的标签是 latest,每次使用该镜像都会从远程下载
spec.containers[].imagePullPolicyString定义镜像拉取策略,有Always、 Never、IfNotPresent三个值可选
(1) Always: 意思是每次都尝试重新拉取镜像
(2) Never: 表示仅使用本地镜像
(3) IfNotPresent: 如果本地有镜像就使用本地镜像,没有就拉取在线镜像
Always
spec.containers[].command[]List指定容器启动命令,因为是数组可以指定多个默认使用镜像打包时使用的启动命令
spec.containers[].args[]List指定容器启动命令参数,因为是数组可以指定多个
spec.containers[].workingDirString指定容器的工作目录
spec.containers[].volumeMounts[]List指定容器内部的存储卷配置
spec.containers[].volumeMounts[].nameString指定可以被容器挂载的存储卷的名称
spec.containers[].volumeMounts[].mountPathString指定可以被容器挂载的存储卷的路径
spec.containers[].volumeMounts[].readOnlyBoolean设置存储卷路径的读写模式,ture 或者 falsefalse
spec.containers[].ports[]List指定容器需要用到的端口列表
spec.containers[].ports[].nameString指定端口名称
spec.containers[].portsl].containerPortString指定容器需要监听的端口号
spec.containers[].ports[].hostPortString指定容器所在主机需要监听的端口号,默认跟上面containerPort相同, 注意设置了hostPort同一台主机无法启动该容器的相同副本(因为主机的端口号不能相同,这样会冲突)
spec.containers[].ports[].protocolString指定端口协议,支持TCP和UDPTCP
spec.containers[].env[]List指定容器运行前需设置的环境变量列表
spec.containers[].env[].nameString指定环境变量名称
spec.containers[].env[].valueString指定环境变量值
spec.containers[].resourcesObject指定资源限制和资源请求的值(这里开始就是设置容器的资源上限)
spec.containers[].resources.limitsObject指定设置容器运行时资源的运行上限
spec.containers[].resources.limits.cpuString指定CPU的限制,单位为core数,将用于docker run --Cpu-shares参数 (这里前面文章Pod资源限制有讲过)
spec.containers[].resources.limits.memoryString指定MEM内存的限制,单位为MIB、GiB
spec.containers[].resources.requestsObject指定容器启动和调度时的限制设置
spec. containers[].resources.requests.cpuStringCPU请求,单位为core数,容器启动时初始化可用数量
spec. containers[].resources.requests .memoryString内存请求,单位为MIB、GiB, 容器启动的初始化可用数量

3. 额外参数项

参数名字段类型说明默认值
spec.restartPolicyString定义Pod的重启策略,可选值为Always、OnFailure、Never
1.AIways:Pod 一旦终止运行,则无论容器是如何终止的,kubelet服务都将重启它。
2.OnFailure:只有Pod以非零退出码终止时,kubelet才会重启该容器。如果容器正常结束(退出码为0),则kubelet将不会重启它。
3. Never:Pod终止后,kubelet将退出码报告给Master,不会重启该Pod
Always
spec.nodeSelectorObject定义Node的Label过滤标签,以key:value格式指定
spec.imagePullSecretsObject定义pull镜像时使用secret名称,以name:secretkey格式指定
spec.hostNetworkBoolean定义是否使用主机网络模式。设置true表示使用宿主机网络,不使用docker网桥,同时设置了true将无法在同一台宿主机上启动第二个副本false

spec.restartPolicy 仅指通过同一节点上的 kubelet 重新启动容器。失败的容器由 kubelet 以五分钟为上限的指数退避延迟(10秒,20秒,40 秒...)重新启动,并在成功执行十分钟后重置。如 Pod 文档 中所述,一旦绑定到一个节点,Pod 将 永远不会重新绑定到另一个节点。

三、Pod yaml 示例

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: app
    image: hub.zyx.com/library/myapp:v1
  - name: test
    image: hub.zyx.com/library/myapp:v1
kubectl apply -f pod.yaml

kubectl describe pod myapp-pod  # describe 查看容器信息

kubectl logs myapp-pod test  # 查看 pod 中指定容器的日志

kubectl get pod -o wide/json/yaml  # 以指定的格式输出
kubectl delete podName
kubectl create -f pod.yaml

四、容器的生命周期

在这里插入图片描述

1. Init Container

  • Pod 能够具有多个容器,应用运行在容器里面,但是它也可能有一个或多个先于应用容器启动的 Init 容器
  • Init 容器与普通的容器非常像,除了如下两点:
    • Init 容器总是运行到成功完成为止
    • 每个 Init 容器都必须在下一个 Init 容器启动之前成功完成
  • 如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init 容器成功为止。然而,如果Pod 对应的 restartPolicy 为 Never,它不会重新启动

(1)init容器作用

  • 因为 Init 容器具有与应用程序容器分离的单独镜像,所以它们的启动相关代码具有如下优势:

    • 它们可以包含并运行实用工具,但是出于安全考虑,是不建议在应用程序容器镜像中包含这些实用工具的

    • 它们可以包含使用工具和定制化代码来安装,但是不能出现在应用程序镜像中。例如,创建镜像没必要FROM另一个镜像,只需要在安装过程中使用类似sed、awk、python或dig这样的工具。

    • 应用程序镜像可以分离出创建和部署的角色,而没有必要联合它们构建一个单独的镜像。

    • Init 容器使用 LinuxNamespace,所以相对应用程序容器来说具有不同的文件系统视图。因此,它们能够具有访问 Secret 的权限,而应用程序容器则不能。

    • 它们必须在应用程序容器启动之前运行完成,而应用程序容器是并行运行的,所以Init容器能够提供了一种简单的阻塞或延迟应用容器的启动的方法,直到满足了一组先决条件。

(2)Init容器特殊说明

  • 在 Pod 启动过程中,Init 容器会==按顺序在网络数据卷初始化之后启动==。每个容器必须在下一个容器启动之前成功退出(网络和数据卷初始化是在 pause)

  • 如果由于运行时或失败退出,将导致容器启动失败,它会根据 Pod 的 restartPolicy 指定的策略进行重试。例如,如果 Pod 的 restartPolicy 设置为 Always,Init容器失败时会不断重试,直到达到他的最大上限为止

  • 在所有的 Init 容器没有成功之前,Pod 将不会变成 Ready 状态。Init 容器的端口将不会在 Service 中进行聚集。正在初始化中的 Pod 处于 Pending 状态,但应该会将 Initializing 状态设置为true

  • 如果 Pod 重启,所有 Init 容器必须重新执行

  • 对 Init 容器 spec 的修改(kubectl edit pod)被限制在容器 image 字段,修改其他字段都不会生效(不会自动重启)。更改 Init 容器的image 字段,会自动重启该 Init 容器,相当于重启 Pod

  • Init 容器具有应用容器的所有字段。除了 readinessProbe(就绪检测),因为 Init 容器无法定义不同于完成(completion)的就绪(readiness)之外的其他状态。这会在验证过程中强制执行

  • 在 Pod 中的==每个 app 和 Init 容器的名称必须唯一==;与任何其它容器共享同一个名称,会在验证时抛出错误(同一个 Pod 中 Init 的端口是可以相同的)

(3)Init 容器示例

  1. 编写资源清单,创建 init-pod.yaml:

    apiVersion: v1
    kind: Pod
    metadata:
      name: myapp-pod
      labels:
        app: myapp
    spec:
      containers:	# 这是主容器
      - name: myapp-container
        image: busybox		# 如果镜像的标签是 latest 且下载策略是 Always,每次使用该镜像都会从远程下载
        command: ['sh', '-c', 'echo The app is running! && sleep 3600']
      initContainers:	# 这是 init 容器
      - name: init-myservice
        image: busybox
        # 循环检测 myservice 域名是否能被解析,如果可以解析,退出循环
        command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;'] 
      - name: init-mydb
        image: busybox
        # 循环检测 mydb 是否能被解析,如果可以解析,退出循环
        command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
    
  2. 启动 pod 并查看日志

    [root@k8s-master01 ~]# kubectl create -f init-pod.yaml
    pod/myapp-pod created
    
    [root@k8s-master01 ~]# kubectl get pod
    NAME        READY   STATUS     RESTARTS   AGE
    myapp-pod   0/1     Init:0/2   0          9s
    
    # 查看详细信息及日志:
    kubectl describe pod myapp-pod
    kubectl logs myapp-pod -c init-myservice
    
  3. 创建 service.yaml,启动名为 myservice 的 service

    kind: Service
    apiVersion: v1
    metadata:
      name: myservice
    spec:
      ports:
      - protocol: TCP
        port: 80
        targetPort: 9376
    
    [root@k8s-master01 ~]# kubectl create -f service.yaml
    service/myservice created
    
    # 可以看到,Init 容器成功启动了一个
    [root@k8s-master01 ~]# kubectl get pod
    NAME        READY   STATUS     RESTARTS   AGE
    myapp-pod   0/1     Init:1/2   0          25m
    
    # 查看详细信息及日志:
    kubectl describe pod myapp-pod
    kubectl logs myapp-pod -c init-myservice
    kubectl get svc		# 查看刚创建的 Service (svc 是 Service 的简写)
    

    k8s DNS 解析原理: 这里是引用

  4. 同理,创建 db.yaml,启动名为 mydb 的 service

    kind: Service
    apiVersion: v1
    metadata:
      name: mydb
    spec:
      ports:
      - protocol: TCP
        port: 80
        targetPort: 9377
    
    [root@k8s-master01 ~]# kubectl create -f db.yaml
    service/mydb created
    
    # 稍等片刻,发现 pod Running
    [root@k8s-master01 ~]# kubectl get pod
    NAME        READY   STATUS    RESTARTS   AGE
    myapp-pod   1/1     Running   0          34m
    

2. 探针

(1)探针描述

  • 探针是由每一个 Node 上的 kubelet 对容器执行的定期诊断。要执行诊断,kubelet 调用==由容器实现的 Handler==。有三种类型的处理程序:

    • ExecAction:在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。
    • TCPSocketAction:对指定容器端口的IP地址进行 TCP 检查。如端口打开,则诊断被认为是成功的。
    • HTTPGetAction:对指定的端口和路径上的容器的IP地址执行 HTTPGet 请求。如果响应的状态码大于等于 200 且小于400,则诊断被认为是成功的
  • 每次探测都将获得以下三种结果之一:

    • 成功:容器通过了诊断。
    • 失败:容器未通过诊断。
    • 未知:诊断失败,因此不会采取任何行动

探测方案也有两种:

  • livenessProbe: 生存检测

    • 指示容器是否正在运行。如果存活探测失败,则 kubelet 会杀死容器,并且容器将受到其重启策略的影响。如果容器不提供存活探针,则默认状态为 Success(会随着容器的生命周期一直存在)
  • readinessProbe: 就绪检测

    • 指示容器是否准备好服务请求。如果就绪探测失败,端点控制器将从与 Pod 匹配的所有 Service 的端点中删除该 Pod 的 IP 地址。初始延迟之前的就绪状态默认为 Failure。如果容器不提供就绪探针,则默认状态为 Success
  • 区别:readinessProbe 检测成功之后,主容器才能宣布对外能够正常访问,否则状态都是 Failure。而 livenessProbe 跟随着容器的整个生命周期,会循环检测容器中的资源是否可用。

(2)实例

就绪检测 - httpGet
  • 就绪检测 nginx 的 index1.html 存在否,存在的话,就绪检测通过
apiVersion: v1
kind: Pod
metadata:
  name: readiness-httpget-pod
  namespace: default
spec:
  containers:
  - name: readiness-httpget-container
    image: wangyanglinux/myapp:v1
    imagePullPolicy: IfNotPresent
    readinessProbe:		# 就绪检测
      httpGet:			# httpGet 检测方案
        port: 80		# 端口 80
        path: /index1.html	# 请求文件
      initialDelaySeconds: 1	# 初始化检测的延时,容器启动 1 之后再开始检测
      periodSeconds: 3		# 重试的检测时间

可以看到,容器在运行(Running),但是没有 Ready:

[root@k8s-master01 ~]# kubectl get pod
NAME                    READY   STATUS    RESTARTS   AGE
readiness-httpget-pod   0/1     Running   0          12s

# 查看日志信息,显示 HTTP 请求状态码为 404
[root@k8s-master01 ~]# kubectl describe pod readiness-httpget-pod
Type     Reason     Age                   From                            Message
----     ------     ----                  ----                            -------
Warning  Unhealthy  75s (x22 over 2m18s)  kubelet, localhost.localdomain  Readiness probe failed: HTTP probe failed with statuscode: 404

手动进入容器,添加 index1.html 之后:

# 进入容器, 如果 POD 中有多个容器,则需要 -c 指定容器名
# kubectl exec (POD | TYPE/NAME) [-c CONTAINER] [flags] -- COMMAND [args...] [options]
kubectl exec readiness-httpget-pod -it -- /bin/sh

# 添加文件
cd /usr/share/nginx/html/
echo "123" > index1.html
exit

# 查看 Pod,已经 READY
[root@k8s-master01 ~]# kubectl get pod
NAME                    READY   STATUS    RESTARTS   AGE
readiness-httpget-pod   1/1     Running   0          6m25s
存活检测 - ExecAction

模板说明:pod 创建,容器初始化完成后,创建文件后休眠 60 秒后删除该文件,存活检测在容器创建后延时 1s 进行检测,重试时间间隔 3s。

apiVersion: v1
kind: Pod
metadata:
  name: liveness-exec-pod
  namespace: default
spec:
  containers:
  - name: liveness-exec-container
    image: busybox
    imagePullPolicy: IfNotPresent
    # 创建一个文件,休眠 60s,然后把该文件删除
    command: ["/bin/sh","-c","touch /temp/live ; sleep 60 ; rm -rf /temp/live; sleep 3600"]
    livenessProbe:		# 存活检测
      exec:
        # 检测该文件是还存在, 如果存在,返回值为 0
        command: ["test","-e","/temp/live"]
      initialDelaySeconds: 1	# 容器启动后延时 1s 再开始检测
      periodSeconds: 3			# 每隔 3s 检测一次

容器启动时会创建 /temp/live 文件,60s 之后删除该文件。当 livenessProbe 发现文件不存在,就会重启容器,重启后的容器 60秒之后又会删除 /temp/live 文件,所以容器不断被重启, 在这里插入图片描述

存活检测 - HTTPGetAction
  • 每隔一段时间(3s),检测 nginx 中的 index1.html 能否被访问,如果不能访问,就重启容器
apiVersion: v1
kind: Pod
metadata:
  name: liveness-httpget-pod
  namespace: default
spec:
  containers:
  - name: liveness-httpget-container
    image: wangyanglinux/myapp:v1
    imagePullPolicy: IfNotPresent
    ports:
    - name: http
      containerPort: 80
    livenessProbe:
      httpGet:
        port: http				# http 和 80 等价
        path: /index1.html
      initialDelaySeconds: 1
      periodSeconds: 3
      timeoutSeconds: 10		# 最大超时时间
存活检测 - TCPSocketAction

创建一个 pod,镜像文件是 nginx,存活检测镜像容器中的 nginx(80),使用 tcp 连接,端口指定8080,当探测结果失败时(不可访问),重启容器

apiVersion: v1
kind: Pod
metadata:
  name: liveness-tcp-pod
  namespace: default
spec:
  containers:
  - name: liveness-tcp-container
    image: wangyanglinux/myapp:v1
    imagePullPolicy: IfNotPresent
    livenessProbe:
      initialDelaySeconds: 1
      timeoutSeconds: 5
      periodSeconds: 3
      tcpSocket:
        port: 8080		# nginx 是 80 端口

3. Start/Stop

Pod hook(钩子)是由 Kubernetes 管理的 kubelet 发起的,当容器中的进程启动前或者容器中的进程终止之前运行,这是包含在容器的生命周期之中。可以同时为 Pod 中的所有容器都配置 hook Hook 的类型包括两种: p exec:执行一段命令 p HTTP:发送HTTP请求

apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-start-stop-pod
  namespace: default
spec:
  containers:
  - name: readiness-start-stop-container
    image: nginx
    imagePullPolicy: IfNotPresent
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh","-c","echo Hello from the postStart handler > /usr/share/message"]
      preStop:
        exec:
          command: ["/bin/sh","-c","echo Hello from the postStop handler > /usr/share/message"]

五、Pod 的状态(phase)

Pod 的 status 字段是一个 PodStatus 对象,PodStatus中有一个 phase 字段。

Pod 的相位(phase)是 Pod 在其生命周期中的简单宏观概述。该阶段并不是对容器或 Pod 的综合汇 总,也不是为了做为综合状态机

Pod 相位的数量和含义是严格指定的。除了本文档中列举的状态外,不应该再假定 Pod 有其他的 phase 值

下面是 phase 可能的值:

  • 挂起(Pending):Pod 已被 Kubernetes 系统接受,但有一个或者多个容器镜像尚未创建。等待时间 包括调度 Pod 的时间和通过网络下载镜像的时间,这可能需要花点时间
  • 运行中(Running):该 Pod 已经绑定到了一个节点上,Pod 中所有的容器都已被创建。至少有一个容 器正在运行,或者正处于启动或重启状态
  • 成功(Succeeded):Pod 中的所有容器都被成功终止(返回码为0),并且不会再重启
  • 失败(Failed):Pod 中的所有容器都已终止了,并且至少有一个容器是因为失败终止。也就是说,容器以非 0 状态退出或者被系统终止
  • 未知(Unknown):因为某些原因无法取得 Pod 的状态,通常是因为与 Pod 所在主机通信失败

在这里插入图片描述

资源控制器

Kubernetes 中内建了很多 controller(控制器),这些相当于一个状态机,用来控制 Pod 的具体状态和行为

一、RC、RS

RC (ReplicationController )主要的作用就是用来确保容器应用的副本数始终保持在用户定义的副本数 。即如果有容器异常退出,会自动创建新的Pod来替代;而如果异常多出来的容器也会自动回收

Kubernetes 官方建议使用 RS(ReplicaSet ) 替代 RC (ReplicationController ) 进行部署,RS 跟 RC 没有本质的不同,只是名字不一样,并且 RS 支持集合式的 selector (标签)

通过命令行查看详细的 ReplicaSet 清单定义规则:

kubectl explain rs 
kubectl explain rs.spec.template 

RS 清单实例

重点:

  • ==RS 通过标签(matchLabels)来管理 Pod==

1. RS 资源清单实例

apiVersion: apps/v1  #api版本定义
kind: ReplicaSet  #定义资源类型为ReplicaSet
metadata:  #元数据定义
  name: frontend
  namespace: default
spec:  # ReplicaSet的规格定义
  replicas: 3  #定义副本数量为3个
  selector:  #标签选择器,定义匹配Pod的标签
    matchLabels:  # RS通过labels来确定某个Pod是否归该RS管,即RS通过标签来监控Pod
      tier: frontend
  template:  #Pod的模板定义,与上面Pod的定义一致
    metadata:  #Pod的元数据定义
      name: myapp-pod  #自定义Pod的名称
      labels:  #定义Pod的标签,需要和上面的标签选择器内匹配规则中定义的标签一致,可以多出其他标签
        tier: frontend
    spec:  #Pod的规格定义
      containers:  #容器定义
      - name: mynginx  #容器名称
        image: hub.zyx.com/library/nginx:v1  #容器镜像
        imagePullPolicy: IfNotPresent  #拉取镜像的规则
        env:
        - name: GET_HOSTS_FROM
          value: dns
        ports:  #暴露端口
        - name: http  #端口名称
          containerPort: 80

2. 创建RS并查看

# 创建 RS
[root@k8s-master01 yaml]# kubectl create -f rs.yaml

# 查看创建的 rs
[root@k8s-master01 yaml]# kubectl get rs
NAME       DESIRED   CURRENT   READY   AGE
frontend   3         3         3       23m

# 查看rs创建的 3 个 Pod,
[root@k8s-master01 yaml]# kubectl get pod
NAME             READY   STATUS    RESTARTS   AGE
frontend-cbxp2   1/1     Running   0          23m
frontend-qnfcd   1/1     Running   0          23m
frontend-qqngq   1/1     Running   0          23m

# 删除 Pod后,RS会自动重建 Pod,维持设置的副本数(3),通过 Pod 后的随机值可以看出,Pod是重新创建的
[root@k8s-master01 yaml]# kubectl delete pod --all
pod "frontend-cbxp2" deleted
pod "frontend-qnfcd" deleted
pod "frontend-qqngq" deleted
[root@k8s-master01 yaml]# kubectl get pod
NAME             READY   STATUS    RESTARTS   AGE
frontend-5lgj9   1/1     Running   0          14s
frontend-gxhrv   1/1     Running   0          14s
frontend-rwhc5   1/1     Running   0          14s

3. 修改 labels

# 查看Pod的标签
[root@k8s-master01 yaml]# kubectl get pod --show-labels
NAME             READY   STATUS    RESTARTS   AGE     LABELS
frontend-5lgj9   1/1     Running   0          5m20s   tier=frontend
frontend-gxhrv   1/1     Running   0          5m20s   tier=frontend
frontend-rwhc5   1/1     Running   0          5m20s   tier=frontend

# 修改其中一个Pod的标签
[root@k8s-master01 yaml]# kubectl label pod frontend-5lgj9 tier=abc --overwrite=true
pod/frontend-5lgj9 labeled

# RS会通过匹配 labels 来确定哪些 Pod 是自己管理的
# 当改变了 frontend-5lgj9 的 labels 时,该 Pod 已经不归 frontend 这个 RS 管了,所以 RS 又重新创建了一个Pod
[root@k8s-master01 yaml]# kubectl get pod --show-labels
NAME             READY   STATUS    RESTARTS   AGE   LABELS
frontend-5lgj9   1/1     Running   0          84m   tier=abc
frontend-7fbp8   1/1     Running   0          4s    tier=frontend
frontend-gxhrv   1/1     Running   0          84m   tier=frontend
frontend-rwhc5   1/1     Running   0          84m   tier=frontend

# 删除RS,Pod也会被删除
[root@k8s-master01 yaml]# kubectl delete rs frontend
[root@k8s-master01 yaml]# kubectl get pod --show-labels
NAME             READY   STATUS    RESTARTS   AGE   LABELS
frontend-5lgj9   1/1     Running   0          98m   tier=abc

4. 扩容/缩容

可以直接通过vim 编辑清单文件修改replicas字段,也可以通过kubect edit 命令去编辑。kubectl还提供了一个专用的子命令scale用于实现应用规模的伸缩,支持从资源清单文件中获取新的目标副本数量,也可以直接在命令行通过“--replicas”选项进行读取。

#修改rs配置文件
# kubectl edit rs frontend
 
#修改Pod副本数量提升为5个

5. 删除

# 删除rs,并删除管理的pod
kubectl delete rs [rs名字] 

# 删除rs,不删除管理的pod “--cascade=false”选项,取消级联关系。
kubectl delete rs [rs名字] --cascade=false

二、Deployment

在这里插入图片描述

Deployment 为 Pod 和 ReplicaSet 提供了一个声明式定义 (declarative) 方法,用来替代以前的ReplicationController 来方便的管理应用。典型的应用场景包括 - ① 定义 Deployment 来创建 Pod 和 ReplicaSet - ② 滚动升级和回滚应用 - ③ 扩容和缩容 - ④ 暂停和继续 Deployment

  • 滚动更新:会创建一个新副本的rs1,旧的rs的pod减少一个时,rs1会新加一个,直到全部增减完成
  • 声明式尽量用apply

通过命令行查看详细的 Deployment 清单定义规则:

kubectl explain deployment 
kubectl explain deployment.spec.template 

Deployment 清单实例

1. Deployment 资源清单实例

apiVersion: apps/v1  #api版本定义
kind: Deployment  #定义资源类型为Deploymant
metadata:  #元数据定义
  name: nginx-deployment  #deployment控制器名称
  namespace: default  #名称空间
spec:  #deployment控制器的规格定义
  replicas: 3  #定义deployment副本数量为2个
  selector:  #标签选择器,定义匹配Pod的标签
    matchLabels:
      app: nginx-deployment
  template:  #Pod的模板定义
    metadata:  #Pod的元数据定义
      labels:  #定义Pod的标签,和上面的标签选择器标签一致,可以多出其他标签
        app: nginx-deployment
    spec:  #Pod的规格定义
      containers:  #容器定义
      - name: nginx  #容器名称
        image: hub.zyx.com/library/nginx:v1  #容器镜像
        ports:  #暴露端口
        - name: http  #端口名称
          containerPort: 80

2. 创建Deployment并查看

kubectl get deployment 命令所显示的字段有:

  • NAME:Deployment 的名称。
  • READY:显示应用程序的可用的副本数。显示的模式是“就绪个数/期望个数”。
  • UP-TO-DATE:显示为了达到期望状态已经更新的副本数。
  • AVAILABLE:显示应用可供用户使用的副本数。
  • AGE:显示应用程序运行的时间。
# 创建Deployment对象
# --record 可以方便的查看 revision 的变化(roolout 被触发(spec.template被更改)就会创建一个 revision)
kubectl apply -f deployment.yaml --record

[root@k8s-master01 yaml]# kubectl get deployment
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           4m30s
[root@k8s-master01 yaml]# kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-645ccc47cc   3         3         3       4m32s
[root@k8s-master01 yaml]# kubectl get pod
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-645ccc47cc-885sz   1/1     Running   0          4m37s
nginx-deployment-645ccc47cc-k5jcm   1/1     Running   0          4m37s
nginx-deployment-645ccc47cc-rwnk5   1/1     Running   0          4m37s

3. 扩容

kubectl scale deployment nginx-deployment --replicas=5

4. 更新

修改 Pod 模板相关的配置参数便能完成 Deployment 控制器资源的更新。由于是声明式配置,因此对 Deployment 控制器资源的修改尤其适合使用 applypatch 命令来进行;如果仅只是修改容器镜像,set image 命令更为易用。

  • 更新容器的镜像:kubectl set image deployment/[deployment名称] [容器名称]=[镜像名称]
kubectl set image deployment/nginx-deployment nginx=wangyanglinux/myapp:v2

# 可以看到原来的 RS 已经停用并做为备份,并创建了新的 RS
[root@k8s-master01 yaml]# kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-645ccc47cc   0         0         0       3h45m
nginx-deployment-6c67f64d64   5         5         4       8s

5. 回滚

  • 可以通过设置 .spec.revisonHistoryLimit 项来指定 deployment 最多保留多少 revision 历史记录。默认的会保留所有的 revision;如果将该项设置为 0Deployment 就不允许回退了
# 回到上一个版本
kubectl rollout undo deployment/nginx-deployment

# 查看当前的更新状态
# 如果 rollout 成功完成, 该命令将返回一个 0 值的 Exit Code
kubectl rollout status deployments/nginx-deployment
# echo $?		# 输出 0

# 查看当前版本(自定义 Pod 的输出)
kubectl get pods -o custom-columns=Name:metadata.name,Image:spec.containers[0].image

# 通过该命令查看更新历史记录,Pod 模板被修改就会创建一个 revision
kubectl rollout history deployment/nginx-deploy
# 查询结果,创建 Deployment 时加上 --record 才会显示CHANGE-CAUSE
# REVISION  CHANGE-CAUSE
# 1         kubectl apply --filename=deployment.yaml --record=true
# 2         kubectl apply --filename=deployment.yaml --record=true


# 回滚到指定版本
kubectl rollout undo deployment/nginx-deployment --to-revision=1

# 暂停 deployment 的更新
kubectl rollout pause deployment/nginx-deployment

5. 更新策略

  1. Deployment 可确保在更新时仅关闭一定数量的 Pod。默认情况下,它确保至少所需 Pods ==75%== 处于运行状态(最大不可用比例为 25%)。

  2. Deployment 还确保仅所创建 Pod 数量只可能比期望 Pods 数高一点点。 默认情况下,它可确保启动的 Pod 个数比期望个数最多多出 ==25%==(最大峰值 25%)。

  3. 如果旧的 Pod 创建还没有达到期望数时(如期望数是 5,但现在只创建了3个),就更新了 Pod。在这种情况下,Deployment 会立即杀掉已创建的旧 Pod,并开始创建新 Pod。它不会等到所有的旧 Pod 都创建完成后才开始创建新 Pod

例如,如果仔细查看上述 Deployment (kubectl describe deployment),将看到它首先创建了一些新的 Pod,然后删除了一些旧的 Pods。并且创建新的 Pods 创建没有到达 25% ,它不会杀死老 Pods,直到有足够的数量新的 Pods 已经出现。 然后开始杀死老 Pods ,在足够数量的旧 Pods 被杀死前并没有创建新 Pods。它确保至少 4 个 Pod 可用,同时最多总共 7 个 Pod 可用。

三、DaemonSet

确保全部(或者一些)Node 上运行==一个== Pod 的副本(同一个 Node 上运行多个 Pod 需要多个 DaemonSet)。当有 Node 加入集群时,也会为他们新增一个Pod 。当有 Node 从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod

  • 使用 DaemonSet 的一些典型用法

    • ① 运行集群存储 daemon,例如在每个 Node 上运行glusterd、ceph

    • ② 在每个 Node 上运行日志收集 daemon,例如fluentd、logstash

    • ③ 在每个 Node 上运行监控 daemon,例如Prometheus Node Exporter、collectd、Datadog 代理、New Relic 代理,或 Ganglia gmond

DaemonSet 清单实例

apiVersion: apps/v1             #api版本定义
kind: DaemonSet                 #定义资源类型为DaemonSet
metadata:                       #元数据定义
  name: daemonset-nginx         #daemonset控制器名称
  namespace: default            #名称空间
  labels:                       #设置daemonset的标签
    app: daemonset
spec:                           #DaemonSet控制器的规格定义
  selector:                     #指定匹配pod的标签
    matchLabels:                #指定匹配pod的标签
      app: daemonset-nginx      #注意:这里需要和template中定义的标签一样
  template:                     #Pod的模板定义
    metadata:                   #Pod的元数据定义
      name: nginx
      labels:                   #定义Pod的标签,需要和上面的标签一致,可以多出其他标签
        app: daemonset-nginx
    spec:                       #Pod的规格定义
      containers:               #容器定义
      - name: nginx-pod         #容器名字
        image: hub.zyx.com/library/nginx:v1     #容器镜像
        ports:                  #暴露端口
        - name: http            #端口名称
          containerPort: 80     #暴露的端口

可以看到两个节点上各运行一个:

[root@k8s-master01 yaml]# kubectl get pod -o wide
NAME                                READY   STATUS    RESTARTS   AGE    IP            NODE         NOMINATED NODE   READINESS GATES
daemonset-nginx-js4tc               1/1     Running   0          82s    10.244.1.21   k8s-node01   <none>           <none>
daemonset-nginx-kkc5q               1/1     Running   0          9s     10.244.2.21   k8s-node02   <none>           <none>

四、Job

job负责批处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个pod成功结束

特殊说明:

  • spec.template 格式同 Pod
  • 容器的 restartPolicy 仅支持 NeverOnFailure,因为 Job 就是批处理任务,执行完自动退出,如果是 Always,那么就会不断的执行
  • 单个 Pod 时,默认 Pod 成功运行后(返回码为 0) Job 即结束
  • .spec.completions:标志Job结束需要成功运行的 Pod 个数,默认为 1
  • .spec.parallelism:标志并行运行的 Pod 的个数,默认为 1
  • .spec.activeDeadlineSeconds:标志失败 Pod 的重试最大时间,超过这个时间不会继续重试
apiVersion: batch/v1		# kubectl explain job 查看 job 版本
kind: Job
metadata:
  name: pi
spec:
  template:
    metadata:
      name: pi
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl","-Mbignum=bpi","-wle","print bpi(2000)"]  # 通过 perl 进行圆周率计算,输出小数点后2000位
      restartPolicy: Never		# 重启策略

五、CronJob(在特定的时间循环创建 Job)

  • 管理基于时间的 Job,即

    1. 在给定时间点只运行一次
    2. 周期性地在给定时间点运行
  • 使用前提条件:当前使用的 Kubernetes 集群,版本 >= 1.8(对 CronJob)。

  • 典型的用法

    • 在给定的时间点调度 Job 运行

    • 创建周期性运行的 Job,例如:数据库备份、发送邮件

1. CronJob Spec

  • .spec.schedule:调度,必需字段,指定任务运行周期,格式同 Cron
  • .spec.jobTemplate:Job 模板,必需字段,指定需要运行的任务,格式同 Job
  • .spec.startingDeadlineSeconds:启动 Job 的期限(秒级别),该字段是可选的。如果因为任何原因而错过了被调度的时间,那么错过执行时间的 Job 将被认为是失败的。如果没有指定,则没有期限
  • .spec.concurrencyPolicy:并发策略,该字段也是可选的。它指定了如何处理被 Cron Job 创建的 Job 的并发执行。只允许指定下面策略中的一种:
    • Allow (默认):允许并发运行 Job
    • Forbid :禁止并发运行,如果前一个还没有完成,则直接跳过下一个
    • Replace :取消当前正在运行的 Job,用一个新的来替换
    注意:当前策略只能应用于同一个 Cron Job 创建的 Job。如果存在多个 Cron Job,它们创建的 Job 之间总是允许并发运行。
  • .spec.suspend:挂起,该字段也是可选的。如果设置为 true ,后续所有执行都会被挂起。它对已经开始执行的 Job 不起作用。默认值为 false 。
  • .spec.successfulJobsHistoryLimit.spec.failedJobsHistoryLimit:历史限制,是可选的字段。它们指定了可以保留多少完成和失败的 Job。默认情况下,它们分别设置为 3 和 1 。设置限制的值为 0 ,相关类型的 Job 完成后将不会被保留。

2. CronJob 清单实例

apiVersion: batch/v1beta1 
kind: CronJob 
metadata: 
  name: hello 
spec: 
  schedule: "*/1 * * * *" 
  jobTemplate: 
    spec: 
      template: 
        spec: 
          containers: 
          - name: hello 
            image: busybox 
            args: 		# 运行命令,输出当前时间
            - /bin/sh 
            - -c 
            - date; echo Hello from the Kubernetes cluster 
          restartPolicy: OnFailure 
[root@k8s-master01 yaml]# kubectl get cronjob
NAME    SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
hello   */1 * * * *   False     0        <none>          29s

[root@k8s-master01 yaml]# kubectl get job
NAME               COMPLETIONS   DURATION   AGE
hello-1603320660   1/1           108s       2m4s
hello-1603320720   0/1           64s        64s
hello-1603320780   0/1           4s         4s

# 删除 CronJob
kubectl delete deployment/nginx-deployment

3. CronJob 限制

  • 创建 Job 操作应该是 ==幂等的==。

  • CronJob 仅负责创建与其调度时间相匹配的 Job,而 Job 又负责管理其代表的 Pod。

六、StatefulSet

  • StatefulSet 作为 Controller 为 Pod 提供唯一的标识。它可以保证部署和 scale 的顺序

  • 一个完整的 StatefulSet 应用由三个部分组成: headless service(无头服务)、StatefulSet controllervolumeClaimTemplate(PVC)。

    • Headless Service:用来定义 Pod 网络标识( DNS domain);

    • volumeClaimTemplates :存储卷申请模板,创建 PVC,指定 pvc 名称大小,将自动创建 pvc,且 pvc 必须由存储类供应;

    • StatefulSet :定义具体应用,名为 Nginx,有三个 Pod 副本,并为每个 Pod 定义了一个域名部署 statefulset。

    为什么需要 headless service 无头服务? 在用 Deployment 时,每一个 Pod 名称是没有顺序的,是随机字符串,因此是 Pod 名称是无序的,但是在 statefulset 中要求必须是有序 ,每一个 pod 不能被随意取代,pod 重建后 pod 名称还是一样的。而 pod IP变化的,所以是以 Pod 名称来识别。pod 名称是 pod 唯一性的标识符,必须持久稳定有效。这时候要用到无头服务,它可以给每个 Pod 一个唯一的名称

    除此之外,StatefulSetHeadless Service 的基础上又为 StatefulSet 控制的每个 Pod 副本创建了一个 DNS 域名,这个域名的格式为: $(podname).(headless server name) FQDN:$(podname).(headless server name).namespace.svc.cluster.local

    为什么需要 volumeClaimTemplate? 对于有状态的副本集都会用到持久存储,对于分布式系统来讲,它的最大特点是数据是不一样的,所以各个节点不能使用同一存储卷,每个节点有自已的专用存储,但是如果在 Deployment 中的 Pod template 里定义的存储卷,是所有副本集共用一个存储卷,数据是相同的,因为是基于模板来的;而 statefulset 中每个 Pod 都要自已的专有存储卷,所以 statefulset 的存储卷就不能再用 Pod 模板来创建了,于是 statefulSet 使用 volumeClaimTemplate,称为卷申请模板,它会为每个 Pod 生成不同的 pvc,并绑定 pv,从而实现各 pod 有专用存储。这就是为什么要用 volumeClaimTemplate 的原因。

StatefulSet 清单实例

# headless service
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
    - port: 80
      name: web
  clusterIP: None	# 通过指定 clusterIP 为 None 实现 headless service
  selector:
    app: nginx		# (A) A,B,C 三处要相同,根据 label 来匹配哪些 pod 归无头服务管
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx		# (B) A,B,C 三处要相同,根据 label 匹配决定哪些 pod 归StatefulSet管
  serviceName: "nginx"		# 指定 Service 名称(上面创建的,一定要是个无头服务)
  replicas: 3		# 副本数
  template:
    metadata:
      labels:
        app: nginx		# (C) A,B,C 三处要相同,label标签
    spec:
      containers:		# 容器信息
        - name: nginx
          image: wangyanglinux/myapp:v2
          ports:
            - containerPort: 80		# 释放的端口
              name: web		# 端口名字
          volumeMounts:		# 挂载
            - name: www
              mountPath: /usr/share/nginx/html	 # 容器内目录
  volumeClaimTemplates:		# 卷请求声明模板(pvc模板)
    - metadata:
        name: www
      spec:
        accessModes: [ "ReadWriteOnce" ]	# 指定要请求的卷的访问模式
        storageClassName: "nfs"		# 指定要请求的卷的类名,只有与 PV 中的storageClassName 相同时,才会匹配
        resources:
          requests:
            storage: 1Gi	# 指定要请求的卷大小必须满足 1G

StatefulSet 是为了解决有状态服务的问题(对应 Deployments 和 ReplicaSets 是为无状态服务而设计),其应用场景包括:

  • 稳定的持久化存储,即 Pod 重新调度后还是能访问到相同的持久化数据,基于 PVC 来实现

  • 稳定的网络标志,即 Pod 重新调度后其 PodName 和 HostName 不变,基于Headless Service(即没有 Cluster IP 的 Service)来实现

  • 有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从 0 到 N-1,在下一个 Pod 运行之前所有之前的 Pod 必须都是 RunningReady 状态),基于 init containers 来实现

  • 有序收缩,有序删除(即从 N-1 到 0)

  • Hroizontal Pod Autoscaling

    • 应用的资源使用率通常都有高峰和低谷的时候,如何削峰填谷,提高集群的整体资源利用率,让service中的Pod个数自动调整呢?这就有赖于Horizontal Pod Autoscaling了,顾名思义,使Pod水平自动缩放

kubectl 常用命令

kubectl create 和 kubectl apply区别

K8S的apiVersion版本说明

使用 kubectl explain pod 查看版本