Всем привет, с вами Низамов Илья. Сервер взаимодействия 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С под полными правами обработку
CollaborationSystemRegister.epf— она лежит рядом с дистрибутивом. В типовых конфигурациях есть и встроенный путь: «Администрирование → Органайзер → Обсуждения». - Указать адрес сервера
ws://<адрес машины с Docker>:9999. - Ввести почту администратора и код из письма.
Проверить регистрацию из 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С.
Дальше по теме сервера взаимодействия
- Сервер не стартует или база не регистрируется — разбор ошибок сервера взаимодействия по журналу.
- Ставите сервер взаимодействия без контейнеров — установка на Ubuntu Server: каждый сервис управляется напрямую через systemd.
- Переносите уже работающий сервер в облако — переезд в Yandex Cloud (Windows → Ubuntu).
- Нужен обмен файлами через объектное хранилище — настройка Amazon S3.
- Хотите показывать пользователю прогресс длительной операции 1С в реальном времени — взаимодействие сервера и клиента через WebSocket.