Привет, Хабр!

Меня зовут Андрей Жуйков, я руководитель направления СУБД компании «Диасофт».

Контейнеры давно стали стандартом современной разработки. Согласно отчету Docker State of Application Development 2025, они используются примерно в 92% IT-организаций и фактически стали универсальным способом упаковки и запуска приложений независимо от платформы и окружения. Это тот случай, когда инфраструктура перестает мешать и начинает экономить время.

Именно поэтому Digital Q.DataBase доступна, в том числе, в виде Docker-образа. Это позволяет за несколько минут попробовать Oracle- и MS SQL-совместимую СУБД на Windows, Linux и macOS, ограничившись несколькими командами, без сложной установки и длительного онбординга. Полноценная рабочая среда готова к использованию сразу после старта контейнера.

Мы предоставляем бесплатную полнофункциональную версию Digital Q.DataBase с единственным ограничением до 8 ядер vCPU. Скачать ее можно на сайте https://database.diasoft.ru, нажав кнопку «Скачать бесплатно». Далее предлагается два варианта загрузки с регистрацией личного кабинета и без нее. Для быстрого старта можно выбрать вариант без регистрации.

В результате вы получаете архив tar.gz с PDF-мануалами, примерами T-SQL и PL/SQL-скриптов, release notes текущей версии и еще одним tar.gz с Docker-образом на базе Ubuntu 22.04.3 LTS. Образ уже содержит серверный и клиентский пакеты СУБД, а пользователю доступны сервер базы данных, терминальные SQL-клиенты для трех диалектов, Мастер переноса и Мастер сравнения БД.

По сути, после того как вы скачали архив и подготовили директорию, для запуска Digital Q.DataBase достаточно четырех команд: загрузить Docker-образ (load), запустить контейнер (run), при необходимости подправить pg_hba.conf и перезапустить его для применения изменений (restart).

Сначала загружаем Docker-образ из локального файла. Образ не тянется из registry, а импортируется напрямую, что типично для офлайн-поставок и enterprise-дистрибуций, где важно контролировать версию и не зависеть от внешних репозиториев.

sudo docker load -i docker-qdatabase64xid-17-17.4.25110901-ubuntu22_x86_64.tar.gz

Loaded image: qdatabase_docker:latest

Проверяем, что образ успешно загрузился и доступен локально.

docker images

REPOSITORY         TAG       IMAGE ID       CREATED       SIZE

qdatabase_docker   latest    6bc296d4622c   2 weeks ago   1.49GB

После этого выполняем запуск одной командой. Образ собран под архитектуру amd64, поэтому платформа указывается явно. На системах с arm64, например на Apple Silicon, этот параметр можно задать опционально, чтобы избежать предупреждений. Далее задается фиксированное имя, включается автоматический рестарт, увеличивается объем shared memory для корректной работы СУБД, пробрасываются порты и данные выносятся в Docker volume.

sudo docker run -it --platform=linux/amd64 --restart always --name=qdatabase-17 --shm-size=1g \
--env="DISPLAY" --volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \
-p 5445:5445 -p 1433:1433 -p 1521:1521 \
-v qdb_data:/var/lib/qdatabase-17/data qdatabase_docker

Порты пробрасываются с соответствием один к одному между хостом и контейнером. Это позволяет подключаться к базе по PostgreSQL, MS SQL и Oracle-протоколам без нестандартных настроек. Если на вашем хосте порт 5445 по какой-то причине занят, можно указать любой другой свободный порт, например 5459. При этом порт службы внутри контейнера при необходимости настраивается в файле postgresql.conf.

Далее проверяем, что контейнер запущен и какие порты опубликованы (можно обычный docker ps).

docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
NAMES          STATUS          PORTS

qdatabase-17   Up 53 seconds   0.0.0.0:1433->1433/tcp, [::]:1433->1433/tcp, 0.0.0.0:1521->1521/tcp, [::]:1521->1521/tcp, 0.0.0.0:5445->5445/tcp, [::]:5445->5445/tcp

Дополнительно смотрим фактический проброс портов.

sudo docker port qdatabase-17

1433/tcp -> 0.0.0.0:1433

1433/tcp -> [::]:1433
1521/tcp -> 0.0.0.0:1521
1521/tcp -> [::]:1521
5445/tcp -> 0.0.0.0:5445
5445/tcp -> [::]:5445

После запуска смотрим, где расположен основной конфигурационный файл сервера.

 docker exec -it qdatabase-17 bash -lc 'qsql -U qdbadmin -d qdb -c "SHOW config_file;"'
                config_file

--------------------------------------------
/var/lib/qdatabase-17/data/postgresql.conf
(1 row)

Этот файл содержит ключевые параметры работы СУБД, включая настройки памяти, процессов и портов.

Далее проверяем, сколько vCPU видит система внутри контейнера:

docker exec -it qdatabase-17 bash -lc 'grep -c ^processor /proc/cpuinfo'
8

Это важно, поскольку бесплатная версия поддерживает до 8 vCPU включительно. Если система видит большее количество ядер, СУБД может уйти в циклический перезапуск. В этом случае в каталоге /var/log/qdatabase-17/появляются логи с сообщением Digital Q.DataBase: License not initialized, reason: Core count violation.

Чтобы этого избежать, число vCPU необходимо ограничить на уровне Docker Engine или виртуальной машины, например в Docker Desktop через настройки General → Resources → CPU limit = 8. В конце статьи приведена отдельная команда для проверки и диагностики этой ситуации.

После этого проверяем файл сетевых правил доступа.

docker exec -it qdatabase-17 bash -lc 'qsql -U qdbadmin -d qdb -c "SHOW hba_file;"'
                hba_file

----------------------------------------
/var/lib/qdatabase-17/data/pg_hba.conf
(1 row)

Открываем файл pg_hba.conf для редактирования и временно разрешаем подключения с любых адресов. Это делается только для тестов и демо.

docker exec -it qdatabase-17 bash -lc 'nano /var/lib/qdatabase-17/data/pg_hba.conf'

В моем случае последние три строки разрешают доступ для всех подключений:

docker exec -it qdatabase-17 bash -lc 'tail -n 3 /var/lib/qdatabase-17/data/pg_hba.conf'

# allow all

host all all 0.0.0.0/0 md5
hostallall ::/0 md5

После этого перезапускаем контейнер, чтобы база перечитала конфигурацию и применила новые сетевые настройки.

docker restart qdatabase-17

Теперь заходим внутрь контейнера и начинаем проверку работы СУБД в разных диалектах.

sudo docker exec -it qdatabase-17 /bin/bash
root@ec348e89381b:/#

PostgreSQL-диалект

Сначала подключаемся к базе Digital Q.DataBase в родном PostgreSQL-диалекте PL/pgSQL под пользователем qdbadmin и выполняем базовые операции. Digital Q.DataBase полностью поддерживает нативный PL/pgSQL, поэтому начинать логично именно с него. Да, нам гораздо интереснее PL/SQL и T-SQL, но для первой проверки возьмем простой набор команд, который одинаково удобно выполнять и в консоли, и в DBeaver.

qsql -U qdbadmin qdb;
qsql (17.4)
Type "help" for help.
qdb=#

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(50) NOT NULL,
  email VARCHAR(100) UNIQUE,
  age INTEGER,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
;
CREATE TABLE

Командой ALTER ROLE мы можем в любой момент поменять пароль пользователя. Дефолтовый пароль: qdbadmin

qdb=# SELECT * FROM users;
id | name | email | age | created_at
----+------+-------+-----+------------
(0 rows)

qdb=# \dt
         List of relations
 Schema | Name  | Type  |  Owner  
--------+-------+-------+----------
 public | users | table | qdbadmin
(1 row)

qdb=# DROP TABLE users;
DROP TABLE

qdb=# ALTER ROLE qdbadmin WITH PASSWORD 'qdbadmin';
ALTER ROLE

qdb=# \q

Microsoft-диалект

Теперь самое время попробовать T-SQL. Подключимся через консоль, создадим базу qdb_demo_sales, а затем настроим к ней соединение в DBeaver  по порту 1433 и продолжим работу уже в привычном интерфейсе.

tsql -H localhost -p 1433 -U qdbadmin -P qdbadmin -D master

locale is "C"
locale charset is "ANSI_X3.4-1968"
using default charset "ISO-8859-1"
Setting master as default database in login packet
Changed database context to 'master'.
1>


IF DB_ID('qdb_demo_sales') IS NOT NULL
BEGIN
    ALTER DATABASE qdb_demo_sales
        SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
    DROP DATABASE qdb_demo_sales;
END;
GO

CREATE DATABASE qdb_demo_sales;
GO

Выходим (через exit) и далее создаем таблицу продаж. Цена хранится в UnitPrice, количество в Quantity, а сумма сделки и месяц продажи рассчитываются автоматически через вычисляемые колонки. Это позволяет не дублировать бизнес-логику в каждом запросе и сразу готовить данные для аналитики.

USE qdb_demo_sales;
GO

CREATE TABLE dbo.Sales (
    SaleID INT IDENTITY(1,1) PRIMARY KEY,
    ProductType NVARCHAR(50)  NOT NULL,
    ProductName NVARCHAR(150) NOT NULL,
    SaleDate    DATE          NOT NULL,
    Quantity    INT           NOT NULL CHECK (Quantity > 0),
    UnitPrice   DECIMAL(12,2) NOT NULL CHECK (UnitPrice >= 0),
    TotalAmount AS (Quantity * UnitPrice),
    SaleMonth   AS (FORMAT(SaleDate, 'yyyy-MM'))
);
GO

После этого загружаем тестовые данные. В примере используются продажи iPhone за 2025 год с ценами в рублях.

INSERT INTO dbo.Sales (ProductType, ProductName, SaleDate, Quantity, UnitPrice) VALUES
(N'Электроника', N'iPhone 15 Pro Max 256GB',   '2025-01-10', 2, 129990.00),
(N'Электроника', N'iPhone 15 Pro 128GB',       '2025-01-22', 1, 109990.00)
(N'Электроника', N'iPhone 15 128GB',           '2025-02-05', 3,  89990.00),
(N'Электроника', N'iPhone 14 Plus 128GB',      '2025-03-12', 1,  79990.00),
(N'Электроника', N'iPhone SE (3rd Gen) 64GB',  '2025-05-18', 4,  37990.00),
(N'Электроника', N'iPhone 15 Pro Max 512GB',   '2025-11-08', 1, 149990.00);
GO

Теперь, не меняя структуру данных и не создавая временных таблиц, мы строим итоговый аналитический отчет одним SQL-запросом.

Через CTE в одном месте задается период анализа, в других считаются ключевые показатели, определяется самый продаваемый товар и месяц с максимальной выручкой.

WITH base AS (
  SELECT *
  FROM dbo.Sales
  WHERE SaleDate BETWEEN CAST('2025-01-01' AS DATE)
                      AND CAST('2025-12-31' AS DATE)
),
kpi AS (
  SELECT
    COUNT(*)         AS количество_продаж,
    SUM(TotalAmount) AS выручка,
    AVG(TotalAmount) AS средний_чек,
    MAX(TotalAmount) AS максимальная_продажа,
    MIN(TotalAmount) AS минимальная_продажа
  FROM base
),
top_product AS (
  SELECT TOP (1)
    ProductName      AS топ_товар,
    SUM(TotalAmount) AS выручка_топ_товара
  FROM base
  GROUP BY ProductName
  ORDER BY SUM(TotalAmount) DESC
),
top_month AS (
  SELECT TOP (1)
    SaleMonth        AS лучший_месяц,
    SUM(TotalAmount) AS выручка_лучшего_месяца
  FROM base
  GROUP BY SaleMonth
  ORDER BY SUM(TotalAmount) DESC
)
SELECT
  k.количество_продаж,
  k.выручка,
  k.средний_чек,
  k.максимальная_продажа,
  k.минимальная_продажа,
  p.топ_товар,
  p.выручка_топ_товара,
  m.лучший_месяц,
  m.выручка_лучшего_месяца
FROM kpi k
CROSS JOIN top_product p
CROSS JOIN top_month m;
GO

В результате мы получаем полноценный аналитический отчет поверх реальных данных, написанный в привычном T-SQL-стиле. Все это выполняется в одном контейнере Digital Q.DataBase, который одновременно поддерживает PostgreSQL, MS SQL Server и Oracle-диалекты.

Для работы с TDS-диалектом достаточно настроить DBeaver или SSMS. Если клиент запускается с нуля, максимум, что потребуется, – это автоматически скачать драйвер одной кнопкой.

Далее указываем хост, порт 1433 и учётные данные по умолчанию qdbadmin:qdbadmin.

Oracle-диалект

Oracle-диалект тестируется иначе. Подключение через DBeaver здесь не используется, так как TNS недоступен.

Зато Oracle-совместимый SQL стабильно проверяется напрямую внутри контейнера через qclient, без внешних драйверов и дополнительной настройки.

Подключаемся к Oracle-диалекту:

qclient qdb://SYSDBA:SYSDBA@localhost:1521/qdbora

Теперь давайте напишем небольшой пример без таблиц и вспомогательных объектов, чтобы сосредоточиться именно на возможностях языка и системных пакетов. В этом скрипте используются DBMS_ASSERT, DBMS_RANDOM, UTL_CALL_STACK и DBMS_LOB, которые позволяют проверить корректность имен, сгенерировать данные и поработать с CLOB. Такой пример удобно запускать прямо из консоли и использовать как быстрый smoke-test Oracle-диалекта.

SET SERVEROUTPUT ON

DECLARE
  v_name   VARCHAR2(128);
  v_num    NUMBER;
  v_clob   CLOB;
  v_buf    VARCHAR2(4000);
  v_len    NUMBER := 4000;
BEGIN
  DBMS_OUTPUT.PUT_LINE('== QDB Oracle-диалект: mini demo ==');

  v_name := DBMS_ASSERT.SIMPLE_SQL_NAME('products_demo');
  DBMS_OUTPUT.PUT_LINE('DBMS_ASSERT: ' || v_name);

  v_num := TRUNC(DBMS_RANDOM.VALUE(1, 100));
  DBMS_OUTPUT.PUT_LINE('DBMS_RANDOM: ' || TO_CHAR(v_num));

  DBMS_OUTPUT.PUT_LINE('UTL_CALL_STACK.SUBPROGRAM(1): ' || UTL_CALL_STACK.SUBPROGRAM(1));

  DBMS_LOB.CREATETEMPORARY(v_clob, TRUE);
  DBMS_LOB.WRITE(v_clob, LENGTH('Hello from DBMS_LOB'), 1, 'Hello from DBMS_LOB');
  DBMS_LOB.READ(v_clob, v_len, 1, v_buf);
  DBMS_OUTPUT.PUT_LINE('DBMS_LOB: ' || v_buf);
  DBMS_LOB.FREETEMPORARY(v_clob);

  DBMS_OUTPUT.PUT_LINE('== OK ==');
END;
/

Проверка SQL-имен через DBMS_ASSERT, генерация случайных значений с DBMS_RANDOM, доступ к стеку вызовов через UTL_CALL_STACK и работа с CLOB через DBMS_LOB.

== QDB Oracle-диалект: mini demo ==
DBMS_ASSERT: products_demo
DBMS_RANDOM: 54
UTL_CALL_STACK.SUBPROGRAM(1): __anonymous_block
DBMS_LOB: Hello from DBMS_LOB
== OK ==

     PL/SQL procedure successfully completed.

Напоминаю, что внутри дистрибутива, будь то Docker-контейнер или deb-пакеты для Astra Linux, есть папка samples. В ней собраны примеры как для T-SQL, так и для PL/SQL, которые позволяют быстро начать работу и сразу протестировать возможности нашей СУБД. Эти примеры дают понятный и удобный порог входа и помогают быстрее разобраться с функциональностью платформы.

Лицензия и отладка

Если контейнер с Digital Q.DataBase не запускается, одна из частых причин – ограничение лицензии по количеству ядер.

Лицензия ограничивает число процессорных ядер, на которых может работать продукт, и неважно, физические это ядра или виртуальные. В Docker и виртуальных машинах учитываются vCPU, то есть логические процессоры, которые видит операционная система. Проверка выполняется по данным ОС, в том числе через файл /proc/cpuinfo.

Чтобы быстро понять, в этом ли проблема, можно запустить отладочный контейнер и посмотреть, сколько CPU реально доступно внутри:

docker run --rm -it --name qdb-debug \
--platform linux/amd64 \
--entrypoint bash \
qdatabase_docker -lc '
/opt/qdatabase-17/start_qdb1.sh >/dev/null 2>&1 &
sleep 8

 echo "===== CPU VISIBILITY ====="
echo -n "nproc: "
nproc
echo -n "/proc/cpuinfo processors: "
grep -c ^processor /proc/cpuinfo

echo
echo "===== CORE COUNT VIOLATION ====="
grep -RIn "Core count violation" /var/log/qdatabase-17 | head -n 5
'

Если в выводе видно, что система видит больше ядер, чем разрешено лицензией, и в логах присутствует сообщение Core count violation, значит причина найдена. Решение простое: зайти в настройки Docker Desktop, открыть раздел General или Resources, выставить CPU limit в значение, укладывающееся в лицензию, например, 8 ядер, нажать Apply & Restart и перезапустить контейнер.

После примен��ния этих настроек ограничение по ядрам больше не нарушается, лицензия инициализируется корректно.

===== CPU VISIBILITY =====
nproc: 10
/proc/cpuinfo processors: 10

===== CORE COUNT VIOLATION =====
/var/log/qdatabase-17/postgresql-09.log:1:2026-02-09 18:47:21.144 MSK 22 @ from  [vxid: txid:0] [] [] LOG: 
Q.DataBase: License not initialized, reason: Core count violation.

Мастера

О том, как работают наши мастера переноса и сравнения БД, читайте предыдущую статью.

Для запуска мастеров в контейнере достаточно подготовить YAML-файлы конфигурации: отдельно описать подключение к исходной БД (source.yaml) и к целевой Digital Q.DataBase (target.yaml), а при выборочном переносе – добавить файл с перечнем объектов (cs.yaml).

Команды переноса и сравнения выполняются внутри уже запущенного контейнера, поэтому сначала открываем терминал контейнера через docker exec, а затем запускаем нужный бинарник из каталога /opt/qdb/bin.

Такой подход удобен тем, что не нужно поднимать дополнительные контейнеры: сервер и утилиты уже находятся в одном окружении, а конфиги можно хранить на хосте и передавать внутрь (например, через volume) или создавать прямо в контейнере.

После переноса мастер сравнения запускается тем же способом – через docker exec и команду сравнения с YAML-конфигом.

Зайти в контейнер
sudo docker exec -it qdatabase-17 /bin/bash

Пример: полный перенос
/opt/qdb/bin/mssql_migrator -config-source source.yaml -config-target target.yaml -scope all -target-action new

Пример: частичный перенос
/opt/qdb/bin/mssql_migrator -config-source source.yaml -config-target target.yaml -scope custom -object-file cs.yaml -target-action new

Пример: сравнение
/opt/qdb/bin/db_compare_mssql -config compare.yaml

Вывод

Digital Q.DataBase в Docker позволяет за четыре команды проверить совместимость с PostgreSQL, T-SQL и Oracle-диалектом без отдельной установки и сложных окружений. Все ключевые сценарии, от аналитических запросов до миграции и smoke-тестов, выполняются в одном контейнере и сразу дают нужный результат.