В наше время наличие масштабируемой и надежной инфраструктуры имеет решающее значение для успеха вашего сайта. Мониторинг производительности NGINX и работоспособности ваших приложений с балансировкой нагрузки имеет решающее значение. Знание объема трафика, получаемого вашими виртуальными серверами, позволяет масштабировать при необходимости, а отслеживание уровня ошибок позволяет эффективно сортировать приложения. Мониторинг и использование этих показателей помогают вашим пользователям получить надежный и полезный опыт.
Функция мониторинга активности в NGINX Plus позволяет легко получать ключевые показатели нагрузки и производительности в режиме реального времени. Большой объем полезных данных о трафике, проходящем через NGINX Plus, доступен как на встроенной панели инструментов, так и в удобном для анализа формате JSON через API на основе HTTP.
Существует много эффективных способов использования данных, предоставляемых API мониторинга активности в реальном времени. Например, вы можете захотеть отслеживать восходящий трафик конкретного виртуального сервера и автоматически масштабировать Docker- контейнеры, используя API-интерфейс реконфигурации NGINX Plus « на лету» . Возможно, вы захотите вывести метрики напрямую в стандартный формат и записать их в файл журнала, чтобы вы могли отправлять метрики в стек ELK или кластер Splunk . Или, возможно, вы хотите собирать и отправлять данные API в агрегатор данных, такой как statsd и collectd, для других целей построения графиков или ведения журналов.
Члены сообщества Go написали большое количество пакетов, которые могут предоставить нужную вам функциональность. В качестве другого примера я также использовал сторонний пакет go-statsd-client для отправки некоторых моих статистических данных непосредственно в StatsD.
StatsD — это сетевой демон, который работает на платформе Node.js и прослушивает статистику, такую как счетчики и таймеры, отправляемую по UDP или TCP, и отправляет агрегаты одной или нескольким подключаемым внутренним службам.
О Go
Go, также известный как golang, — это язык программирования с открытым исходным кодом, разработанный Google, который позволяет легко создавать простые, надежные и эффективные программы. Если вы хотите узнать больше о Go, посмотрите «Как написать код Go» и «Go by Example» . На сайте Go также имеется обширная документация по различным доступным пакетам . Цель этого блога — показать вам некоторые основные методы и пакеты Go, которые можно использовать для сбора метрик API мониторинга активности NGINX Plus.
$ go version
go version go1.4.2 linux/amd64
$ sudo /usr/sbin/nginx -V
nginx version: nginx/1.9.4 (nginx-plus-r7)
Я протестировал образец скрипта на Go версии 1.4.2 и NGINX Plus R7 (на основе NGINX версии 1.9.4 с открытым исходным кодом).
Импорт пакетов Go
Первое, что мы определяем в скрипте, это имя пакета и пакеты, которые мы хотим импортировать во время выполнения или сборки.
package main
import (
"encoding/json"
"errors"
"fmt"
"github.com/cactus/go-statsd-client/statsd"
"io/ioutil"
"log"
"net/http"
"time"
)
В моем примере скрипта я использую несколько пакетов, которые доступны в стандартной библиотеке Go . Вот список этих пакетов вместе с кратким описанием того, для чего они используются.
- encoding / json — Кодирование и декодирование объектов JSON
- ошибки — управление сообщениями об ошибках
- fmt — Форматирование функций ввода / вывода (печать данных на терминал)
- io / ioutil — реализация утилит ввода / вывода
- log — регистрация и ошибки печати
- net / http — Создание HTTP-подключения клиента к конечной точке API
- время — создание механизма сна в нашем скрипте
Определение структуры данных JSON
В Go вы должны определить структуру данных вашего JSON. Обратите внимание, что Go просто игнорирует любые данные JSON, которые вы не определили в своей структуре данных. В этом примере я получаю статистику соединения только из API NGINX Plus.
type NginxResponse struct {
Connections struct {
Accepted int64 `json:"accepted"`
Active int64 `json:"active"`
Dropped int64 `json:"dropped"`
Idle int64 `json:"idle"`
} `json:"connections"`
}
Если вы решите использовать эту структуру данных JSON и добавить значения зон апстрима и сервера, имейте в виду, что некоторые имена полей данных будут уникальными для вашего экземпляра и конфигурации NGINX Plus. В Интернете существует множество инструментов, которые легко преобразуют дамп JSON в структуру Go, но как только вы поймете, как определяется структура, очень легко создать свой собственный.
Определение функций NginxStatus и SendStatsD
Мой скрипт Go состоит из трех функций: NginxStatus, SendStatsD,
и main
функции, которую я расскажу в этом порядке. NginxStatus
Функция делает вызов к NGINX Plus живой активности мониторинга API.
Сначала я определяю сервер NGINX Plus, затем выполняю запрос GET к API мониторинга активности в реальном времени. Я проверяю, что код статуса в ответе, 200 OK
и использую пакет ошибок, чтобы зарегистрировать проблему, если нет. Я также откладываю закрытие соединения до завершения моей функции, после чего оно автоматически закрывает соединение.
Затем я считываю тело ответа в переменную и декодирую JSON, используя структуру, определенную мной в разделе «Определение структуры данных JSON». Я передаю данные обратно в основную функцию. Я также распечатываю статистику на экране и передаю ее в функцию SendStatsD.
func NginxStatus() (*NginxResponse, error) {
// assign variable for NGINX Plus server
var nginxStatusServer string = "my.nginx.server.com"
// perform a request to the NGINX Plus status API
resp, err := http.Get(fmt.Sprintf("http://%s/status", nginxStatusServer))
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, errors.New("Non 200 OK")
}
// clean up the connection
defer resp.Body.Close()
// read the body of the request into a variable
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// unmarshall the JSON data into a variable
var er NginxResponse
if err := json.Unmarshal(data, &er); err != nil {
return nil, err
}
return &er, nil
}
В моей функции SendStatsD я определяю переменные и тип, который я хочу отправить непосредственно в StatsD. В начале функции я определяю сервер StatsD и порт, к которому я буду подключаться, а также имя клиента. Затем я устанавливаю соединение, регистрирую любые непредвиденные ошибки и снова откладываю закрытие соединения. Затем я назначаю переменную интервалу очистки для StatsD, а другую — части имени метрики. Интервал очистки сообщает StatsD, как долго ждать отправки данных в бэкэнд. Наконец, в конце моей функции SendStatsD я отправляю все данные в StatsD, используя встроенную функцию Inc.
func SendStatsD(gs string, gt string, gv int64) {
// assign variables for statsd server and metric name prefix
var statsdServer string = "127.0.0.1:8125"
var gk string = "nginx"
// connect to statsd
client, err := statsd.NewClient(statsdServer, gk)
if err != nil {
log.Println(err)
}
// clean up the connections
defer client.Close()
// assign variables for metric name and interval
var fi float32 = 1.0
var st string = "status"
var sn string = "my_nginx_server_com"
// send metrics to StatsD
client.Inc(st+"."+sn+"."+gs+"."+gt, gv, fi)
}
Очень важно, что это правильно сконструировано, потому что оно сообщает StatsD, как организовать ваши данные. Думайте о названии метрики как о структуре папок с точкой в качестве разделителя между каждым уровнем. То, как вы называете пространство имен метрики, определяет, где данные хранятся и доступны в веб-интерфейсе Graphite. То, как вы храните свои данные, зависит от вас, но я рекомендовал вам использовать следующее соглашение об именах при определении имен метрик. Здесь я использую угловые скобки, чтобы было легче увидеть сегменты имени; они не используются в реальных именах.
<namespace>.<section-type>.<server-name>.<target noun>.<stat adjective or verb>
например:
nginx.status_api.my_nginx_server_com.connections.active
В этом примере StatsD отправляет данные API NGINX Plus о подключениях, которые в данный момент активны на сервере my.nginx.server.com. Обратите внимание, что вы заменяете любые точки или пробелы в сегменте имени символом подчеркивания.
Определение основной функции
В main
функции я создаю бесконечный цикл, а затем просто вызываю функции, описанные выше, чтобы легко получать и отправлять данные по мере необходимости.
func main() {
for {
// query the NginxStatus function to get data
nr, err := NginxStatus()
if err != nil {
log.Println(err)
}
// print the statistics on screen
fmt.Println("Connections Accepted:", nr.Connections.Accepted)
fmt.Println("Connections Dropped:", nr.Connections.Dropped)
fmt.Println("Connections Active:", nr.Connections.Active)
fmt.Println("Connections Idle", nr.Connections.Idle)
// send metrics to the SendStatsD function
go SendStatsD("connections", "accepted", nr.Connections.Accepted)
go SendStatsD("connections", "dropped", nr.Connections.Dropped)
go SendStatsD("connections", "active", nr.Connections.Active)
go SendStatsD("connections", "idle", nr.Connections.Idle)
// sleep for one second
time.Sleep(time.Millisecond * 1000)
}
}
Здесь я присваиваю переменную результатам вызова API для NGINX Plus, используя функцию NginxStatus. Затем я использую fmt
пакет для вывода на экран некоторых статистических данных о своем соединении, а функцию SendStatsD — для передачи этих данных в Graphite.
Я также запускаю sleep
функцию для вставки задержки в одну секунду между каждым запросом. В этом блоге я не буду подробно рассказывать о том, как управлять связью или распределением времени. При создании вашего скрипта вам нужно будет определить, как вы хотите справиться с таймаутами или задержками соединения и как учитывать время между каждым запросом.
Вот пример вывода экрана после запуска скрипта. Данные, отправляемые в StatsD, прозрачны и могут быть проверены с помощью веб-интерфейса Graphite.
$ go run main.go
Connections Accepted: 36480962
Connections Dropped: 0
Connections Active: 13
Connections Idle 30
Полный сценарий
Вот полный текст сценария для справки. Интервалы важны в Go, поэтому обязательно проверяйте синтаксис, если вы получаете какие-либо ошибки.
package main
import (
"encoding/json"
"errors"
"fmt"
"github.com/cactus/go-statsd-client/statsd"
"io/ioutil"
"log"
"net/http"
"time"
)
type NginxResponse struct {
Connections struct {
Accepted int64 `json:"accepted"`
Active int64 `json:"active"`
Dropped int64 `json:"dropped"`
Idle int64 `json:"idle"`
} `json:"connections"`
}
func NginxStatus() (*NginxResponse, error) {
// assign variable for nginx plus server
var nginxStatusServer string = "my.nginx.server.com"
// perform a request to the NGINX Plus status API
resp, err := http.Get(fmt.Sprintf("http://%s/status", nginxStatusServer))
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, errors.New("Non 200 OK")
}
// clean up the connection
defer resp.Body.Close()
// read the body of the request into a variable
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// unmarshall the JSON data into a variable
var er NginxResponse
if err := json.Unmarshal(data, &er); err != nil {
return nil, err
}
return &er, nil
}
func SendStatsD(gs string, gt string, gv int64) {
// assign variables for statsd server and metric name prefix
var statsdServer string = "127.0.0.1:8125"
var gk string = "nginx"
// connect to statsd
client, err := statsd.NewClient(statsdServer, gk)
if err != nil {
log.Println(err)
}
// clean up the connections
defer client.Close()
// assign variables for metric name and interval
var fi float32 = 1.0
var st string = "status"
var sn string = "my_nginx_server_com"
// send metrics to statsd
client.Inc(st+"."+sn+"."+gs+"."+gt, gv, fi)
}
func main() {
for {
// query the NginxStatus function to get data
nr, err := NginxStatus()
if err != nil {
log.Println(err)
}
// print the statistics on screen
fmt.Println("Connections Accepted:", nr.Connections.Accepted)
fmt.Println("Connections Dropped:", nr.Connections.Dropped)
fmt.Println("Connections Active:", nr.Connections.Active)
fmt.Println("Connections Idle", nr.Connections.Idle)
// send metrics to the SendStatsD function
go SendStatsD("connections", "accepted", nr.Connections.Accepted)
go SendStatsD("connections", "dropped", nr.Connections.Dropped)
go SendStatsD("connections", "active", nr.Connections.Active)
go SendStatsD("connections", "idle", nr.Connections.Idle)
// sleep for one second
time.Sleep(time.Millisecond * 1000)
}
}
Резюме
Использование Go для доступа к API мониторинга активности NGINX Plus может обеспечить дополнительную гибкость в отношении данных, уже доступных вам в NGINX Plus. Статистические данные, которые вы извлекаете из NGINX, могут помочь улучшить ведение журналов, автоматизировать мониторинг активности в реальном времени и создавать графики или диаграммы на основе истории производительности NGINX Plus.
Первоначально написано Кевином Джонсом в блоге NGINX.