Содержание урока · 10 разделов
- Установка client_golang: две команды
- Что уже видно на /metrics без вашего кода
- Метрики, на которые стоит взглянуть первыми
- Триста горутин — это много? Проверка curl-ом за пять минут
- promhttp.Handler(): что происходит на каждый запрос
- Вариация: /metrics на отдельном порту
- Спорное мнение: часть дефолтных метрик можно отключить
- Песочница урока
- Коротко: что запомнить
- Вопросы и ответы
Порог входа в мониторинг — то, что продаёт его скептикам: пять строк, и сервис наблюдаем. Никаких агентов, брокеров и отдельных баз: библиотека ставится одной командой, регистрируется в стандартном net/http, а на /metrics уже живёт дюжина метрик, которые вам не надо писать. План урока такой: сначала установка client_golang и минимальный сервер, потом смотрим, что отдаёт endpoint из коробки, и разбираем, как promhttp устроен внутри, чтобы при проблемах знать, куда смотреть. Все примеры — обычные программы net/http, поэтому в сервис на chi или gin они переносятся без единой правки.
Установка client_golang: две команды#
Начинаем с нового модуля. Первая команда — go mod init с именем модуля, вторая — go get github.com/prometheus/client_golang/prometheus: она добавит библиотеку в go.mod и подтянет зависимости. В коде пригодятся два пакета: prometheus — сами типы метрик и реестр, prometheus/promhttp — готовый HTTP-хендлер. Требования скромные: актуальный стабильный Go, никаких переменных окружения и внешних сервисов настраивать не надо. Про версии: библиотека живёт в первой мажорной версии, API стабилен, и код из документации компилируется годами — примеры не протухнут, даже если вернётесь к ним через год. После go get загляните в go.mod: на старте это одна запись, без десятков транзитивных зависимостей — клиент собран на стандартной библиотеке.
Минимальный сервис с /metrics
GOpackage main
import (
"net/http"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func main() {
http.Handle("/metrics", promhttp.Handler())
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("привет"))
})
http.ListenAndServe(":8080", nil)
}Весь секрет — строка http.Handle с promhttp.Handler(): это обычный http.Handler из стандартной библиотеки, без адаптеров и фреймворков. Хендлер отдаёт содержимое реестра по умолчанию, где уже зарегистрированы коллекторы Go-процесса и процесса операционной системы. Запуск — go run ., после чего страница доступна на localhost:8080/metrics.
Что уже видно на /metrics без вашего кода#
Разберём инцидент, с которого начинается половина историй про внедрение мониторинга. Сервис выставил /metrics, страница открывается в браузере, всё выглядит готовым — и команда уверена, что наблюдаемость есть. Через месяц падает база, заказы встают, а дежурный узнаёт об этом от поддержки: сервер Prometheus никто не настраивал, за метриками никто не приходил, страница просто молчала в пустоту. Ошибка тонкая: живой /metrics — половина контракта, вторая половина — кто-то, кто регулярно забирает данные. Поэтому первый чек после запуска сервиса — не браузер, а настройка цели: запись job в prometheus.yml и зелёная метрика up на стороне сервера. До урока про скрейпинг достаточно curl-а: страница отвечает на localhost — кодовая половина контракта выполнена, дальше её должен кто-то читать. А теперь откройте страницу и найдите строки, начинающиеся с go_ и process_ — это работа дефолтных коллекторов, которые client_golang регистрирует автоматически. go_goroutines — текущее число горутин; семейство go_memstats_* — память из runtime: выделено, занята куча, сколько отдал сборщику; go_gc_duration_seconds — длительности сборок мусора в виде гистограммы; process_cpu_seconds_total — процессорное время процесса; process_open_fds — открытые файловые дескрипторы; process_resident_memory_bytes — резидентная память. Для половины штатных вопросов — «не текут ли горутины», «сколько ест память», «не выжрали ли дескрипторы» — этих метрик хватает без единой строчки вашего кода. Остальное — собственные метрики домена: заказы, очереди, латентность вашей бизнес-логики. Их заведём со следующего урока, а пока важно другое: endpoint живой, формат читается, и это уже работающий мониторинг.
Метрики, на которые стоит взглянуть первыми#
- go_goroutines — число горутин прямо сейчас; медленный рост к вечеру — первый симптом утечки.
- go_memstats_heap_inuse_bytes — занятая куча: живая память под ваши данные.
- go_gc_duration_seconds — гистограмма длительностей сборок мусора; хвосты растут под нагрузкой.
- process_cpu_seconds_total — суммарное процессорное время процесса; счётчик, из которого считается загрузка.
- process_open_fds — открытые дескрипторы; если не убывает после закрытия соединений, есть куда смотреть.
- process_resident_memory_bytes — резидентная память процесса, как её видит операционная система.
Триста горутин — это много? Проверка curl-ом за пять минут#
Ошибка интерпретации, которая ждёт на самих числах. Человек открывает /metrics, видит go_goroutines 340 и пишет в чат: «горутин триста сорок, это утечка». Число без контекста не значит ничего: под пиком триста горутин — штатная работа пула воркеров, а в другом сервисе пятьдесят — катастрофа, если обычно их двадцать. Метрика становится сигналом только в динамике и относительно базы сравнения: рост на порядок за ночь при неизменном трафике — повод смотреть; стабильные триста при живом потоке — норма. Откуда берутся горутины и как их считать — отдельная наука, у нас для неё есть урок про конкурентность, а здесь достаточно правила: сырые значения дефолтных метрик — материал для графиков, а не для вердиктов. Сравнивайте значение с собственным прошлым — вчерашним утром, прошлым пик-часом — и только потом делайте выводы. Практическая вариация для закрепления — минимальный чек-лист после установки. Первый шаг — curl с ключом silent и просмотр первых строк: должны прийти комментарии HELP и TYPE, а не HTML-страница ошибки. Второй — grep по go_goroutines: если строки нет, что-то съело хендлер — чаще всего middleware, который отдал 404, или прокси, переписавший путь. Третий — убедиться, что страница отдаётся и с внешнего адреса, если Prometheus стоит на другой машине: localhost и имя хоста — разные адреса, и новички неделями ищут «потерянные» метрики, хотя сервис просто слушал внутренний интерфейс. Четвёртый — посмотреть Content-Type: текст с версией формата подтверждает, что отвечает именно promhttp, а не случайный хендлер. Пять минут, и у сервиса есть гарантированная точка, куда можно стучаться мониторингом.
Интерпретатор сайта исполняет чистый Go без сторонних модулей и TCP-сокетов, поэтому примеры урока запускаются только локальным Go. Схема одна на весь курс: go mod init, go get github.com/prometheus/client_golang/prometheus, go run . — и curl на localhost. Пять минут на настройку, дальше всё живёт в вашем терминале.
promhttp.Handler(): что происходит на каждый запрос#
Разберём хендлер по слоям, чтобы при проблемах понимать, где искать. На каждый GET /metrics promhttp собирает значения: собирает все метрики реестра через Gatherer — по умолчанию DefaultGatherer, куда попадает всё, зарегистрированное через prometheus.MustRegister. Затем сериализует в текстовый формат экспозиции и отвечает с Content-Type text/plain; более компактный protobuf включается согласованием через заголовок Accept, но для человека и curl текст — вариант по умолчанию. Сбор происходит в момент запроса, а не в фоне: значение go_goroutines на странице — это значение прямо сейчас, поэтому scrape-интервал задаёт и разрешение ваших графиков. Отсюда практика: держите /metrics на том же порту, что и сервис, но следите за весом реестра — тысяча метрик с лейблами замедлит каждый скрейп; кардинальности посвящён отдельный урок второй половины курса.
Отдельный реестр: когда нужен контроль
GOreg := prometheus.NewRegistry()
reg.MustRegister(collectors.NewGoCollector())
http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{}))
// процессные метрики появятся, только если
// зарегистрировать их отдельно:
reg.MustRegister(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}))NewRegistry создаёт пустой реестр вместо дефолтного, а HandlerFor связывает хендлер именно с ним. Так собирают аккуратные страницы: только свои метрики, только Go-коллектор или несколько эндпоинтов с разным содержимым. Пакет collectors входит в client_golang; тему реестров и имён до конца разберёт урок про namespace, а дефолтные метрики — урок о встроенных метриках процесса.
Вариация: /metrics на отдельном порту#
Разбор реестров подводит к полезной вариации примера: второй порт для метрик. Идея в двух ListenAndServe: основной сервер слушает внешний 8080 и про метрики не знает, а второй, поднятый в горутине на localhost:9090, отдаёт только /metrics. Выигрыш двойной: экспозиция физически недоступна из интернета, минуя любые правила прокси, и скрейп не попадает в статистику основного роутера — серии не метят сами себя. Цена тоже понятна: два порта в конфигурации, две проверки в readiness, дисциплина в файрволе. В оркестраторах чаще оставляют один порт и закрывают путь сетевыми политиками; на виртуалках и в монолитах второй порт — самый дешёвый способ изолировать экспозицию. Выбор не принципиален, пока он осознанный: случайно открытый наружу endpoint дороже любой схемы, о чём врезка ниже.
promhttp не умеет авторизацию: endpoint, который торчит наружу, отдаёт постороннему устройство вашего сервиса — маршруты, размеры, поведение под нагрузкой, версии зависимостей. Рабочие варианты: держать /metrics на внутреннем интерфейсе или отдельном порту, закрывать сетевыми политиками, в крайнем случае — middleware с проверкой токена. Сканеры находят открытые /metrics за минуты.
Спорное мнение: часть дефолтных метрик можно отключить#
Спорное мнение на десерт: часть go_-метрик я отключаю в крупных сервисах, и это не ересь. Каждый скрейп собирает всё, что зарегистрировано: пятьдесят серий памяти, гистограмма сборок мусора, рантайм-бухгалтерия — на ферме из двухсот подов это тысячи серий, которые никто не открывает. В зрелых сервисах я оставляю go_goroutines, пару серий памяти и процессные дескрипторы, остальное убираю отдельным реестром, как показано выше, когда появляется конкретный потребитель. Граница, где я ограничиваю сам себя: пока сервис один и команды нет — не трогайте дефолт, бесплатная видимость важнее экономии серий. Отключение — инструмент зрелого мониторинга, у которого уже есть дашборды и алерты, а не первой недели проекта. Тему больших серий по-настоящему разберёт урок про кардинальность, а сегодня достаточно правила: сначала потребитель, потом отключение.
Два пакета, зарегистрировавшие коллекторы в один реестр, уронят процесс вторым MustRegister при старте: реестр не терпит дубликатов. Лечится правилом владения: реестр создаёт main и передаёт параметром, пакеты регистрируют свои метрики в том, что получили. Падение при старте — удачный исход: хуже, когда дубликат живёт и молча портит экспозицию.
Проверка: содержимое дефолтного хендлера
Что отдаёт promhttp.Handler() сразу после запуска сервиса, до регистрации ваших метрик?
Проверка: Content-Type
Какой Content-Type у ответа /metrics по умолчанию?
Что вернёт несуществующий путь
Прочитайте код и выберите, что он выведет:
func main() {
http.Handle("/metrics", promhttp.Handler())
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("привет"))
})
http.ListenAndServe(":8080", nil)
}
// запрос: GET /unknownСоберите каркас сервиса
Восстановите путь импорта, регистрацию маршрута и создание хендлера.
Порядок команд в терминале
Расставьте команды от создания модуля до проверки метрик на живом сервере.
Строки
Ваша программа
Нажимайте на строки слева, чтобы собрать программу
Карточки: client_golang
Endpoint жив — теперь в него можно класть свои числа. Следующий урок — Counter: prometheus.NewCounter, MustRegister, Inc и Add на живом примере заказов, включая ловушки вроде обнуления при рестарте и паники на отрицательном Add. Позже — Gauge для текущих состояний и отдельный урок про встроенные метрики процесса, которые мелькали сегодня: горутины, память и сборки мусора разберём поимённо с советами, на что смотреть. Если сервис собран на chi, маршрут /metrics вписывается тем же http.Handle рядом с остальными — совместимость с net/http полная. Карта курса — на его странице; там же видно, где начнутся PromQL и Grafana, а пока цель одна: запустите сервис локально и убедитесь, что grep по go_goroutines что-то находит.
Песочница урока
Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.
Запустите код, чтобы увидеть вывод.
Коротко: что запомнить
- client_golang ставится двумя командами: go mod init и go get github.com/prometheus/client_golang/prometheus.
- promhttp.Handler() — готовый http.Handler: вешается на /metrics и отдаёт реестр по умолчанию.
- Из коробки идут метрики go_ и process_: горутины, память, сборки мусора, дескрипторы, процессорное время.
- Сбор значений происходит на каждый скрейп: значения честные «прямо сейчас», интервал задаёт разрешение графиков.
- NewRegistry и HandlerFor дают отдельный реестр, когда нужен контроль содержимого страницы.
- /metrics не должен быть доступен из интернета: у promhttp нет авторизации.
Домашнее задание
Соберите сервис урока локально: go mod init, go get, go run. Убедитесь curl-ом, что /metrics отдаёт текст, а корень — привет; найдите на странице три метрики go_ и одну process_. Затем вынесите /metrics на отдельный порт вторым ListenAndServe в горутине и подумайте, какой вариант ближе к вашему продакшену.
Личные заметкиПусто
Вопросы и ответы по уроку
Какая версия Go нужна для client_golang?
Актуальная стабильная: библиотека следует за релизами Go и не поддерживает старые. Требования видны в go.mod библиотеки; дополнительных инструментов ставить не нужно.
Почему на /metrics столько метрик, которых я не создавал?
client_golang автоматически регистрирует коллекторы Go-runtime и процесса: горутины, память, сборки мусора, дескрипторы, процессор. Это нормально и полезно; убрать их можно отдельным реестром или опциями коллекторов.
Как спрятать /metrics от внешних пользователей?
Внутренний интерфейс или отдельный порт, сетевые политики, reverse-proxy с запретом пути, в крайнем случае middleware с токеном. Внутри promhttp авторизации нет — открытый endpoint отдаёт устройство сервиса наружу.
Термины урока
Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.
Изучайте дальше
Следующая ступень учебного пути
WebSocket в Go: real-time чаты и уведомления
Курс WebSocket на Go: gorilla/websocket, эхо-сервер, hub-паттерн, комнаты, авторизация соединений, ping/pong, reconnect и масштабирование. 25 уроков.
- Поднимать WS-сервер и апгрейдить HTTP-соединения
- Писать readPump/writePump без гонок на каждое соединение
- Строить hub: регистрацию, рассылку и комнаты
- Авторизовывать соединения и ограничивать сообщения