一、容器探针

k8s中probe 是由 kubelet 对容器执行的定期诊断。 要执行诊断,kubelet 既可以在容器内执行代码,也可以发出一个网络请求。

检查机制

使用探针来检查容器有四种不同的方法。 每个探针都必须准确定义为这四种机制中的一种:

exec:在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。

grpc:使用 gRPC 执行一个远程过程调用。 目标应该实现 gRPC健康检查。 如果响应的状态是 "SERVING",则认为诊断成功。 gRPC 探针是一个 alpha 特性,只有在你启用了 "GRPCContainerProbe" 特性门控时才能使用。

httpGet对容器的 IP 地址上指定端口和路径执行 HTTP GET 请求。如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的。

tcpSocket对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开,则诊断被认为是成功的。 如果远程系统(容器)在打开连接后立即将其关闭,这算作是健康的。

探测结果

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

Success(成功)容器通过了诊断。

Failure(失败)容器未通过诊断。

Unknown(未知)诊断失败,因此不会采取任何行动

探测类型

针对运行中的容器,kubelet 可以选择是否执行以下三种探针,以及如何针对探测结果作出反应:

livenessProbe:指示容器是否正在运行。如果存活态探测失败,则 kubelet 会杀死容器, 并且容器将根据其重启策略决定未来。如果容器不提供存活探针, 则默认状态为 Success

readinessProbe:指示容器是否准备好为请求提供服务。如果就绪态探测失败, 端点控制器将从与 Pod 匹配的所有服务的端点列表中删除该 Pod 的 IP 地址。 初始延迟之前的就绪态的状态值默认为 Failure。 如果容器不提供就绪态探针,则默认状态为 Success

startupProbe指示容器中的应用是否已经启动。如果提供了启动探针,则所有其他探针都会被 禁用,直到此探针成功为止。如果启动探测失败,kubelet 将杀死容器,而容器依其 重启策略进行重启。 如果容器没有提供启动探测,则默认状态为 Success

二、通过探针优雅的解决服务部署过程中注册中心服务平滑过渡问题

        我们后端采用Spring Cloud(Spring Cloud Alibaba)微服务结构技术路线进行开发,采用nacos作为注册中心。而服务注册到nacos是需要时间的,经过测试从应用启动注册到注册中心大约在1分钟左右,而一般的容器探测只是探测服务是否达到了可用的状态,没有采用注册中心的这种服务自然是一般的就绪性探测或者存活性探测或者启动探测都可以实现。这种探测在于即使探测服务可用了,但是并没有及时注册进注册中心中,若是只有一个pod的情况下,在容器探测成功后,新的pod变成了可用状态了,便停掉了第一个pod,但是这个时候服务并没有注册到注册中心,就造成了服务短暂不可用。

SpringBoot的actuator

其实actuator是用来帮助用户监控和操作SprinBoot应用的,这些监控和操作都可以通过http请求实现,如下图,http://localhost:7777/actuator/health地址返回的是应用的健康状态

kubernetes推荐的内核版本 kubernetes prow_容器探针

 需引以下maven:

<!-- 引入Actuator监控依赖 -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

在SpringBoot-2.3版本中,actuator新增了两个地址:/actuator/health/liveness和/actuator/health/readiness,前者用作kubernetes的存活探针,后者用作kubernetes的就绪探针,需要先在配置文件中开启,如下:

management:
  endpoint:
    health:
      probes:
        enabled: true
  health:
    livenessstate:
      enabled: true
    readinessstate:
      enabled: true

kubernetes推荐的内核版本 kubernetes prow_大数据_02

 利用SpringBoot的接口来作为容器探针的健康检测,按照如下就可:

readinessProbe:
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
httpGet:
  scheme: HTTP
  port: 9999
  path: /actuator/health/readiness
livenessProbe:
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
httpGet:
  scheme: HTTP
  port: 9999
  path: /actuator/health/liveness

自己编写容器探针接口

/**
 * @author BD-PC40
 * @version 1.0
 * @description: 容器探针接口,用来进行探测服务是否注册进入注册中心
 * @date 2022/7/12 10:58
 */
@Slf4j
@RestController
@RequestMapping("nacos")
public class HealthController {
    @Autowired
    private DiscoveryClient discoveryClient;

    @GetMapping("/health/{services}")
    public ResponseEntity<Object> getService(@PathVariable("services")String services) throws NacosException {
        //从nacos中根据serverId获取实例 方法一(采用此方法,DiscoveryClient 代表的就是:服务发现操作对象)
        if (StringUtils.isBlank(services)){
            return new ResponseEntity<Object>(HttpStatus.BAD_REQUEST);
        }
        List<ServiceInstance> instances = discoveryClient.getInstances(services);
        Map<String, Integer> ipMap = new HashMap<>();
        if (instances.size() > 0){
            instances.forEach(key ->{
                ipMap.put(key.getHost(), key.getPort());
            });
        }
        //从nacos中根据serverId获取实例 方法二 采用nacos的java的SDK
//        Properties properties = new Properties();
//        properties.put("serverAddr", "192.168.4.25:8849");
//        properties.put("namespace", "caseretrieval-dev");
//        NamingService naming = NamingFactory.createNamingService(properties);
//        List<Instance> instancesList = naming.selectInstances("case-gateway", true);
        //从nacos中根据serverId获取实例 方法三,采用nacos的OPEN-api
        //http://192.168.4.25:8849/nacos/v1/ns/instance/list?serviceName=case-gateway&namespaceId=caseretrieval-dev
        //获取本机IP地址
        String hostAddress = "127.0.0.1";
        try {
            InetAddress localHost = InetAddress.getLocalHost();
            hostAddress = localHost.getHostAddress();
            log.info("当前服务本机ip是:"+hostAddress);
        } catch (UnknownHostException e) {
            log.error(e.getMessage(), e);
        }
        //查看本机服务是否已经注册到nacos中
        if (ipMap.containsKey(hostAddress)){
            return new ResponseEntity<Object>(HttpStatus.OK);
        }else {
            return new ResponseEntity<Object>(HttpStatus.SERVICE_UNAVAILABLE);
        }
    }
}