1 保持pod健康
只要将pod调度到某个节点,该节点上的Kubelet就会运行pod的容器,从此只要该pod存在,就会保持运行。如果容器的主进程崩溃,Kubelet将重启容器。如果应用程序中有一个导致它每隔一段时间就会崩溃的bug,Kubernetes会自动重启应用程序,所以即使应用程序本身没有做任何特殊的事,在Kubernetes中运行也能自动获得自我修复的能力。
1.1 存活探针
Kubernetes可以通过存活探针(liveness probe)检查容器是否还在运行。可以为pod中的每个容器单独指定存活探针。如果探测失败,Kubernetes将定期执行探针并重新启动容器。
Kubernetes有以下三种探测容器的机制:
- HTTP GET探针对容器的IP地址(指定的端口和路径)执行HTTPGET请求。
- TCP套接字探针尝试与容器指定端口建立TCP连接。
- Exec探针在容器内执行任意命令,并检查命令的退出状态码。如果状态码是0,则探测成功。所有其他状态码都被认为失败。
1.2 创建基于HTTP的存活指针
apiVersion:v1
kind:Pod
metadata:
name:kubia-liveness
spec:
containers:
-image:luksa/kubia-unhealthy #这个镜像包含了(不知道怎么的)坏掉的应用
name:kubia
livenessProbe: #一个HTTP GET存活指针
httpGet: #
path:/ #HTTP请求的路径
port:8080 #探针连接的网络端口
1.3 使用存活指针
要查看存活探针是如何工作的,需要尝试立即创建该pod。大约一分半钟后,容器将重启。可以通过运行kubectl get看到容器重启的次数。
1.4 配置存活探针的附加属性
定义探针时可以自定义附加参数。例如,要设置初始延迟,可以将initialDelaySeconds 属性添加到存活探针的配置中,如下面的代码清单所示。
livenessProbe:
httpGet:
path:/
port:8080
initialDelaySeconds:15 #Kubernetes会在第一次探测前等待15秒
1.5 创建有效的存活指针
对于在生产中运行的pod,一定要定义一个存活探针。没有探针的话,Kubernetes无法知道你的应用是否还活着。只要进程还在运行,Kubernetes会认为容器是健康的。
简易的存活探针仅仅检查了服务器是否响应。
保持探针轻量
存活探针不应消耗太多的计算资源,并且运行不应该花太长时间。
无须在探针中实现重试循环
存活探针小结:
知道Kubernetes会在容器崩溃或其存活探针失败时,通过重启容器来保持运行。这项任务由承载pod的节点上的Kubelet执行---在主服务器上运行的Kubernetes Control Plane组件不会参与此过程。
但如果节点本身崩溃,那么Control Plane必须为所有随节点停止运行的pod创建替代品。它不会为你直接创建的pod执行此操作。这些pod只被Kubelet管理,但由于Kubelet本身运行在节点上,所以如果节点异常终止,它将无法执行任何操作。
为了确保应用程序在另一个节点上重新启动,需要使用ReplicationController或类似机制管理pod。
2 了解ReplicationController
ReplicationController是一种Kubernetes资源,可确保它的pod始终保持运行状态。如果pod因任何原因消失(例如节点从集群中消失或由于该pod已从节点中逐出),则ReplicationController会注意到缺少了pod并创建替代pod。
2.1 ReplicationController的操作
ReplicationController会持续监控正在运行的pod列表,并保证相应“类型”的pod的数目与期望相符。如正在运行的pod太少,它会根据pod模板创建新的副本。如正在运行的pod太多,它将删除多余的副本。
控制器的协调流程:
ReplicationController的三部分:
- label selector(标签选择器),用于确定ReplicationController作用域中有哪些pod
- replica count(副本个数),指定应运行的pod数量
- pod template(pod模板),用于创建新的pod副本
更改标签选择器和pod模板对现有pod没有影响
使用ReplicationController的好处:
- 确保一个pod(或多个pod副本)持续运行,方法是在现有pod丢失时启动一个新pod
- 集群节点发生故障时,它将为故障节点上运行的所有pod(即受ReplicationController 控制的节点上的那些pod)创建替代副本
- 它能轻松实现pod的水平伸缩——手动和自动都可以
2.2 创建一个ReplicationController
代码清单 ReplicationController的YAML定义:kubia-rc.yaml
apiVersion:v1
kind:ReplicationController #配置定义了ReplicationController(RC)
metadata:
name:kubia
spec:
replicas:3
selector: #pod选择器决定了RC的操作对象
app:kubia #
template: #下面为创建新pod的模板
metadata:
labels:
app:kubia
spec:
containers:
-name:kubia
image:luksa/kubia
ports:
-containerPort:8080
要创建ReplicationController,请使用已知的kubectl create命令:
$ kubectl create -f kubia-rc.yaml
2.3 使用ReplicationController
获取有关ReplicationController的信息:
$ kubectl get rc
可以通过kubectl describe 命令看到ReplicationController的附加信息:
$ kubectl describe rc kubia
控制器如何创建新的pod
控制器通过创建一个新的替代pod来响应pod的删除操作
2.4 将pod移入或移出ReplicationController的作用域
由ReplicationController创建的pod并不是绑定到ReplicationController。在任可时刻,ReplicationController管理与标签选择器匹配的pod。通过更改pod的标签,可以将它从ReplicationController的作用域中添加或删除。
2.5 修改pod模板
使用以下命令编辑ReplicationController:
$ kubectl edit rc kubia
2.6 水平缩放pod
ReplicationController扩容:
$ kubectl scale rc kubia --replicas=10
通过编辑定义来缩放 ReplicationController
$ kubectl edit rc kubia
当文本编辑器打开时,找到spec.replicas字段并将其值更改为10
2.7 删除一个ReplicationController
当通过kubectl delete删除ReplicationController时,pod也会被删除。但是由于由ReplicationController创建的pod不是ReplicationController的组成部分,只是由其进行管理,因此可以只删除ReplicationController并保持pod运行。
当使用kubectl delete
删除ReplicationController
时,可以通过给命令增加--cascade=false
选项来保持pod的运行。
$ kubectl delete rc kubia --cascade=false
3 使用ReplicaSet而不是ReplicationController
3.1 比较ReplicaSet 和ReplicationController
ReplicaSet的行为与ReplicationController完全相同,但pod选择器的表达能力更强。ReplicationController的标签选择器只允许包含某个标签的匹配pod,但ReplicaSet的选择器还允许匹配缺少某个标签的pod,或包含特定标签名的pod,不管其值如何。
另外,举个例子,单个ReplicationControler无法将pod与标签env=production和env=devel同时匹配。它只能匹配带有env=production标签的pod或带有env=devel标签的pod。但是一个ReplicaSet可以匹配两组pod并将它们视为一个大组。
同样,无论ReplicationController的值如何,ReplicationController都无法仅基于标签名的存在来匹配pod,而ReplicaSet则可以。例如,ReplicaSet可匹配所有包含名为env的标签的pod,无论ReplicaSet的实际值是什么(可以理解为env=*)。
3.2 定义ReplicaSet
代码清单 ReplicaSet的YAML定义:kubia-replicaset.yaml
apiVersion:apps/v1beta2
kind:ReplicaSet
metadata:
name:kubia
spec:
replicas:3
selector:
matchLabels: #这里使用了更简单的mathLabels选择器,这非常类似于ReplicationController
app:kubia #的选择器
template: #该模板与ReplicationController中的相同
metadata:
labels:
app:kubia
spec:
containers:
-name:kubia
image:luksa/kubia
3.3 创建和检查 ReplicaSet
使用kubectl create
命令根据YAML文件创建ReplicaSet。之后,可以使用kubectl get
和kubectl describe
来检查ReplicaSet:
$ kubectl get rs
$ kubectl describe rs
3.4 使用ReplicaSet的更富表达力的标签选择器
ReplicaSet相对于ReplicationController的主要改进是它更具表达力的标签选择器。之前故意在第一个ReplicaSet示例中,用较简单的matchLabels选择器来确认ReplicaSet与ReplicationController没有区别。现在,将用更强大的matchExpressions属性来重写选择器,如下面的代码清单所示。
selector:
matchExpressions:
- key:app #此选择器要求改pod包含名为"app"的标签
operator:In #
values: # 标签的值必须是"kubia"
-kubia #
可以给选择器添加额外的表达式。如示例,每个表达式都必须包含一个key、一个operator(运算符),并且可能还有一个values的列表(取决于运算符)。
会看到四个有效的运算符:
- In:Label的值必须与其中一个指定的values匹配
- NotIn:Label的值与任何指定的values不匹配
- Exists:pod必须包含一个指定名称的标签(值不重要),使用此运算符时,不应指定values字段
- DoesNotExist:pod不得包含有指定名称的标签,values属性不得指定。
4 使用DaemonSet在每个节点上运行一个pod
4.1 使用DaemonSet 在每个节点上运行一个pod
要在所有集群节点上运行一个pod,需要创建一个DaemonSet对象,这很像一个ReplicationController或ReplicaSet,除了由DaemonSet创建的pod,已经有一个指定的目标节点并跳过Kubernetes调度程序。它们不是随机分布在集群上的。
4.2 使用DaemonSet只在特定的节点上运行pod
DaemonSet将pod部署到集群中的所有节点上,除非指定这些pod只在部分节点上运行。这是通过pod模板中的nodeSelector属性指定的,这是DaemonSet定义的一部分(类似于ReplicaSet 或ReplicationController中的pod模板)。
5 运行执行单个任务的pod
ReplicationController、ReplicaSet和DaemonSet会持续运行任务,永远达不到完成态。这些pod中的进程在退出时会重新启动。但是在一个可完成的任务中,其进程终止后,不应该再重新启动。
5.1 Job资源
Job允许你运行一种pod,该pod在内部进程成功结束时,不重启容器。一旦任务完成,pod就被认为处于完成状态。
在发生节点故障时,该节点上由Job管理的pod 将按照ReplicaSet的pod的方式,重新安排到其他节点。如果进程本身异常退出(进程返回错误退出代码时),可以将Job配置为重新启动容器。
5.2 定义Job资源
代码清单 Job的YAML定义:exporter.yaml
apiVersion:batch/v1 #Job属于batch API组,版本为v1
kind:Job
metadata:
name:batch-job
spec: #没有指定pod选择器,它将根据pod模板中的标签创建
template:
metadata:
labels:
app:batch-job
spec:
restartPolicy:OnFailure #Job不能使用Always为默认的重新启动策略
containers:
-name:main
image:luksa/batch-job
在一个pod的定义中,可以指定在容器中运行的进程结束时,Kubernetes会做什么。这是通过pod配置的属性restartPolicy完成的,默认为Always。Job pod不能使用默认策略,因为它们不是要无限期地运行。因此,需要明确地将重启策略设置为OnFailure或Never。此设置防止容器在完成任务时重新启动(pod被Job管理时并不是这样的)。
6 安排Job定期运行或在将来运行一次
Job资源在创建时会立即运行pod。但是许多批处理任务需要在特定的时间运行,或者在指定的时间间隔内重复运行。在Linux和类UNIX操作系统中,这些任务通常被称为cron任务。Kubernetes也支持这种任务。
Kubernetes中的cron任务通过创建CronJob资源进行配置。运行任务的时间表以知名的cron格式指定,所以熟悉常规cron任务,将在几秒钟内了解Kubernetes的CronJob。
在配置的时间,Kubernetes 将根据在CronJob对象中配置的Job模板创建Job资源。创建Job资源时,将根据任务的pod模板创建并启动一个或多个pod副本,如你在前一部分中所了解的那样。
6.1 创建一个CronJob
代码清单 CronJob资源的YAML:cronjob.yaml
apiversion:batch/v1betal
kind:CronJob
metadata:
name:batch-job-every-fifteen-minutes
spec:
schedule:"0,15,30,45****" #这项工作应该每天在每小时0、15、30、45分钟运行
jobTemplate:
spec:
template: #以下部分为CronJob创建Job资源用到的模板
metadata:
labels:
app:periodic-batch-job
spec:
restart Policy:OnFailure
containers:
-name:main
image:1uksa/batch-job
配置时间表安排(简单版):
- 分钟
- 小时
- 每月中的第几天
- 月
- 星期几
配置Job模板
CronJob通过CronJob规范中配置的jobTemplate属性创建任务资源。
6.2 了解计划任务的运行方式
在计划的时间内,CronJob资源会创建Job资源,然后Job创建pod。
可能发生Job或pod创建并运行得相对较晚的情况。你可能对这项工作有很高的要求,任务开始不能落后于预定的时间过多。在这种情况下,可以通过指定CronJob规范中的startingDeadlineSeconds字段来指定截止日期,如下面的代码清单所示。
apiversion:batch/v1betal
kind:CronJob
spec:
schedule:"0,15,30,45****
startingDeadlineSeconds:15 #pod最迟必须在预定时间后15秒开始运行
...
假设工作运行的时间是10:30:00。如果因为任何原因10:30:15不启动,任务将不会运行,并将显示为Failed。
7 小结
- 使用存活探针,让Kubernetes在容器不再健康的情况下立即重启它(应用程序定义了健康的条件)。
- 不应该直接创建pod,因为如果它们被错误地删除,它们正在运行的节点异常,或者它们从节点中被逐出时,它们将不会被重新创建。
- ReplicationController始终保持所需数量的pod副本正在运行。
- 水平缩放pod与在ReplicationController上更改所需的副本个数一样简单。
- pod 不属于ReplicationController,如有必要可以在它们之间移动。
- ReplicationController将从pod模板创建新的pod。更改模板对现有的pod没有影响。
- ReplicationController 应该替换为ReplicaSet和DaemonSet,它们提供相同的能力,但具有额外的强大功能。
- ReplicationController 和ReplicaSet 将pod 安排到随机集群节点,而DaemonSet确保每个节点都运行一个DaemonSet中定义的pod实例。
- 执行批处理任务的pod应通过KubernetesJob资源创建,而不是直接或通过ReplicationController或类似对象创建。
- 需要在未来某个时候运行的Job可以通过CronJob资源创建。