前言

经过前面的一系列文章,我们对k8s应该也算是简单的入门了。本文开始,会逐步开始讲解一些进阶的知识点。在前面的文章中,我们主要是围绕如何完整地发布一个可对外访问的服务。其中重点是以springboot+vuejs前后端分离项目为主的。后续的文章也会是围绕springboot+vuejs,不过会基于其扩展一些新的服务,如redis、rabbitmq、elk等。

关于健康检测

健康检查(Health Check)是让系统知道您的应用实例是否正常工作的简单方法。 如果您的应用实例不再工作,则其他服务不应访问该应用或向其发送请求。 相反,应该将请求发送到已准备好的应用程序实例,或稍后重试。 系统还应该能够使您的应用程序恢复健康状态。

默认情况下,当 Pod 中的所有容器启动时,Kubernetes 开始向 Pod 发送流量,并在崩溃时重新启动容器。如果Pod启动后就立即对外服务,其实是不太合理的。因为Pod启动成功并不代表容器里面部署的服务就能对外服务了,比如springboot项目,容器启动成功后,springboot启动需要一些初始化工作,真正要能对外访问,快则需要几秒,慢则需要十几、二十秒,甚至更久。 Kubernetes 在设计上已经考虑到了这点,它可以通过创建自定义运行状况检查来使部署更加健壮 。这里涉及到一个关键字:容器探针

容器探针

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

  • ExecAction:在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。
  • CPSocketAction:对指定端口上的容器的 IP 地址进行 TCP 检查。如果端口打开,则诊断被认为是成功的。
  • HTTPGetAction:对指定的端口和路径上的容器的 IP 地址执行 HTTP Get 请求。如果响应的状态码大于等于200 且小于 400,则诊断被认为是成功的。

每次探测都将获得以下三种结果之一:

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

Kubelet 可以选择是否执行在容器上运行的两种探针执行和做出反应:

  • livenessProbe(存活探针):指示容器是否正在运行。如果存活探测失败,则 kubelet 会杀死容器,并且容器将受到其 重启策略的影响。如果容器不提供存活探针,则默认状态为 Success
  • readinessProbe(就绪探针):指示容器是否准备好服务请求。如果就绪探测失败,端点控制器将从与 Pod 匹配的所有 Service 的端点中删除该 Pod 的 IP 地址。初始延迟之前的就绪状态默认为 Failure。如果容器不提供就绪探针,则默认状态为 Success

高级 liveness 探针示例

探针的定义,也是k8s编排的重要内容。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mldong-admin
  namespace: mldong-admin-test
spec:
  selector:
    matchLabels:
      app: mldong-admin
  replicas: 1
  template:
    metadata:
      labels:
        app: mldong-admin
    spec:
      containers:
        - name: mldong-admin
          env:
            - name: TZ
              value: Asia/Shanghai
          image: registry.cn-zhangjiakou.aliyuncs.com/mldong/java/mldong-admin:202007220017_c608761
          livenessProbe:
              httpGet:
                # 当没有定义 "host" 时,使用 "PodIP"
                # host: my-host
                # 当没有定义 "scheme" 时,使用 "HTTP" scheme 只允许 "HTTP" 和 "HTTPS"
                # scheme: HTTPS
                path: /healthz # 这要求服务得有这个接口地址
                port: 8080     # 服务对应的端口
                httpHeaders:   # 可以携带请求头
                - name: X-Custom-Header
                  value: Awesome
              initialDelaySeconds: 30   #第一次健康检查的时间
              periodSeconds: 5    #检查周期
              timeoutSeconds: 5   #检查超时时间
              successThreshold: 1 #成功次数判定成功
              failureThreshold: 1 #失败次数判定失败
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080
              name: port
              protocol: TCP
          command: ["/bin/sh"]
          args: ["-c", "set -e && java -jar app.jar --spring.profiles.active=test --server.port=8080"]

探针类型livenessProbereadinessProbe,与容器定义的image同级,两者可同时存在,前者是定义存活探针, 健康状态检查,周期性检查服务是否存活,检查结果失败,将重启容器 。后者是定义就绪探针, 可用性检查,周期性检查服务是否可用,不可用将从service的endpoints中移除 。

如上,定义了一个存活探针livenessProbe,其使用的httpGet的方式去检查的,请求路径为/healthz,端口为8080,请求头httpHeadersX-Custom-Header=Awesome。第一次健康检查的时间initialDelaySeconds为pod启动后30s后进行检测;检查周期periodSeconds为5s;检查超时时间timeoutSeconds为5s;成功次数successThreshold为1判定成功;失败次数failureThreshold为1判定失败。

检查方式说明:

  • httpGet 检测某个 http 请求的返回状态码 2xx,3xx正常, 4xx,5xx错误

参数说明

参数默认值说明
host当没有定义 “host” 时,使用 “PodIP”请求域名
schemeHTTP只允许 “HTTP” 和 “HTTPS”
path/请求地址,这需要接口服务中有该请求地址
port80服务对应的端口
httpHeaders请求头name/value

样例:

httpGet:
    path: /healthz
    port: 8080
    httpHeaders:
    - name: X-Custom-Header
      value: Awesome
  • exec 执行一段命令 返回值为0, 非0

参数说明:

参数默认值说明
command要执行的命令,数组

样例:

exec: #执行方式
    command:  #初始命令
    - cat
    - /tmp/healthy
  • tcpSocket 测试某个端口是否能够连接

参数说明:

参数默认值说明
port80端口

样例:

tcpSocket:
	port: 80

检查规则说明

参数默认值说明
initialDelaySeconds第一次健康检查的时间,太小的话,可能会造成pod无限重启。
periodSeconds检查周期
timeoutSeconds检查超时时间
successThreshold1成功次数判断成功
failureThreshold1失败次数判断失败

实战演示

在实战前,先学习如下命令:

for a in {1..10};do curl http://vueadmin.mldong.com/api/login;date +"%Y%m%d%H%M%S";sleep 2;done;

每隔2秒钟进行一次接口访问,循环10次。

效果图如下:

存活探针

  1. 新建v1.yaml
cat << EOF > /mldong/k8s/mldong-probe/v1.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: mldong-probe
---
apiVersion: v1
kind: Service
metadata:
  name: mldong-admin-nodeport
  namespace: mldong-probe
spec:
  type: NodePort
  ports:
  - port: 8080
    targetPort: 8080
  selector:
    app: mldong-admin
---
apiVersion: v1
kind: Service
metadata:
  name: mldong-admin
  namespace: mldong-probe
spec:
  type: ClusterIP
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: mldong-admin
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mldong-admin
  namespace: mldong-probe
spec:
  selector:
    matchLabels:
      app: mldong-admin
  replicas: 1
  template:
    metadata:
      labels:
        app: mldong-admin
    spec:
      containers:
        - name: mldong-admin
          env:
            - name: TZ
              value: Asia/Shanghai
          image: registry-vpc.cn-zhangjiakou.aliyuncs.com/mldong/java/mldong-admin:202007220017_c608761
          livenessProbe:
              tcpSocket:
                port: 8080     # 服务对应的端口
              initialDelaySeconds: 15  #第一次健康检查的时间
              periodSeconds: 5    #检查周期
              timeoutSeconds: 5   #检查超时时间
              successThreshold: 1 #成功次数判定成功
              failureThreshold: 1 #失败次数判定失败
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8080
              name: port
              protocol: TCP
          command: ["/bin/sh"]
          args: ["-c", "set -e && java -jar app.jar --spring.profiles.active=test --server.port=8080"]
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
  name: mldong-admin-ingress
  namespace: mldong-probe
spec:
  rules:
    - host: a.test.com
      http:
        paths:
          - backend:
              serviceName: mldong-admin
              servicePort: 8080
            path: /
EOF

  1. 另外开启一个终端,执行如下命令:
kubectl get pods -A -w | grep mldong-probe
  1. 旧终端执行如下命令:
kubectl apply -f v1.yaml

正常启动效果如下:

4. 删除重来

kubectl delete -f v1.yaml

​ 修改成其他非服务端口

tcpSocket:
  port: 8081
  1. 重新运行
kubectl apply -f v1.yaml

效果如下:

其实就是监听的服务不存在,重启容器。

最后清一下空间

kubectl delete -f v1.yaml

就绪探针

  1. 新建v2.yaml

    cat << EOF > /mldong/k8s/mldong-probe/v2.yaml
    apiVersion: v1
    kind: Namespace
    metadata:
      name: mldong-probe
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: mldong-admin-nodeport
      namespace: mldong-probe
    spec:
      type: NodePort
      ports:
      - port: 8080
        targetPort: 8080
      selector:
        app: mldong-admin
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: mldong-admin
      namespace: mldong-probe
    spec:
      type: ClusterIP
      ports:
      - port: 8080
        protocol: TCP
        targetPort: 8080
      selector:
        app: mldong-admin
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: mldong-admin
      namespace: mldong-probe
    spec:
      selector:
        matchLabels:
          app: mldong-admin
      replicas: 1
      template:
        metadata:
          labels:
            app: mldong-admin
        spec:
          containers:
            - name: mldong-admin
              env:
                - name: TZ
                  value: Asia/Shanghai
              image: registry-vpc.cn-zhangjiakou.aliyuncs.com/mldong/java/mldong-admin:202007220017_c608761
              readinessProbe:
                  tcpSocket:
                    port: 8080     # 服务对应的端口
                  initialDelaySeconds: 15  #第一次健康检查的时间
                  periodSeconds: 5    #检查周期
                  timeoutSeconds: 5   #检查超时时间
                  successThreshold: 1 #成功次数判定成功
                  failureThreshold: 1 #失败次数判定失败
              imagePullPolicy: IfNotPresent
              ports:
                - containerPort: 8080
                  name: port
                  protocol: TCP
              command: ["/bin/sh"]
              args: ["-c", "set -e && java -jar app.jar --spring.profiles.active=test --server.port=8080"]
    ---
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      annotations:
      name: mldong-admin-ingress
      namespace: mldong-probe
    spec:
      rules:
        - host: a.test.com
          http:
            paths:
              - backend:
                  serviceName: mldong-admin
                  servicePort: 8080
                path: /
    EOF
    
  2. /etc/hosts新增一行,即1中配置的host

  3. 另外开启一个终端,执行如下命令

    for a in {1..30};do curl http://a.test.com/api/login;date +"%Y%m%d%H%M%S";sleep 2;done;
    
  4. 旧终端执行如下命令

    kubectl apply -f v2.yaml
    

    效果如下:

    开始服务不存在,访问的是nginx-ingress的默认服务。服务正常启动后,接口访问正常。这里只是演示单个服务启动的样例。其实在我们做系统版本升级的时候也是这样子的。比如现在服务版本1要升级到服务版本2,我们只需要修改一下镜像的版本,然后再执行kubectl apply -f k8s.yaml。这样新的服务版本就可以做到不停服更新了。

最后清一下空间

kubectl delete -f v2.yaml

项目源码地址

  • 后端

https://gitee.com/mldong/mldong

  • 前端

https://gitee.com/mldong/mldong-vue

小结

本文简单介绍了k8s的健康检查-存活探针与就绪探针,并简单地做了一下案例演示。使用存活探针与就绪探针的目的都是为了让我们的服务更健壮。大家可以根据自身服务的情况去选择使用哪一种类型的探针和检查方式。据说Spring Boot 2.3提供K8s活性和就绪性探针,感兴趣的同学可以去了解一下。

Logo

前往低代码交流专区

更多推荐