Daily MaverickBOK BLEUS : France resist Springboks’ second-half rearguard to win Paris thrillerCNN TürkHAVA DURUMU 14 KASIM: Yarın havalar nasıl olacak? Yağmur yağacak mı? İstanbul, Ankara, İzmir, Bursa'da hava durumu nasıl olacak?PunchBanned pesticides: Poison on sale in Nigerian markets (1)Inquirer EntertainmentA tiny hand, baby Vans and a new chapter for Avril Lavigne — motherhoodZDF heuteAktuelle Pressemitteilungen des ZDFAntara NewsStarr Chile Assigned A Rating from AM BestArs TechnicaStarship flight 15 powers to orbit—setting up a possible catch on SaturdayColliderNetflix's 'The Diplomat' Already Has the Perfect Ending Hiding in Plain Sight연합뉴스46세 호나우지뉴, 11년 만에 공식전 선발 출격…300호골 정조준G1Resultados das Loterias Caixa매일경제고경표, 음주운전 사고…청소차 들이받아La NaciónArgentina vs. Uruguay, en vivo: el minuto a minuto del amistoso en la Bombonera
The Daily Newsstand · Free, Always
Saturday, November 14, 2026

3 базы данных в Docker за один вечер: PostgreSQL 18 + ClickHouse 26.8 + Qdrant 1.19

Translate

TL;DR: Поднял локально полный аналитический стек — реляционная БД, OLAP‑аналитика и векторный поиск для RAG. Всё в одном compose‑файле, данные — на томах WSL, ноль облачных счетов. Под катом — установка за три шага и три ловушки, о которых молчат гайды.

Зачем ещё один «поднять базы в Docker»?

Для пет‑проектов на стыке классического SQL, аналитики и поиска по эмбеддингам нужен сразу целый зоопарк: реляционная БД для транзакций, колоночная — для аналитики, векторная — для семантического поиска. Облачные managed‑решения для такого экспериментатора — стрельба из пушки по воробьям: счёт капает даже на простаивающих инстансах.

Поэтому альтернатива: один docker-compose.yml, три контейнера, данные на томах WSL. Состав стека:

Сервис

Версия

Роль в стеке

PostgreSQL

18

Реляционные данные, история диалогов, статистика

ClickHouse

26.8

OLAP: аналитические запросы по большим таблицам

Qdrant

1.19

Векторное хранилище для RAG и семантического поиска

Всё это — часть моего проекта, где я собираю локальный AI‑стек без облаков. Следующие статьи серии — про локального AI‑репетитора английского на Ollama и Telegram‑боте, это — фундамент для его данных.

Установка (3 шага)

Создаём папку:

bashmkdir -p ~/nrk_data_stack/db_stack
cd ~/nrk_data_stack/db_stack
mkdir -p ~/nrk_data_stack/db_stack
cd ~/nrk_data_stack/db_stack

Кладём рядом два файла из репозитория — .env и docker-compose-db.yml:

👉 GitHub db_stack

Что внутри compose‑файла

PostgreSQL: healthcheck и правильный restart

yamlhealthcheck:
  test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
  interval: 10s
  timeout: 5s
  retries: 5
  start_period: 30s
restart: unless-stopped
healthcheck:
  test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
  interval: 10s
  timeout: 5s
  retries: 5
  start_period: 30s
restart: unless-stopped

Без healthcheck Docker считает контейнер «запущенным» сразу после старта процесса — даже если PostgreSQL ещё не принимает соединения. pg_isready честно проверяет готовность именно нашей базы, а start_period: 30s даёт базе время на первичную инициализацию: в этот окно неудачные проверки не съедают лимит retries. Это критично, если другие сервисы стека ждут БД через depends_on: condition: service_healthy.

И важная мелочь: именно restart: unless-stopped, а не restart_policy — работает только в Swarm‑стеке, в обычном compose она молча игнорируется. (Проверка: в вашем случае — или «молча ничего не делала», или явно ругалась на unknown key.)

Публикация порта сделана через "${POSTGRES_PORT}:5432" — распространённая ошибка в гайдах: написать просто ${POSTGRES_PORT}. В таком виде значение‑число из .env публикует порт с тем же номером на хосте, а значение вида 5433 без двоеточия вовсе не публикует ничего — compose ждёт формат хост:контейнер.

ClickHouse: ulimits и два порта

yamlulimits:
  nofile:
    soft: 262144
    hard: 262144
ulimits:
  nofile:
    soft: 262144
    hard: 262144

ClickHouse активно работает с открытыми файлами, и дефолтный лимит Docker (1024) на реальных нагрузках кончается быстро — с трудно диагностичными ошибками вида «Too many open files». nofile: 262144 — рекомендованное самим ClickHouse значение.

Портов два, и это не дублирование: 8123 — HTTP‑интерфейс (им на него ходят веб‑консоль /play, драйверы HTTP, curl), 9000 — нативный протокол, по которому работает консольный clickhouse-client и многие клиентские библиотеки. Забыть пробросить 9000 — значит получить работающий дашборд и неработающего клиента.

Qdrant: gRPC и где живут данные

yamlports:
  - "${QDRANT_API_PORT}:6333"   # REST API и веб-дашборд
  - "${QDRANT_GRPC_PORT}:6334"  # gRPC — нужен Python-клиенту!
ports:
  - "${QDRANT_API_PORT}:6333"   # REST API и веб-дашборд
  - "${QDRANT_GRPC_PORT}:6334"  # gRPC — нужен Python-клиенту!

Официальный Python‑клиент Qdrant по умолчанию ходит через gRPC на порту 6334. Если пробросить только 6333 — REST и дашборд будут работать, а клиент упадёт с загадочным connection error. Второй порт не опция, а необходимость для Python.

С томами здесь тонкость, специфичная для WSL:

yamlvolumes:
  - ${QDRANT_VOLUME_PATH}:/qdrant/storage
volumes:
  - ${QDRANT_VOLUME_PATH}:/qdrant/storage

Папка лежит внутри файловой системы Ubuntu (ext4), поэтому bind mount безопасен. А вот bind mount с путей /mnt/c, /mnt/f (диски Windows) или \wsl.localhost со стороны Windows — известная мина: медленный I/O через 9P‑протокол и реальный риск потери данных при записи из Linux на файловую систему Windows. Все «живые» данные стека поэтому храним внутри WSL, а Windows‑диски используем только для статики.

Запускаем:

bashdocker compose -f docker-compose-db.yml up -d
docker compose -f docker-compose-db.yml up -d

Проверка, что всё живо

bash# Статус контейнеров (postgres должен быть healthy)
docker compose -f docker-compose-db.yml ps

# PostgreSQL
docker exec -it nrk-postgres psql -U nrk -d nrk_db -c "SELECT version();"

# ClickHouse
docker exec -it nrk-clickhouse clickhouse-client \
  --user nrk --password "$CLICKHOUSE_PASSWORD" \
  --query "SELECT version()"

# Qdrant (с API-ключом из .env)
curl http://localhost:6333/collections -H "api-key: ваш_ключ"
# Статус контейнеров (postgres должен быть healthy)
docker compose -f docker-compose-db.yml ps

# PostgreSQL
docker exec -it nrk-postgres psql -U nrk -d nrk_db -c "SELECT version();"

# ClickHouse
docker exec -it nrk-clickhouse clickhouse-client \
  --user nrk --password "$CLICKHOUSE_PASSWORD" \
  --query "SELECT version()"

# Qdrant (с API-ключом из .env)
curl http://localhost:6333/collections -H "api-key: ваш_ключ"

Веб‑интерфейсы из коробки:

Три ловушки, о которых молчат гайды

Ловушка 1. PostgreSQL 18: путь данных изменился

В официальных образах PostgreSQL 18 расположение каталога данных сместилось: теперь это /var/lib/postgresql, а не /var/lib/postgresql/data, как в образах прежних версий. Скопированная из старого гайда строка монтирования с /data на конце выглядит рабочей — контейнер стартует, база отвечает. Но том монтируется мимо реального PGDATA: запишете данные, пересоздадите контейнер — и обнаружите пустую базу. Молча, без единой ошибки в логах. Самое противное в этой ловушке — она не стреляет сразу: «успешно работает» до первого пересоздания контейнера. Монтируйте /var/lib/postgresql.

Ловушка 2. ClickHouse: одиночный файл конфига как мина замедленного действия

Классический паттерн «смонтирую config.xml в контейнер» ломается двумя способами. Если файла нет на хосте, Docker создаст вместо него пустую директорию с именем config.xml — и сервер упадёт при старте с невнятной ошибкой. Если файл есть, но не соответствует ожиданиям образа — упадёт уже по‑другому. Надёжный путь: не монтировать основной config.xml, а класть свои переопределения в config.d/ и users.d/ — ClickHouse подхватит их поверх дефолтов. А если всё‑таки монтируете целиком — сначала достаньте дефолтный конфиг из образа и правьте его на хосте.

Ловушка 3. Qdrant: защиты через ADMIN_USER/PASSWORD не существует

В гайдах по Docker‑стекам часто мелькает конструкция с переменными вроде QDRANT__SERVICE__ADMIN_USER — копирующие её получают веб‑дашборд и API без какой‑либо аутентификации, а иллюзию защищённости. У Qdrant авторизация одна: QDRANT__SERVICE__API_KEY (двойные подчёркивания — это вложенность секций конфига: service.api_key). Ключ задаётся в .env, клиент обязан передавать его в каждом запросе через заголовок api-key. Проверить, что защита работает, просто: запрос к /collections без заголовка должен вернуть 403.

Заключение

Три контейнера, один compose‑файл — и полноценный полигон для данных: SQL, OLAP и векторный поиск работают локально, пока вы спите, а облачный счётчик — нет. Из следующего шага проекта: подключение стека к AI‑репетитору в телеграмм на Ollama.

Ссылки:

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.