Applications d’auto-guérison avec Kubernetes et Spring Boot

1. Introduction

Dans ce tutoriel, nous allons parler des https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/de KKernernetes .[ sondes ]et montrons comment nous pouvons exploiter le HealthIndicator.html[ HealthIndicator pour avoir une vue précise de l’état de notre application.

Pour les besoins de ce didacticiel, nous allons utiliser une expérience pré-existante avec Spring Boot Actuator , Kubernetes et Docker .

2. Kubernetes sondes

Kubernetes définit deux sondes différentes que nous pouvons utiliser pour vérifier périodiquement si tout fonctionne comme prévu: liveness et readiness .

2.1. Vive et Disponibilité

  • Avec les sondes Liveness et Readiness , Kubelet peut agir dès qu’il détecte que quelque chose ne va pas et réduit les temps d’arrêt de notre application. **

Les deux sont configurés de la même manière, mais ils ont une sémantique différente et Kubelet effectue différentes actions en fonction de celle qui est déclenchée:

  • Readiness - Readiness vérifie si notre Pod est prêt à démarrer

recevoir du trafic. Notre Pod est prêt lorsque tous ses conteneurs sont prêt ** Liveness - Contrairement à leadiness , liveness vérifie si notre Pod

devrait être redémarré. Il peut détecter les cas d’utilisation où notre application est en cours d’exécution mais dans un état où elle est incapable de progresser. par exemple, il est dans l’impasse

Nous configurons les deux types de sonde au niveau du conteneur:

apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  containers:
  - name: goproxy
    image: k8s.gcr.io/goproxy:0.1
    ports:
    - containerPort: 8080
    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 10
      timeoutSeconds: 2
      failureThreshold: 1
      successThreshold: 1
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 20
      timeoutSeconds: 2
      failureThreshold: 1
      successThreshold: 1

Nous pouvons configurer un certain nombre de champs pour contrôler plus précisément le comportement de nos sondes:

  • initialDelaySeconds - Après avoir créé le conteneur, ** attend

_n secondes avant d’initier la vérification periodSeconds_ - À quelle fréquence cette analyse doit-elle être exécutée , par défaut

10 secondes; le minimum est de 1 seconde timeoutSeconds - Combien de temps attendons-nous ** avant de temporiser la sonde,

défaut à 1 seconde; le minimum est encore 1 seconde failureThreshold - Essayez n fois avant d’abandonner ** . Dans le cas

de la lecture, notre pod sera marqué comme non prêt, alors que d’abandonner dans le cas de liveness signifie le redémarrage du Pod . Le défaut ici est 3 échecs, avec le minimum étant 1 successThreshold - Ceci est le nombre minimum de visites consécutives

succès pour que la sonde soit considérée comme ayant réussi après un échec ** . La valeur par défaut est 1 succès et son minimum est 1

Dans ce cas , nous avons opté pour une sonde tcp , mais il existe d’autres types de sondes que nous pouvons également utiliser.

2.2. Sonde t ypes

Selon notre cas d’utilisation, un type de sonde peut s’avérer plus utile qu’un autre. Par exemple, si notre conteneur est un serveur Web, l’utilisation d’une sonde http pourrait être plus fiable qu’une sonde tcp .

Heureusement, Kubernetes a trois types de sondes que nous pouvons utiliser:

  • exec - Exécute les instructions bash dans notre conteneur . Pour

Par exemple, vérifiez qu’un fichier spécifique existe. Si l’instruction renvoie un code d’échec, la sonde échoue tcpSocket - Essaie d’établir une connexion tcp au conteneur,

en utilisant le port spécifié . Si la connexion n’est pas établie, le sonde échoue httpGet - Envoie une requête HTTP GET au serveur en cours d’exécution

dans le conteneur et en écoutant sur le port spécifié. Tout code supérieur ou égal à 200 et inférieur à 400 indique un succès

Il est important de noter que les sondes HTTP ont des champs supplémentaires, en plus de ceux mentionnés précédemment:

  • hôte - Nom d’hôte auquel se connecter, valeur par défaut de l’IP de notre pod

  • scheme - Schéma à utiliser pour se connecter, HTTP ou HTTPS ,

avec le défaut étant HTTP path ** - Le chemin d’accès au serveur Web

  • httpHeaders - En-têtes personnalisés à définir dans la demande

  • port - Nom ou numéro du port auquel accéder dans le conteneur

3. Printemps Actionneur et Kubernetes Auto-guérison Capacités

Maintenant que nous avons une idée générale sur la façon dont Kubernetes__ est capable de détecter si notre application est dans un état défectueux, voyons comment nous pouvons tirer avantage de l’Acteur un œil attentif non seulement sur notre application mais aussi sur ses dépendances! **

Aux fins de ces exemples, nous allons utiliser Minikube .

3.1. Actionneur et son __HealthIndicator __s

Considérant que Spring a un certain nombre de __HealthIndicator s prêts à être utilisés, refléter l’état de certaines dépendances de notre application par rapport aux sondes de Kubernetes est aussi simple que d’ajouter le Actuator dépendance à notre pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

3.2. Exemple de vivacité

Commençons par une application qui démarrera normalement et après 30 secondes sera transition en a cassé état .

Nous allons émuler un état défaillant en creating un HealthIndicator qui vérifie si une variable boolean est true .

Nous initialiserons la variable à true , puis nous planifierons une tâche pour la remplacer par false après 30 secondes:

@Component
public class CustomHealthIndicator implements HealthIndicator {

    private boolean isHealthy = true;

    public CustomHealthIndicator() {
        ScheduledExecutorService scheduled =
          Executors.newSingleThreadScheduledExecutor();
        scheduled.schedule(() -> {
            isHealthy = false;
        }, 30, TimeUnit.SECONDS);
    }

    @Override
    public Health health() {
        return isHealthy ? Health.up().build() : Health.down().build();
    }
}

Avec notre HealthIndicator en place, nous devons dockerize notre application:

FROM openjdk:8-jdk-alpine
RUN mkdir -p/usr/opt/service
COPY target/** .jar/usr/opt/service/service.jar
EXPOSE 8080
ENTRYPOINT exec java -jar/usr/opt/service/service.jar

Ensuite, nous créons notre template Kubernetes :

apiVersion: apps/v1
kind: Deployment
metadata:
  name: liveness-example
spec:
  ...
    spec:
      containers:
      - name: liveness-example
        image: dbdock/liveness-example:1.0.0
        ...
        readinessProbe:
          httpGet:
            path:/health
            port: 8080
          initialDelaySeconds: 10
          timeoutSeconds: 2
          periodSeconds: 3
          failureThreshold: 1
        livenessProbe:
          httpGet:
            path:/health
            port: 8080
          initialDelaySeconds: 20
          timeoutSeconds: 2
          periodSeconds: 8
          failureThreshold: 1
  • Nous utilisons une sonde httpGet pointant vers le point de terminaison de la santé de l’actionneur. ** Toute modification de l’état de notre application (et de ses dépendances) se reflétera sur la salubrité de notre déploiement.

Après avoir déployé notre application sur Kubernetes , nous pourrons voir les deux sondes en action: après environ 30 secondes, notre Pod sera marqué comme non prêt et retiré de la rotation; Quelques secondes plus tard, le Pod est redémarré.

Nous pouvons voir les événements de notre Pod exécutant kubectl describe pod liveness-example :

Warning  Unhealthy 3s (x2 over 7s)   kubelet, minikube  Readiness probe failed: HTTP probe failed ...
Warning  Unhealthy 1s                kubelet, minikube  Liveness probe failed: HTTP probe failed ...
Normal   Killing   0s                kubelet, minikube  Killing container with id ...

3.3. Exemple de préparation

Dans l’exemple précédent, nous avons vu comment utiliser un HealthIndicator pour refléter l’état de notre application sur la santé d’un déploiement de Kubernetes .

Utilisons-le sur un cas d’utilisation différent: supposons que notre application a besoin de un bit de heure avant il peut recevoir le trafic . Par exemple, il doit charger un fichier en mémoire et valider son contenu.

C’est un bon exemple du moment où nous pouvons tirer parti d’une sonde readiness .

Modifions les modèles HealthIndicator et Kubernetes à partir de l’exemple précédent et adaptons-les à ce cas d’utilisation:

@Component
public class CustomHealthIndicator implements HealthIndicator {

    private boolean isHealthy = false;

    public CustomHealthIndicator() {
        ScheduledExecutorService scheduled =
          Executors.newSingleThreadScheduledExecutor();
        scheduled.schedule(() -> {
            isHealthy = true;
        }, 40, TimeUnit.SECONDS);
    }

    @Override
    public Health health() {
        return isHealthy ? Health.up().build() : Health.down().build();
    }
}

Nous initialisons la variable à false et, après 40 secondes, une tâche s’exécutera et la définissons sur true.

Ensuite, nous dockérisons et déployons notre application en utilisant le modèle suivant:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: readiness-example
spec:
  ...
    spec:
      containers:
      - name: readiness-example
        image: dbdock/readiness-example:1.0.0
        ...
        readinessProbe:
          httpGet:
            path:/health
            port: 8080
          initialDelaySeconds: 40
          timeoutSeconds: 2
          periodSeconds: 3
          failureThreshold: 2
        livenessProbe:
          httpGet:
            path:/health
            port: 8080
          initialDelaySeconds: 100
          timeoutSeconds: 2
          periodSeconds: 8
          failureThreshold: 1

Bien que similaires, il y a quelques changements dans la configuration des sondes que nous devons signaler:

  • Puisque nous savons que notre application a besoin d’environ 40 secondes pour devenir

prêt à recevoir du trafic, nous avons augmenté le initialDelaySeconds de notre sonde readiness à 40 secondes De même, nous avons augmenté le initialDelaySeconds ** de

notre liveness sonde jusqu’à 100 secondes pour éviter d’être tué prématurément par Kubernetes

S’il n’a toujours pas terminé après 40 secondes, il lui reste environ 60 secondes pour terminer. Après cela, notre sonde liveness va démarrer et redémarrer le Pod.

4. Conclusion

Dans cet article, nous avons parlé des sondes Kubernetes et de la façon dont nous pouvons utiliser l’actionneur de Spring pour améliorer la surveillance de l’état de nos applications.

La mise en œuvre complète de ces exemples est disponible à l’adresse over sur Github .