Иногда может случиться так, что Grafana перестает показывать графики, показывает в веб-интерфейсе ошибки. Зайдя в логи сервиса ты можешь увидеть частые сообщения:
error="database is locked"
Что за ошибка? #
Опишу так, как это понял я.
По умолчанию Grafana использует в качестве базы данных файл SQLite, и все было бы хорошо, если бы не версия 8.0.0, в которой разработчики перелопатили систему alert-ов. При увеличении количества настроенных алертов база данных может не справляться с нагрузкой и блокироваться.
Даже разработчики подтверждают такую проблему:
Первое решение, временное #
Скорее всего первое, что тебе попадется в Google по этому запросу, это статья в community Графаны с советом забекапить SQLite базу данных в новый файл, поменять их местами и перезапустить Графану.
sqlite3 grafana.db '.clone grafana-new.db'
mv grafana.db grafana-old.db
mv grafana-new.db grafana.db
Такое приходилось делать, когда внезапно перезагружаешь контейнер или машину с графаной. И оно действительно работает.
Но спустя пару месяцев наши разработчики решили добавить десяток алертов, что привело к не выходящей из состояния блокировки базы данных.
Пришлось прибегнуть к
Второе решение, постоянное #
Это переехать на другую базу данных. Выбираем PostgreSQL.
В моем случае Grafana развернута в контейнере с помощью docker-compose, таким образом мы просто развернем рядом еще один контейнер с базой данных. Но есть проблема, мы же хотим сначала перенести SQLite БД в PostgreSQL.
Поднимаем контейнер с PostgreSQL 13, подключаем к нему директорию, в которой он будет хранить данные. Я сделаю это с помощью docker-compose.
Создаю директорию для хранения данных:
mkdir -p /var/lib/postgresql/data
Можно также воспользоваться docker volume.
Создаю docker-compose.yml:
version: '3.9'
services:
postgres:
image: postgres:13.10-alpine3.17
restart: always
container_name: postgres-grafana
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=superstrongpassword
ports:
- '127.0.0.1:5432:5432'
volumes:
- /var/lib/postgresql/data:/var/lib/postgresql/data
Запускаю его:
docker-compose up -d
Зайдем в PostgreSQL и создадим новую БД:
docker exec -ti postgres-grafana /bin/sh
psql
CREATE DATABASE grafana;
Теперь нам нужно создать в базе данных таблицы с нужными схемами данных. Для этого мы запустим Grafana и подключим ее к свежей базе. Потом выключим. Добавляю в docker-compose.yml новый сервис:
grafana:
image: grafana/grafana:version
container_name: grafana
user: "2017"
volumes:
- /var/lib/grafana:/var/lib/grafana
environment:
GF_DATABASE_TYPE: postgres
GF_DATABASE_HOST: postgres:5432
GF_DATABASE_NAME: grafana
GF_DATABASE_USER: postgres
GF_DATABASE_PASSWORD: superstrongpassword
GF_DATABASE_SSL_MODE: disable
ports:
- 3000:3000
Запускаем и мониторим логи, дожидаясь завершения миграций:
docker logs -f grafana
После того, как миграции закончатся, можно выключить Grafana и приступить к переносу БД SQLite в PostgreSQL.
docker-compose stop grafana
Перенос с помощью pgloader #
Ориентировался я на статью.
TL;DR у меня не получилось
Первым делом я решил воспользоваться утилитой pgloader, которую можно установить из стандартных репозиториев:
apt install pgloader
Disclaimer: именно из-за pgloader пришлось разворачивать PostgreSQL 13, а не 14. Дело в том, что в 14 версии начали использовать метод шифрования паролей по умолчанию scram-sha-256
вместо md5
. pgloader не умеет с ним работать. Можно изменить, но это лишние действия.
Создаю файл-скрипт для переноса БД:
load database
from sqlite:///var/lib/grafana.db
into postgresql://postgres:superstrongpassword@127.0.0.1/grafana
with data only, reset sequences
set work_mem to '16MB', maintenance_work_mem to '512 MB';
Запускаем утилиту:
pgloader script.load
По идее, после этого должно все работать. Но нет. У меня перестала работать авторизация. После логина выбрасывало обратно на ввод логина/пароля.
Перенос с помощью grafana-migrate #
Как я смог понять проблему — Grafana записывает некоторые значения в БД в виде hex, что не очень хорошо переносится в PostgreSQL с помощью pgloader.
grafana-migrate — это самописная утилита, которая лежит в github https://github.com/wbh1/grafana-sqlite-to-postgres
Disclaimer: если перед этим ты делал что-то с базой через pgloader, то лучше будет удалить БД, создать снова и провести миграции.
Скачиваем бинарник с утилитой:
wget https://github.com/wbh1/grafana-sqlite-to-postgres/releases/download/v2.2.3/grafana-migrate_linux_amd64-v2.2.3
Формируем команду и запускаем утилиту:
./grafana-migrate /var/lib/grafana/grafana.db 'postgres://postgres:superstrongpassword@127.0.0.1:5432/grafana?sslmode=disable'
Здесь возникает подводный камень:
У меня grafana.db
весит 150 мегабайт.
- На сервере с HDD дисками процедура миграции БД не закончилась за 12 часов.
- На сервере с SSD дисками это было быстрее, но я не стал ждать дольше часа.
- На сервере с nvme диском эта процедура заняла чуть меньше 30 минут.
После этого я просто перенес директорию с данными /var/lib/postgresql/data
с “быстрого” сервера на другой, где должна была крутиться Grafana.
Полезности #
Давай доформируем окончательно наш docker-compose.yml файл:
version: '3.9'
services:
postgres:
image: postgres:13.10-alpine3.17
restart: always
container_name: postgres-grafana
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=superstrongpassword
ports:
- '127.0.0.1:5432:5432'
volumes:
- /var/lib/postgresql/data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
grafana:
image: grafana/grafana:9.0.4
container_name: grafana
user: "2017"
depends_on:
postgres:
condition: service_healthy
volumes:
- /var/lib/grafana:/var/lib/grafana
environment:
GF_DATABASE_TYPE: postgres
GF_DATABASE_HOST: postgres:5432
GF_DATABASE_NAME: grafana
GF_DATABASE_USER: postgres
GF_DATABASE_PASSWORD: superstrongpassword
GF_DATABASE_SSL_MODE: disable
ports:
- 127.0.0.1:3000:3000
Я добавил healthcheck для сервиса postgres, который раз в 5 секунд проверяет, доступна ли база данных. Это удобно на этапе запуска:
- если Grafana запустится до того, как запустится postgres - возникнет ошибка. Параметр depends_on будет ждать, пока сервис postgres не перейдет в состояние healthy.
Так же я закрепил контейнер с Grafana за интерфейсом 127.0.0.1. У меня уже настроен Nginx reverse proxy, который будет проксировать запросы извне, на 127.0.0.1:3000 - в Grafana.
🌈 Надеюсь, тебе понравилась статья. Рекомендую подписаться на канал в Телеграме, чтобы не пропускать полезную информацию: