Перейти к основному содержимому

Самовосстанавливающиеся приложения с Kubernetes и Spring Boot

· 7 мин. чтения

1. Введение

В этом руководстве мы поговорим о зондах Kubernetes и продемонстрируем, как мы можем использовать HealthIndicator Actuator для получения точного представления о состоянии нашего приложения. **** **[](https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/actuate/health/HealthIndicator.html)**

Для целей этого руководства мы предполагаем, что у нас уже есть опыт работы с Spring Boot Actuator , Kubernetes и Docker .

2. Зонды Kubernetes

Kubernetes определяет два разных зонда, которые мы можем использовать для периодической проверки, все ли работает должным образом: живость и готовность .

2.1. Живучесть и готовность

Благодаря зондам Liveness и Readiness Kubelet может действовать, как только обнаружит, что что-то не так, и свести к минимуму время простоя нашего приложения.

Оба настроены одинаково, но имеют разную семантику, и Kubelet выполняет разные действия в зависимости от того, какое из них срабатывает:

  • ГотовностьГотовность проверяет , готов ли наш модуль начать получать трафик . Наш Pod готов , когда готовы все его контейнеры.
  • Живучесть — в отличие от готовности , живость проверяет, следует ли перезапустить наш под . Он может выбрать варианты использования, когда наше приложение работает, но находится в состоянии, когда оно не может развиваться; например, он в тупике

Мы настраиваем оба типа зонда на уровне контейнера:

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

Есть ряд полей, которые мы можем настроить для более точного управления поведением наших зондов:

  • initialDelaySeconds — после создания контейнера подождите n секунд, прежде чем запускать зонд.
  • PeriodSecondsкак часто следует запускать этот зонд , по умолчанию 10 секунд; минимум 1 секунда
  • timeoutSecondsкак долго мы ждем , прежде чем истечет время проверки, по умолчанию 1 секунда; минимум снова 1 секунда
  • failureThreshold — попробуйте n раз, прежде чем сдаться . В случае готовности наш под будет помечен как не готовый, тогда как отказ в случае живости означает перезапуск пода . По умолчанию здесь 3 сбоя, минимум 1.
  • SuccessThreshold — это минимальное количество последовательных успешных попыток проверки, которые считаются успешными после неудачной попытки . По умолчанию он равен 1 успеху, и его минимум также равен 1.

В данном случае мы выбрали TCP - зонд, однако мы можем использовать и другие типы зондов.

2.2. Типы зондов

В зависимости от нашего варианта использования один тип зонда может оказаться более полезным, чем другой. Например, если наш контейнер представляет собой веб-сервер, использование пробы http может быть более надежным, чем проба tcp .

К счастью, в Kubernetes есть три разных типа зондов, которые мы можем использовать:

  • execвыполняет инструкции bash в нашем контейнере . Например, проверьте, существует ли определенный файл. Если инструкция возвращает код ошибки, зонд не работает.
  • tcpSocket — пытается установить tcp -соединение с контейнером, используя указанный порт . Если не удается установить соединение, зонд не работает.
  • httpGetотправляет HTTP-запрос GET на сервер , работающий в контейнере и прослушивающий указанный порт. Любой код больше или равен 200 и меньше 400 указывает на успех

Важно отметить, что HTTP - запросы имеют дополнительные поля, помимо тех, о которых мы упоминали ранее:

  • host — имя хоста для подключения, по умолчанию IP-адрес нашего модуля.
  • схема — схема, которая должна использоваться для подключения, HTTP или HTTPS , по умолчанию — HTTP .
  • path — путь доступа на веб-сервере
  • httpHeaders — настраиваемые заголовки для установки в запросе.
  • port — имя или номер порта для доступа в контейнере

3. Spring Actuator и возможности самовосстановления Kubernetes

Теперь, когда у нас есть общее представление о том, как Kubernetes может определить, находится ли наше приложение в неисправном состоянии , давайте посмотрим, как мы можем использовать Actuator Spring, чтобы внимательно следить не только за нашим приложением, но и за его зависимостями! ** ** `` ****

В этих примерах мы будем полагаться на Minikube .

3.1. Актуатор и его индикаторы работоспособности

Учитывая, что Spring имеет ряд готовых к использованию HealthIndicator , отражение состояния некоторых зависимостей нашего приложения от зондов Kubernetes так же просто, как добавление зависимости Actuator в наш pom.xml:

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

3.2. Пример живости

Начнем с приложения, которое загрузится нормально и через 30 секунд перейдет в неисправное состояние . ** ** ** **

Мы собираемся эмулировать неисправное состояние, создав HealthIndicator , который проверяет, является ли логическая переменная истинной . Мы инициализируем переменную значением true , а затем запланируем задачу, чтобы изменить ее на значение false через 30 секунд:

@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();
}
}

Имея HealthIndicator , нам нужно докеризовать наше приложение:

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

Далее мы создаем наш шаблон 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

Мы используем зонд httpGet , указывающий на конечную точку работоспособности Actuator . Любое изменение состояния нашего приложения (и его зависимостей) будет отражаться на работоспособности нашего развертывания.

После развертывания нашего приложения в Kubernetes мы сможем увидеть оба зонда в действии: примерно через 30 секунд наш Pod будет помечен как неготовый и снят с ротации; через несколько секунд Pod перезапускается.

Мы можем видеть, как события нашего модуля, выполняющего kubectl , описывают 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. Пример готовности

В предыдущем примере мы увидели, как можно использовать HealthIndicator для отражения состояния нашего приложения в отношении работоспособности развертывания Kubernetes .

Давайте использовать его в другом случае: предположим, что нашему приложению требуется некоторое время , прежде чем оно сможет получать трафик . Например, ему нужно загрузить файл в память и проверить его содержимое. ** ** ** ** ** ** ****

Это хороший пример того, когда мы можем воспользоваться проверкой готовности .

Давайте изменим шаблон HealthIndicator и Kubernetes из предыдущего примера и адаптируем их к этому варианту использования:

@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();
}
}

Мы инициализируем переменную значением false , и через 40 секунд будет выполнена задача, установившая для нее значение true.

Затем мы докеризуем и развертываем наше приложение, используя следующий шаблон:

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

Несмотря на схожесть, в конфигурации датчиков есть несколько изменений, на которые следует обратить внимание:

  • Поскольку мы знаем, что нашему приложению требуется около 40 секунд, чтобы подготовиться к приему трафика, мы увеличили initialDelaySeconds нашего теста готовности до 40 секунд .
  • Точно так же мы увеличили initialDelaySeconds нашего теста живучести до 100 секунд, чтобы избежать преждевременного уничтожения Kubernetes . ``

Если он все еще не закончился через 40 секунд, у него еще есть около 60 секунд, чтобы закончить. После этого наш зонд живости сработает и перезапустит под.

4. Вывод

В этой статье мы говорили о зондах Kubernetes и о том, как мы можем использовать Spring Actuator для улучшения мониторинга работоспособности нашего приложения.

Полную реализацию этих примеров можно найти на Github .