k8s通过liveness来探测微服务的存活性,判断什么时候该重启容器实现自愈。比如访问 Web 服务器时显示 500 内部错误,可能是系统超载,也可能是资源死锁,此时 httpd 进程并没有异常退出,在这种情况下重启容器可能是最直接最有效的解决方案。
k8s通过readiness来探测微服务的什么时候准备就绪(例如初始化时,连接数据库,加载缓存数据等等,可能需要一段时间),然后将容器加入到server的负载均衡池中,对外提供服务。
k8s默认健康检查机制:
每个容器启动时都会执行一个进程,此进程由 Dockerfile 的 CMD 或 ENTRYPOINT 指定。如果进程退出时返回码非零,则认为容器发生故障,Kubernetes 就会根据 restartPolicy
重启容器。如果不特意配置,Kubernetes 将对两种探测采取相同的默认行为。
OK,那我们就来实现一下,新建.Net Core Api项目 k8s-healthcheck,新增Heathchecks控制器:
[Route("/api/v1/heathchecks")]public class HeathchecksController : Controller{private readonly static DateTime _beginUtc = DateTime.Now;[HttpGet][Route("test")]public ActionResult<IEnumerable<string>> Test(){return new string[] { "value1", "value2" };}[HttpGet][ProducesResponseType((int)HttpStatusCode.OK)][ProducesResponseType((int)HttpStatusCode.ServiceUnavailable)][Route("liveness")]public async Task<IActionResult> Liveness(){return await Task.Run<IActionResult>(() =>{if(DateTime.UtcNow.Subtract(_beginUtc).TotalSeconds>60*10){Console.WriteLine("{0} HealthChecks.Api is dead start restarting...",DateTime.Now);return this.NotFound();}else{Console.WriteLine("{0} HealthChecks.Api is alive.",DateTime.Now);return this.Ok();}});}[HttpGet][ProducesResponseType((int)HttpStatusCode.OK)][ProducesResponseType((int)HttpStatusCode.ServiceUnavailable)][Route("readiness")]public async Task<IActionResult> Readiness(){return await Task.Run<IActionResult>(() =>{if(DateTime.UtcNow.Subtract(_beginUtc).TotalSeconds<30){Console.WriteLine("{0} HealthChecks.Api is not ready...",DateTime.Now);return this.NotFound();}else{Console.WriteLine("{0} HealthChecks is ready,start accepting traffic...", DateTime.Now);return this.Ok();}});}}
解释一下我们这里自定义的 liveness 和 readiness检查机制:
liveness:存活10分钟,如果当前时间超过服务启动时间10分钟,则探测失败,否则探测成功。Kubernetes 如果连续执行 3 次 Liveness 探测均失败,就会杀掉并重启容器。
readiness:准备就绪30秒,30秒后,如果连续 3 次 Readiness 探测均失败后,容器将被重置为不可用,不接收 service 转发的请求。
从上面可以看到,我们可以根据自身的需求来实现这两种机制,然后,提供给k8s进行探测。
k8s默认是根据命令进行探测的,由于我们需要与微服务结合,所以需要在yml文件中指定为http方式(备注:k8s提供了三种container probes方式:command、TCP check、HTTP Get,其他的方式希望大家下去自己实践),k8s对于http方式探测成功的判断条件是请求的返回代码在 200-400 之间。
该项目的 deploy.yaml 内容如下:
apiVersion: apps/v1 kind: Deployment metadata:namespace: k8s-ecoysystem-appsname: k8s-healthchecklabels:app: k8s-healthcheck spec:replicas: 3selector:matchLabels:app: k8s-healthchecktemplate:metadata:namespace: k8s-ecoysystem-appslabels:app: k8s-healthcheckspec:containers:- name: k8s-healthcheckimagePullPolicy: Alwaysimage: 1151322093/k8s-healthcheckports:- containerPort: 80readinessProbe:httpGet:path: /api/v1/heathchecks/readinessport: 80scheme: HTTP initialDelaySeconds: 30periodSeconds: 60 livenessProbe:httpGet:path: /api/v1/heathchecks/livenessport: 80scheme: HTTP initialDelaySeconds: 120periodSeconds: 60
运行:kubectl apply -f deploy.yaml,然后通过 kubectl get pod -n k8s-ecoysystem-apps查看当前运行的pod,可以看到创建开始是不可以的,Ready状态数量是0。
稍等等待一会发现都可以用了:
然后我们通过 kubectl describe pod k8s-healthcheck-5c85bdcb69-f9zk9 -n k8s-ecoysystem-apps 命令可以查看更具体的信息:
刚开始readiness返回404不可用状态,不过我们设置的是30秒检查一次,所以很快状态就消除了,通过dashboard界面我们也可以看到更直观的信息:
等待10分钟过后,Liveness检测将会返回失败,pod处于不可用状态:
继续等待一会,集群就会自愈完成,前面说过,Liveness检测3次失败就会删除pod,并重启,重启之后就一轮新的检测:
从上面图中可以看到集群已经重启过1次,继续等待一段时间,如图:
Liveness 探测和 Readiness 探测是独立执行的,二者之间没有依赖,可以单独使用,也可以同时使用。用 Liveness 探测判断容器是否需要重启以实现自愈;用 Readiness 探测判断容器是否已经准备好对外提供服务。
OK,大功告成!!