Liveness and Readiness Probes
1. Overview
Neste tutorial, veremos como o Spring Boot 2.3 se integra aos testes do Kubernetes para criar uma experiência nativa da nuvem ainda mais agradável.
Primeiro, começaremos com um pouco de fundo sobre as sondas do Kubernetes. Em seguida, vamos trocar de marcha e ver como o Spring Boot 2.3 suporta essas sondas.
2. Kubernetes Probes
Ao usar o Kubernetes como nossa plataforma de orquestração, o kubelet em cada nó é responsável por manter os pods nesse nó saudáveis.
Por exemplo, às vezes nossos aplicativos podem precisar de um pouco de tempo antes de serem capazes de aceitar solicitações. O kubelet pode garantir que o aplicativo receba solicitações somente quando estiver pronto. Além disso, se o processo principal de um pod falhar por qualquer motivo, o kubelet reiniciará o contêiner.
Para cumprir essas responsabilidades, o Kubernetes tem duas sondas: sondas de vivacidade e sondas de prontidão.
O kubelet usará o teste de prontidão para determinar quando o aplicativo está pronto para aceitar solicitações. Mais especificamente, um pod está pronto quando todos os seus contêineres estiverem prontos.
Da mesma forma, o kubelet pode verificar se uma cápsula ainda está viva através de sondas vivas. Basicamente, a sonda de vivacidade ajuda o kubelet a saber quando ele deve reiniciar um contêiner.
Agora que estamos familiarizados com os conceitos, vamos ver como funciona a integração do Spring Boot.
3. Liveness and Readiness in Actuator
A partir do Spring Boot 2.3, as classes LivenessStateHealthIndicator e ReadinessStateHealthIndicator exporão o estado de disponibilidade e prontidão do aplicativo. Quando implantamos nosso aplicativo no Kubernetes, o Spring Boot registra automaticamente esses indicadores de integridade.
Como resultado, podemos usar /actuator/health/liveness e /actuator/health/readiness endpoints como nossas sondas de vida e prontidão, respectivamente.
Por exemplo, podemos adicioná-los à nossa definição de pod para configurar o teste de vivacidade como uma solicitação HTTP GET:
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 3
periodSeconds: 3
Copy
Normalmente, deixamos que a Spring Boot decida quando levantar essas sondas para nós. Mas, se quisermos, podemos habilitá-los manualmente em nosso application.properties.
Se estivermos trabalhando com o Spring Boot 2.3.0 ou 2.3.1, podemos habilitar os testes mencionados através de uma propriedade de configuração:
management.health.probes.enabled=true
Copy
No entanto, desde o Spring Boot 2.3.2, essa propriedade foi preterida devido à confusão de configuração.
Se trabalharmos com o Spring Boot 2.3.2, podemos usar as novas propriedades para habilitar testes de vivacidade e prontidão:
management.endpoint.health.probes.enabled=true
management.health.livenessState.enabled=true
management.health.readinessState.enabled=true
Copy
3.1. Readiness and Liveness State Transitions
O Spring Boot usa dois enums para encapsular diferentes estados de prontidão e vivacidade. Para o estado de prontidão, há um enum chamado ReadinessState com os seguintes valores:
- O estado ACCEPTING_TRAFFIC representa que o aplicativo está pronto para aceitar tráfego
- O estado REFUSING_TRAFFIC significa que o aplicativo ainda não está disposto a aceitar nenhuma solicitação
Da mesma forma, o enum LivenessState representa o estado de vivacidade do aplicativo com dois valores:
- O valor CORRETO significa que o aplicativo está em execução e seu estado interno está correto
- Por outro lado, o valor BROKEN significa que o aplicativo está sendo executado com algumas falhas fatais
Veja como o estado de prontidão e vivacidade muda em termos de eventos do ciclo de vida do aplicativo no Spring:
- Registrando ouvintes e inicializadores
- Preparando o Ambiente
- Preparando o ApplicationContext
- Carregando definições de bean
- Alterando o estado de vivacidade para CORRETO
- Chamando o aplicativo e os executores de linha de comando
- Alterando o estado de prontidão para ACCEPTING_TRAFFIC
Quando o aplicativo estiver em funcionamento, nós (e o próprio Spring) podemos alterar esses estados publicando AvailabilityChangeEvents apropriado.
4. Managing the Application Availability
Os componentes do aplicativo podem recuperar o estado atual de prontidão e vivacidade injetando a interface ApplicationAvailability:
@Autowired private ApplicationAvailability applicationAvailability;
Copy
Então podemos usá-lo da seguinte maneira:
assertThat(applicationAvailability.getLivenessState())
.isEqualTo(LivenessState.CORRECT);
assertThat(applicationAvailability.getReadinessState())
.isEqualTo(ReadinessState.ACCEPTING_TRAFFIC);
assertThat(applicationAvailability.getState(ReadinessState.class))
.isEqualTo(ReadinessState.ACCEPTING_TRAFFIC);
Copy
4.1. Updating the Availability State
Também podemos atualizar o estado do aplicativo publicando um evento AvailabilityChangeEvent:
assertThat(applicationAvailability.getLivenessState())
.isEqualTo(LivenessState.CORRECT);
mockMvc.perform(get("/actuator/health/liveness"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value("UP"));
AvailabilityChangeEvent.publish(context, LivenessState.BROKEN);
assertThat(applicationAvailability.getLivenessState())
.isEqualTo(LivenessState.BROKEN);
mockMvc.perform(get("/actuator/health/liveness"))
.andExpect(status().isServiceUnavailable())
.andExpect(jsonPath("$.status").value("DOWN"));
Copy
Como mostrado acima, antes de publicar qualquer evento, o ponto de extremidade /actuator/health/liveness retorna uma resposta 200 OK com o seguinte JSON:
{
"status": "OK"
}
Copy
Em seguida, depois de quebrar o estado de disponibilidade, o mesmo ponto de extremidade retorna uma resposta indisponível de serviço 503 com o seguinte JSON:
{
"status": "DOWN"
}
Copy
Quando mudamos para um estado de prontidão de REFUSING_TRAFFIC, o valor de status será OUT_OF_SERVICE:
assertThat(applicationAvailability.getReadinessState())
.isEqualTo(ReadinessState.ACCEPTING_TRAFFIC);
mockMvc.perform(get("/actuator/health/readiness"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value("UP"));
AvailabilityChangeEvent.publish(context, ReadinessState.REFUSING_TRAFFIC);
assertThat(applicationAvailability.getReadinessState())
.isEqualTo(ReadinessState.REFUSING_TRAFFIC);
mockMvc.perform(get("/actuator/health/readiness"))
.andExpect(status().isServiceUnavailable())
.andExpect(jsonPath("$.status").value("OUT_OF_SERVICE"));
Copy
4.2. Listening to a Change
Podemos registrar ouvintes de eventos para serem notificados quando um estado de disponibilidade do aplicativo for alterado:
@Component
public class LivenessEventListener {
@EventListener
public void onEvent(AvailabilityChangeEvent<LivenessState> event) {
switch (event.getState()) {
case BROKEN:
// notify others
break;
case CORRECT:
// we're back
}
}
}
Copy
Aqui estamos ouvindo qualquer mudança no estado de vida do aplicativo.
5. Auto-Configurations
Antes de concluir, vamos ver como o Spring Boot configura automaticamente esses testes em implantações do Kubernetes. A classe AvailabilityProbesAutoConfiguration é responsável por registrar condicionalmente os testes de disponibilidade e prontidão.
De fato, há uma condição especial que registra as sondas quando uma das seguintes situações é verdadeira:
- Kubernetes é o ambiente de implantação
- A propriedade management.health.probes.enabled é definida como true
Quando um aplicativo atende a qualquer uma dessas condições, a configuração automática registra beans de LivenessStateHealthIndicator e ReadinessStateHealthIndicator.
6. Conclusion
Neste artigo, vimos como podemos usar o Spring Boot fornece dois testes de integridade para integração com o Kubernetes.