Статьи

Почему здоровье весны падает, вниз, вверх, вверх, вверх и вниз снова?

Зачем

Наше новое клиентское приложение JavaScript регулярно вызывает конечную точку /health нашего сервера Grails для определения состояния офлайн. Все стало «смешным» с этим.

Эту конечную точку мы получаем бесплатно , поскольку Grails основан на Spring Boot, который поставляется с подпроектом Spring Boot Actuator.

Это дает нам множество конечных точек, которые позволяют нам отслеживать и взаимодействовать с нашим приложением, включая /health который возвращает информацию о здоровье.

Итак, наш JS-клиент проверяет, может ли он достичь этой конечной точки /health , выполняемой каждые несколько секунд, чтобы определить, включен ли пользователь или нет. Ничего особенного, и мы могли бы позже переключиться на использование домашней страницы Google или чего-то еще, но пока это работает.

Сбой проверки здоровья

На localhost все всегда выглядит нормально, но как только я получил конвейер Jenkins, наконец, для развертывания приложения на наших тестовых серверах после каждой сборки, и мы начали проверять приложение там, все стало забавно.

Обычно у нас была серия совершенно хороших звонков.

1
2
3
4
GET https://tst.example.com/health 200 ()
GET https://tst.example.com/health 200 ()
GET https://tst.example.com/health 200 ()
etc

В других случаях каждые несколько секунд мы видели ошибки, накапливающиеся в Chrome Inspector. Проверка работоспособности завершится неудачно с кодом состояния HTTP 503 Service unavailable в течение длительного времени.

1
2
3
4
GET https://tst.example.com/health 503 ()
GET https://tst.example.com/health 503 ()
GET https://tst.example.com/health 503 ()
etc

Затем через некоторое время мы снова получим хорошие звонки!

1
2
3
GET https://tst.example.com/health 200 ()
GET https://tst.example.com/health 200 ()
etc

Ответ на эти неудачные запросы только что сказал

1
{"status":"DOWN"}

Это — по замыслу — не очень наглядно.

Я, конечно, сам не писал никаких показателей здоровья, так почему это было бы «вниз»?

Опытные Spring Booters знают, что он подхватит любой индикатор здоровья на пути к классам и по умолчанию поставляется с несколькими. Какие из них на самом деле используются, может быть загадкой, потому что по умолчанию эта конечная точка классифицируется Spring Boot как «чувствительная» и, таким образом, не предоставляет слишком много информации внешнему миру.

Мне пришлось сделать проверку работоспособности немного более «болтливой», установив следующие настройки:

1
endpoints.health.sensitive: false

Теперь вызов конечной точки вручную выявил соперников!

01
02
03
04
05
06
07
08
09
10
11
12
13
14
{
  "status":"DOWN",
  "diskSpace":{
    "status":"DOWN",
    "total":8579448832,
    "free":20480,
    "threshold":10485760
  },
  "db":{
    "status":"UP",
    "database":"H2",
    "hello":1
  }
}

Общий статус «вниз» является совокупным результатом (в данном случае: 2) автоматически настроенных индикаторов состояния, перечисленных прямо сейчас.

Что сразу пришло в голову, когда я увидел это:

  • Почему я еще не удалил H2
  • Эй, на тестовом сервере уже не хватает места ?!

База данных H2 является зависимостью по умолчанию в любом приложении Grails, но наше приложение не использует ее — ни в производстве, ни для тестирования — поэтому мы определенно удалим ее из зависимостей. Это меньше волнует.

Что касается дискового пространства, это хороший DiskSpaceHealthIndicator (действительно, часть автоматически настраиваемых индикаторов ), который говорит мне, что все нездорово .

По умолчанию установлен порог 10485760 байт или 10 МБ — минимальное дисковое пространство, которое должно быть доступно.

И … есть только 20 КБ свободного места? Всего 8 концертов.

Это довольно низкое число

В первые 0,7 секунды я не поверил индикатору здоровья, представляете?

Таким образом, я отправил SSH на тестовый сервер, чтобы проверить доступное дисковое пространство с помощью утилиты df :

1
2
3
4
[Ted@server-01t ~]$ df -h
Filesystem             Size  Used Avail Use% Mounted on
/dev/mapper/rhel-root  8.0G  8.0G   20K 100% /
...

Правильно, по крайней мере, проверка здоровья говорит правду там: на самом деле осталось совсем немного места.

Я передал это своему коллеге по ИТ, который предоставил эту машину для расследования. Казалось, что уже были некоторые дампы Java-кучи от более ранних экспериментов, занимающих место — что, как мне сказали, будет удалено как можно скорее.

Лучше проверьте и другой узел.

1
2
3
[Ted@server-02t ~]$ df -h
Filesystem             Size  Used Avail Use% Mounted on
/dev/mapper/rhel-root  8.0G  5.3G  2.8G  66% /

Там достаточно места.

Подожди минуту? «Другой узел?» Да, у нас есть 2 тестовых сервера, 01t и 02t .

В этот момент я понял: поведение, которое я видел, было связано с тем, что loadbalancer перенаправил запрос на tst.example.com либо на server-01t либо на другой server-02t . Одной из них было недостаточно места на диске, что объясняет, что индикатор работоспособности приложения Grails на этом сервере говорит «не работает», что приводит к HTTP 503.

При наблюдении за этими вызовами состояния здоровья (которые постоянно отправляются нашим клиентом JS) через Chrome Inspector остался один маленький вопрос: почему у нас есть полоса (иногда 50х) «взлетов» ( 200 ), а затем куча «спадов» ”( 503 ) тогда в, казалось бы, случайном порядке?

Балансировщик нагрузки должен держать нас «фиксированными» на том узле, где клиент JS впервые делает свои запросы, так как мы настраиваем наши серверы таким образом.

Если бы loadbalancer отправлял каждый запрос (на tst.example.com ) циклически на сервер 1 или 2, я ожидал бы более (случайный) ответ, например, «вверх» , «вниз» , «вниз» , «вверх» , «Вниз» , «вверх» , «вверх» , «вниз» , «вверх» .

Что ж, казалось, что во время окна, пока я наблюдал за этим поведением, остальная часть команды все еще разрабатывала функции и… подталкивала к Git, который Дженкинс берет, который развертывается на обоих серверах. Из-за повторного развертывания приложения для последовательной передачи на сервер loadbalancer «видит» недоступность приложения на одном сервере (при наличии достаточного дискового пространства: «вверх» , «вверх» , «вверх» , «вверх» , «вверх»). » ) На время развертывания и перенаправляет трафик на другой сервер (практически без дискового пространства: « вниз » , « вниз » , « вниз » )…

… Который вскоре обновляется новой WAR, и запросы снова оказываются на другом сервере (с достаточным количеством дискового пространства: «вверх» , «вверх» , «вверх» , «вверх» , «вверх» ).

Стоит снова 3 часа из моей жизни. Включая некоторое время, записывая эти вещи здесь (но я думаю, что это того стоит)

Урок выучен

Знай свой процесс

Знание того, что есть балансировщик нагрузки и несколько узлов (и как они работают), помогает. И то, что ваш CI-сервер постоянно развертывает новые версии в вашей среде, которая находится под следствием, не помогает. Но в целом знание этого помогло прояснить наблюдаемое поведение.

Изучите «разумные» значения по умолчанию вашей структуры.

В случае Grails 3 и Spring Boot, узнайте материал, который автоматически настраивается из classpath , осмотрите его и убедитесь, что он будет именно тем, что вам действительно нужно .

Мы избавимся от H2 и рассмотрим те показатели работоспособности, которые нам действительно нужны, возможно, отключение автоконфигурации вообще. Мы очистили дампы кучи Java, которые привели к заполнению диска. Мы еще раз подтвердили, что команда Unix будет контролировать ОС, включая дисковое пространство, так что нам по крайней мере больше не нужен DiskSpaceHealthIndicator

Опубликовано на Java Code Geeks с разрешения Теда Винке, партнера нашей программы JCG . Посмотрите оригинальную статью здесь: Почему у Spring’s Health вниз, вниз, вверх, вверх, вверх и вниз снова?

Мнения, высказанные участниками Java Code Geeks, являются их собственными.