Резервное копирование 1С 8.3 в dt не закрывая сессии пользователей (ibcmd.exe)

17.10.2023

Введение

Важность резервного копирования баз 1С нельзя недооценить, поскольку оно является ключевым инструментом для защиты и восстановления данных в случае непредвиденных ситуаций, таких как системные ошибки, аппаратные сбои, человеческие ошибки или даже хакерские атаки или просто кривые руки.

Поддержание надежного процесса резервного копирования является критическим аспектом для организаций, работающих с программным обеспечением 1С, и обеспечивает спокойствие и уверенность в случае возникновения проблем с данными.

Сейчас уже врятли кто-то пользуется файловой базой данных 1С. Скорее всего, в 99% случаев у каждого база данных либо на PostgreSQL либо на MSSQLServer. Но резервное копирование, а именно dump базы данных в формате *.dump или *.bak имеет как свои плюсы, так и минусы. Одним из критических минусов (лично для меня) – это то, что восстановление такой резервной копии 1С напрямую зависит от версии SQL сервера, которым она была создана. На этом фоне резервная копия 1С в формате *.dt выглядит намного привлекательнее, так как она не зависит от версии SQL сервера и её можно восстановть в любую базу 1С.

Да, не забываем про инкрементальные бэкапы, их некто не отменял, да и я не говорил что нужно полностью отказаться от резервного копирования 1с средствами самого SQL сервера. Но, для надёжности я предлагаю делать резервную копию в *.dt как дополнение к основой системе резервного копирования.

Хотя кому-то описаный ниже вариант может подойти и в качестве осного инструмента.

В то же время при выгрузке информационной базы в файл и последующей загрузке предполагается, что данные в базе данных находятся в корректном состоянии.

Если в базе данных информация находится с нарушениями, то выгрузка может быть выполнена, а процесс загрузки может завершиться неудачно.

И так, я предлагаю следующую связку:

  • Создаём резервную копию при помощи утилиты ibcmd.exe в формате *.dt (штатная утилита 1С)
  • Запаковываем его в архив с паролем при помощи 7z (скачать)
  • Отправляем пароль себе в Telegram
  • Отправляем файл с архивом на сетевой диск (в моём случае это Synology)

Можите использовать мою связку, но можно что-то из этого выкинуть или что-то своё добавить туда. На вкус и цвет все фломастеры разные.

Немного о ibcmd.exe

Что такое за утилита ibcmd.exe и зачем она нужна?

ibcmd.exe – это утилита администрирования, которую можно установить вместе с кластером серверов 1С и она является одной из утилит Автономного сервера. После установки автономного сервера 1С в каталоге bin появятся две утилиты:

  • ibsrv.exe – это файл самого автономного сервера
  • ibcmd.exe – а это, нужная нам утилита администрирования (которая в свою очередь работает в 2х режимах)
    • Server – создание или изменения конфигурационных файлов для Автономного сервера.
    • Infobase – выполнение различных действий с информационными базами.

ibcmd представляет из себя консоньлную утилиту, а согласно документации, предназначена для конфигурирования сервера. Но на самом деле спектр возможностей намного шире:

  • Создание базы
  • Создать/загрузить выгрузку из dt-файла.
  • Создать/загрузить Конфигурацию из cf-файла.
  • Создать/загрузить Конфигурацию из xml-файлов.
  • Загрузка/выгрузка xml, cfe, сf, dt.
  • Обновление конфигурации базы данных.
  • Проверка конфигурации.
  • Восстановление конфигурации базы данных.
  • Очистка информационной базы.

В этой статье ibcmd.exe нам нужна для создания резервной копии в формате *.dt. Будем использовать режи infobase.

Почему всё-таки ibcmd.exe?

  • ibcmd может выполнять команды непосредственно на сервере, без установленного клиента.
  • ibcmd не требует установленной лицензии, ни клиентской ни серверной.
  • ibcmd может выгрузить базу даже если в этот момент запущен конфигуратор или в базе есть активные пользовательские сессии.
  • ibcmd работает в формате командной строки, что нам сильно упрощает жизнь.

Вот пример команды для выполнения резервного копирования базы 1С в .dt средствами ibcmd:

ibcmd.exe infobase dump --dbms=MSSQLServer --db-server=SQL_SERVER_NAME --db-name=SQL_DB_NAME --db-user=DB_USERNAME --db-pwd=DB_PASSWORD --user=1C_USERNAME --password=1C_PASSWORD C:\backup\SQL_DB_NAME_date.dt
  • --dbms – тип SQL сервера с базами данных.
  • --db-server – адрес или доменное имя по которому доступен SQL сервер.
  • --db-name – имя базы данных на SQL сервере.
  • --db-user – имя пользователя на SQL сервере, которому доступны необходимые базы данных.
  • --db-pwd – пароль от этого пользователя.
  • --user – имя пользователя в 1С от этой базы.
  • --password – и пароль от него. (ранее программа работала без пользователя 1С)
  • C:\backup\SQL_DB_NAME_date.dt – путь, куда будет выгружена база данных.

По сути весь скрипт резервного копирования 1С при помощи ibcmd будет строиться вокруг команды выше.

Пишем скрипт резервного копирования 1С

Для написания скрипта я буду использовать язык программирования Python, так как в этом случае мне просто-напросто удобнее его использовать, нежели чем powershell или bat.

Будем использовать модуль logging для логирования хода выполнения скрипта в файл msg.log. А также модули ниже:

import logging, subprocess, os
from subprocess import check_output
from datetime import datetime
import re, requests, secrets, string

Определим необходимые переменные для скрипта. Думаю, что по подписи там всё предельно понятно за что каждая переменная будет отвечать. Комментарии излишне на мой взгляд.

ibcmdlocation = 'C:\\Program Files\\1cv8\\8.3.23.1782\\bin\\'
dbms = 'MSSQLServer'
dbserver = '"localhost"'
dbnames = ['ooo_romashka_bp', 'ooo_romashka_zup', 'ooo_romashka_ut']
dbuser = 'SQL_USER'
dbpwd = '"SQL_PASSWORD"'
user = '"1C_USER"'
password = '"1C_PASSWORD"'

nasuser = 'NAS_USER'
naspassword = 'NAS_USER_PASSWORD'
destination = 'C:\\backup\\'

Далее, перед циклом for подключим сетевой диск, куда будем складывать наши резервные копии, где NAS_IP_ADDRES – это аресс вашего сетевого диска.

os.popen(f'NET USE \\\\NAS_IP_ADDRES\\BACKUP /u:{nasuser} "{naspassword}"')

Создаем на нём папку с датой под наши резервные копии 1С:

folderdate = datetime.today().strftime('%d%m%Y')
os.popen(f'mkdir  \\\\NAS_IP_ADDRES\\BACKUP\\{folderdate}')

Генерируем пароль под создаваемые аривы. Тоесть, каждый день от создаваемых архивов будет новый пароль. Длину пароля я установил в 20 символов.

alphabet = string.ascii_letters + string.digits
zp_password = ''.join(secrets.choice(alphabet) for i in range(20)) 

Создадим функцию для отправки сообщения через Telegram бота при помощи API. Подробнее с самим API можно ознакомиться тут. Функция в себя будет принимать сообщение и отправлять нам в ТГ. Для этого нужно будет зарегистрировать бота и узнать нашь ID, но об этом чуть ниже. А пока:

def send_tg(msg):
    string = f"https://api.telegram.org/botTOKEN/sendMessage?chat_id=CHAT_ID&text={msg}"
    requests.get(string).json()

Далее по скрипту отправим уведомление себе в телеграмм о начале выполнения скрипта резервного копирования 1С по расписанию:

send_tg(f'''{datetime.today().strftime('%d.%m.%Y')}\n{zp_password}''')

Не забываем, мы же хотели логировать всё происходящее в файл:

logging.basicConfig(filename='C:\\backup\\msg.log', encoding='utf-8', filemode='a', format='%(asctime)s - %(levelname)s - %(message)s', level=logging.INFO)

Теперь можно приступить к написанию самого цикла, в котором будем проходиться по всем базам в списке dbnames :

for dbname in dbnames:
    logging.info(f'База [{dbname}] - начало резервного копирования')
    
    datedumpstamp = datetime.today().strftime('%d%m%Y_%H%M%S')
    dbfile =  f'{dbname}_{datedumpstamp}.dt'
    command = f'"{ibcmdlocation}ibcmd.exe" infobase dump --dbms={dbms} --db-server={dbserver} --db-name={dbname} --db-user={dbuser} --db-pwd={dbpwd} --user={user} --password={password} {destination}{dbfile}'

    backup = check_output(command, shell=True).decode('utf-8')
    backup = re.sub('^\s+|\n|\r|\s+$', '', backup)
    logging.info(f'{backup}')

    logging.info(f'База [{dbname}] - резервная копия успешно выгружена!')

    ### ШИФРОВАНИЕ АРХИВА
    zp_file = f'{destination}{dbname}_{datedumpstamp}.7z'    
    zp_command = f'"C:\Program Files\\7-Zip\\7z.exe" a -mhe=on -ssw -mx0 -p{zp_password} {zp_file} {destination}{dbfile}'
    zp = check_output(zp_command, shell=True).decode('utf-8')
    zp = re.sub('^\s+|\n|\r|\s+$', '', zp)
    logging.info(f'{zp}')

    os.popen(f'del -f {destination}{dbfile}')
    os.popen(f'move {zp_file} \\\\NAS_IP_ADDRES\\BACKUP\\{folderdate}\\{dbname}_{datedumpstamp}.7z')
	
    logging.info(f'База [{dbname}] - перенесена на сетевой диск!')

После того как циклом пройдёмся по всем базам нам нужно закрыть открытое соединение. Для верности я сбрасываю процесс rphost.exe, но это не обязательно. И отправлю себе уведомление что резервное копирование 1С успешно завершено.

os.popen('NET USE \\\\NAS_IP_ADDRES\\BACKUP /D')
os.popen('Taskkill /IM rphost.exe /F')
send_tg('Резервное копирование завершено')

Готово! Ниже скрипт целиком:

import logging, subprocess, os
from subprocess import check_output
from datetime import datetime
import re, requests, secrets, string

logging.basicConfig(filename='C:\\backup\\msg.log', encoding='utf-8', filemode='a', format='%(asctime)s - %(levelname)s - %(message)s', level=logging.INFO)

ibcmdlocation = 'C:\\Program Files\\1cv8\\8.3.23.1782\\bin\\'
dbms = 'MSSQLServer'
dbserver = '"localhost"'
dbnames = ['ooo_romashka_bp', 'ooo_romashka_zup', 'ooo_romashka_ut']
dbuser = 'SQL_USER'
dbpwd = '"SQL_PASSWORD"'
user = '"1C_USER"'
password = '"1C_PASSWORD"'
nasuser = 'NAS_USER'
naspassword = 'NAS_USER_PASSWORD'
destination = 'C:\\backup\\'

os.popen(f'NET USE \\\\NAS_IP_ADDRES\\BACKUP /u:{nasuser} "{naspassword}"')

folderdate = datetime.today().strftime('%d%m%Y')
os.popen(f'mkdir  \\\\NAS_IP_ADDRES\\BACKUP\\{folderdate}')

alphabet = string.ascii_letters + string.digits
zp_password = ''.join(secrets.choice(alphabet) for i in range(20)) 

def send_tg(msg):
    string = f"https://api.telegram.org/botTOKEN/sendMessage?chat_id=CHAT_ID&text={msg}"
    requests.get(string).json()
	
send_tg(f'''{datetime.today().strftime('%d.%m.%Y')}\n{zp_password}''')


for dbname in dbnames:
    logging.info(f'База [{dbname}] - начало резервного копирования')
    
    datedumpstamp = datetime.today().strftime('%d%m%Y_%H%M%S')
    dbfile =  f'{dbname}_{datedumpstamp}.dt'
    command = f'"{ibcmdlocation}ibcmd.exe" infobase dump --dbms={dbms} --db-server={dbserver} --db-name={dbname} --db-user={dbuser} --db-pwd={dbpwd} --user={user} --password={password} {destination}{dbfile}'

    backup = check_output(command, shell=True).decode('utf-8')
    backup = re.sub('^\s+|\n|\r|\s+$', '', backup)
    logging.info(f'{backup}')

    logging.info(f'База [{dbname}] - резервная копия успешно выгружена!')

    ### ШИФРОВАНИЕ АРХИВА
    zp_file = f'{destination}{dbname}_{datedumpstamp}.7z'    
    zp_command = f'"C:\Program Files\\7-Zip\\7z.exe" a -mhe=on -ssw -mx0 -p{zp_password} {zp_file} {destination}{dbfile}'
    zp = check_output(zp_command, shell=True).decode('utf-8')
    zp = re.sub('^\s+|\n|\r|\s+$', '', zp)
    logging.info(f'{zp}')

    os.popen(f'del -f {destination}{dbfile}')
    os.popen(f'move {zp_file} \\\\NAS_IP_ADDRES\\BACKUP\\{folderdate}\\{dbname}_{datedumpstamp}.7z')
	
    logging.info(f'База [{dbname}] - перенесена на сетевой диск!')
	
	
os.popen('NET USE \\\\NAS_IP_ADDRES\\BACKUP /D')
os.popen('Taskkill /IM rphost.exe /F')
send_tg('Резервное копирование завершено')

Теперь дело за малым – создать в планировщике задачу на выполнение этого скрипта. Тут у меня не получилось запустить сразу скрипт *.py, хотя установленный python был прописан в PATH и работал в окне командной строки. Тогда я создать рядом с скриптом *.bat файл:

C:\backup\script.py

Создаём Telegram бота для уведомлений по работе скрипта

Для создания бота перехоим в @BotFather. Выбираем Меню – /newbot, далее придумываем для него имя и уникальную ссылку на него.

Теперь, чтобы узнать нашь chat_id (чтобы бот писал нам в личные сообщения) переходим в бота @chatIDrobot и пишем ему любое сообщение. В ответ он нам скажет какой у нас chat_id.

Забираем эту информацию и редактируем в скрипте функцию отправки уведомления в Telegram:

def send_tg(msg):
    string = f"https://api.telegram.org/botTOKEN/sendMessage?chat_id=CHAT_ID&text={msg}"
    requests.get(string).json()

Настраиваем Synology под хранение резервных копий

Отлично! Все резервые копии у нас созданы, запакованы и отправлены на сетевой диск. Но я не хочу, чтобы они лежали с доступом к ним под пользователем NAS_USER. Что я сделал дальше. Я ограничил пользовтаелся NAS_USER чтобы он имел доступ только к каталогу BACKUP и создал ещё один каталог ARCHIVE к которому имеет доступ только администратор. После этого я в планировщике задач создал скрипт, который по расписанию (через три часа после запуска скрипта резервного копирования 1с) ищет всё что туда было положено пользователем NAS_USER и перемешает в ARCHIVE, куда есть доступ только у администратора.

find /volume1/BACKUP/ -type d -user "NAS_USER" -exec mv -t /volume1/BACKUP/ARCHIVE {} +

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

Резервное копирование 1с через командную строку

Изначально я сделал вот такой вот скрипт на *.bat для тестового окружения, где базы крутились на PostgreSQL. Решил приложить как бонусом к статье, может кому будет полезно.

@echo off
chcp 1251
:: формируем формат времени в переменную _date
for /f "delims=" %%# in ('powershell get-date -format "{ddMMyyyy_HHmm}"') do @set _date=%%#
for /f "delims=" %%# in ('powershell get-date -format "{HH:mm:ss}"') do @set _time=%%#
:: массив баз данных дл¤ резервного копирования
set db-name[0]=ooo_romashka_bp
set db-name[1]=ooo_romashka_zup
:: логины и пароли к SQL
set db-user=ЛОГИН_SQL
set db-pwd=ПАРОЛЬ_SQL
:: логины и пароли к 1C
set user=ЛОГИН_1С
set password=ПАРОЛЬ_1С

echo ======================================================
echo Резервное копирование запущено %_date% в %_time%
echo ======================================================
:: цикл резервного копирования в зависимости от кол-ва баз нужно редактировать параметр IN (0 1 1)
FOR /L %%i IN (0 1 1) DO  (
    ::  call echo %%i = %%names[%%i]%%
	::  обновим времяв переменной _time
	for /f "delims=" %%# in ('powershell get-date -format "{HH:mm:ss}"') do @set _time=%%#
	:: ну а тут чисто для меня удобный вывод. меняйте как вам было бы удобно
	call echo .
	call echo ======================================================
	call echo [%_time%] Pезервное копирование базы: %%db-name[%%i]%%
	call echo ------------------------------------------------------
	call "C:\Program Files\1cv8\8.3.21.1624\bin\ibcmd.exe" infobase dump --dbms=PostgreSQL --db-server="localhost" --db-name=%%db-name[%%i]%% --db-user=%db-user% --db-pwd="%db-pwd%" --user="%user%" --password="%password%" "C:\backup\%%db-name[%%i]%%_%_date%.dt"
	call echo ------------------------------------------------------
	call echo [%_time%] Pезервная копия создана: %%db-name[%%i]%%_%_date%.dt
	call echo ======================================================
)
:: а тут пауза ... убирайте\оставляйте по желанию :-)
:: pause

Заключене

В заключение хочу сказать, что скрипт работает, но его можно усовершенствовать. Есть масса идей по этому поводу, может быть позже буду дописывать статью. Хочу отметить, что ibcmd infobase dump очень удобная утилита для резервного копирования баз 1С в формат *.dt без завершения всех активных сессий пользователей 1С.

Работа уведомлений через телеграм бота о ходе реервного копирования 1С в DT через ibcmd infobase dump
Работа уведомлений через телеграм бота

Спасибо за внимание, надеюсь что данный материал вам будет полезен!

Автору на кофе ☕ или просто поднять мотивацию писать больше статей для вас 😉 !
Отдельное спасибо всем, кто отправляет донатики 😀! Очень приятно и неожиданно. Благодаря вам сайт может и в дальнейшем обходиться без рекламных баннеров.
3590