Установка сервера взаимодействия 1С в Docker

Всем привет, с вами Низамов Илья. Сервер взаимодействия 1С в Docker у меня теперь поднимается одной командой docker compose up -d: четыре контейнера (PostgreSQL, Hazelcast, Elasticsearch и сам сервер 30.0.42) и ни одного ручного шага после неё. Стек я собирал для своего сервиса, где сервер взаимодействия 1С принимает сообщения из баз клиентов, и проверил его на Docker Desktop с Linux-контейнерами. Ниже — Dockerfile, точка входа, которая настраивает сервер при каждом старте, docker-compose и шесть ловушек, на которых ломается установка. Все файлы и скрипты проверки можно скачать архивом в конце статьи.

Если вы ещё не знакомы с сервером взаимодействия, начните с обзора: что это и из каких компонентов состоит. Нужна установка без контейнеров — есть вариант для Ubuntu Server. А если сервер нужен сразу рабочим, под вашу инфраструктуру и с мониторингом, это отдельная услуга — настройка Сервера взаимодействия 1С под ключ.

Как устроен сервер взаимодействия изнутри и как написать свой WebSocket-сервер для уведомлений из 1С, разбираем на курсе «Сервер взаимодействия 1С на Python».

Что изменилось с версии 2020 года

Первая версия этой статьи вышла в 2020 году: Ubuntu 21.04, OpenJDK 8, deb-пакеты, все три компонента в одном контейнере, admin/admin и ручной curl после запуска. Сейчас сервер поставляется одним архивом с консольным установщиком, требует Java 11, а у старой схемы нашлись проблемы, которые проявляются только при перезапуске. Поэтому стек собран заново.

Было (2020) Стало (30.0.42)
Java OpenJDK 8 LibericaJDK 11, как требует 1С
Установка dpkg -i *.deb + правленый ring 1ce-installer-cli install all на отдельной ступени сборки
Контейнеры один на сервер, Hazelcast и Elasticsearch + PostgreSQL по контейнеру на роль, один образ на три роли
Процессы службы через sudo и sh в конце скрипта пусковик 1С первым процессом, журнал в docker compose logs
Настройка один раз при сборке образа при каждом старте из переменных окружения
Хранилище разговоров ручной curl после запуска регистрируется автоматически, повторно не задваивается
Пароль /admin/** admin/admin обязателен, умолчания нет
Готовность depends_on без условий проверки здоровья, сервер ждёт базу, кэш и поиск

Из чего состоит сервер взаимодействия в Docker?

Контейнер Образ Роль
postgres postgres:17-alpine база сервера cs_db
hazelcast 1c-cs:30.0.42 Hazelcast 3.9, распределённый кэш
elasticsearch тот же образ Elasticsearch 5.6, поиск по сообщениям
cs тот же образ сам сервер взаимодействия

Образ один на три роли. Компоненты приезжают одним дистрибутивом и ставятся одним установщиком, поэтому собирать три образа — значит трижды распаковывать одни и те же 270 MB. Роль выбирается командой контейнера: cs, elasticsearch или hazelcast. Образ весит 1,7 GB, из них 334 MB — сама поставка 1С, остальное — JDK и Debian.

Структура проекта:

1c-cs-docker/
├── Dockerfile               образ на три роли
├── entrypoint.sh            настройка при старте
├── docker-compose.yml       четыре сервиса и проверки здоровья
├── .env.example             пароли, порты, версия
├── postgres/initdb/
│   └── 01-uuid-ossp.sh      расширение в базе сервера
├── scripts/
│   ├── check.sh             проверка стека по порядку
│   └── backup.sh            копия базы с ротацией
├── dist/                    сюда кладётся дистрибутив 1С
└── security/                хранилище ключей для wss

Подготовка: Docker, ядро и дистрибутив

Docker Compose второй версии. В файле есть additional_contexts и depends_on с условиями, старый docker-compose через дефис их не прочтёт:

docker compose version
# Docker Compose version v2.x или новее

На Windows Docker Desktop должен работать в режиме Linux-контейнеров. Памяти стеку нужно около 3 GB.

Параметр ядра. Elasticsearch здесь слушает сеть, а не loopback, и поэтому включает боевой режим, а боевой режим требует vm.max_map_count не меньше 262144. Контейнер этот параметр задать не может, его ставят на хосте:

cat /proc/sys/vm/max_map_count          # нужно >= 262144
sudo sysctl -w vm.max_map_count=262144
echo 'vm.max_map_count=262144' | sudo tee /etc/sysctl.d/99-elasticsearch.conf

В Docker Desktop значение обычно уже 262144, на чистом Linux-сервере его почти всегда приходится ставить.

Дистрибутив. Нужен linux-архив сервера взаимодействия с портала релизов 1С, например 1c_cs_30.0.42_linux_x86_64.tar.gz. Внутри лежат 1ce-installer-cli и пять компонентов .car. Архив кладётся в dist/:

cp /путь/к/1c_cs_30.0.42_linux_x86_64.tar.gz dist/

Версию сервера выбирают под платформу баз, а не «какую посвежее». Для 8.3.25 нужна линейка 25.0.x, для 8.3.27 — 27.0.x и выше. Если версии не совпадают, WebSocket-соединение устанавливается, а потом сервер отвечает Unknown event: setOptions и регистрация базы не проходит.

Dockerfile: две ступени сборки

# syntax=docker/dockerfile:1.7
ARG JDK_IMAGE=bellsoft/liberica-openjdk-debian:11

# --- Установка -------------------------------------------------------------
FROM ${JDK_IMAGE} AS installer
ARG CS_DIST_FILE=1c_cs_30.0.42_linux_x86_64.tar.gz
COPY --from=dist ${CS_DIST_FILE} /tmp/cs-dist.tar.gz

RUN set -eux; \
    mkdir -p /tmp/distro; \
    tar -xzf /tmp/cs-dist.tar.gz -C /tmp/distro; \
    cd /tmp/distro; \
    ./1ce-installer-cli install all \
        --ignore-hardware-checks \
        --ignore-signature-warnings; \
    rm -rf /tmp/distro /tmp/cs-dist.tar.gz

# --- Рабочий образ ---------------------------------------------------------
FROM ${JDK_IMAGE}

RUN set -eux; \
    apt-get update; \
    apt-get install -y --no-install-recommends curl ca-certificates procps gawk; \
    rm -rf /var/lib/apt/lists/*

COPY --from=installer /opt/1C /opt/1C
COPY --from=installer /etc/1C /etc/1C
COPY --from=installer /etc/profile.d/1ce.sh /etc/profile.d/1ce.sh

RUN set -eux; \
    groupadd -g 1000 cs; \
    useradd -u 1000 -g cs -M -d /var/lib/1ce -s /bin/bash cs; \
    mkdir -p /var/lib/1ce/cs /var/lib/1ce/es /var/lib/1ce/hc; \
    chown -R cs:cs /var/lib/1ce /opt/1C /etc/1C

COPY --chown=cs:cs entrypoint.sh /usr/local/bin/cs-entrypoint
RUN chmod 0755 /usr/local/bin/cs-entrypoint

USER cs
WORKDIR /var/lib/1ce
EXPOSE 8087 9999 8443 8444 9200 9300 5701
ENTRYPOINT ["/usr/local/bin/cs-entrypoint"]
CMD ["cs"]

Разберу решения, которые здесь не очевидны.

Зачем отдельная ступень установки. Слой с архивом на 270 MB остался бы в образе навсегда, даже если удалить файл следующей командой. На ступени installer архив распаковывается и устанавливается, а в рабочий образ копируется только результат.

Откуда берётся дистрибутив. COPY --from=dist берёт файл из отдельного контекста сборки, который задаётся в compose через additional_contexts. В основной контекст архив не попадает, и Docker не гоняет его демону при каждой сборке.

Два ключа установщика. --ignore-signature-warnings обязателен: установщик и ring в поставке не подписаны, и без ключа установка останавливается на вопросе в консоли. --ignore-hardware-checks отключает проверку памяти хоста, в контейнере она не нужна.

Зачем /etc/1C. Установщик кладёт продукт в /opt/1C, а реестр установленных компонентов — в /etc/1C. Если его не скопировать, ring не найдёт компоненты и ответит «команда не поддерживается».

Зачем gawk. Пусковой скрипт ring вызывает awk с функцией gensub, которой нет у mawk из Debian. Работать ring не перестаёт, но каждый вызов печатает awk: function gensub never defined, и эту строку потом ищут в журнале как причину отказа.

Почему не root и зачем каталоги заранее. Elasticsearch 5.6 не запускается от root — это его собственная проверка. Поэтому все три роли работают от пользователя cs. Каталоги экземпляров создаются в образе с этим владельцем, потому что именованный том Docker наследует владельца каталога, поверх которого его монтируют. Без этого том придёт с владельцем root, и процесс не сможет записать ни конфигурацию, ни журнал.

entrypoint.sh: настройка при каждом старте

Главное решение стека: конфигурация задаётся при старте контейнера, а не руками после установки. Каждый запуск заново раскладывает параметры из переменных окружения. Том с экземпляром можно удалить целиком, и стек поднимется в том же виде, а на чистой машине после docker compose up ничего не нужно доделывать руками.

Экземпляр 1С (instance) — это каталог с config/, logs/ и data/. ring помнит зарегистрированные экземпляры в /etc/1C, то есть в образе, а сам каталог лежит на томе. Если пересоздать контейнер, запись в реестре пропадёт, а данные останутся. Поэтому у функции две ветки:

ensure_instance() {
    local subsystem="$1" dir="$2" name="$3"
    if [ -d "${dir}/config" ]; then
        log "экземпляр ${name} уже есть на томе — регистрирую в реестре ring"
        "${RING}" "${subsystem}" instance register --dir "${dir}" --name "${name}" || true
    else
        log "создаю экземпляр ${name} в ${dir}"
        "${RING}" "${subsystem}" instance create --dir "${dir}" --name "${name}"
    fi
}

Пусковик хранит PID в daemon.pid внутри каталога экземпляра, то есть на томе. При старте он отказывается работать, если файл есть и процесс с этим номером жив. В контейнере номера процессов начинаются с единицы, и PID прошлого запуска сейчас обычно принадлежит самому entrypoint. Файл снимается перед запуском:

clear_stale_pid() {
    local dir="$1"
    if [ -f "${dir}/daemon.pid" ]; then
        log "снимаю daemon.pid от прошлого запуска ($(cat "${dir}/daemon.pid" 2>/dev/null))"
        rm -f "${dir}/daemon.pid"
    fi
}

Дальше роль cs раскладывает настройки. REST обслуживания по умолчанию слушает 127.0.0.1, и тогда снаружи контейнера /rs/health недоступен. Это значение пишется файлом:

cat > "${dir}/config/maintenance.yml" <<EOF
server:
  address: 0.0.0.0
  port: ${maint_port}
EOF

Пулов JDBC два, и оба должны смотреть в одну базу: common — рабочий, privileged — служебный, через него Flyway накатывает миграции при старте. Если они разойдутся, сервер поднимется, но схему базы не обновит.

for pool in common privileged; do
    "${RING}" cs --instance "${name}" jdbc pools --name "${pool}" set-params \
        --url "${jdbc_url}" \
        --username "${db_user}" \
        --password "${CS_DB_PASSWORD}" >/dev/null
done

WebSocket — порт, к которому подключается 1С:Предприятие. websocket.hostname здесь означает адрес, на котором сервер открывает сокет, а не имя, которое он сообщает клиенту. При установке на машину туда пишут адрес машины. В контейнере такого адреса нет, и сервер падает с BindException: Cannot assign requested address. Верное значение в контейнере — 0.0.0.0:

# сокращено: при CS_WS_WSS=true в полном файле добавляются ключи хранилища
"${RING}" cs --instance "${name}" websocket set-params \
    --hostname "${CS_WS_BIND_ADDRESS:-0.0.0.0}" --port "${ws_port}" --wss false

Учётные записи HTTP в поставке — admin/admin, а под этой учёткой лежит /admin/**: регистрация баз, хранилище, все настройки сервера. Пароль приходит из окружения, умолчания нет. Формат ring — name::password::role, поэтому двоеточия и запятые в пароле запрещены, и entrypoint это проверяет:

case "${CS_ADMIN_PASSWORD}${CS_METRICS_PASSWORD}" in
    *:*|*,*) die "в паролях HTTP нельзя использовать ':' и ','" ;;
esac
"${RING}" cs --instance "${name}" http_security set-params \
    --users "${CS_ADMIN_USER:-admin}::${CS_ADMIN_PASSWORD}::ADMIN" \
    --metric-agent "${CS_METRICS_USER:-prometheus}::${CS_METRICS_PASSWORD}::PROMETHEUS_ADMIN" >/dev/null

Последний шаг ручной установки — регистрация сервера хранения разговоров через POST /admin/bucket_server. Без неё сервер работает, но /rs/health отвечает allShardsOk: false. Дождаться готовности сервера может только уже запущенный процесс, поэтому регистрация уходит фоном, а entrypoint передаёт управление пусковику через exec. Перед регистрацией фоновый процесс читает текущий список, поэтому повторный старт хранилище не задваивает:

(
    set +e
    for i in $(seq 1 150); do
        curl -sf -m 5 "${base}/rs/health" >/dev/null 2>&1 && break
        sleep 2
    done
    current="$(curl -sf -m 10 -u "${auth}" "${base}/admin/bucket_server" 2>/dev/null)"
    case "${current}" in
        *'"url"'*) log "хранилище уже зарегистрировано, повторная инициализация не нужна" ;;
        *)         log "регистрирую сервер хранения разговоров"
                   # сокращено: в полном файле в теле ещё username и password базы
                   curl -sS -m 30 -X POST -H 'Content-Type: application/json' -u "${auth}" \
                       -d "{\"url\":\"jdbc:postgresql://${db_host}:${db_port}/${db_name}\",...,\"enabled\":true}" \
                       "${base}/admin/bucket_server" ;;
    esac
) &

exec "$(launcher_for 1c-cs-server-small)" start --instance="${dir}"

exec делает пусковик первым процессом контейнера: он получает сигнал остановки от Docker, а его журнал идёт в docker compose logs.

Где ring, а где файл, я решал не по вкусу. Всё, что ring умеет, задаётся через него: он проверяет значения и сам пишет YAML, так что за формат файла отвечает производитель. Файлами задаются только три вещи, которых в ring нет или которые от окружения не зависят: адрес REST, сетевой адрес Elasticsearch и адрес узла Hazelcast.

Hazelcast и Elasticsearch в соседних контейнерах

При ручной установке оба компонента слушают loopback, и сервер ходит к ним на 127.0.0.1. В контейнерах так не получится, и у каждого компонента есть своя ловушка.

Hazelcast. В поставочном hazelcast.xml адрес узла — 127.0.0.1. По этому адресу Hazelcast выбирает, где слушать самому. С файлом из поставки узел объявляет себя Member [127.0.0.1]:5701: контейнер жив, порт «открыт», а сервер из соседнего контейнера до кэша не доходит. Entrypoint подставляет адрес контейнера:

ip="$(hostname -i | awk '{print $1}')"
sed -E -i \
    -e "s#<interface>[0-9.]+</interface>#<interface>${ip}</interface>#" \
    -e "s#<member>[0-9.]+</member>#<member>${ip}</member>#" \
    "${dir}/config/hazelcast.xml"
exec "$(launcher_for 1c-cs-hazelcast)" start --instance="${dir}"

Именно адрес контейнера, а не 0.0.0.0: этот адрес Hazelcast сообщает клиенту, и следующее соединение клиент открывает по нему.

Elasticsearch. Сервер взаимодействия ходит к нему транспортом на порт 9300, не по HTTP, так что слушать только loopback нельзя. Параметра сетевого адреса у ring elasticsearch нет, поэтому конфигурация пишется файлом:

cat > "${dir}/config/elasticsearch.yml" <<EOF
cluster.name: ${CS_ES_CLUSTER_NAME:-1ce-cs}
node.name: node-1
network.host: 0.0.0.0
discovery.zen.minimum_master_nodes: 1
EOF

heap="${CS_ES_HEAP:-1g}"
exec "$(launcher_for 1c-cs-elasticsearch)" start --instance="${dir}" \
    --jvmopt="-Xms${heap}" --jvmopt="-Xmx${heap}"

Как только network.host отличается от loopback, Elasticsearch 5.6 включает bootstrap-проверки и не стартует, если хотя бы одна не прошла. Их три: Xms равен Xmx (иначе initial heap size not equal to maximum heap size), не меньше 65536 файловых дескрипторов и 2048 потоков (задаются ulimits в compose) и vm.max_map_count на хосте. Кучу я задаю явно ещё и потому, что умолчание JVM считается от памяти хоста.

docker-compose.yml

Полный файл — в архиве. Главные места:

x-cs-image: &cs-image
  build:
    context: .
    additional_contexts:
      dist: ${CS_DIST_DIR:-./dist}
    args:
      CS_DIST_FILE: ${CS_DIST_FILE:-1c_cs_30.0.42_linux_x86_64.tar.gz}
  image: 1c-cs:${CS_VERSION:-30.0.42}
  restart: unless-stopped

services:
  postgres:
    image: ${CS_PG_IMAGE:-postgres:17-alpine}
    environment:
      POSTGRES_DB: ${CS_DB_NAME:-cs_db}
      POSTGRES_USER: ${CS_DB_USER:-cs_user}
      POSTGRES_PASSWORD: ${CS_DB_PASSWORD:?задайте CS_DB_PASSWORD в .env}
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./postgres/initdb:/docker-entrypoint-initdb.d:ro
    ports:
      - "127.0.0.1:${CS_PG_HOST_PORT:-15432}:5432"

  hazelcast:
    <<: *cs-image
    command: ["hazelcast"]
    healthcheck:
      test: ["CMD", "bash", "-c", "exec 3<>/dev/tcp/127.0.0.1/5701"]

  elasticsearch:
    <<: *cs-image
    command: ["elasticsearch"]
    ulimits:
      nofile: { soft: 65536, hard: 65536 }
      nproc: 4096

  cs:
    <<: *cs-image
    command: ["cs"]
    depends_on:
      postgres:      { condition: service_healthy }
      hazelcast:     { condition: service_healthy }
      elasticsearch: { condition: service_healthy }
    ports:
      - "${CS_MAINT_HOST_PORT:-8087}:8087"
      - "${CS_WS_PORT:-9999}:${CS_WS_PORT:-9999}"
      - "${CS_INTEGRATION_HOST_PORT:-8443}:8443"
    healthcheck:
      test: ["CMD-SHELL", "curl -sf http://127.0.0.1:8087/rs/health | grep -q '\"status\":\"UP\"'"]
      start_period: 90s

Якорь x-cs-image. Compose собирает только сервисы с build. Если бы секция сборки была у одного сервиса, а у двух других только image, на чистой машине они пошли бы за образом в реестр, где его нет.

Расширение uuid-ossp. Скрипт из postgres/initdb выполняет штатный entrypoint PostgreSQL, уже подключённый к базе cs_db. Так расширение гарантированно попадает в базу сервера, а не в postgres. Если ошибиться базой, Flyway при первом старте упадёт с function uuid_generate_v4() does not exist, и сервер молча остановится.

psql -v ON_ERROR_STOP=1 --username "${POSTGRES_USER}" --dbname "${POSTGRES_DB}" <<'SQL'
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
SQL
psql -v ON_ERROR_STOP=1 --username "${POSTGRES_USER}" --dbname "${POSTGRES_DB}" \
     -At -c "SELECT uuid_generate_v4()" > /dev/null

Вторая команда проверяет расширение сразу: если оно не работает, инициализация базы падает сейчас, а не через минуту в журнале сервера.

Проверка Hazelcast через bash. У Hazelcast 3.9 нет HTTP, поэтому проверка открывает TCP-порт. /dev/tcp — возможность bash, а CMD-SHELL запускает /bin/sh, то есть dash. С CMD-SHELL проверка падала бы всегда, даже на исправном узле.

Одинаковый порт WebSocket внутри и снаружи. Этот номер человек вводит в строку подключения при регистрации базы. Если опубликовать порт под другим номером, однажды получите «сервер недоступен» без единой ошибки в журнале.

PostgreSQL только на петле. База нужна администратору с этой машины, из сети к ней ходить незачем.

Файл .env

cp .env.example .env

Обязательно заполнить три пароля: CS_DB_PASSWORD, CS_ADMIN_PASSWORD, CS_METRICS_PASSWORD. Без них compose не стартует: переменные объявлены через ${...:?}. Порты по умолчанию стандартные: 9999, 8087, 8443. Если на машине уже стоит сервер взаимодействия без Docker, поставьте свободные.

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

Как запустить и проверить сервер?

docker compose up -d --wait

При первом запуске команда соберёт образ (около минуты на прогретом кэше базового образа), затем параллельно поднимет PostgreSQL, Hazelcast и Elasticsearch, а сервер запустит только после того, как все трое пройдут проверку здоровья. --wait возвращает управление, когда здоров весь стек. На моей машине сборка и запуск с пустыми томами заняли 1 мин 12 с, повторный подъём — 32 с.

Порядок проверки такой: сначала кто жив, потом журнал, и только потом curl. scripts/check.sh проходит его сам. Вот его вывод на стеке из архива (порты в тестовом прогоне были нестандартные):

1. Параметр ядра vm.max_map_count (Elasticsearch требует >= 262144)
  [ OK ] vm.max_map_count = 262144
2. Контейнеры
  [ OK ] postgres здоров
  [ OK ] hazelcast здоров
  [ OK ] elasticsearch здоров
  [ OK ] cs здоров
3. Здоровье сервера: http://127.0.0.1:28087/rs/health
  {"status":"UP","mainDbOk":true,"allShardsOk":true,"hazelcast":{"available":true,
   "members":["172.30.0.4:5701"]},"elasticsearchOk":true,"mediaClusterOk":false,
   "mediaServers":{},"pushOk":false}
  [ OK ] status UP
  [ OK ] база подключена
  [ OK ] хранилище разговоров есть
  [ OK ] Hazelcast подключён
  [ OK ] Elasticsearch отвечает
  [ !! ] mediaClusterOk=false — норма без MinIO
  [ !! ] pushOk=false — норма без службы push-уведомлений
4. Порты
  [ OK ] /admin/** закрыт паролем (401)
  [ OK ] учётка admin принята (200)
  [ OK ] WebSocket слушает на 29999
  [ OK ] интеграции слушают на 28443

mediaClusterOk: false и pushOk: false — норма для исправного сервера, пока не настроены MinIO для файлов и служба push-уведомлений. В hazelcast.members стоит настоящий адрес контейнера, а не 127.0.0.1: значит, подстановка в hazelcast.xml сработала. Что означает каждое поле /rs/health и что делать, если какое-то из них покраснело, разобрано в статье об ошибках сервера взаимодействия.

В журнале сервера видна вся настройка при старте:

docker compose logs cs --no-log-prefix | grep entrypoint
[entrypoint:cs] создаю экземпляр cs_instance в /var/lib/1ce/cs
[entrypoint:cs] jdbc/common -> jdbc:postgresql://postgres:5432/cs_db?currentSchema=public
[entrypoint:cs] jdbc/privileged -> jdbc:postgresql://postgres:5432/cs_db?currentSchema=public
[entrypoint:cs] websocket 0.0.0.0:29999, wss=false
[entrypoint:cs] integration https://example.ru, endpoint http на 8443
[entrypoint:cs] http_security: пользователь admin
[entrypoint:cs] запуск сервера взаимодействия
[entrypoint:cs] регистрирую сервер хранения разговоров

После docker compose down и повторного up первые строки меняются: «экземпляр cs_instance уже есть на томе», «снимаю daemon.pid от прошлого запуска» и «хранилище уже зарегистрировано». Так видно, что обе ловушки перезапуска отработали.

Четыре предупреждения Illegal reflective access при старте — шум Java 11 на библиотеке Hazelcast, собранной под Java 8. Предупреждение Flyway про PostgreSQL 17 («support has not been tested») имеет уровень WARN, всё работает.

Помните разницу: docker compose down удаляет контейнеры, а тома оставляет. docker compose down -v удаляет и тома, то есть всю переписку, индексы и настроенные экземпляры.

Как зарегистрировать информационную базу?

Этот шаг делает человек: регистрация требует почту администратора и код подтверждения из письма.

  1. Открыть в базе 1С под полными правами обработку CollaborationSystemRegister.epf — она лежит рядом с дистрибутивом. В типовых конфигурациях есть и встроенный путь: «Администрирование → Органайзер → Обсуждения».
  2. Указать адрес сервера ws://<адрес машины с Docker>:9999.
  3. Ввести почту администратора и код из письма.

Проверить регистрацию из 1С можно так:

Сообщить(СистемаВзаимодействия.ИнформационнаяБазаЗарегистрирована()); // Истина
Сообщить(СистемаВзаимодействия.АдресСервера());                       // ws://<хост>:9999
Сообщить(СистемаВзаимодействия.ВерсияСервера());                      // 30.0.42

СистемаВзаимодействия.ПолучитьТипыВнешнихСистем() на сервере 30.0.42 возвращает WebChat, Telegram, MAX, VK, Webhook, WhatsApp Devino — это типы интеграций, которые можно подключать.

TLS: интеграции и wss

Шифрования здесь два, и они не связаны друг с другом.

Интеграции (Webhook). Публичный адрес точки подключения обязан начинаться с https:// — это требование зашито в код сервера, отключить его нельзя. Сам endpoint при этом слушает обычный http на 8443, а TLS снимает reverse proxy. Адрес задаётся без пути, потому что сервер сам достраивает <publicUrl>/integration/webhook/<токен>/callback:

CS_INTEGRATION_PUBLIC_URL=https://example.ru
location /integration/ {
    client_max_body_size 256k;
    proxy_set_header Host              $host;
    proxy_set_header X-Real-IP         $remote_addr;
    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass http://127.0.0.1:8443;
}

Проверка без nginx: curl http://127.0.0.1:8443/integration/webhook/test/callback отвечает 405. Это значит, что путь существует и ждёт POST.

WebSocket (wss). Это шифрование канала между сервером и 1С:Предприятием. По умолчанию выключено: либо TLS снимает снаружи nginx с Upgrade, либо сервер и базы стоят в доверенной сети. Включается хранилищем ключей в security/ и тремя строками .env:

keytool -genkey -alias websocket-keystore -keyalg RSA -keystore security/websocket-keystore.jks
keytool -export -alias websocket-keystore -file security/websocket-keystore.cer \
        -keystore security/websocket-keystore.jks
CS_WS_WSS=true
CS_WS_KEYSTORE_PATH=/var/lib/1ce/security/websocket-keystore.jks
CS_WS_KEYSTORE_PASSWORD=<пароль хранилища>

С самоподписанным сертификатом .cer придётся установить в доверенные корневые центры на каждом компьютере, где запускается 1С, иначе регистрация ответит certificate verify failed. Десять рабочих мест — десять установок, и так при каждой смене сертификата. Для рабочего сервера берите сертификат домена.

Обновление версии и резервные копии

Перед обновлением обязательна копия базы. scripts/backup.sh снимает pg_dump из контейнера в backups/, хранит последние 14 копий и подходит для cron:

./scripts/backup.sh
# 2026-09-17 09:25:08 копия backups/cs_db-20260917-092508.sql.gz (8.0K)

# каждую ночь в 03:15
15 3 * * * /opt/1c-cs-docker/scripts/backup.sh >> /var/log/cs-backup.log 2>&1

Обновление — новый архив в dist/, в .env новые CS_DIST_FILE и CS_VERSION, и перезапуск в порядке производителя: сервер останавливается первым и поднимается последним.

docker compose build
docker compose stop cs
docker compose up -d hazelcast elasticsearch
docker compose up -d --wait cs
./scripts/check.sh

Схему базы сервер обновляет сам через Flyway при старте. Отключать базы 1С обработкой перед обновлением не нужно: на время обновления клиенты показывают значок с восклицательным знаком, а потом переподключаются сами. Шаги для перехода через мажорную версию, например переиндексацию Elasticsearch, описаны в update.pdf рядом с дистрибутивом, и выполнять их стоит вручную, прочитав документ.

Откат: вернуть прежние значения в .env и выполнить docker compose up -d. Старый образ остаётся на диске, а схема базы откатится только из копии — поэтому копию и снимают до обновления.

Чек-лист портов и сети

Порт хоста Что это Кому открыть
9999 WebSocket, сюда подключается 1С:Предприятие компьютерам с 1С
8087 REST обслуживания: /rs/health, /admin/**, метрики только администратору
8443 точка подключения интеграций (Webhook) только reverse proxy
127.0.0.1:15432 PostgreSQL сервера только с этой машины
9200, 9300, 5701 Elasticsearch и Hazelcast не публикуются, не открывать
  • Все четыре контейнера работают в одной сети compose и находят друг друга по именам сервисов: postgres, elasticsearch, hazelcast.
  • Метрики для Prometheus отдаются на /actuator/prometheus под учёткой CS_METRICS_USER.
  • Чего в стеке нет: MinIO для вложений (mediaClusterOk: false), службы push, внешнего мониторинга и лимитов памяти контейнеров. Первые две строки нужны только тем, кто пользуется файлами в переписке и мобильными клиентами.

Установить сервер 1С:Предприятия, кластер и PostgreSQL на Linux, к которым потом подключается сервер взаимодействия, мы разбираем на курсе «Сервер 1С:Предприятие».

Материалы к статье

Архив 1c-cs-docker.zip скачивается по кнопке в блоке «Материалы к статье» сразу под текстом. Внутри все файлы из статьи в полном виде, с комментариями к каждому решению:

  • Dockerfile, entrypoint.sh, docker-compose.yml, .env.example;
  • postgres/initdb/01-uuid-ossp.sh;
  • scripts/check.sh — проверка стека по порядку, код выхода 1 при любом провале;
  • scripts/backup.sh — копия базы с ротацией и примером для cron;
  • README.md — краткая инструкция, порты, ловушки, обновление.

Дистрибутива 1С в архиве нет: у него своя лицензия, скачивайте его с портала релизов 1С.

Дальше по теме сервера взаимодействия

Курс по теме статьи

Сервер 1с в дата центре (вебинар)

Установка и настройка сервера 1С:Предприятие на практике: подбор железа и тюнинг, клиент-серверный режим, кластер, PostgreSQL, запуск на Linux. Реальный проект, а не виртуалка.

3 000 ₽

Перейти к курсу «Сервер 1с в дата центре (вебинар)»

Материалы к статье

1c-cs-docker.zip · 25,8 KB

Скачать

Частые вопросы