Проверяя систему продажи билетов для моей компании, я заметил, что появился новый ответ на запрос, который долгое время находился в паузе (пауза в нем была открыта, и никто не проверял ее по какой-либо причине, возможно, кто-то был слишком занят и не его не было в очереди). В новом сообщении говорилось что-то вроде: «Привет, ребята? это происходит? Когда-либо?'

Итак, у меня было немного свободного времени, и я решил действовать. Необходимо было контролировать API. В API было определено несколько конечных точек, каждая из которых давала важную информацию об экземпляре приложения, развернутом на сервере. Информация представлена ​​в формате JSON и имеет запись «значение», определенную значениями, которые необходимо отслеживать.

Я не эксперт по JSON, однако формат не так уж сложен для понимания, и после проверки некоторой документации и пары поисковых запросов в Google с ним легко справиться. С общим пониманием о работах над Bash, и общим пониманием, которое в основном сводится к двум словам — › google-fu (поскольку 99% знакомых мне сисадминов слишком боятся признаться в этом публично, я так и сделаю).

Анализ

После проверки URL-адреса API с помощью curl я мог сказать, что были представлены некоторые конечные точки. На самом деле все конечные точки, необходимые разработчикам, должны быть проверены.

Примечание. Если вам не нужны объяснения этого, а вы просто хотите увидеть сценарий, он размещен в конце этой статьи.

Для этого все, что вам нужно использовать, это однострочник на терминале, такой как этот:

curl -s http://amazingapi.local/api

Примечание: вы можете использовать curl с любым URL-адресом, будь то с доменным именем или IP-адресом без DNS, если вам нужны дополнительные инструкции по этому поводу, не стесняйтесь обращаться к справочным страницам для команда с использованием man curl

Работа с JSON

После проверки вывода команды curl мы получаем что-то вроде этого:

curl -s http://amazingapi.local/api
{“names”:[“jvm.memory.max”,”jdbc.connections.active”,”process.files.max”,”jvm.gc.memory.promoted”,”tomcat.cache.hit”,”system.load.average.1m”,”tomcat.cache.access”,”jvm.memory.used”,”jvm.gc.max.data.size”,”jdbc.connections.max”,”jdbc.connections.min”,”jvm.gc.pause”,”jvm.memory.committed”,”http.server.requests”,”system.cpu.count”,”tomcat.global.sent”,”jvm.buffer.memory.used”,”tomcat.sessions.created”,”jvm.threads.daemon”,”system.cpu.usage”,”jvm.gc.memory.allocated”,”tomcat.global.request.max”,”hikaricp.connections.idle”,”hikaricp.connections.pending”,”tomcat.global.request”,”tomcat.sessions.expired”,”hikaricp.connections”,”jvm.threads.live”,”jvm.threads.peak”,”tomcat.global.received”,”hikaricp.connections.active”,”hikaricp.connections.creation”,”process.uptime”,”tomcat.sessions.rejected”,”process.cpu.usage”,”tomcat.threads.config.max”,”jvm.classes.loaded”,”hikaricp.connections.max”,”hikaricp.connections.min”,”jvm.classes.unloaded”,”tomcat.global.error”,”tomcat.sessions.active.current”,”tomcat.sessions.alive.max”,”jvm.gc.live.data.size”,”tomcat.servlet.request.max”,”hikaricp.connections.usage”,”tomcat.threads.current”,”tomcat.servlet.request”,”hikaricp.connections.timeout”,”process.files.open”,”jvm.buffer.count”,”jvm.buffer.total.capacity”,”tomcat.sessions.active.max”,”hikaricp.connections.acquire”,”tomcat.threads.busy”,”process.start.time”,”tomcat.servlet.error”]}

Не многие люди будут читать весь вывод, прежде всего потому, что, хотя он и в формате JSON, он минимизирован, поэтому за ним не так легко следить, однако именно здесь появляется наш новый лучший друг, когда дело доходит до JSON: jq. Если вы направите вывод команды curl в jq, мы увидим что-то вроде этого:

curl -s http://amazingapi.local/api | jq
{
 “names”: [
 “jvm.memory.max”,
 “http.server.requests”,
 “jdbc.connections.active”,
 “process.files.max”,
 “jvm.gc.memory.promoted”,
 “tomcat.cache.hit”,
 “system.load.average.1m”,
 “tomcat.cache.access”,
 “jvm.memory.used”,
 “jvm.gc.max.data.size”,
 “jdbc.connections.max”,
 “jdbc.connections.min”,
 “jvm.gc.pause”,
 “jvm.memory.committed”,
 “system.cpu.count”,
 “tomcat.global.sent”,
 “jvm.buffer.memory.used”,
 “tomcat.sessions.created”,
 “jvm.threads.daemon”,
 “system.cpu.usage”,
 “jvm.gc.memory.allocated”,
 “tomcat.global.request.max”,
 “hikaricp.connections.idle”,
 “hikaricp.connections.pending”,
 “tomcat.global.request”,
 “tomcat.sessions.expired”,
 “hikaricp.connections”,
 “jvm.threads.live”,
 “jvm.threads.peak”,
 “tomcat.global.received”,
 “hikaricp.connections.active”,
 “hikaricp.connections.creation”,
 “process.uptime”,
 “tomcat.sessions.rejected”,
 “process.cpu.usage”,
 “tomcat.threads.config.max”,
 “jvm.classes.loaded”,
 “hikaricp.connections.max”,
 “hikaricp.connections.min”,
 “jvm.classes.unloaded”,
 “tomcat.global.error”,
 “tomcat.sessions.active.current”,
 “tomcat.sessions.alive.max”,
 “jvm.gc.live.data.size”,
 “tomcat.servlet.request.max”,
 “hikaricp.connections.usage”,
 “tomcat.threads.current”,
 “tomcat.servlet.request”,
 “hikaricp.connections.timeout”,
 “process.files.open”,
 “jvm.buffer.count”,
 “jvm.buffer.total.capacity”,
 “tomcat.sessions.active.max”,
 “hikaricp.connections.acquire”,
 “tomcat.threads.busy”,
 “process.start.time”,
 “tomcat.servlet.error”
 ]
}

Если вам нужно установить jq, это так же просто, как: apt install jq -y в дистрибутивах на основе Debian или yum install jq -y в дистрибутивах на основе RHEL.

Это НАМНОГО проще понять, и мы начинаем находить небольшую проблему, есть много конечных точек. Существует несколько способов создания сценария для мониторинга API, большинство из них включает создание одного сценария для одного API, однако существует множество API, которые необходимо отслеживать как в инфраструктуре этой компании, так и в любой другой, которую мы хотим изучить.

Рассуждение

В этом конкретном случае несколько разработчиков сказали мне, что не все API имеют одинаковые конечные точки, но большинству из них не нужны учетные данные, поскольку они фильтруются по IP-адресам, что значительно упрощает задачу. Я не буду вдаваться в подробности того, как это сделать с аутентифицированным API, возможно, в другой статье; но реализация аутентификации на основе токенов или аутентификации на основе учетных данных в сценарии не должна занимать больше пары строк.

Вернемся к API. Как мы с вами видим, конечных точек много, и мы не знаем, изменятся ли они в любой день, какие-то будут удалены, какие-то добавлены. Нам нужно сделать скрипт, который можно адаптировать и настроить в рамках нашего инструмента мониторинга, чтобы он учитывал новые изменения. Кроме того, этот API обслуживается приложением Tomcat, это не всегда так.

Поскольку мы можем получить список конечных точек из команды curl, которую мы выполнили ранее, мы также можем проверить конкретную конечную точку. В случае первой записи: jvm.memory.max мы можем сделать так:

Выбор значений

curl -s http://amazingapi.local/api/jvm.memory.max | jq
{
 “name”: “jvm.memory.max”,
 “description”: “The maximum amount of memory in bytes that can be used for memory management”,
 “baseUnit”: “bytes”,
 “measurements”: [
 {
 “statistic”: “VALUE”,
 “value”: 33096204287
 }
 ],
 “availableTags”: [
 {
 “tag”: “area”,
 “values”: [
 “heap”,
 “nonheap”
 ]
 },
 {
 “tag”: “id”,
 “values”: [
 “Compressed Class Space”,
 “PS Survivor Space”,
 “PS Old Gen”,
 “Metaspace”,
 “PS Eden Space”,
 “Code Cache”
 ]
 }
 ]
}

Удивительно, мы видим ценность, которую предоставляет API, а также несколько дополнительных данных, которые не служат для наших нужд прямо сейчас, и мы не будем слишком углубляться в это. Основным атрибутом, который нам нужен в выходных данных JSON, является запись VALUE, которая принадлежит массиву измерений:

 “measurements”: [
 {
 “statistic”: “VALUE”,
 “value”: 33096204287
 }
 ],

Варианты для jq

С помощью jq мы можем получить массив внутри объекта с помощью одинарных кавычек и квадратных скобок для обозначения массива, например:

curl -s http://amazingapi.local/api/jvm.memory.max | jq ‘.measurements[]’
{
 “statistic”: “VALUE”,
 “value”: 33096204287
}

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

curl -s http://amazingapi.local/api/jvm.memory.max | jq ‘.measurements[]’ | jq ‘.value’
33096204287

Это число, которое выводит команда, — это «Максимальный объем памяти в байтах, который можно использовать для управления памятью», как указано в предыдущем запросе, который мы сделали для API. Это число можно преобразовать из скрипта множеством различных способов, однако мы не будем этого делать, потому что нам нужно, чтобы скрипт был как можно более экономным, чтобы его можно было использовать со многими API и многими конечными точками.

Теперь мы знаем, как получить конкретное значение из конечной точки с помощью *curl*, нам просто нужно запрограммировать его во что-то, что может быть использовано программным обеспечением для мониторинга. Поскольку большинство программного обеспечения для мониторинга позволяет использовать скрипты bash или sh, или ksh, или любую другую оболочку, установленную на хост-компьютере программного обеспечения для мониторинга, мы просто будем использовать Bash.

Сценарии

Обратите внимание, что этот скрипт использует некоторые манипуляции с текстом, которые можно понять, используя справочные страницы на вашей консоли Linux. Сначала этот сценарий может показаться трудным для чтения (извините, не извиняйте), но он довольно прост, когда вы на самом деле переходите от строки к строке.

Начнем с первого сегмента:

#!/bin/bash
if [[ -z $1 ]] ; then
 echo “This script has to be used with two parameters”
 echo “Usage:”
 echo “check_api.sh <api_url> <endpoint>”
 echo “e.g.:”
 echo “ check_api.sh http://exampleurl.com/api cpu.sys.util”
 exit 1
fi

Мы просто инициализируем скрипт и добавляем описание, если скрипт не получает параметры, которые он ожидает при запуске, он покажет какое-то справочное сообщение и пример.

После этого мы определяем пару переменных, которые будут использоваться:

url=$1
probe=$2
endpoints=$`curl -s $1 | \
jq ‘.names[]’| awk -F\” ‘{print $2}’|cut -c 1-`

Эти переменные выполняют простую работу. Первые две настроены на получение параметров от выполнения скрипта, третья — это команда, которая получает список точек с сервера API (как мы обнаружили ранее).

Затем мы вводим список конечных точек в массив, определенный как: endpointsarray:

endpointsarray=( $endpoints )

И, наконец, настоящая магия:

for i in “${endpointsarray[@]}”
do
 specs=`curl -s $1$i | \
 jq ‘.measurements[]’ | jq ‘.value’`
 echo $i $specs |grep $probe | cut -d “ “ -f2-
done

Этот цикл for просто проходит через массив и получает значение из конечной точки. В нем есть некоторые манипуляции с текстом из-за того, что скрипт будет выбрасывать некоторую информацию, которая не нужна, если вы хотите увидеть некоторые из них, вы можете поэкспериментировать со скриптом, попробуйте посмотреть, что делает команда *cut* в теме.

Надеюсь, это пролило свет на некоторые вещи, которые, возможно, нуждаются в свете. Полный скрипт выглядит следующим образом.

Окончательный сценарий

Вкратце: скрипт, который получает все конечные точки из API и получает значения из одной конечной точки:

#!/bin/bash
if [[ -z $1 ]] ; then
 echo “This script has to be used with two parameters”
 echo “Usage:”
 echo “check_api.sh <api_url> <endpoint>”
 echo “e.g.:”
 echo “ check_api.sh http://exampleurl.com/api cpu.sys.util”
 exit 1
fi
url=$1
probe=$2
endpoints=$`curl -s $1 | \
jq ‘.names[]’| awk -F\” ‘{print $2}’|cut -c 1-`
endpointsarray=( $endpoints )
for i in “${endpointsarray[@]}”
do
 specs=`curl -s $1$i | \
 jq ‘.measurements[]’ | jq ‘.value’`
 echo $i $specs |grep $probe | cut -d “ “ -f2-
done