SQLite в Docker: жить хорошо, а хорошо жить — ещё лучше (Litestream)
Иногда хочется, чтобы всё было просто. Взял Rails-приложение, завернул в Docker, запустил — и оно работает. Без отдельной СУБД, без монтирования хранилища для картинок и пр.
Но вот незадача: если взять SQLite, положить её в контейнер и запустить — при docker rm ваша база улетает в никуда. Навсегда. Вместе с пользователями, настройками и всем, что вы так старательно назаполняли.
Сегодня расскажу, как я в pet-проекте RuNiT решил эту проблему с помощью Litestream — и просто кайфую.
Проблема
Классический путь деплоя pet-проекта выглядит так:
- Поднять
PostgreSQLв отдельном контейнере (найти под него постоянное хранилище и сервак) - Прокинуть volume
- Понять, что вы тратите больше времени на администрирование СУБД, и хранилище картинок, чем на разработку фич
Для pet-проекта это ощутимый оверхед. SQLite решает первый пункт, но создаёт два других:
- файл базы живёт внутри контейнера и при пересоздании исчезает
- бэкапов нет, и если сервер упал — всё, приплыли.
Тут бы и сказать "ну, SQLite не для продакшена", но мне как-то попадался проект Litestream - Streaming SQLite Replication
Litestream
Litestream — это standalone-утилита (написана на Go, ~14k stars на GitHub) для потоковой репликации SQLite. Она следит за WAL (Write-Ahead Log) вашей базы и в реальном времени отправляет изменения в S3-совместимое хранилище: Yandex Object Storage, MinIO, RustFS и многие другие.
- Continuous backup — каждая транзакция стримится в облако почти мгновенно
- Restore при старте — при пересоздании контейнера база автоматически восстанавливается из реплики
- Point-in-time recovery — можно откатиться к состоянию на конкретную секунду.
- Ноль изменений в коде приложения —
Litestreamработает как отдельный процесс. - Дёшево — object storage стоит копейки.
Звучит как магия? По сути — да. Но магия очень простая и надёжная - разработчик говорит что работает в проде стабильно.
Как запустить
Устанавливаем Litestream прямо в образ:
#Dockerfile RUN set -ex && curl -sL https://github.com/benbjohnson/litestream/releases/download/v0.5.9/litestream-0.5.9-linux-x86_64.deb -o litestream.deb \ && dpkg -i litestream.deb \ && rm litestream.deb
Передаём credentials для S3 и URL реплики:
services:
app:
environment:
- LITESTREAM_ACCESS_KEY_ID=${LITESTREAM_ACCESS_KEY_ID-key}
- LITESTREAM_SECRET_ACCESS_KEY=${LITESTREAM_SECRET_ACCESS_KEY-secret}
- REMOTE_S3_FILE=${REMOTE_S3_FILE-s3://11.11.11.11:9000/}Главный трюк — в docker/start_service.sh, который является CMD в Dockerfile:
set -ex
# 1. Восстановить базу из S3, если она не существует локально
litestream restore
-o storage/${RAILS_ENV}.sqlite3
-if-replica-exists
-if-db-not-exists
${REMOTE_S3_FILE}/${RAILS_ENV}.sqlite3
# 2. Стандартные Rails шаги (idempotent — безопасны для существующей базы)
bundle exec rails db:create
bundle exec rails db:migrate
bundle exec rails db:seed
# 3. Запускаем Rails ПОД управлением Litestream
exec bundle exec litestream replicate
-exec "rails s"
storage/${RAILS_ENV}.sqlite3
${REMOTE_S3_FILE}/${RAILS_ENV}.sqlite3- litestream restore — если локального файла
storage/production.sqlite3нет, а в S3 есть бэкап, база скачивается. Флаги-if-replica-existsи-if-db-not-existsделают команду безопасной: она не упадёт, если бэкапа ещё нет (первый деплой), и не перезапишет существующую базу если она есть
2. db:create db:migrate db:seed — классика Rails
3. litestream replicate -exec "rails s" — Litestream запускается как supervisor. Он начинает реплицировать WAL в S3 и одновременно запускает дочерний процесс rails s. Если Rails упадёт, Litestream тоже завершится.
Результат: контейнер можно остановить, удалить, пересоздать на другом хосте — база восстановится из S3 и продолжит реплицироваться. Красота!
Профит и подводные камни
- Минимальная инфраструктура — один контейнер, без отдельной СУБД
- Персистентность — данные не пропадают при удалении контейнера
- Бэкап "из коробки" — каждая транзакция летит в S3, RPO стремится к нулю
- Простое восстановление —
litestream restoreили автовосстановление при старте - Дёшево —
SQLiteбесплатна, object storage стоит копейки - Не требует изменений в коде приложения
- Point-in-time recovery — можно восстановить базу на конкретный момент времени
- Только один writer —
SQLiteподдерживает только одного писателя. Это значит, что приложение должно работать в единственном экземпляре (single-node). Ни о каком горизонтальном масштабировании речи не идёт, но в списке ссылок будет сюрприз :) - Зависимость от object storage — если S3 недоступен,
Litestreamнакапливает WAL локально, но при длительном отключении может потребоваться внимание - Не замена PostgreSQL для высоконагруженных систем — для pet-проекта и небольших сервисов подходит идеально, для HA — нет, но можно посмотреть в ссылочки в конце статьи...
SQLite + Litestream — это мощная комбинация для «лёгкого» деплоя. Вы получаете полноценную реляционную базу данных в одном файле, непрерывный backup в S3 и автоматическое восстановление при перезапуске контейнера — без необходимости поднимать и обслуживать отдельную СУБД.
Для pet-проекта это часто идеальный баланс между простотой и надёжностью.
Мои любимые ссылки, которые стоит почитать:
- Litestream и GitHub — официальный сайт и исходники
- Вот он сюрприз: LiteFS - Distributed SQLite - как сделать, горизонтальное масштабирование с
SQLite. Говорят, работает в проде :) - Ну и ссылочка на сам проект Беговой Клуб -=БеГиМ=-