Скрипт bash для корректного обновления платформы 1С &НаСервере. Рецепты от Капитана

—

от автора

Скрипт bash для корректного обновления серверной части платформы 1С Предприятия (сохраняет и восстанавливает настройки служб, даже если их несколько, изменяет версию платформы во всех веб-публикациях и делает еще много полезных мелочей не включенных в штатный инсталлятор) Иными словами “обновление под ключ” или “нажал и забыл”

Телефонный опрос, проведенный утром 1 января, дал следующие результаты:2% — «да?» 3% — «алло?» 95% — затруднились ответить
www.anekdot.ru©
Новый Год у сисадмина отличается от обычных дней тем, что на столе рядом с пивом лежат мандаринки …
сетевой фольклор©

Ведь именно новогодние каникулы, это то время, пока коллеги лежат носом в оливье, ИТ-шники всех стран обновляют сервера, сворачивают базы, переходят на новые релизы.
В ноябре уже пора об этом задумываться.

Внимание! Все действия нужно производить осознанно и понимая их последствия

Скрипт предоставляется бесплатно по принципу «AS IS» / «Как есть».

Автор гарантирует, что никаких действий, кроме обновления серверной части платформы 1С, скрипт не производит, но не несёт ответственности за ваши данные. 

Постановка задачи

Установщики платформы 1С от вендора для linux, да и для windows тоже, как виде для пакетного менеджера, так в виде программы установки сделанной VMWare InstallBuilder не умеют и скорее всего не будут уметь, сохранять настройки для службы сервера 1С сделанные вручную, особенно если серверов 1С несколько на разных портах на одном логическом сервере.

Тем более обновлять версии платформы во всех веб-публикациях.

Для не совсем внимательного пользователя это может быть проблемой.

Тем более если этот пользователь работает на linux.

Импортозамещение шагает по стране уже два года, но для linux пока не нашел ничего достойного.

Поэтому привел свой скрипт в удобоваримый вид (не обошлось без помощи gemini) и просто положу его здесь.

Решение

Скрипт на bash, который проверяет.

  • что запущен с правами администратора

  • наличие архива установки или распакованного файла установки

  • если выбрано обновление, то соответствие установленной версии платформы и заданной ключом командной строки

  • сохраняет файлы старых служб сервера 1С и ras с возможными нетиповыми настройками

  • создает новые файлы служб сервера 1С и ras с перенесенными настройками

  • останавливает службу сервера 1С и ras

  • устанавливает новую версию платформы 1С

  • переносит в ее каталог новые файлы служб сервера 1С и ras

  • запускает службы сервера 1С и ras

  • при успешном запуске удаляет старую версию платформы 1С

  • во всех найденных веб-публикациях изменяет версию платформы 1С

  • перезапускает веб-сервер

Выполняется как любой скрипт.

Может обновлять 8.3.х.х на 8.5.х.х и обратно, что не менее актуально в настоящий момент см. багтрекер 1С.

Любители острых ощущений могут его даже в cron запланировать на ночь и утром удивиться результату.

Использование скрипта: $PROGNAME параметр1 параметр2
Первый параметр это номер обновляемой версии 1С, если 0 то чистая установка
Второй параметр это номер новой версии 1С
Файл установки 1С должен находиться в одном каталоге со скриптом
Если указан номер обновляемой версии 1С, то должен существовать каталог установки обновляемой версии 1С
Пример: $PROGNAME 0 8.3.24.1586
Пример: $PROGNAME 8.3.23.1586 8.3.24.1586

Поскольку настроящий сисадмин должен быть не только умный, но и ленивый, не буду заставлять его ходить на github, а положу весь скрипт под кат. Но кто сходит, тот найдет еще пару скриптов: на 1С Исполнитель и bash для обновления только веб-публикаций для IIS и Apache

Лицензия MIT
#!/bin/bash# Copyright (c) 2026 [@capitan_nemo_spb]# # Данная лицензия разрешает лицам, получившим копию данного программного # обеспечения и сопутствующей документации (в дальнейшем именуемого "Программное # обеспечение"), безвозмездно использовать Программное обеспечение без ограничений, # включая неограниченное право на использование, копирование, изменение, слияние, # публикацию, распространение, сублицензирование и/или продажу копий Программного # обеспечения, а также лицам, которым предоставляется данное Программное # обеспечение, при соблюдении следующих условий:# # Указанное выше уведомление об авторских правах и данные условия лицензии должны # быть включены во все копии или значимые части Программного обеспечения.# # ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ "КАК ЕСТЬ", БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, # ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ НЕ ОГРАНИЧИВАЯСЬ ГАРАНТИЯМИ ТОВАРНОЙ # ПРИГОДНОСТИ, СООТВЕТСТВИЯ ПО НАЗНАЧЕНИЮ И НЕНАРУШЕНИЯ ПРАВ. НИ В КАКОМ СЛУЧАЕ # АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ПО КАКИМ-ЛИБО ИСКАМ, ЗА # УЩЕРБ ИЛИ ПО ИНЫМ ТРЕБОВАНИЯМ, ВОЗНИКШИМ В СВЯЗИ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ ИЛИ # ИСПОЛЬЗОВАНИЕМ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ.set -euo pipefailPROGNAME="$(basename "$0")"SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)"usage() {    cat <<EOFИспользование программы: $PROGNAME параметр1 параметр2Первый параметр это номер обновляемой версии 1С, если 0 то чистая установкаВторой параметр это номер новой версии 1СФайл установки 1С должен находиться в одном каталоге со скриптомЕсли указан номер обновляемой версии 1С, то должен существовать каталог установкиПример: $PROGNAME 0 8.3.24.1586Пример: $PROGNAME 8.3.23.1586 8.3.24.1586EOF}log() {    echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*"}err() {    echo "[$(date +'%Y-%m-%d %H:%M:%S')] ОШИБКА: $*" >&2}replace_version_in_file() {    local target_file="$1"    if [[ -f "$target_file" ]]; then        perl -pE "s/\Q${old_version}\E/${new_version}/g" -i "$target_file"    fi}http_replace_version() {    local conf_file="$1"    if grep -qF "${old_version}" "${conf_file}"; then        log "Изменяется версия в файле веб-публикации: ${conf_file}"        perl -i".bak_${current_date}" -pE "s/\Q${old_version}\E/${new_version}/g" "${conf_file}"    fi}if [[ "${1:-}" =~ ^(-h|--help)$ ]]; then    usage    exit 0fiif [[ "$(id -u)" -ne 0 ]]; then    err "Скрипт должен быть запущен с правами администратора"    exit 1fiif [[ $# -ne 2 ]]; then    usage    exit 1fiold_version=$1new_version=$2current_date=$(date +'%Y%m%d_%H%M%S')bak_dir="${SCRIPT_DIR}/bak_${current_date}"log "Проверяется существование архива/файла установки"new_archive="${SCRIPT_DIR}/server64_${new_version//./_}.zip"shopt -s nullglobinstaller_matches=( "${SCRIPT_DIR}"/*-"${new_version}"-*.run )shopt -u nullglobif [[ ${#installer_matches[@]} -eq 0 ]]; then    if [[ -f "${new_archive}" ]]; then        log "Разархивируется ${new_archive}..."        unzip -o "${new_archive}" -d "${SCRIPT_DIR}" "*${new_version}*.run"        shopt -s nullglob        installer_matches=( "${SCRIPT_DIR}"/*-"${new_version}"-*.run )        shopt -u nullglob    else        err "Не обнаружен файл установки или архива новой версии 1С ${new_archive}"        exit 1    fifiif [[ ${#installer_matches[@]} -eq 0 || ! -f "${installer_matches[0]}" ]]; then    err "Не обнаружен файл установки новой версии 1С"    exit 1finew_file="${installer_matches[0]}"chmod +x "${new_file}"old_dir="/opt/1cv8/x86_64/${old_version}"new_dir="/opt/1cv8/x86_64/${new_version}"srv_suffixes=()ras_suffixes=()if [[ "${old_version}" != "0" ]]; then    log "Старая версия не 0 (${old_version}), проверяется существование каталога установки"    if [[ ! -f "${old_dir}/uninstaller-full" ]]; then        err "Не обнаружен каталог обновляемой версии 1C ${old_version}"        exit 1    fi    mkdir -p "${bak_dir}"    log "Сохраняются файлы старых служб с возможными нетиповыми настройками"    # Поиск всех служб сервера 1С вида srv1cv8-OLD_VER@.service, srv1cv8-OLD_VER@2541.service и т.д.    shopt -s nullglob    old_srv_services=( "${old_dir}"/srv1cv8-"${old_version}"@*.service )    shopt -u nullglob    if [[ ${#old_srv_services[@]} -eq 0 ]]; then        log "Службы srv1cv8 не найдены в ${old_dir}, используется шаблон по умолчанию"        srv_suffixes+=( "" )    else        for srv_path in "${old_srv_services[@]}"; do            srv_file="$(basename "${srv_path}")"            # Извлекаем суффикс после @ и до .service (например: "" или "2541")            suffix="${srv_file#srv1cv8-${old_version}@}"            suffix="${suffix%.service}"            srv_suffixes+=( "${suffix}" )            new_srv_name="srv1cv8-${new_version}@${suffix}.service"            log "Резервное копирование и адаптация службы: ${srv_file} -> ${new_srv_name}"            cp "${srv_path}" "${bak_dir}/${new_srv_name}"            replace_version_in_file "${bak_dir}/${new_srv_name}"        done    fi    # Поиск всех служб RAS вида ras-OLD_VER.service, ras-OLD_VER-2545.service, ras-OLD_VER@.service и т.д.    shopt -s nullglob    old_ras_services=( "${old_dir}"/ras-"${old_version}"*.service )    shopt -u nullglob    if [[ ${#old_ras_services[@]} -eq 0 ]]; then        log "Службы RAS не найдены в ${old_dir}"    else        for ras_path in "${old_ras_services[@]}"; do            ras_file="$(basename "${ras_path}")"            # Извлекаем суффикс после ras-${old_version} и до .service (например: "" для ras-8.3.23.service, "-2545" или "@2545")            suffix="${ras_file#ras-${old_version}}"            suffix="${suffix%.service}"            ras_suffixes+=( "${suffix}" )            new_ras_name="ras-${new_version}${suffix}.service"            log "Резервное копирование и адаптация службы RAS: ${ras_file} -> ${new_ras_name}"            cp "${ras_path}" "${bak_dir}/${new_ras_name}"            replace_version_in_file "${bak_dir}/${new_ras_name}"        done    fi    log "Останавливается старая версия платформы 1С"    for suffix in "${srv_suffixes[@]}"; do        inst="default"        [[ -n "${suffix}" ]] && inst="${suffix}"        srv_unit="srv1cv8-${old_version}@${suffix}.service"        srv_inst="srv1cv8-${old_version}@${inst}.service"        systemctl stop "${srv_inst}" 2>/dev/null || true        systemctl disable "${srv_inst}" 2>/dev/null || true        for unit_link in \            "/etc/systemd/system/${srv_unit}" \            "/etc/systemd/system/${srv_inst}" \            "/etc/systemd/system/multi-user.target.wants/${srv_inst}"        do            if [[ -e "${unit_link}" || -L "${unit_link}" ]]; then                rm -f "${unit_link}"            fi        done    done    log "Останавливаются старые службы RAS"    for suffix in "${ras_suffixes[@]}"; do        ras_unit="ras-${old_version}${suffix}.service"        # Если служба шаблонная (содержит @), определяем имя инстанса        if [[ "${suffix}" == *"@"* ]]; then            inst_suffix="${suffix#*@}"            [[ -z "${inst_suffix}" ]] && inst_suffix="default"            ras_inst="ras-${old_version}@${inst_suffix}.service"        else            ras_inst="${ras_unit}"        fi        systemctl stop "${ras_inst}" 2>/dev/null || true        systemctl disable "${ras_inst}" 2>/dev/null || true        for unit_link in \            "/etc/systemd/system/${ras_unit}" \            "/etc/systemd/system/${ras_inst}" \            "/etc/systemd/system/multi-user.target.wants/${ras_inst}"        do            if [[ -e "${unit_link}" || -L "${unit_link}" ]]; then                rm -f "${unit_link}"            fi        done    done    systemctl daemon-reload    systemctl reset-failed "srv1cv8-*" "ras-*" 2>/dev/null || trueelse    srv_suffixes+=( "" )    ras_suffixes+=( "" )filog "Производится установка новой версии платформы 1С (${new_version})...""${new_file}" --mode unattended --enable-components "server,ws,server_admin,additional_admin_functions,liberica_jre,ru"log "Проверяется, что новая версия установилась..."new_service="${new_dir}/srv1cv8-${new_version}@.service"if [[ ! -f "${new_service}" ]]; then    err "Некорректно установилась новая версия 1C: файл ${new_service} не найден"    exit 1fiif [[ "${old_version}" != "0" ]]; then    log "Удаляется старая версия платформы 1С (${old_version})..."    "${old_dir}/uninstaller-full" --mode unattended    log "Восстановление конфигураций служб srv1cv8..."    for suffix in "${srv_suffixes[@]}"; do        target_srv="srv1cv8-${new_version}@${suffix}.service"        bak_srv_file="${bak_dir}/${target_srv}"        dest_srv_file="${new_dir}/${target_srv}"        if [[ -f "${bak_srv_file}" ]]; then            if [[ -f "${dest_srv_file}" ]]; then                mv "${dest_srv_file}" "${bak_dir}/${target_srv}.dist"            fi            log "Копирование файла службы: ${target_srv} -> ${new_dir}/"            cp "${bak_srv_file}" "${dest_srv_file}"        fi    done    log "Восстановление конфигураций служб RAS..."    for suffix in "${ras_suffixes[@]}"; do        target_ras="ras-${new_version}${suffix}.service"        bak_ras_file="${bak_dir}/${target_ras}"        dest_ras_file="${new_dir}/${target_ras}"        if [[ -f "${bak_ras_file}" ]]; then            if [[ -f "${dest_ras_file}" ]]; then                mv "${dest_ras_file}" "${bak_dir}/${target_ras}.dist"            fi            log "Копирование файла службы RAS: ${target_ras} -> ${new_dir}/"            cp "${bak_ras_file}" "${dest_ras_file}"        fi    donefilog "Запускаются службы 1С..."systemctl daemon-reloadfor suffix in "${srv_suffixes[@]}"; do    inst="default"    [[ -n "${suffix}" ]] && inst="${suffix}"    srv_unit_file="${new_dir}/srv1cv8-${new_version}@${suffix}.service"    srv_inst_name="srv1cv8-${new_version}@${inst}"    if [[ -f "${srv_unit_file}" ]]; then        systemctl link "${srv_unit_file}"        systemctl enable "${srv_inst_name}" --now    else        err "Файл службы ${srv_unit_file} не найден для регистрации!"    fidonefor suffix in "${ras_suffixes[@]}"; do    ras_unit_file="${new_dir}/ras-${new_version}${suffix}.service"    if [[ "${suffix}" == *"@"* ]]; then        inst_suffix="${suffix#*@}"        [[ -z "${inst_suffix}" ]] && inst_suffix="default"        ras_inst_name="ras-${new_version}@${inst_suffix}"    else        ras_inst_name="ras-${new_version}${suffix}.service"    fi    if [[ -f "${ras_unit_file}" ]]; then        systemctl link "${ras_unit_file}"        systemctl enable "${ras_inst_name}" --now    fidonelog "Ожидание запуска служб..."for ((i=0; i<15; i++)); do    all_active=true    for suffix in "${srv_suffixes[@]}"; do        inst="default"        [[ -n "${suffix}" ]] && inst="${suffix}"        if ! systemctl is-active --quiet "srv1cv8-${new_version}@${inst}"; then            all_active=false            break        fi    done    if [[ "${all_active}" == "true" ]]; then        for suffix in "${ras_suffixes[@]}"; do            ras_unit_file="${new_dir}/ras-${new_version}${suffix}.service"            [[ ! -f "${ras_unit_file}" ]] && continue            if [[ "${suffix}" == *"@"* ]]; then                inst_suffix="${suffix#*@}"                [[ -z "${inst_suffix}" ]] && inst_suffix="default"                ras_inst_name="ras-${new_version}@${inst_suffix}"            else                ras_inst_name="ras-${new_version}${suffix}.service"            fi            if ! systemctl is-active --quiet "${ras_inst_name}"; then                all_active=false                break            fi        done    fi    if [[ "${all_active}" == "true" ]]; then        log "Все службы успешно перешли в активное состояние."        break    fi    sleep 2donefor suffix in "${srv_suffixes[@]}"; do    inst="default"    [[ -n "${suffix}" ]] && inst="${suffix}"    systemctl status "srv1cv8-${new_version}@${inst}" --no-pager -l || truedonefor suffix in "${ras_suffixes[@]}"; do    ras_unit_file="${new_dir}/ras-${new_version}${suffix}.service"    [[ ! -f "${ras_unit_file}" ]] && continue    if [[ "${suffix}" == *"@"* ]]; then        inst_suffix="${suffix#*@}"        [[ -z "${inst_suffix}" ]] && inst_suffix="default"        ras_inst_name="ras-${new_version}@${inst_suffix}"    else        ras_inst_name="ras-${new_version}${suffix}.service"    fi    systemctl status "${ras_inst_name}" --no-pager -l || truedoneif [[ "${old_version}" != "0" ]]; then    web_conf_dirs=()    [[ -d '/etc/httpd' ]] && web_conf_dirs+=( '/etc/httpd' )    [[ -d '/etc/apache2' ]] && web_conf_dirs+=( '/etc/apache2' )    if [[ ${#web_conf_dirs[@]} -gt 0 ]]; then        for conf_dir in "${web_conf_dirs[@]}"; do            readarray -d '' -t find_files < <(find "${conf_dir}" -name '*.conf' -size +1c -type f -print0)            for file_path in "${find_files[@]}"; do                http_replace_version "${file_path}"            done        done        log "Перезапускается веб-сервер..."        if systemctl list-unit-files 2>/dev/null | grep -qE '^(httpd|apache2)\.service'; then            systemctl restart httpd 2>/dev/null || systemctl restart apache2 2>/dev/null || true        fi    fifilog "Обновление/установка платформы 1С успешно завершена!"

Будет интересно, напишу про обновление клиентов.

Статья продолжает серию публикаций:

Серия «Рецепты от Капитана» на всякий случай

ссылка на оригинал статьи https://habr.com/ru/articles/1096964/