本篇已加入《.NET Core on K8S学习实践系列文章索引》,能够点击查看更多容器化技术相关系列文章。html
所谓Health Check,就是健康检查,即防微杜渐。K8S是一个编排引擎能够帮助咱们快捷地部署容器集群,若是部署上错误的容器致使服务崩溃,一般状况下咱们都会经过一些高可用机制进行故障转移。可是,前提条件是有健康检查。node
K8S天然帮咱们考虑到了这个问题,健康检查是K8S的重要特性之一,默认有健康检查机制,此外还能够主动设置一些自定义的健康检查。nginx
默认状况下,每一个容器启动时都会执行一个进程,由Dockerfile中的CMD或ENTRYPOINT指定。若是进程退出时的返回码不为0,则认为容器发生了故障,K8S会根据重启策略(restartPolicy)重启容器。web
例以下面这个例子,它模拟了容器发生故障的场景,注意下面配置文件中的args选项的定义:数据库
apiVersion: v1 kind: Pod metadata: name: edc-healthcheck-demo labels: test: healthcheck spec: restartPolicy: OnFailure containers: - name: healthcheck image: busybox imagePullPolicy: IfNotPresent args: - /bin/sh - -c - sleep 10; exit 1
其中 sleep 10; exit 1表明启动10秒以后就非正常退出(返回码不为0),而后经过kubectl建立Pod:json
kubectl apply -f health-check.yaml
过一段时间后查看Pod的状态,以下图所示:后端
能够看到,该容器已经重启了2次。也能够看出,restartPolicy简单直接暴力有效,不禁感叹重启大法好!api
可是,也要正视一个问题:必须等到进程退出后的返回值是非零才会触发重启策略,不能直接监测容器是不是健康。缓存
那么,K8S中有没有更好的机制可以实现智能一点的健康检查呢?答案就是使用Liveness与Readinesss。app
一句话Liveness:若是检测有问题(若是健康检查失败),重启pod!至于怎么检测,你说了算(自定义判断容器是否健康的条件)!
Liveness提供了一些重要的参数:
initialDelaySeconds:容器启动后第一次执行探测是须要等待多少秒,看运行的服务而定。 periodSeconds:执行探测的频率,默认是10秒,最小1秒。 timeoutSeconds:探测超时时间,默认1秒,最小1秒。 successThreshold:探测失败后,最少连续探测成功多少次才被认定为成功,默认是1,对于liveness必须是1,最小值是1。 failureThreshold:探测成功后,最少连续探测失败多少次才被认定为失败。默认是3。最小值是1.
下面实践一个小例子建立一个Pod:
#command本身定义,例子为 /tmp/healthy 不存在则认为pod有问题,你们根据实际业务来自定义。 apiVersion: v1 kind: Pod metadata: labels: test: liveness name: liveness-demo spec: containers: - name: liveness image: busybox args: - /bin/sh - -c - touch /tmp/healthy; sleep 30; rm -rf/tmp/healthy; sleep 10 livenessProbe: exec: command: - cat - /tmp/healthy initialDelaySeconds: 10 periodSeconds: 5
这里启动pod后会建立文件夹 /tmp/healthy,30秒后删除,在咱们的设置中,若是 /tmp/healthy 存在,则认为容器处于正常状态,不然认为发生故障。
须要注意的就是livenessProbe部分的定义了:
(1)探测方法:经过cat命令查看/tmp/healthy是否存在;若是返回值为0,则探测成功;不然,探测失败;
(2)initialDelaySeconds: 10 => 容器启动10秒以后开始执行liveness探测;
(3)periodSeconds: 5 => 每5秒执行一次liveness探测;若是连续执行3次探测都失败,那么就会杀掉并重启容器;
下面快速地验证一下:
(1)kubectl建立demo
kubectl apply -f liveness-demo.yaml
(2)查看pod日志
kubectl describe pod liveness-demo
结果以下图所示:
30秒以后,/tmp/healthy 被删除了,liveness探测失败,又过了几十秒,重复探测均失败后,开启了重启容器。
上面的例子使用的是Liveness的exec探针,此外K8S还有几种其余类型的探针:
针对tcpSocket的例子:这里会检测80端口是否能够正常访问;
#检测80端口是否联通 apiVersion: v1 kind: Pod metadata: labels: test: readiness name: readiness-tcp spec: containers: - name: readiness image: nginx readinessProbe: failureThreshold: 3 tcpSocket: port: 80 initialDelaySeconds: 10 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 10
针对httpGet的例子:这里会检测index.html文件是否能够正常访问;
#访问80端口的index.html文件是否存在 apiVersion: v1 kind: Pod metadata: labels: test: readiness name: readiness-httpget spec: containers: - name: readiness image: nginx readinessProbe: failureThreshold: 3 httpGet: path: /index.html port: 80 scheme: HTTP initialDelaySeconds: 10 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 10
一句话Readiness:若是检查失败,K8S会将该Pod从服务代理的分发后端去除,再也不让其接客(分发请求给该Pod)。若是检测成功,那么K8S就会将容器加入到分发后端,从新对外接客(对外提供服务)。
下面继续以上面Liveness的例子来实践一下:
apiVersion: v1 kind: Pod metadata: labels: test: readiness name: readiness-demo spec: containers: - name: readiness image: busybox args: - /bin/sh - -c - touch /tmp/healthy; sleep 30; rm -rf/tmp/healthy; sleep 10 readinessProbe: exec: command: - cat - /tmp/healthy initialDelaySeconds: 10 periodSeconds: 5
readinessProbe的配置语法与livenessProbe彻底一致,但执行后的效果却不同,见下图所示:
能够看出:
(1)刚被建立时,其READY状态为不可用;
(2)15秒(initialDelaySeconds + periodSeconds = 10 + 5 = 15)以后,第一次进行Readiness探测成功,其READY状态变为可用。
(3)30秒以后,/tmp/healthy被删除,连续3次Readiness探测均失败后,其READY状态又变为了避免可用。
此外,咱们也能够经过 kubectl describe pod readiness-demo 查看到更想起的日志信息。
Liveness与Readiness都是K8S的Health Check机制,Liveness探测是重启容器,而Readiness探测则是将容器设置为不可用,不让其再接受Service转发的请求。
Liveness与Readiness是独立执行的,两者无依赖,能够单独使用也能够同时使用。
对于多副本应用,当执行Scale Up操做时,新的副本会做为后端服务加入到Service的负载均衡列表中。可是,不少时候应用的启动都须要必定的时间作准备(好比加载缓存、链接数据库等等),这时咱们能够经过Readiness探测判断容器是否真正就绪,从而避免将请求发送到还未真正就绪的后端服务。
下面一个示例YAML配置文件定义了Readiness探测,重点关注readinessProbe部分:
apiVersion: apps/v1 kind: Deployment metadata: name: edc-webapi-deployment namespace: aspnetcore spec: replicas: 2 selector: matchLabels: name: edc-webapi template: metadata: labels: name: edc-webapi spec: containers: - name: edc-webapi-container image: edisonsaonian/k8s-demo:1.2 ports: - containerPort: 80 imagePullPolicy: IfNotPresent readinessProbe: httpGet: scheme: HTTP path: /api/health port: 80 initialDelaySeconds: 10 periodSeconds: 5 --- apiVersion: v1 kind: Service metadata: name: edc-webapi-service namespace: aspnetcore spec: type: NodePort ports: - nodePort: 31000 port: 8080 targetPort: 80 selector: name: edc-webapi
对于readinessProbe部分:
(1)schema指定了协议,这里是HTTP协议,也能够是HTTPS协议;
(2)path指定访问路径,这里是咱们自定义的一个Controller中的接口:简单地返回一个状态码为200的响应;
[Produces("application/json")] [Route("api/Health")] public class HealthController : Controller { [HttpGet] public IActionResult Get() => Ok("ok"); }
(3)port指定端口,这里是容器的端口80;
(4)initialDelaySeconds和periodSeconds指定了容器启动10秒以后开始探测,而后每隔5秒执行探测,若是发生3次以上探测失败,则该容器会从Service的负载均衡中移除,直到下次探测成功后才会从新加入。
假设如今有一个正常运行的多副本应用,咱们要对其进行滚动更新即Rolling Update,K8S会逐步用新Pod替换旧Pod,结果就有可能发生这样的一个场景:当全部旧副本被替换以后,而新的Pod因为人为配置错误一直没法启动,所以整个应用将没法处理请求,没法对外提供服务,后果很严重!
所以,Readiness探测还提供了用于避免滚动更新中出现这种状况的一些解决办法,好比maxSurge和maxUnavailable两个参数,用来控制副本替换的数量。
继续以上面的YAML配置文件为例,重点关注strategy部分:
apiVersion: apps/v1 kind: Deployment metadata: name: edc-webapi-deployment namespace: aspnetcore spec: strategy: rollingupdate: maxSurge: 25% maxUnavailable: 25% replicas: 10 selector: matchLabels: name: edc-webapi template: metadata: labels: name: edc-webapi spec: containers: - name: edc-webapi-container image: edisonsaonian/k8s-demo:1.2 ports: - containerPort: 80 imagePullPolicy: IfNotPresent readinessProbe: httpGet: scheme: HTTP path: /api/health port: 80 initialDelaySeconds: 10 periodSeconds: 5 --- apiVersion: v1 kind: Service metadata: name: edc-webapi-service namespace: aspnetcore spec: type: NodePort ports: - nodePort: 31000 port: 8080 targetPort: 80 selector: name: edc-webapi
(1)maxSurge : 25% => 控制滚动更新过程当中副本总数超过预期(这里预期是10个副本 replicas: 10)的上限,能够是数值也能够是百分比,而后向上取整。这里写的百分比,默认值是25%;
若是预期副本数为10,那么副本总数的最大值为RoundUp(10 + 10 * 25%)=13个。
(2)maxUnavailable : 25% => 控制滚动更新过程当中不可用的副本(这里预期是10个副本 replicas: 10)占预期的最大比例,能够是数值也能够是百分比,而后向下取整,一样地默认值也是25%;
若是预期副本总数为10,那么可用的副本数至少要为10-roundDown(10 * 25%)=10-2=8个。
综上看来,maxSurge的值越大,初始建立的新副本数量就越多;maxUnavaliable值越大,初始销毁的旧副本数量就越多;
本文探索了K8S中的默认健康检查机制以及Liveness和Readiness两种各有特色的探测机制,并经过一些小例子进行了说明。不过因为笔者也是初学,对于这一块没有过多实践经验,所以也是讲的比较粗浅,也但愿之后可以有更多的实际经验分享与各位。
(1)CloudMan,《天天5分钟玩转Kubernetes》
(2)李振良,《一天入门Kubernets教程》
(3)马哥(马永亮),《Kubernetes快速入门》
(4)华仔,《[译]Kubernetes最佳实践:使用Readiness和Liveness探测作Health Check》
(5)benjanmin杨,《K8S中的Health Check》
(6)条子在洗澡,《K8S健康性检查-探测》