Обслуживая 1С, мы сталкивается с проблемой копий баз для разработки и тестирования. Особенно это больно на больших базах (от 100Гб). Делать копии средствами 1С - совсем моветон, а pg_dump/pg_restore - долго. А если нам надо не одну, а три или более копии рабочей базы? Допустим, у нас сразу несколько разработчиков хотят выполнить разные доработки / эксперименты / тесты. Допустим, рабочая база 250Гб. Тогда потребуется 1Тб для разработки. Зачастую у компаний не одна база. Зачастую требуются копии не только текущей точки, но и на определенные моменты времени. Даже с настроенным бекапом с PITR, восстановление инстанса с отдельной базой - целое дело.
Для быстрых экспериментов с базами PostgreSQL - есть классное решение от postgres.ai
Представьте, что копия рабочего инстанса создается мгновенно (секунды), с возможностью выбора даты. Представьте, что для нескольких параллельных экспериментов с базой в 250Гб, не потребуется терабайт. Ребята сделали классный сервис над ZFS, реализующий "тонкие клоны" и дают бесплатную Community Edition!
Более подробно в их GitLab-реппозитории
Кому-то лучше воспринимать из видеопрезентации
Как запустить это для 1С?
Есть официальная инструкция, по ней довольно легко запускается решение в варианте с логическим копированием (через dump/restore), но это "из коробки" не сработает для PostgreSQL для 1С. Для 1С же мы используем не ванильный PostgreSQL, а специфичный, именно это создает сложность. Мы должны будем переопределить шаблоны (images) docker контейнеров на свои (как минимум из-за локалей ru_RU.UTF-8).
У нас в Яндекс.Облаке развернут типичный сетап инфраструктуры 1С:
- - сервер БД PostgreSQL (1С сборка от Postgres Pro)
- - сервер 1С
- - сервер лицензирования
- - web-сервер
Там же рядом (для скорости - в той же зоне доступности) создаем виртуалку +/- не сильно хуже рабочего сервера БД, чтобы разработчикам было комфортно разрабатывать/тестировать. Для системы можно использовать небольшой SSD и отдельно добавляем дополнительный диск для баз данных, объемом как минимум равным объему рабочего инстанса PostgreSQL. Объем будет определяться тем, какую глубину снепшотов захотите хранить и как много параллельной разработки/экспериментов. За счет сжатия ZFS у нас получается в 2-3 раза меньший объем.
Подготовка dev-сервера
Устанавливаем Docker
Устанавливаем Docker Compose
Проверяем, как идентифицировался доп.диск в виртуалке
$ sudo lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
loop0 7:0 0 63.3M 1 loop /snap/core20/1822
loop1 7:1 0 111.9M 1 loop /snap/lxd/24322
loop2 7:2 0 49.8M 1 loop /snap/snapd/18357
loop3 7:3 0 87M 1 loop /snap/lxd/28373
vda 252:0 0 40G 0 disk << системный диск
_00;^72;vda1 252:1 0 1M 0 part
^92;^72;vda2 252:2 0 40G 0 part /
vdb 252:16 0 186G 0 disk << доп.диск под ZFS для баз PostgreSQL
Создаем zfs-pool на /dev/vdb:
sudo zpool create -f \
-O compression=on \
-O atime=off \
-O recordsize=128k \
-O logbias=throughput \
-m /var/lib/dblab/dblab_pool \
dblab_pool \
/dev/vdb
(выше в инструкции на postgres.ai описан трейдоф о размере блока, я оставляю 128k в угоду большему сжатию)
Проверяем результат:
$ zfs list
NAME USED AVAIL REFER MOUNTPOINT
dblab_pool 408K 179G 96K /var/lib/dblab/dblab_pool
Нам потребуются два docker образа:
- один для контейнера, выполняющего и поддерживающего реплику основного инстанса PostgreSQL
- второй для контейнеров, запускающих тонкие клоны, именно с ним будут работать 1С разработчики
Для первого мы за основу берем оригинальный Dockerfile от postgres.ai, добавляем ему локали и я убрал установку доп.модулей PostgreSQL.
Dockerfile.dblab-ru:
ARG PG_SERVER_VERSION=15
# Build the extended image
FROM postgres:${PG_SERVER_VERSION}-bullseye
LABEL maintainer="postgres.ai"
ARG PG_SERVER_VERSION
ENV PG_SERVER_VERSION=${PG_SERVER_VERSION:-15}
ARG PG_UNIX_SOCKET_DIR
ENV PG_UNIX_SOCKET_DIR=${PG_UNIX_SOCKET_DIR:-"/var/run/postgresql"}
ARG PG_SERVER_PORT
ENV PG_SERVER_PORT=${PG_SERVER_PORT:-5432}
RUN apt-get clean && rm -rf /var/lib/apt/lists/partial \
# remove the "beta" and "rc" suffix in the PG_SERVER_VERSION variable (if exists)
&& PG_SERVER_VERSION="$( echo ${PG_SERVER_VERSION} | sed 's/beta.*//' | sed 's/rc.*//' )" \
&& apt-get update -o Acquire::CompressionTypes::Order::=gz \
&& apt-get install --no-install-recommends -y wget make gcc unzip sudo git \
curl libc6-dev apt-transport-https ca-certificates pgxnclient bc \
build-essential libssl-dev krb5-multidev libkrb5-dev lsb-release apt-utils \
&& apt-get install --no-install-recommends -y postgresql-server-dev-${PG_SERVER_VERSION} \
# plpython3 (procedural language implementation for Python 3.x)
&& apt-get install --no-install-recommends -y postgresql-plpython3-${PG_SERVER_VERSION} \
# remove all auxilary packages to reduce final image size
&& cd / && rm -rf /tmp/* && apt-get purge -y --auto-remove \
gcc make wget unzip curl libc6-dev apt-transport-https git \
postgresql-server-dev-${PG_SERVER_VERSION} pgxnclient build-essential \
libssl-dev krb5-multidev comerr-dev krb5-multidev libkrb5-dev apt-utils lsb-release \
libgssrpc4 libevent-dev libbrotli-dev \
&& apt-get clean -y autoclean \
&& rm -rf /var/lib/apt/lists/* \
# remove standard pgdata
&& rm -rf /var/lib/postgresql/${PG_SERVER_VERSION}/ \
# add ru locale
&& /bin/sed -i 's/# ru_RU.UTF-8/ru_RU.UTF-8/g; s/# en_US.UTF-8/en_US.UTF-8/g' /etc/locale.gen && /usr/sbin/locale-gen && /bin/echo "LANG=en_US.UTF-8" >> /etc/default/locale
EXPOSE ${PG_SERVER_PORT}
# Prepare Postgres start script
RUN echo "#!/bin/bash" > /pg_start.sh && chmod a+x /pg_start.sh \
&& echo "chown -R postgres:postgres \${PGDATA} \${PG_UNIX_SOCKET_DIR}" \
>> /pg_start.sh \
&& printf "sudo -Eu postgres /usr/lib/postgresql/$(echo ${PG_SERVER_VERSION} | sed 's/beta.*//' | sed 's/rc.*//')/bin/postgres -D \${PGDATA} -k \${PG_UNIX_SOCKET_DIR} -p \${PG_SERVER_PORT} >& /proc/1/fd/1 \n" \
>> /pg_start.sh \
# Infinite sleep to allow restarting Postgres
&& echo "/bin/bash -c \"trap : TERM INT; sleep infinity & wait\"" \
>> /pg_start.sh
CMD ["/pg_start.sh"]
Для второго мы сами на базе ubuntu:22.04 разворачиваем PostgreSQL от Рostgres PRO
Dockerfile.1c:
FROM ubuntu:22.04
ARG PG_SERVER_VERSION
ENV PG_SERVER_VERSION=${PG_SERVER_VERSION:-15}
ARG PG_UNIX_SOCKET_DIR
ENV PG_UNIX_SOCKET_DIR=${PG_UNIX_SOCKET_DIR:-"/var/run/postgresql"}
ARG PG_SERVER_PORT
ENV PG_SERVER_PORT=${PG_SERVER_PORT:-5432}
RUN apt-get clean && rm -rf /var/lib/apt/lists/partial \
# install dependencies
&& apt-get update -o Acquire::CompressionTypes::Order::=gz \
&& apt-get install --no-install-recommends -y apt-transport-https ca-certificates \
wget curl sudo
RUN wget --no-check-certificate https://repo.postgrespro.ru/1c/1c-15/keys/pgpro-repo-add.sh && \
sh pgpro-repo-add.sh && \
apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y postgrespro-1c-15 && \
/bin/sed -i 's/# ru_RU.UTF-8/ru_RU.UTF-8/g; s/# en_US.UTF-8/en_US.UTF-8/g' /etc/locale.gen && /usr/sbin/locale-gen && /bin/echo "LANG=en_US.UTF-8" >> /etc/default/locale && \
rm -rf /var/lib/apt/lists/*
EXPOSE ${PG_SERVER_PORT}
# Prepare Postgres start script
RUN echo "#!/bin/bash" > /pg_start.sh && chmod a+x /pg_start.sh \
&& echo "chown -R postgres:postgres \${PGDATA} \${PG_UNIX_SOCKET_DIR}" \
>> /pg_start.sh \
&& printf "sudo -Eu postgres /opt/pgpro/1c-15/bin/postgres -D \${PGDATA} -k \${PG_UNIX_SOCKET_DIR} -p \${PG_SERVER_PORT} >& /proc/1/fd/1 \n" \
>> /pg_start.sh \
# Infinite sleep to allow restarting Postgres
&& echo "/bin/bash -c \"trap : TERM INT; sleep infinity & wait\"" \
>> /pg_start.sh
CMD ["/pg_start.sh"]
Билдим оба образа:
sudo docker build -t pg-dblab-ru:v1 -f Dockerfile.dblab-ru .
sudo docker build -t pg-1c-ru:v1 -f Dockerfile.1c .
`pg-dblab-ru:v1` и `pg-1c-ru:v1`нужно будет указать далее в настройках в качестве образов контейнеров
Получившиеся образы:
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
pg-1c-ru v1 f2abd2f089e2 3 seconds ago 249MB
pg-dblab-ru v1 4f129cca299a 50 minutes ago 427MB
Чтобы перейти к заполнению файла настроек `~/.dblab/engine/configs/server.yml` нам потребуется небольшая подготовка продуктового инстанса PostgreSQL
Подготовка продуктового инстанса PostgreSQL
- Создадим пользователя для репликации
CREATE USER pgdev_user REPLICATION LOGIN PASSWORD 'superpassword';
- Разрешим подключение с определенного IP
в файл /var/lib/pgpro/1c-15/data/pg_hba.conf добавляем запись:
# TYPE DATABASE USER ADDRESS METHOD
host replication pgdev_user 10.х.х.х/32 md5
где 10.х.х.х - адрес вашего Dev-инстанса
- Включим настройки физической репликации
в файле /var/lib/pgpro/1c-15/data/postgresql.conf проверяем:
wal_level = replica # set WAL level to 'replica' for physical replication
max_wal_senders = 10 # max number of walsender processes
- Создадим слот репликации
SELECT pg_create_physical_replication_slot('dev_replica');
В процессе, не забываем перезапустить, чтобы применились настройки:
`sudo systemctl restart postgrespro-1c-15.service` или `SELECT pg_reload_conf();`
Подготовка файла настроек DBLab
Исходный пример файла настроек
`mkdir -p .dblab/engine/configs`
Редактируем файл ~/.dblab/engine/configs/server.yml под свои данные:
verificationToken: "secret_token" # Меняем дефолтный token авторизации на что-то свое
...
# Configure PostgreSQL containers
databaseContainer: &db_container
# Database Lab provisions thin clones using Docker containers and uses auxiliary containers.
# We need to specify which Postgres Docker image is to be used for that.
# The default is the extended Postgres image built on top of the official Postgres image
# (See https://postgres.ai/docs/database-lab/supported_databases).
# Any custom or official Docker image that runs Postgres. Our Dockerfile
# (See https://gitlab.com/postgres-ai/custom-images/-/tree/master/extended)
# is recommended in case if customization is needed.
#dockerImage: "postgresai/extended-postgres:14" # переопределяем
dockerImage: "pg-1c-ru:v1" # на свой шаблон контейнера
...
# Adjust PostgreSQL configuration
# Параметры контейнеров для клонов
databaseConfigs: &db_configs
configs:
# In order to match production plans with Database Lab plans set parameters related to Query Planning as on production.
# shared_buffers: 1GB
# shared_preload_libraries – copy the value from the source
# Adding shared preload libraries, make sure that there are "pg_stat_statements, auto_explain, logerrors" in the list.
# It is necessary to perform query and db migration analysis.
# Note, if you are using PostgreSQL 9.6 and older, remove the logerrors extension from the list since it is not supported.
#shared_preload_libraries: "pg_stat_statements, pg_stat_kcache, auto_explain, logerrors"
shared_preload_libraries: "" ### отключаем доп.модули
# work_mem and all the Query Planning parameters – copy the values from the source.
# Detailed guide: https://postgres.ai/docs/how-to-guides/administration/postgresql-configuration#postgresql-configuration-in-clones
# work_mem: "100MB"
# ... put Query Planning parameters here
### и добавляем свои параметры PostgreSQL, адаптируя под dev-машинку
lc_messages: "en_US.UTF-8"
lc_monetary: "ru_RU.UTF-8"
lc_numeric: "ru_RU.UTF-8"
lc_time: "ru_RU.UTF-8"
max_connections: 100
default_text_search_config: 'pg_catalog.russian'
shared_buffers: 2988MB # 25% of RAM
temp_buffers: 128MB
...
### Далее в блоке provision
provision:
<<: *db_container
...
# IP addresses that can be used to access clones.
# By default, using a loop-back to accept only local connections.
# The empty string means "all available addresses".
# The option supports multiple IPs (using comma-separated format) and IPv6 addresses (for example, [::1])
#cloneAccessAddresses: "127.0.0.1"
cloneAccessAddresses: ""
### по-хорошему тут нужно указать адрес сервера 1С
### у нас это все в закрытом контуре, поэтому оставляю пустым
### Далее в блоке retrieval
retrieval:
# The jobs section must not contain physical and logical restore jobs simultaneously.
jobs:
- physicalRestore
- physicalSnapshot
spec:
# Restores database data from a physical backup.
physicalRestore:
options:
#dockerImage: "postgresai/extended-postgres:14"
dockerImage: "pg-dblab-ru:v1"
### переопределяем шаблон контейнера на адаптированный (в нем добавлену ru локали)
<<: *db_container
# Defines the tool to restore data.
tool: customTool
...
### Далее в блоке Sync задаем критичные параметры PostgreSQL
sync:
# Enable running of a sync instance.
enabled: true
...
# Add PostgreSQL configuration parameters to the sync container.
# Параметры контейнера репликации
configs:
shared_buffers: 2GB
### для работающей репликации нам нужны
### не все настройки рабочего инстанса
### а только эти два критичных:
max_connections: 100
max_locks_per_transaction: 256
### Далее задаем переменные окружения
envs:
PGUSER: "pgdev_user"
PGPASSWORD: "********"
PGHOST: "10.х.х.х"
PGPORT: 5432
customTool:
# To use pg_basebackup, specify environment variables in "envs".
# Do not edit PostgreSQL data directory (-D).
# Note that command chains are not supported here; if you need to use a more
# complicated snippet, create a shell script, use --mount (-v) option
# when starting a container with Database Lab and use path to it here.
# Write your data to dataDir defined in "global.config"
#command: "pg_basebackup -P -R -X stream -D /var/lib/dblab/dblab_pool/data"
command: "pg_basebackup -P -R --slot=dev_replica -X stream -D /var/lib/dblab/dblab_pool/data"
### и переопределяем команду репликации
# PostgreSQL "restore_command" configuration option.
restore_command: ""
### в блоке physicalSnapshot тоже переопределяем шаблон контейнера
physicalSnapshot:
options:
...
# Promote PGDATA after data fetching.
promotion:
<<: *db_container
# Enable PGDATA promotion.
enabled: true
### переопределяем на свой шаблон
dockerImage: "pg-dblab-ru:v1"
...
### И последнее, задаем расписание снепшотов
### И их количество (глубину)
# Scheduler contains tasks that run on a schedule.
scheduler:
# Snapshot scheduler creates a new snapshot on a schedule.
snapshot:
# Timetable defines in crontab format: https://en.wikipedia.org/wiki/Cron#Overview
timetable: "0 0 * * *" ### раз в сутки в полночь создаем снепшот реплики
# Retention scheduler cleans up old snapshots on a schedule.
retention:
### каждый час проверяем нужно ли удалить старые снепшоты
timetable: "0 * * * *"
# Limit defines how many snapshots should be hold.
limit: 10 ### задаем количество хранимых снепшотов
### При такой настройке у нас будет доступны слепки рабочих баз
### за последние 10 дней
Ссылка на полный вариант нашего файла настроек: dev-server-1c-example.yml в начале статьи.
Все готово, чтобы запустить сервис.
docker-compose.yml
version: '3.8'
services:
dblab_server:
container_name: dblab_server
labels:
- dblab_control
privileged: true
ports:
- "2345:2345"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /var/lib/dblab:/var/lib/dblab/:rshared
- .dblab/engine/configs:/home/dblab/configs
- .dblab/engine/meta:/home/dblab/meta
- .dblab/engine/logs:/home/dblab/logs
- /sys/kernel/debug:/sys/kernel/debug:rw
- /lib/modules:/lib/modules:ro
- /proc:/host_proc:ro
environment:
- DOCKER_API_VERSION=1.39
restart: unless-stopped
image: postgresai/dblab-server:3.5.0
Настройки запуска контейнера получаются не самые безопасные, поэтому лучше dev-машинку запускать в закрытом контуре, не выставляя наружу.
Можно использовать виртуалку с web-сервером как Jump Server. Тогда сначала устанавливаем соединение: `ssh -N -L 2346:127.0.0.1:2346 dev-server`
Затем в браузере можем открывать: http://127.0.0.1:2346
Результат
После запуска будет создано несколько контейнеров:
$ sudo docker ps -a --format "table {{.ID}}\t{{.Names}}\t{{.State}}\t{{.Image}}"
CONTAINER ID NAMES IMAGE
ad6c0f30a73b dblab_sync_xxxxxx pg-1c-ru:v2
9ca5fc83ce16 dblab_embedded_ui_xxxxxx postgresai/ce-ui:latest
2805086d0ede dblab_server postgresai/dblab-server:3.5.0
dblab_server - основной сервис, в нем смотрим все логи процесса
dblab_embedded_ui_xxxxxx - из названия понятно, что это фронтенд
dblab_sync_xxxxxx - контейнер репликации
Далее по заданному расписанию будут создаваться ежедневные снепшоты. И еще при старте основного сервиса, автоматически запускается создание снепшота (отключается в настройках server.xml).
Теперь мы можем создавать для разработчиков клоны инстанса PostgreSQL на любой из снепшотов. Клон можно отметить как защищенный (автоматически не удаляемый). В настройках server.xml можно задать "время неактивности", после которого клоны будут удаляться автоматически (по умолчанию 3 часа).
При создании клона задаем произвольный ID, имя пользователя и сложный пароль. Эти данные будем далее использовать при добавлении dev-базы на сервере 1С. Клонам автоматически по порядку назначаются порты 6000, 6001... (диапазон задается в server.xml). Теперь можем добавить базу в 1С:
Пользователь и пароль БД: uclone / ***
Сервер: dev-server port=6000
Где dev-server - имя или IP-адрес dev-сервера
Классно, что у клона можно вызвать сброс (reset) на нужную точку (на нужный снепшот) и таким образом производить множество быстрых итераций экспериментов с базами 1С.
Как это выглядит на ZFS:
zfs list
NAME USED AVAIL REFER MOUNTPOINT
dblab_pool 20.9G 158G 20.5G /var/lib/dblab/dblab_pool
dblab_pool/clone_pre_20240503184521 70.9M 158G 20.5G /var/lib/dblab/dblab_pool/clones/clone_pre_20240503184521
dblab_pool/dblab_clone_6000 126M 158G 20.6G /var/lib/dblab/dblab_pool/clones/dblab_clone_6000
/var/lib/dblab/dblab_pool/data - тут расположена реплика
dblab_pool/clone_pre_20240503184521 - снепшот реплики после первого запуска
dblab_pool/dblab_clone_6000 - клон, с которым можем проводить эксперименты.