Kubernetes Pod篇:带你轻松玩转Pod
本文将对Kubernetes如何发布与管理容器应用进行详细说明,主要包括Pod概述、基本用法、生命周期、Pod的控制和调度管理、Pod的升级和回滚,以及Pod的扩容机制等内容,并结合具体详细的示例,带你轻松玩转Pod,开启Kubernetes容器的编排之路。
1、Pod概述
1.1 Pod是什么?
Pod
是Kubernetes中的原子对象,是基本构建单元。
Pod
表示集群上一组正在运行的容器。通常创建Pod是为了运行单个主容器。Pod 还可以运行可选的sidecar容器,以实现诸如日志记录之类的补充特性。(如:在Service Mesh中,和应用一起存在的istio-proxy
、istio-init
容器)
一个Pod
中可以包含多个容器(其他容器作为功能补充),负责处理容器的数据卷、秘钥、配置。
1.2 为什么要引入Pod概念?
原因1:Kubernetes可扩展
Kubernetes不会直接和容器打交道,Kubernetes的使用者能接触到的资源只有Pod,而Pod里可以包含多个容器。当我们在Kubernetes里用kubectl执行各种命令操作各类资源时,是无法直接操作容器的,往往都是借助于Pod。
Kubernetes并不是只支持Docker这一个容器运行时。 为了让Kubernetes不和某种特定的容器运行时进行技术绑死,而是能无需重新编译源代码就能够支持多种容器运行时技术的替换,和我们面向对象设计中引入接口作为抽象层一样,在Kubernetes和容器运行时之间我们引入了一个抽象层,即容器运行时接口(CRI:Container Runtime Interface)。
<u>借助CRI这个抽象层,使得Kubernetes不依赖于底层某一种具体的容器运行时实现技术,而是直接操作Pod,Pod内部再管理多个业务上紧密相关的用户业务容器,这种架构更便于Kubernetes的扩展。</u>
原因2:易管理
假设Kubernetes中没有Pod的概念,而是直接管理容器,那么有些容器天生需要紧密关联,如:在ELK中,日志采集Filebeat需要和应用紧密部署在一起。如果将紧密关联的一组容器作为一个单元,假设其中一个容器消亡了,此时这个单元的状态应该如何定义呢?应该理解成整体消亡,还是个别消亡?
这个问题不易回答的原因,是因为包含了这一组业务容器的逻辑单元,没有一个统一的办法来代表整个容器组的状态,这就是Kubernetes引入Pod的概念,并且每个Pod里都有一个Kubernetes系统自带的pause容器的原因,通过引入pause这个与业务无关并且作用类似于Linux操作系统守护进程的Kubernetes系统标准容器,以pause容器的状态来代表整个容器组的状态。
对于这些天生需要紧密关联的容器,可以放在同一个Pod里,以Pod为最小单位进行调度、扩展、共享资源及管理生命周期。
原因3:通讯、资源共享
Pod里的多个业务容器共享Pause容器的IP,共享Pause容器挂接的Volume,这样既简化了密切关联的业务容器之间的通信问题,也很好地解决了它们之间的文件共享问题。
相同的namespace可以用localhost通信,可以共享存储等。
1.3 Pod能够带来什么好处
搞清楚了Pod的由来,它到底能够为我们带来哪些好处呢?
- Pod做为一个可以独立运行的服务单元,简化了应用部署的难度,以更高的抽象层次为应用部署管提供了极大的方便。
- Pod做为最小的应用实例可以独立运行,因此可以方便的进行部署、水平扩展和收缩、方便进行调度管理与资源的分配。
- Pod中的容器共享相同的数据和网络地址空间,Pod之间也进行了统一的资源管理与分配。
2、Pod基本用法
无论通过命令kubectl
,还是Dashboard图形管理界面来操作,都离不开资源清单文件的定义。如果采用Dashboard图形管理界面操作,最终还是基于kubectl命令操作的,这里只介绍使用kubectl命令来操作Pod。
关于kubectl命令更多说明,可以参考官方文档:https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#-strong-getting-started-strong-
Pod资源清单中有几个重要属性:apiVersion
、kind
、metadata
、spec
以及status
。其中apiVersion
和kind
是比较固定的,status
是运行时的状态,所以最重要的就是metadata
和spec
两个部分。
(Kubernetes资源清单的定义,可参考上一篇文章:Kubernetes资源清单篇:如何创建资源?)
先来定义一个简单的Pod资源文件,命名为frontend-pod.yml:
示例中的Pod是在命名空间test中定义的,所以接下来的执行命令中都涉及指定命名空间的参数
-n test
。如果在默认命名空间default中定义,无需指定参数-n执行。
apiVersion: v1 kind: Pod metadata: name: frontend namespace: test # 如果没有命名空间test,需提前创建。也可以使用默认命名空间default,即:namespace属性标签可以不定义 labels: app: frontend spec: containers: - name: frontend image: xcbeyond/vue-frontend:latest # 发布在DockerHub中的镜像 ports: - name: port containerPort: 80 hostPort: 8080
可以使用命令kubectl explain pod
来查看各个属性标签的具体用法及含义。
[xcbeyond@bogon ~]$ kubectl explain pod KIND: Pod VERSION: v1 DESCRIPTION: Pod is a collection of containers that can run on a host. This resource is created by clients and scheduled onto hosts. FIELDS: apiVersion <string> APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources kind <string> Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds metadata <Object> Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata spec <Object> Specification of the desired behavior of the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status status <Object> Most recently observed status of the pod. This data may not be up to date. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
2.1 创建
基于资源清单文件来创建Pod,kubectl create -f <filename>
:
[xcbeyond@localhost ~]$ kubectl create -f frontend-pod.yml pod/frontend created
2.2 查看状态
创建完Pod后,想知道Pod的运行状态,可通过命令kubectl get pods -n <namespace>
查看:
(default命名空间,可不指定-n参数,非default则需指定具体namespace,否则查询不到。)
[xcbeyond@localhost ~]$ kubectl get pods -n test NAME READY STATUS RESTARTS AGE frontend 1/1 Running 0 36s
2.3 查看配置
如果想了解一个正在运行的Pod配置,可通过命令kubectl get pod <pod-name> -n <namespace> -o <json|yaml>
查看:
(-o参数用于指定输出配置格式,json、yaml格式)
此时查看结果处于运行态的结果,其中包含很多属性,我们只需关注关键属性即可。
[xcbeyond@localhost ~]$ kubectl get pod frontend -n test -o yaml apiVersion: v1 kind: Pod metadata: creationTimestamp: "2020-11-19T08:33:20Z" labels: app: frontend managedFields: - apiVersion: v1 fieldsType: FieldsV1 fieldsV1: f:metadata: f:labels: .: {} f:app: {} f:spec: f:containers: k:{"name":"frontend"}: .: {} f:image: {} f:imagePullPolicy: {} f:name: {} f:ports: .: {} k:{"containerPort":80,"protocol":"TCP"}: .: {} f:containerPort: {} f:hostPort: {} f:name: {} f:protocol: {} f:resources: {} f:terminationMessagePath: {} f:terminationMessagePolicy: {} f:dnsPolicy: {} f:enableServiceLinks: {} f:restartPolicy: {} f:schedulerName: {} f:securityContext: {} f:terminationGracePeriodSeconds: {} manager: kubectl-create operation: Update time: "2020-11-19T08:33:20Z" - apiVersion: v1 fieldsType: FieldsV1 fieldsV1: f:status: f:conditions: k:{"type":"ContainersReady"}: .: {} f:lastProbeTime: {} f:lastTransitionTime: {} f:status: {} f:type: {} k:{"type":"Initialized"}: .: {} f:lastProbeTime: {} f:lastTransitionTime: {} f:status: {} f:type: {} k:{"type":"Ready"}: .: {} f:lastProbeTime: {} f:lastTransitionTime: {} f:status: {} f:type: {} f:containerStatuses: {} f:hostIP: {} f:phase: {} f:podIP: {} f:podIPs: .: {} k:{"ip":"172.18.0.5"}: .: {} f:ip: {} f:startTime: {} manager: kubelet operation: Update time: "2020-11-23T08:10:40Z" name: frontend namespace: test resourceVersion: "28351" selfLink: /api/v1/namespaces/test/pods/frontend uid: be4ad65c-e426-4110-8337-7c1dd542f647 spec: containers: - image: xcbeyond/vue-frontend:latest imagePullPolicy: Always name: frontend ports: - containerPort: 80 hostPort: 8080 name: port protocol: TCP resources: {} terminationMessagePath: /dev/termination-log terminationMessagePolicy: File volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount name: default-token-bbmj5 readOnly: true dnsPolicy: ClusterFirst enableServiceLinks: true nodeName: minikube preemptionPolicy: PreemptLowerPriority priority: 0 restartPolicy: Always schedulerName: default-scheduler securityContext: {} serviceAccount: default serviceAccountName: default terminationGracePeriodSeconds: 30 tolerations: - effect: NoExecute key: node.kubernetes.io/not-ready operator: Exists tolerationSeconds: 300 - effect: NoExecute key: node.kubernetes.io/unreachable operator: Exists tolerationSeconds: 300 volumes: - name: default-token-bbmj5 secret: defaultMode: 420 secretName: default-token-bbmj5 status: conditions: - lastProbeTime: null lastTransitionTime: "2020-11-19T08:33:20Z" status: "True" type: Initialized - lastProbeTime: null lastTransitionTime: "2020-11-23T08:10:40Z" status: "True" type: Ready - lastProbeTime: null lastTransitionTime: "2020-11-23T08:10:40Z" status: "True" type: ContainersReady - lastProbeTime: null lastTransitionTime: "2020-11-19T08:33:20Z" status: "True" type: PodScheduled containerStatuses: - containerID: docker://84d978ee70d806d38b9865021d9c68881cf096960c7eb45e87b3099da85b4f6d image: xcbeyond/vue-frontend:latest imageID: docker-pullable://xcbeyond/vue-frontend@sha256:aa31cdbca5ca17bf034ca949d5fc7d6e6598f507f8e4dad481e050b905484f28 lastState: {} name: frontend ready: true restartCount: 0 started: true state: running: startedAt: "2020-11-23T08:10:40Z" hostIP: 172.17.0.2 phase: Running podIP: 172.18.0.5 podIPs: - ip: 172.18.0.5 qosClass: BestEffort startTime: "2020-11-19T08:33:20Z"
2.4 查看日志
如果想查看标准输出的日志,可以通过命令kubectl logs <pod-name> -n <namespace>
查看:
[xcbeyond@localhost ~]$ kubectl logs frontend -n test
如果Pod中有多个容器,查看特定容器的日志需要指定容器名称kubectl logs <pod-name> -c <container-name>
2.5 修改配置
如果想修改已创建的Pod,如修改某个标签label,有以下几种方式:
(1)通过标签管理命令kubectl label
设置或更新资源对象的labels。
该种方式只是针对标签的修改。
通过命令kubectl get pods -n <namespace> --show-labels
查看标签:
[xcbeyond@localhost ~]$ kubectl get pods -n test --show-labels NAME READY STATUS RESTARTS AGE LABELS frontend 1/1 Running 0 4d app=frontend
通过命令kubectl label pod <pod-name> -n <namespace> <key=value>
新增标签:
[xcbeyond@localhost ~]$ kubectl label pod frontend -n test tir=frontend pod/frontend labeled [xcbeyond@localhost ~]$ kubectl get pods -n test --show-labels NAME READY STATUS RESTARTS AGE LABELS frontend 1/1 Running 0 4d1h app=frontend,tir=frontend
通过命令kubectl label pod <pod-name> -n <namespace> <key=new-value> --overwrite
修改标签:
[xcbeyond@localhost ~]$ kubectl label pod frontend -n test tir=unkonwn --overwrite pod/frontend labeled [xcbeyond@localhost ~]$ kubectl get pods -n test --show-labels NAME READY STATUS RESTARTS AGE LABELS frontend 1/1 Running 0 4d1h app=frontend,tir=unkonwn
(2)通过命令kubectl apply -f <filename>
命令对配置进行更新。
(3)通过命令kubectl edit -f <filename> -n <namespace>
命令对配置进行在线更新。
(4)通过命令kubectl replace -f <filename> -n <namespace> --force
命令强制替换资源对象。
实际上,先删除在替换。
[xcbeyond@localhost ~]$ kubectl replace -f frontend-pod.yml --force pod "frontend" deleted pod/frontend replaced
2.6 删除
通过命令kubectl delete (-f <filename> | pod [<pod-name> | -l label]) -n <namespace>
进行删除。
[xcbeyond@localhost ~]$ kubectl delete pod frontend -n test pod "frontend" deleted
3、Pod生命周期
Pod
对象自从其创建开始至其终止退出的时间范围称为其Pod生命周期。在这段时间中,Pod
会处于多种不同的状态,并执行一些操作。其中,创建主容器(main container
)为必需的操作,其他可选的操作还包括运行初始化容器(init container
)、容器启动后钩子(post start hook
)、容器的存活性探测(liveness probe
)、就绪性探测(readiness probe
)以及容器终止前钩子(pre stop hook
)等,这些操作是否执行则取决于Pod
的定义。如下图所示:
Pod
的status
字段是一个PodStatus
的对象,PodStatus
中有一个phase
字段。
无论是手动创建还是通过Deployment
等控制器创建,Pod
对象总是处于其生命周期中以下几个阶段(phase
)之一:
- 挂起(
Pending
):API Server
已经创建了该Pod
,并已存入etcd
中,但它尚未被调度完成,或者仍处于从仓库下载镜像的过程中。 - 运行中(
Running
):Pod
内所有容器均已创建,被调度至某节点,并且所有容器都已经被kubelet
创建完成,且至少有一个容器处于运行状态、正在启动状态或正在重启状态。 - 成功(
Succeeded
):Pod
内所有容器都已经成功执行后退出终止,且不会再重启。 - 失败(
Failed
):Pod
内所有容器都已退出,并且至少有一个容器是因为失败而终止退出。即容器以非0
状态退出或者被系统禁止。 - 未知(
Unknown
):由于某种原因Api Server
无法正常获取到该Pod
对象的状态信息,可能由于无法与所在工作节点的kubelet
通信所致(网络通讯不畅)。
3.1 Pod的创建过程
Pod
是Kubernetes中的基础单元,了解它的创建过程对于理解其生命周期有很大的帮助,如下图描述了一个Pod
资源对象的典型创建过程。
(1)用户通过kubectl
或其他API
客户端提交了Pod Spec
给API Server
。(create Pod)
(2)API Server
尝试着将Pod
对象的相关信息写入etcd
存储系统中,待写入操作执行完成,API Server
即会返回确认信息至客户端。
(3)API Server
开始反馈etcd
中的状态变化。
(4)所有的kubernetes
组件均使用“watch”
机制来跟踪检查API Server
上的相关的变动。
(5)kube-scheduler
(调度器)通过其“watcher”
觉察到API Server
创建了新的Pod
对象但尚未绑定至任何工作节点。
(6)kube-scheduler
为Pod
对象挑选一个工作节点并将结果信息更新至API Server
。
(7)调度结果信息由API Server
更新至etcd
存储系统,而且API Server
也开始反馈此Pod
对象的调度结果。
(8)Pod
被调度到的目标工作节点上的kubelet
尝试在当前节点上调用Docker
启动容器,并将容器的结果状态返回送至API Server
。
(9)API Server
将Pod
状态信息存入etcd
系统中。
(10)在etcd
确认写入操作成功完成后,API Server
将确认信息发送至相关的kubelet
,事件将通过它被接受。
3.2 重要过程
3.2.1 初始化容器(Init Container)
初始化容器(init container
)是一种专用的容器,用于在启动应用容器(app container)之前启动一个或多个初始化容器,完成应用容器所需的预置条件。
初始化容器与普通的容器非常相似,但它有如下独有特征:
- 初始化容器总是运行到成功完成为止。
- 每个初始化容器都必须在下一个初始化容器启动之前成功完成。
根据Pod的重启策略( restartPolicy
),当init container执行失败,而且设置了 restartPolicy
为Never时,Pod将会启动失败,不再重启;而设置 restartPolicy
为Always时,Pod将会被系统自动重启。
如果一个Pod指定了多个初始化容器,这些初始化容器会按顺序一次运行一个。只有当前面的初始化容器必须运行成功后,才可以运行下一个初始化容器。当所有的初始化容器运行完成后,Kubernetes才初始化Pod和运行应用容器。
3.2.2 容器探测
容器探测(container probe
)是Pod
对象生命周期中的一项重要的日常任务,它是kubelet
对容器周期性执行的健康状态诊断,诊断操作由容器的处理器(handler
)进行定义。Kubernetes
支持三种处理器用于Pod
探测:
ExecAction
:在容器内执行指定命令,并根据其返回的状态码进行诊断的操作称为Exec
探测,状态码为0
表示成功,否则即为不健康状态。TCPSocketAction
:通过与容器的某TCP
端口尝试建立连接进行诊断,端口能够成功打开即为正常,否则为不健康状态。HTTPGetAction
:通过向容器IP
地址的某指定端口的指定path
发起HTTP GET
请求进行诊断,响应码为2xx
或3xx
时即为成功,否则为失败。
任何一种探测方式都可能存在三种结果:“Success”(成功)
、“Failure”(失败)
、“Unknown”(未知)
,只有success
表示成功通过检测。
容器探测分为两种类型:
- 存活性探测(livenessProbe):用于判定容器是否处于“运行”(
Running
)状态。一旦此类检测未通过,kubelet
将杀死容器并根据重启策略(restartPolicy
)决定是否将其重启,未定义存活检测的容器的默认状态为“Success
”。 - 就绪性探测(readinessProbe):用于判断容器是否准备就绪并可对外提供服务。未通过检测的容器意味着其尚未准备就绪,端点控制器(如
Service
对象)会将其IP
从所有匹配到此Pod
对象的Service
对象的端点列表中移除。检测通过之后,会再将其IP
添加至端点列表中。
什么时候使用存活(liveness)和就绪(readiness)探针?
如果容器中的进程能够在遇到问题或不健康的情况下自行崩溃,则不一定需要存活探针,kubelet
将根据Pod
的restartPolicy
自动执行正确的操作。
如果希望容器在探测失败时被杀死并重新启动,那么请指定一个存活探针,并指定restartPolicy
为Always
或OnFailure
。
如果要仅在探测成功时才开始向Pod
发送流量,请指定就绪探针。在这种情况下,就绪探针可能与存活探针相同,但是spec
中的就绪探针的存在意味着Pod
将在没有接收到任何流量的情况下启动,并且只有在探针探测成功才开始接收流量。
如果希望容器能够自行维护,可以指定一个就绪探针,该探针检查与存活探针不同的端点。
注意:如果只想在Pod
被删除时能够排除请求,则不一定需要使用就绪探针;在删除Pod
时,Pod
会自动将自身置于未完成状态,无论就绪探针是否存在。当等待Pod
中的容器停止时,Pod
仍处于未完成状态。
4、Pod调度
在Kubernetes中,实际我们很少会直接创建一个Pod,在大多数情况下会通过RC(Replication Controller)、Deployment、DaemonSet、Job等控制器来完成对一组Pod副本的创建、调度及全生命周期的自动控制任务。
在最早的Kubernetes版本里是没有这么多Pod副本控制器的,只有一个Pod副本控制器RC,这个控制器是这样设计实现的:RC独立于所控制的Pod,并通过Label标签这个松耦合关联关系控制目标Pod实例的创建和销毁,随着Kubernetes的发展,RC也出现了新的继任者Deployment,用于更加自动地完成Pod副本的部署、版本更新、回滚等功能。
严谨地说,RC的继任者其实并不是Deployment,而是ReplicaSet,因为ReplicaSet进一步增强了RC标签选择器的灵活性。之前RC的标签选择器只能选择一个标签,而ReplicaSet拥有集合式的标签选择器,可以选择多个Pod标签,如下所示:
selector: matchLabels: tier: frontend matchExpressions: - {key: tier, operator: In, values: [frontend]}
与RC不同,ReplicaSet被设计成能控制多个不同标签的Pod副本。一种常见的应用场景是,应用APP目前发布了v1与v2两个版本,用户希望APP的Pod副本数保持为3个,可以同时包含v1和v2版本的Pod,就可以用ReplicaSet来实现这种控制,写法如下:
selector: matchLabels: version: v2 matchExpressions: - {key: version, operator: In, values: [v1,v2]}
其实,Kubernetes的滚动升级就是巧妙运用ReplicaSet的这个特性来实现的,同时Deployment也是通过ReplicaSet来实现Pod副本自动控制功能的。
4.1 全自动调度
Deployment或RC的主要功能之一就是全自动部署一个容器应用的多份副本,以及持续监控副本的数量,在集群内始终维护用户指定的副本数量。
示例:
(1)下面以一个Deployment配置的示例,使用这个资源清单配置文件nginx-deployment.yml可以创建一个ReplicaSet,这个ReplicaSet会创建3个Nginx的Pod:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest ports: - name: http containerPort: 80
(2)执行kubectl create
命令创建这个Deployment:
[xcbeyond@localhost ~]$ kubectl create -f nginx-deployment.yml -n test deployment.apps/nginx-deployment created
(3)执行kubectl get deployments
命令查看Deployment的状态:
[xcbeyond@localhost ~]$ kubectl get deployments -n test NAME READY UP-TO-DATE AVAILABLE AGE nginx-deployment 3/3 3 3 17m
该结构表明Deployment已经创建好3个副本,并且所有副本都是最新可用的。
(4)通过执行kubectl get rs
和kubectl get pods
命令可以查看已经创建的ReplicaSet(RS)和Pod信息:
[xcbeyond@localhost ~]$ kubectl get rs -n test NAME DESIRED CURRENT READY AGE nginx-deployment-86b8cc866b 3 3 3 24m [xcbeyond@localhost ~]$ kubectl get pods -n test NAME READY STATUS RESTARTS AGE nginx-deployment-86b8cc866b-7rqzt 1/1 Running 0 27m nginx-deployment-86b8cc866b-k2rwp 1/1 Running 0 27m nginx-deployment-86b8cc866b-rn7l7 1/1 Running 0 27m
从调度策略上来说,这3个Nginx Pod有Kubernetes全自动调度的,它们各自被调度运行在哪个节点上,完全是由Master上的Schedule经过一系列计算得出,用户是无法干预调度过程和调度结果的。
除了使用自动调度,Kubernetes还提供了多种调度策略供用户实际选择,用户只需在Pod的定义中使用NodeSelector
、NodeAffinity
、PodAffinity
、Pod驱逐等更加细粒度的调度策略设置,就能完成对Pod的精准调度。
4.2 NodeSelector:定向调度
Kubernetes Master上的Scheduler(kube-scheduler
)负责实现Pod的调度,整个调度过程通过执行一系列复杂的算法,最终为每个Pod都计算出一个最佳的目标节点,这一过程是自动完成的,通过我们无法知道Pod最终会被调度到哪个节点上。
在实际场景下,可能需要将Pod调度到指定的一些Node上,可以通过Node的标签(Label)和Pod的nodeSelector
属性相匹配,来达到上述目的。比如希望将MySQL数据库调度到一个SSD磁盘的目标节点上,此时Pod模板中的NodeSelector
属性就发挥作用了。
示例:
(1)执行kubectl label nodes <node-name> <label-key>=<label-value>
命令给指定Node添加标签。
如,把SSD磁盘的Node添加标签disk-type=ssd
:
$ kubectl label nodes k8s-node-1 disk-type=ssd
(2)在Pod的资源清单定义中添加上nodeSelector
属性。
apiVersion: v1 kind: Pod metadata: name: mysql labels: env: test spec: containers: - name: mysql image: mysql nodeSelector: disk-type: ssd
(3)通过执行kubectl get pods -o wide
命令查看是否生效。
除了用户可以给Node添加标签,Kubernetes也给Node预定义了一些标签,方便用户直接使用,预定义的标签有:
- kubernetes.io/arch:示例,
kubernetes.io/arch=amd64
。诸如混用 arm 和 x86 节点的情况下很有用。 - kubernetes.io/os:示例,
kubernetes.io/os=linux
。在集群中存在不同操作系统的节点时很有用(例如:混合 Linux 和 Windows 操作系统的节点)。 - beta.kubernetes.io/os (已弃用)
- beta.kubernetes.io/arch (已弃用)
- kubernetes.io/hostname:示例,
kubernetes.io/hostname=k8s-node-1
。 - ……
更多可参考:https://kubernetes.io/docs/reference/kubernetes-api/labels-annotations-taints/
NodeSelector
通过标签的方式,简单实现了限制Pod所在节点的方法,看似既简单又完美,但在真实环境中可能面临以下令人尴尬的问题:
(1)如果NodeSelector
选择的Label不存在或者不符合条件,比如这些目标节点宕机或者资源不足时,该怎么办?
(2)如果要选择多种合适的目标节点,比如SSD磁盘的节点或者超高速硬盘的节点,该怎么办?Kubernetes引入了NodeAffinity
(节点亲和性)来解决上述问题。
(3)不同Pod之间的亲和性(Affinity)。比如MySQL和Redis不能被调度到同一个目标节点上,或者两种不同的Pod必须调度到同一个Node上,以实现本地文件共享或本地网络通信等特殊要求,这就是PodAffinity
要解决的问题。
4.3 NodeAffinity:Node亲和性调度
NodeAffinity
,是Node亲和性的调度策略,是用于解决NodeSelector
不足的一种全新调度策略。
目前有两种方式来表达:
RequiredDuringSchuedlingIgnoredDuringExecution
:必须满足指定的规则才可以调度Pod到Node上(功能与nodeSelector很像,但使用的是不同语法),相当于硬限制。PreferredDuringSchedulingIgnoredDuringExection
:强调优先满足指定规则,调度器会尝试调度Pod到Node上,但并不强求,相当于软限制。多个优先级规则还可以设置权重值,以定义执行的先后顺序。
lgnoredDuringExecution
隐含的意思是,在Pod
资源基于Node亲和性规则调度至某节点之后,节点标签发生了改变而不再符合此节点亲和性规则时 ,调度器不会将Pod
对象从此节点上移出。因此,NodeAffinity
仅对新建的Pod
对象生效。
Node亲和性可通过pod.spec.affinity.nodeAffinity
进行指定。
示例:
设置NodeAffinity
调度规则如下:
requiredDuringSchedulingIgnoredDuringExecution
要求只运行在amd64的节点上(kubernetes.io/arch In amd64)。preferredDuringSchedulingIgnoredDuringExecution
要求尽量运行在磁盘类型为ssd的节点上(disk-type In ssd)。
pod-with-node-affinity.yml
定义如下:
apiVersion: v1 kind: Pod metadata: name: with-node-affinity spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/arch operator: In values: - amd64 preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 preference: matchExpressions: - key: disk-type operator: In values: - ssd containers: - name: with-node-affinity image: busybox
从上面的示例中看到使用了In
操作符。NodeAffinity
语法支持的操作符包括: In
,NotIn
,Exists
,DoesNotExist
,Gt
,Lt
。 你可以使用 NotIn
和 DoesNotExist
来实现节点反亲和行为,即实现排查的功能。
NodeAffinity
规则定义的注意事项如下:
- 如果同时指定了
nodeSelector
和nodeAffinity
,那么两者必须都要满足,才能将Pod调度到指定的节点上。 - 如果
nodeAffinity
指定了多个nodeSelectorTerms
,那么其中一个能够匹配成功即可。 - 如果在
nodeSelectorTerms
中有多个matchExpressions
,则一个节点必须满足所有matchExpressions
才能运行该Pod。
如果你指定了多个与 nodeSelectorTerms
关联的 matchExpressions
,
4.4 PodAffinity:Pod亲和和互斥调度策略
Pod间的亲和与互斥从Kubernetes 1.4版本开始引入的。这一功能让用户从另一个角度来限制Pod所能运行的节点:根据在节点上正在运行的Pod标签而不是节点的标签进行判断和调度,要求对节点和Pod两个条件进行匹配。这种规则可以描述为:如果在具有标签X的Node上运行了一个或者多个符合条件Y的Pod,那么Pod应该运行在这个Node上。
与NodeAffinity
不同的是,Pod是属于某个命名空间的,所以条件Y表达的是一个或者全部命名空间中的一个Label Selector。
和NodeAffinity
相同,Pod亲和和互斥的设置也是requiredDuringSchedulingIgnoredDuringExecution
和PreferredDuringSchedulingIgnoredDuringExection
。
Pod亲和性可通过pod.spec.affinity.podAffinity
进行指定。
4.5 Taints和Tolerations(污点和容忍)
NodeAffinity节点亲和性,是Pod中定义的一种属性,使得Pod能够被调度到某些Node上运行。而Taints则正好相反,它让Node拒绝Pod的运行。
Taints需要和Toleration配合使用,让Pod避开一些不合适的Node。在Node上设置一个或多个Taint之后,除非Pod明确声明能够容忍这些污点,否则无法在这些Node上运行。Toleration是Pod的属性,让Pod能够运行在标注了Taint的Node上。
可使用kubectl taint
命令为Node设置Taint信息:
[xcbeyond@localhost ~]$ kubectl taint nodes node1 key=value:NoSchedule
4.6 Pod优先级调度
在Kubernetes 1.8版本引入了基于Pod优先级抢占(Pod priority Preemption)的调度策略,此时Kubernetes会尝试释放目标节点上优先级低的Pod,以腾出资源空间来安置优先级高的Pod,这种调度方式被称为”抢占式调度“。
可以通过以下几个维度来定义:
- Priority:优先级
- QoS:服务质量等级
- 系统定义的其他度量指标
示例:
(1)创建PriorityClasses,不属于任何命名空间:
apiVersion: scheduling.k8s.io/v1beta1 kind: PriorityClass metadata: name: high-priority value: 1000000 globalDefault: false description: "this priority class should be used for XYZ service pods only."
说明:定义名为high-priority的优先级,优先级为1000000,数字越大,优先级越高。优先级数字大于一亿的数字被系统保留,用于指派给系统组件。
(2)在Pod中引用上述定义的Pod优先级:
apiVersion: v1 kind: Pod metadata: name: frontend namespace: test labels: app: frontend spec: containers: - name: frontend image: xcbeyond/vue-frontend:latest ports: - name: port containerPort: 80 hostPort: 8080 priorityClassName: high-priority # 引用Pod优先级
使用优先级抢占的调度策略可能会导致某些Pod永远无法被成功调度。优先级调度不但增加了系统的复杂性,还可能带来额外不稳定的因素。因此,一旦发生资源紧张的局面,首先要考虑的是集群扩容,如果无法扩容,则再考虑有监管的优先级调度特性,比如结合基于Namespace的资源配额限制来约束任意优先级抢占行为。
4.7 DaemonSet:在每个Node上都调度一个Pod
DaemonSet是Kubernetes 1.2版本新增的一种资源对象,用于管理在集群中每个Node上仅运行一份Pod的副本实例。如下图所示:
DaemonSet的一些典型用法:
- 在每个节点上运行集群守护进程。如:运行一个GlusterFS存储或Ceph存储的Daemon进程。
- 在每个节点上运行日志收集守护进程。如:运行一个日志采集程序,Fluentd或Logstach。
- 在每个节点上运行监控守护进程。如:采集该Node的运行性能数据,Prometheus Node Exporter、collectd等。
4.8 Job:批处理调度
Job
负责批量处理短暂的一次性任务 ,即仅执行一次的任务,它保证批处理任务的一个或多个Pod成功结束。
按照批处理任务实现方式的不同,有以下几种典型模式适用于不同的业务场景:
-
基于Job模版进行扩展:
需要先编写一个通用的Job模版,根据不同的参数生成多个Job json/yml文件用于Job的创建,可以使用相同的标签进行Job管理。
-
按每个工作项目排列的队列:
- 需要用户提前准备好一个消息队列服务,比如rabbitMQ,该服务是一个公共组件,每个工作项目可以往里塞任务消息。
- 用户可以创建并行Job,需要能适用于该消息队列,然后从该消息队列中消费任务,并进行处理直到消息被处理完。
- 该模式下,用户需要根据项目数量填写
spec.completions
, 并行数量.spec.parallelism
可以根据实际情况填写。该模式下就是以所有的任务都成功完成了,job才会成功结束。
-
可变任务数量的队列:
- 需要用户提前准备好一个存储服务来保存工作队列,比如Redis。每个项目可以往该存储服务填充消息。
- 用户可以启动适用于该工作队列的多个并行Job,进行消息处理。与前一个Rabbit消息队列的差异在于,每个Job任务是可以知道工作队列已经空了,这时候便可以成功退出。
- 该模式下,
spec.completions
需要置1, 并行数量.spec.parallelism
可以根据实际情况填写。只要其中有一个任务成功完成,该Job就会成功结束。
-
普通的静态任务:
采用静态方式分配任务项。
考虑到批处理的并行问题,Kubernetes将Job分为以下三种类型:
-
非并行Job:
通常一个Job只运行一个Pod,一旦此Pod正常结束,Job将结束。(Pod异常,则会重启)
-
固定完成次数的并行Job:
并发运行指定数量的Pod,直到指定数量的Pod成功,Job结束。
-
带有工作队列的并行Job:
- 用户可以指定并行的Pod数量,当任何Pod成功结束后,不会再创建新的Pod。
- 一旦有一个Pod成功结束,并且所有的Pods都结束了,该Job就成功结束。
- 一旦有一个Pod成功结束,其他Pods都会准备退出。
4.9 Cronjob:定时任务
类似Linux中的Cron的定时任务CronJob。
定时表达式,格式如下:
Minutes Hours DayofMonth Month DayofWeek Year
-
Minutes:分钟,有效范围为0-59的整数。可以为
,
-
*
/
这4个字符。 -
Hours:小时,有效范围为0-23的整数。可以为
,
-
*
/
这4个字符。 -
DayofMonth:每月的哪一天,有效范围为0-31的整数。可以为
,
-
*
/
?
L
W
C
这8个字符。 -
Month:月份,有效范围为1-12的整数或JAN-DEC。可以为
,
-
*
/
这4个字符。 -
DayofWeek:星期,有效范围为1-7的整数或SUN-SAT,1表示星期天,2表示星期一,以此类推。可以为
,
-
*
/
?
L
C
#
这8个字符。
例如,每隔1分钟执行一次任务,则Cron表达式为*/1 * * * *
示例:
(1)定义CronJob的资源配置文件test-cronjob.yml
:
apiVersion: batch/v1beta1 kind: CronJob metadata: name: test 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
(2)执行kubectl crate
命令创建CronJob:
[xcbeyond@localhost k8s]$ kubectl create -f test-cronjob.yml -n test cronjob.batch/test created
(3)每隔1分钟执行kubectl get cronjob
命令查看任务状态,发现的确是每分钟调度一次:
[xcbeyond@localhost k8s]$ kubectl get cronjob test -n test NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE test */1 * * * * False 1 61s 69s [xcbeyond@localhost k8s]$ kubectl get cronjob test -n test NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE test */1 * * * * False 2 7s 75s [xcbeyond@localhost k8s]$ kubectl get cronjob test -n test NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE test */1 * * * * False 3 28s 2m36s
执行
kubectl delete cronjob
命令进行删除。
4.10 自定义调度器
如果在上述的调度器还是无法满足一些独特的需求,还可以使用任何语言实现简单或复杂的自定义调度器。
5、Pod升级和回滚
当需要升级某个服务时,一般会停止与该服务相关的所有Pod,然后下载新版本镜像并创建新的Pod。如果集群规模较大,则这样的工作就变成了一个挑战,并且长时间的服务不可用,也是很难让用户接受的。
为了解决上述问题,Kubernetes提供了滚动升级能够很好的解决。
如果Pod是通过Deployment创建,则可以在运行时修改Deployment的Pod定义(spc.template)或镜像名称,并应用到Deployment对象上,系统即可完成Deployment的自动更新操作。如果在更新过程中发生了错误,则可以通过回滚操作恢复Pod的版本。
5.1 Deployment的升级
以nginx-deployment.yml
为例:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest ports: - name: http containerPort: 80
(1)已运行的Pod副本数量为3,查看Pod状态:
[xcbeyond@localhost k8s]$ kubectl get pod -n test NAME READY STATUS RESTARTS AGE nginx-deployment-86b8cc866b-7rqzt 1/1 Running 2 53d nginx-deployment-86b8cc866b-k2rwp 1/1 Running 2 53d nginx-deployment-86b8cc866b-rn7l7 1/1 Running 2 53d
(2)现将nginx版本更新为nginx:1.9.1,可通过执行kubectl set image
命令为Deployment设置新的镜像:
[xcbeyond@localhost k8s]$ kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1 -n test deployment.apps/nginx-deployment image updated
kubectl set image
:更新现有的资源对象的容器镜像。
另一种升级方法是使用kubectl edit
命令修改Deployment的配置。
(3)此时(升级过程中),查看Pod状态,发现正在进行升级:
[xcbeyond@localhost k8s]$ kubectl get pod -n test NAME READY STATUS RESTARTS AGE nginx-deployment-79fbf694f6-kplgz 0/1 ContainerCreating 0 96s nginx-deployment-86b8cc866b-7rqzt 1/1 Running 2 53d nginx-deployment-86b8cc866b-k2rwp 1/1 Running 2 53d nginx-deployment-86b8cc866b-rn7l7 1/1 Running 2 53d
升级过程中,可以通过执行
kubectl rollout status
命令查看Deployment的更新过程。
(4)升级完成后,查看Pod状态:
[xcbeyond@localhost k8s]$ kubectl get pod -n test NAME READY STATUS RESTARTS AGE nginx-deployment-79fbf694f6-h7nfs 1/1 Running 0 2m43s nginx-deployment-79fbf694f6-kplgz 1/1 Running 0 7m21s nginx-deployment-79fbf694f6-txrfj 1/1 Running 0 2m57s
对比升级完成前后Pod状态列表中的NAME,已经发生了变化。
查看Pod使用的镜像,已经更新为nginx:1.9.1:
[xcbeyond@localhost k8s]$ kubectl describe pod/nginx-deployment-79fbf694f6-h7nfs -n test Name: nginx-deployment-79fbf694f6-h7nfs Namespace: test …… Containers: nginx: Container ID: docker://0ffd43455aa3a147ca0795cf58c68da63726a3c77b40d58bfa5084fb879451d5 Image: nginx:1.9.1 Image ID: docker-pullable://nginx@sha256:2f68b99bc0d6d25d0c56876b924ec20418544ff28e1fb89a4c27679a40da811b Port: 80/TCP ……
那么Deployment是如何完成Pod更新的呢?
使用kubectl describe deployment/nginx-deployment
命令仔细查看Deployment的更新过程:
初始创建Deployment时,系统创建了一个ReplicaSet(nginx-deployment-86b8cc866b),并创建了3个Pod副本。当更新Deployment时,系统创建了一个新的ReplicaSet(nginx-deployment-79fbf694f6),并将其副本数量扩展到1,然后将旧的ReplicaSet缩减为2。之后,系统继续按照相同的更新策略对新旧两个ReplicaSet进行逐个调整。最后,新的ReplicaSet运行了3个新版本的Pod副本,旧的ReplicaSet副本数量则缩减为0。
如下图所示:
执行kubectl describe deployment/nginx-deployment
命令,查看Deployment的最终信息:
[xcbeyond@localhost k8s]$ kubectl describe deployment/nginx-deployment -n test Name: nginx-deployment Namespace: test CreationTimestamp: Thu, 26 Nov 2020 19:32:04 +0800 Labels: <none> Annotations: deployment.kubernetes.io/revision: 2 Selector: app=nginx Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 25% max unavailable, 25% max surge Pod Template: Labels: app=nginx Containers: nginx: Image: nginx:1.9.1 Port: 80/TCP Host Port: 0/TCP Environment: <none> Mounts: <none> Volumes: <none> Conditions: Type Status Reason ---- ------ ------ Available True MinimumReplicasAvailable Progressing True NewReplicaSetAvailable OldReplicaSets: <none> NewReplicaSet: nginx-deployment-79fbf694f6 (3/3 replicas created) Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 30m deployment-controller Scaled up replica set nginx-deployment-79fbf694f6 to 1 Normal ScalingReplicaSet 25m deployment-controller Scaled down replica set nginx-deployment-86b8cc866b to 2 Normal ScalingReplicaSet 25m deployment-controller Scaled up replica set nginx-deployment-79fbf694f6 to 2 Normal ScalingReplicaSet 25m deployment-controller Scaled down replica set nginx-deployment-86b8cc866b to 1 Normal ScalingReplicaSet 25m deployment-controller Scaled up replica set nginx-deployment-79fbf694f6 to 3 Normal ScalingReplicaSet 24m deployment-controller Scaled down replica set nginx-deployment-86b8cc866b to 0
执行kubectl get rs
命令,查看两个ReplicaSet的最终状态:
[xcbeyond@localhost k8s]$ kubectl get rs -n test NAME DESIRED CURRENT READY AGE nginx-deployment-79fbf694f6 3 3 3 39m nginx-deployment-86b8cc866b 0 0 0 53d
在整个升级过程中,系统会保证至少有两个Pod可用,并且最多同时运行4个Pod,这是Deployment通过复杂的算法完成。Deployment需要确保在整个更新过程中只有一定数量的Pod可能处于不可用状态。在默认情况下,Deployment确保可用的Pod总数至少所需的副本数量(desired)减1,也就是最多1个不可用(maxUnavailable=1)。
这样,在升级过程中,Deployment就能够保证服务不中断,并且副本数量始终维持为指定的数量(desired)。
更新策略
在Deployment的定义中,可以通过spec.strategy
指定Pod更新策略。目前支持两种策略:Recreate(重建)和RollingUpdate(滚动更新),默认值为RollingUpdate。
- Recreate: 设置
spec.strategy.type=Recrate
,表示Deployment在更新Pod时,会先杀掉所有正在运行的Pod,然后创建新的Pod。 - RollingUpdate: 设置
spec.strategy.type=RollingUpdate
,表示Deployment会以滚动更新的方式来逐个更新Pod。同时,可通过设置spec.strategy.rollingUpdate
中的两个参数maxUnavailable
和maxSurge
来控制滚动更新的过程。spec.strategy.rollingUpdate.maxUnavailable
:用于指定Deployment在更新过程中最大不可用状态的Pod数量。 该值可以是具体数字,或者Pod期望副本数的百分比。spec.strategy.rollingUpdate.maxSurge
:用于指定在Deployment更新Pod的过程中Pod总数超过Pod期望副本数部分的最大值。该值可以是具体数字,或者Pod期望副本数的百分比。
5.2 Deployment的回滚
在默认情况下,所有Deployment的发布历史记录都被保留在系统中,以便于我们随时进行回滚。(历史记录数量可配置)
可通过执行kubectl rollout undo
命令完成Deployment的回滚。
(1)执行kubectl rollout history
命令检查某个Deployment部署的历史记录:
[xcbeyond@localhost k8s]$ kubectl rollout history deployment/nginx-deployment -n test deployment.apps/nginx-deployment REVISION CHANGE-CAUSE 1 <none> 2 <none
在创建Deployment时使用
--record
参数,就可以在CHANGE-CAUSE列看到每个版本创建/更新的命令。
(2)如果需要查看指定版本的详细信息,则可加上--revision=<N>
参数:
[xcbeyond@localhost k8s]$ kubectl rollout history deployment/nginx-deployment --revision=2 -n test deployment.apps/nginx-deployment with revision #2 Pod Template: Labels: app=nginx pod-template-hash=79fbf694f6 Containers: nginx: Image: nginx:1.9.1 Port: 80/TCP Host Port: 0/TCP Environment: <none> Mounts: <none> Volumes: <none>
(3)先将撤销本次升级版本,并回滚到上一版本,即:nginx:1.9.1-> nginx:latest。执行kubectl rollout undo
命令:
[xcbeyond@localhost k8s]$ kubectl rollout undo deployment/nginx-deployment -n test deployment.apps/nginx-deployment rolled back
当然,也可以使用--to-revision参数指定回滚到具体某个版本号。
(4)可执行kubectl describe deployment/nginx-deployment
命令查看回滚的整个过程:
[xcbeyond@localhost k8s]$ kubectl describe deployment/nginx-deployment -n test Name: nginx-deployment Namespace: test CreationTimestamp: Thu, 26 Nov 2020 19:32:04 +0800 Labels: <none> Annotations: deployment.kubernetes.io/revision: 3 Selector: app=nginx Replicas: 3 desired | 2 updated | 4 total | 3 available | 1 unavailable StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 25% max unavailable, 25% max surge Pod Template: Labels: app=nginx Containers: nginx: Image: nginx:latest Port: 80/TCP Host Port: 0/TCP Environment: <none> Mounts: <none> Volumes: <none> Conditions: Type Status Reason ---- ------ ------ Available True MinimumReplicasAvailable Progressing True ReplicaSetUpdated OldReplicaSets: nginx-deployment-79fbf694f6 (2/2 replicas created) NewReplicaSet: nginx-deployment-86b8cc866b (2/2 replicas created) Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 5m50s deployment-controller Scaled up replica set nginx-deployment-86b8cc866b to 1 Normal ScalingReplicaSet 4m55s deployment-controller Scaled down replica set nginx-deployment-79fbf694f6 to 2 Normal ScalingReplicaSet 4m55s deployment-controller Scaled up replica set nginx-deployment-86b8cc866b to 2
5.3 RC的滚动升级
对于RC的滚动升级,Kubernetes还提供了一个kubectl rolling-update
命令实现。该命令创建一个新的RC,然后自动控制旧的RC中的Pod副本数量逐渐减少到0,同时新的RC中的Pod副本数量从0逐步增加到目标值,来完成Pod的升级。
5.4 其他对象的更新策略
Kubernetes从1.6版本开始,对DaemonSet和StatefulSet的更新策略也引入类似于Deployment的滚动升级,通过不同的策略自动完成应用的版本升级。
5.4.1 DaemonSet的更新策略
DaemonSet的升级策略包括两种:
OnDelete
:DaemonSet的默认策略。当使用OnDelete
策略对DaemonSet进行更新时,在创建好新的DaemonSet配置之后,新的Pod并不会被自动创建,直到用户手动删除旧版本的Pod,才会触发新建操作。RollingUpdate
:当使用RollingUpdate
策略对DaemonSet进行更新时,旧版本的Pod将被自动删除,然后自动创建新版本的Pod。
5.4.2 StatefulSet的更新策略
Kubernetes从1.6版本开始,针对StatefulSet的更新策略逐渐向Deployment和DaemonSet的更新策略看齐,也将实现RollingUpdate、Partioned和onDelete这些策略,以保证StatefulSet中Pod有序地、逐个地更新,并保留更新历史记录,也能够回滚到某个历史版本。
6、Pod扩容
在实际生产环境下,我们面对不同场景,可能会进行服务实例的调整(增加或减少),以确保能够充分利用好系统资源。此时,可以利用Deployment/RC的Scale机制来完成这些工作。
Kubernetes对Pod扩容提供了手动和自动两种模式:
- 手动模式: 通过执行
kubectl scale
命令或通过RESTful API对一个Deployment/RC进行Pod副本数量的设置。 - 自动模式: 用户根据某个性能指标或自定义业务指标,并指定Pod副本数量的范围,系统将自动在这个范围内根据性能指标的变化进行调整。
6.1 手动模式扩容
以nginx-deployment.yml
为例:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest ports: - name: http containerPort: 80
(1)已运行的Pod副本数量为3,查看Pod状态:
[xcbeyond@localhost ~]$ kubectl get pod -n test NAME READY STATUS RESTARTS AGE nginx-deployment-86b8cc866b-7g4xp 1/1 Running 0 24h nginx-deployment-86b8cc866b-pgh6c 1/1 Running 0 24h nginx-deployment-86b8cc866b-qg26j 1/1 Running 0 23h
(2)通过执行kubectl scale
命令将Pod副本数量从初始的3个更新为5个:
[xcbeyond@localhost ~]$ kubectl scale deployment nginx-deployment --replicas 5 -n test deployment.apps/nginx-deployment scaled [xcbeyond@localhost ~]$ kubectl get pod -n test NAME READY STATUS RESTARTS AGE nginx-deployment-86b8cc866b-7g4xp 1/1 Running 0 24h nginx-deployment-86b8cc866b-dbkms 0/1 ContainerCreating 0 5s nginx-deployment-86b8cc866b-pgh6c 1/1 Running 0 24h nginx-deployment-86b8cc866b-qg26j 1/1 Running 0 23h nginx-deployment-86b8cc866b-xv5pm 0/1 ContainerCreating 0 5s [xcbeyond@localhost ~]$ kubectl get pod -n test NAME READY STATUS RESTARTS AGE nginx-deployment-86b8cc866b-7g4xp 1/1 Running 0 24h nginx-deployment-86b8cc866b-dbkms 1/1 Running 0 79s nginx-deployment-86b8cc866b-pgh6c 1/1 Running 0 24h nginx-deployment-86b8cc866b-qg26j 1/1 Running 0 23h nginx-deployment-86b8cc866b-xv5pm 1/1 Running 0 79s
如果--replicas设置为比当前Pod副本小,则会删除一些运行中的Pod,以实现缩容。
6.2 自动模式扩容
Kubernetes从1.1版本开始,新增了Pod 横向自动扩容的功能(Horizontal Pod Autoscaler,简称 HPA),用于实现基于CPU使用率进行自动Pod扩容的功能。
HPA 与 Deployment、Service 一样,也属于一种Kubernetes资源对象。
HPA 的目标是通过追踪集群中所有 Pod 的负载变化情况(基于Master的kube-controller-manager服务持续监测目标Pod的某种性能指标),来自动化地调整Pod的副本数,以此来满足应用的需求和减少资源的浪费。
目前Kubernetes支持的指标类型如下:
- Pod资源使用率: Pod级别的性能指标,通常是一个比率指,例如CPU 利用率。
- Pod自定义指标: Pod级别的性能指标,通常是一个数值,例如服务在每秒之内的请求数(TPS 或 QPS)。
- Object自定义指标或外部自定义指标: 通常是一个数值,需要容器应用以某种方式提供,例如通过HTTP URL “/metrics”提供,或使用外部服务提供的指标采集URL(如,某个业务指标)。
如何统计和查询这些指标呢?
Kubernetes从1.11版本开始,弃用基于Heapster
组件完成Pod的CPU使用率采集的机制,全面转向基于Metrics Server
完成数据采集。Metrics Server
将采集到的Pod性能指标数据通过聚合API(如,metrics.k8s.io、custom.metrics.k8s.io和external.metrics.k8s.io)提供给HPA控制器进行查询。
6.2.1 HPA的工作原理
Kubernetes中的某个Metrics Server(Heapster或自定义Metrics Server)持续采集所有Pod副本的指标数据。HPA控制器通过Metrics Server的API获取这些数据,基于用户定义的扩容规则进行计算,得到目标Pod副本数量。当目标Pod副本数量与当前副本数量不同时,HPA控制器就向Pod的副本控制器(Deployment、RC或ReplicaSet)发起scale操作(等同于手动模式中执行kubectl scale
命令),调整Pod的副本数量,完成扩容操作。
如下图描述了HPA体系中的关键组件和工作流程:
HPA控制器是基于Master的
kube-controller-manager
服务启动参数--horizontal-pod-autoscaler-sync-period
定义的探测周期(默认值为15s)。
6.2.2 HPA配置说明
自动模式扩容是通过HorizontalPodAutoscaler资源对象提供给用户来定义扩容规则。
HorizontalPodAutoscaler资源对象位于Kubernetes的API组“autoscaling”中,目前包括v1和v2两个版本。
- autoscaling/v1:仅支持基于CPU使用率的自动扩容。
- autoscaling/v2*:支持基于任意指标的自动扩容配置,包括基于资源使用率、Pod指标、其他指标等类型的指标数据。当前版本为autoscaling/v2beta2。
下面对HorizontalPodAutoscaler的配置和用法进行具体说明。
详细配置项可参考:
(1)基于autoscaling/v1版本的HorizontalPodAutoscaler配置:
apiVersion: autoscaling/v1 kind: HorizontalPodAutoscaler metadata: name: nginx spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: nginx minReplicas: 1 maxReplicas: 10 targetCPUUtilizationPercentage: 50
参数说明:
scaleTargetRef
:目标作用对象,可以是Deployment、RC、ReplicaSet。targetCPUUtilizationPercentage
:期望每个Pod的CPU使用率。minReplicas
和maxReplicas
:P大副本数量的最小值和最大值,系统将在这个范围内进行自动扩容操作,并维持每个Pod的CPU使用率为设置值。
使用autoscaling/v1版本的HorizontalPodAutoscaler,需预先安装Heapster组件或Metrics Server,用于采集Pod的CPU使用率。
(2)基于autoscaling/v2beta2版本的HorizontalPodAutoscaler配置:
apiVersion: autoscaling/v2beta2 kind: HorizontalPodAutoscaler metadata: name: nginx spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: nginx minReplicas: 1 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 50
参数说明:
scaleTargetRef
:目标作用对象,可以是Deployment、RC、ReplicaSet。minReplicas
和maxReplicas
:P大副本数量的最小值和最大值,系统将在这个范围内进行自动扩容操作,并维持每个Pod的CPU使用率为设置值。metrics
:目标指标值。type
:定义指标的类型。可设置四种类型,支持设置一个或多个类型的组合:Resource
:基于资源的指标值,如CPU和内存。对应CPU使用率,在target参数中设置averageUtilization定义目标平均CPU使用率;对应内存资源,在target参数中设置AverageValue定义目标平均内存使用值。Pods
:基于Pod的指标,系统将对全部Pod副本的指标值进行平均值计算。其target指标类型只能使用AverageValue。指标的数据通常需要搭建自定义Metrics Server和监控工具进行采集和处理。Object
:基于某种资源对象的指标或应用系统的任意自定义指标。指标的数据通常需要搭建自定义Metrics Server和监控工具进行采集和处理。External
:Kubernetes从1.10版本开始,引入了对外部系统指标的支持。例如,用户使用了公有云服务商提供的消息服务或外部负载均衡,可以基于这些外部服务的性能指标对自己部署在Kubernetes中的服务进行自动扩容操作。
target
:定义相应的指标目标值,系统将在指标数据达到目标值时触发扩容操作。
例1,设置指标的名称为requests-per-second
,其值来源于Ingress “main-route”,将目标值(value)设置为2000,即在Ingress的每秒请求数量达到2000个时触发扩容操作:
metrics: - type: Object object: metric: name: requests-per-second describedObject: apiVersion: extensions/v1beta1 kind: Ingress name: main-route target: type: Value value: 2k
例2,设置指标的名称为http_requests,并且该资源对象具有标签“verb=GET”,在指标平均值达到500时触发扩容操作:
metrics: - type: Object object: metric: name: http_requests selector: 'verb=GET' target: type: AverageValue averageValue: 500
还可以在同一个HorizontalPodAutoscaler资源对象中定义多个类型的指标,系统将针对每种类型的指标都计算Pod副本的目标数量,以最大值为准进行扩容操作。例如:
apiVersion: autoscaling/v2beta2 kind: HorizontalPodAutoscaler metadata: name: nginx spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: nginx minReplicas: 1 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: AverageUtilization averageUtilization: 50 - type: Pods pods: metric: name: packets-per-second target: type: AverageValue averageValue: 1k - type: Object object: metric: name: requests-per-second describedObject: apiVersion: extensions/v1beta1 kind: Ingress target: type: Value value: 10k
例3,设置指标的名称为queue_messages_ready,具有queue=worker_tasks标签在目标指标平均值为30时触发自动扩容操作:
metrics: - type: External external: metric: name: queue_messages_ready selector: 'queue=worker_tasks' target: type: AverageValue averageValue: 30
在使用外部服务的指标时,需安装、部署能够对接到Kubernetes HPA模型的监控系统,并且完成了解监控系统采集这些指标的机制,后续的自动扩容操作才能完成。
6.2.3 基于自定义指标的HPA实践
下面通过一个完整的示例,对如何搭建和使用基于自定义指标的HPA体系进行说明。
基于自定义指标进行自动扩容时,需预先部署自定义Metrics Server,目前可以使用基于Prometheus、Microsoft Azure、Google Stackdriver等系统的Adapter实现自定义Metrics Server。自定义Metrice Server可参考https://github.com/kubernetes/metrics/blob/master/IMPLEMENTATIONS.md#custom-metrics-api的说明。
本节是基于Prometheus监控系统对HPA的基础组件部署和HPA配置进行详细说明。
基于Prometheus的HPA架构如下图所示:
关键组件说明如下:
- Prometheus: 是一个开源的服务监控系统,用于定期采集各Pod的性能指标数据。
- Custom Metrics Server: 自定义Metrics Server,用Prometheus Adapter进行具体实现。它从Prometheus服务采集性能指标数据,通过Kubernetes的Metrics Aggregation层将自定义指标API注册到Master的API Server中以
/apis/custom.metrics.k8s.io
路径提供指标数据。 - HPA Controller: Kubernetes的HPA控制器,基于用户自定义的HorizontalPodAutoscaler进行自动扩容操作。
整个部署过程如下:
(1)开启kube-apiserver
、kube-controller-manager
服务的相关启动参数。
kube-apiserver
、kube-controller-manager
服务默认已经部署在kube-system
命名空间。[xcbeyond@localhost minikube]$ kubectl get pod -n kube-system NAME READY STATUS RESTARTS AGE coredns-6c76c8bb89-p26xx 1/1 Running 11 103d etcd-minikube 1/1 Running 11 103d kube-apiserver-minikube 1/1 Running 11 103d kube-controller-manager-minikube 1/1 Running 11 103d kube-proxy-gcd8d 1/1 Running 11 103d kube-scheduler-minikube 1/1 Running 11 103d storage-provisioner 1/1 Running 29 103d
注:本Kubernetes环境是基于Minikube方式部署的本地环境。
可通过
kubectl describe
命令查看服务目前的启动参数情况,例如:[xcbeyond@localhost minikube]$ kubectl describe pod/kube-apiserver-minikube -n kube-system Name: kube-apiserver-minikube Namespace: kube-system …… Containers: kube-apiserver: …… Command: kube-apiserver --advertise-address=172.17.0.2 --allow-privileged=true --authorization-mode=Node,RBAC --client-ca-file=/var/lib/minikube/certs/ca.crt --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota --enable-bootstrap-token-auth=true --etcd-cafile=/var/lib/minikube/certs/etcd/ca.crt --etcd-certfile=/var/lib/minikube/certs/apiserver-etcd-client.crt --etcd-keyfile=/var/lib/minikube/certs/apiserver-etcd-client.key --etcd-servers=https://127.0.0.1:2379 --insecure-port=0 --kubelet-client-certificate=/var/lib/minikube/certs/apiserver-kubelet-client.crt --kubelet-client-key=/var/lib/minikube/certs/apiserver-kubelet-client.key --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname --proxy-client-cert-file=/var/lib/minikube/certs/front-proxy-client.crt --proxy-client-key-file=/var/lib/minikube/certs/front-proxy-client.key --requestheader-allowed-names=front-proxy-client --requestheader-client-ca-file=/var/lib/minikube/certs/front-proxy-ca.crt --requestheader-extra-headers-prefix=X-Remote-Extra- --requestheader-group-headers=X-Remote-Group --requestheader-username-headers=X-Remote-User --secure-port=8443 --service-account-key-file=/var/lib/minikube/certs/sa.pub --service-cluster-ip-range=10.96.0.0/12 --tls-cert-file=/var/lib/minikube/certs/apiserver.crt --tls-private-key-file=/var/lib/minikube/certs/apiserver.key ……
可通过
kubectl edit
命令对服务启动参数进行更新,如:[xcbeyond@localhost minikube]$ kubectl edit pod kube-apiserver-minikube -n kube-system
在Master的API Server启动Aggregation层,通过设置kube-apiserver服务的下列启动参数进行开启。
--requestheader-client-ca-file=/var/lib/minikube/certs/front-proxy-ca.crt
:客户端CA证书。--requestheader-allowed-names=front-proxy-client
:允许访问的客户端common names列表,通过header中由--requestheader-username-headers
参数指定的字段获取。客户端common names的名称需要在client-ca-file中进行配置,将其设置为空值时,表示任意客户端都可以访问。--requestheader-extra-headers-prefix=X-Remote-Extra-
:请求头中需要坚持的前缀名。--requestheader-group-headers=X-Remote-Group
:请求头中需要检查的组名。--requestheader-username-headers=X-Remote-User
:请求头中需要检查的用户名。--proxy-client-cert-file=/var/lib/minikube/certs/front-proxy-client.crt
:在请求期间验证Aggregator的客户端CA证书。--proxy-client-key-file=/var/lib/minikube/certs/front-proxy-client.key
:在请求期间验证Aggregator的客户端私钥。
配置kube-controller-manager服务中HPA的相关启动参数(可选配置)如下:
--horizontal-pod-autoscaler-sync-period=10s
:HPA控制器同步Pod副本数量的空间间隔,默认值为15s。--horizontal-pod-autoscaler-downscale-stabilization=1m
:执行扩容操作的等待时长,默认值为5min。--horizontal-pod-autoscaler-initial-readiness-delay=30s
:等待Pod达到Read状态的时延,默认值为30min。--horizontal-pod-autoscaler-tolerance=0.1
:扩容计算结果的容忍度,默认值为0.1,表示[-10% - +10%]。
(2)部署Prometheus。
这里使用Prometheus-operator来部署。
Prometheus Operator为监控 Kubernetes service、deployment和Prometheus实例的管理提供了简单的定义,简化在Kubernetes上部署、管理和运行。
(3)部署自定义Metrics Server。
以Prometheus Adapter的实现进行部署。
(4)部署应用程序。
该应用程序提供RESTful接口/metrics
,并提供名为http_requests_total的自定义指标值。
(5)创建一个Prometheus的ServiceMonitor对象,用于监控应用程序提供的指标。
(6)创建一个HorizontalPodAutoscaler对象,用于为HPA控制器提供用户期望的自动扩容配置。
(7)对应用程序发起HTTP访问请求,验证HPA自动扩容机制。
参考文章:

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
怎样才能写出规范的好代码?
本文收录于JavaStarter ,里面有我完整的Java系列文章,学习或面试都可以看看 (一)前言 最近发现一件事情,自己写的代码和公司里工作5到10年的前辈写的代码虽然功能一样,但是他们的代码更规范,更优雅。比如有时候我会给一个需求写一个方法,但是有些人就可以好几个需求通过同一个方法实现。因此有了今天这个疑问,怎样才能写出规范的好代码? (二)什么样的代码是好的代码 我们在写程序的过程中,除了要实现功能之外,还需要考虑: 1.代码重用性(相同功能的代码,不用多次编写) 2.可读性(便于其他程序员阅读与理解) 3.可扩展性(需要新增功能时可以很方便的维护) 4.可靠性(新功能的加入不会对已有的功能造成影响) 5.高内聚、低耦合 而为了实现上面这些目的,我们需要遵循一些原则,因此,设计模式出现了。 (三)设计模式的原则 设计模式的原则,就是指在编写程序时应该遵守的原则,设计模式的原则也是各种设计模式设计的依据。 设计模式常用的原则有以下七种: 1.单一职责原则 2.接口隔离原则 3.依赖倒转原则 4.里式替换原则 5.开闭原则 6.迪米特法则 7.合成复用原则 接下来我将对以上七种设计...
- 下一篇
怎么学编程?编程有路,开源为径
你是否想学编程,但不知道学什么? 害,我刚接触编程时就这心态,根本不知道编程是什么!那些从网上和书上看到,编程相关的只言片语和技术名词,它们就像是一个个奢侈品的牌子,听起来就很高大上,而我仅仅只知道它们很“贵”,除此之外就一概不知了... 可能正因为编程听起来“很先进”,所以我才十分想学,蛋却不知从何下手,当时真希望有人能给我指条路。告诉我: 需要学什么(技术关键字) 学习路线(学习的顺序) 编程有路,开源为径 我在开源世界找了答案。我从 GitHub 上收集了包含:Web 开发、AI、大数据、Go、React 等方面的学习线路项目,组成了编程学习路线集合。这些开源项目以路线图的形式展示了需要学习的知识和顺序,希望可以让小伙伴们不再经历不知道学什么的迷茫期,快速进入学习+成长的高速通道。下面将逐一介绍这些开源项目,方便的话 点亮 Star 支持这些开源项目! 所有《编程学习路线图》已打包好,关注微信公众号:HelloGitHub 回复“路线图”即可获得。 如何成为 xxx 程序员 1、developer-roadmap(Web 程序员) Star 数:162k 该项目是目前 GitHu...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- SpringBoot2全家桶,快速入门学习开发网站教程
- MySQL8.0.19开启GTID主从同步CentOS8
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS7安装Docker,走上虚拟化容器引擎之路
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS6,7,8上安装Nginx,支持https2.0的开启