24 KiB
usefull commands
- Убрать переносы строк: sed -i 's/\r$//' name-file.sh
- find . -type f \( -name '*.sh' -o -name 'validate-env' \) -exec dos2unix {} +
- docker compose stop # мягко останавливает контейнеры (не удаляет)
- sudo shutdown -h now # выглючить ВМ
- Генерация паролей
- (со спец.символами) openssl rand -base64 32
- (без спец.символов) openssl rand -hex 16
- openssl rand -base64 16 | xclip -selection clipboard # Linux
Посмотреть состояние контейнеров docker compose ps --format "table {{.Service}}\t{{.Status}}\t{{.Ports}}"
Туннель до БД: ssh -i C:\Users\MI.ssh\hansel -L 5433:192.168.0.211:5432 root@135.106.164.58 -N
#Обновление проекта mkdir -p ~/.ssh cp /mnt/c/Users/MI/.ssh/hansel ~/.ssh/hansel chmod 600 ~/.ssh/hansel
'''bash'''
rsync -rltD --no-perms --no-owner --no-group -invc --delete
--exclude='.env'
--exclude='.crt'
--exclude='.pem'
--exclude='*.key'
--exclude='secrets/'
-e "ssh -i ~/.ssh/hansel"
/mnt/c/Users/MI/Documents/Assistent/HAN_chat_specification/codebase/backend/
root@135.106.164.58:/opt/han-chat/backend/
-r — рекурсивно.
-l — сохранять символические ссылки.
-t — сохранять время модификации (важно для будущих проверок).
-D — сохранять устройства (на всякий случай, как в -a).
--no-perms --no-owner --no-group — главное исправление: не пытаться копировать права, владельца и группу с Windows на Linux. Это избавит от ложных срабатываний.
-i — покажет только реально измененные файлы (можно заменить на -v, если хотите просто список).
-a (archive) — сохраняет права, время и рекурсивно копирует.
-v (verbose) — выводит список файлов.
-n (dry-run) — главный флаг, показывает, что бы произошло, но не делает этого.
--delete — решение вашей проблемы. Говорит rsync удалять на приемнике (ВМ) файлы, которых нет в источнике (локально).
Важно: не забудьте поставить слэш / в конце пути к локальному проекту, иначе rsync скопирует саму папку внутрь папки на ВМ.
-
Скопировать и автоматически почистить артефакты Когда вы убедитесь, что вывод предыдущей команды вас устраивает, просто уберите флаг -n: rsync -rltD --no-perms --no-owner --no-group -ivc --delete
--exclude='.env'
--exclude='.crt'
--exclude='.pem'
--exclude='.key'
--exclude='secrets/'
--exclude='node_modules/'
--exclude='dist/'
--exclude='.expo/'
--exclude='playwright-report/'
--exclude='test-results/'
--exclude='.log'
-e "ssh -i ~/.ssh/hansel"
/mnt/c/Users/MI/Documents/Assistent/HAN_chat_specification/codebase/backend/
root@135.106.164.58:/opt/han-chat/backend/ -
Копирование env (опционально) scp -i C:\Users\MI.ssh\hansel -r "C:\Users\MI\Documents\job\HAN_new_life\HANapp\Production.env" root@135.106.164.58:/opt/han-chat/backend
cd /opt/han-chat/backend find . -type f ( -name '.sh' -o -name 'validate-env' ) -exec dos2unix {} + chmod +x scripts/validate-env deployment/scripts/.sh redis/scripts/.sh nginx/scripts/.sh
docker compose --env-file .env build frontend-static keycloak
docker compose --env-file .env up -d
--no-deps
--force-recreate frontend-static keycloak
Посмотреть состояние контейнеров docker compose ps --format "table {{.Service}}\t{{.Status}}" docker compose ps --format "table {{.Service}}\t{{.Status}}\t{{.Ports}}"
Архивный способ копирования:
cd /tmp rm han-chat-backend.tar.gz cd /opt/han-chat/backend
#команда складывает архив в ту папку, из которой запускается команда
cd C:\Users\MI\Documents\Assistent
rm C:\Users\MI\Documents\Assistent\han-chat-backend.tar.gz
tar -C C:\Users\MI\Documents\Assistent\HAN_chat_specification\codebase\backend -czf han-chat-backend.tar.gz .
scp -i C:\Users\MI.ssh\hansel C:\Users\MI\Documents\Assistent\han-chat-backend.tar.gz root@135.106.164.58:/tmp/han-chat-backend.tar.gz
tar -xzf /tmp/han-chat-backend.tar.gz find . -type f ( -name '.sh' -o -name 'validate-env' ) -exec dos2unix {} + chmod +x scripts/validate-env deployment/scripts/.sh redis/scripts/.sh nginx/scripts/.sh
docker compose --env-file .env build frontend-static keycloak
docker compose --env-file .env up -d
--no-deps
--force-recreate frontend-static keycloak
Разворачиваем инфраструктуру в Селектел ч1
Создание сети
Создание групп безопасности для сети и для БД
Для сети открываем порты 80, 22, 443
Для БД открываем порты 5432, 5433 но только из CIDR (192.168.0.211\24)
Создаем ВМ, подключаем к новой сети (через порт) и перезагружаем (либо руками вводить новые сетевые настройки - надо выбрать)
Подключение к ВМ и первичный скрипт
++ ssh -i C:\Users\MI.ssh\hansel root@135.106.164.58 ++ scp -i C:\Users\MI.ssh\hansel -r "C:\Users\MI\Documents\Assistent\HAN_chat_specification\codebase\backend\deployment\scripts\setup-vm.sh" root@135.106.164.58:/tmp/setup-vm.sh chmod +x /tmp/setup-vm.sh sudo /tmp/setup-vm.sh
Скрипт создал деплой пользователя. Нужно Перенести туда публичный ключ и дать права на файл и папку
Вставляем публичный ключ новой строкой Далее даем права
sudo nano /home/deploy/.ssh/authorized_keys sudo chmod 700 /home/deploy/.ssh sudo chmod 600 /home/deploy/.ssh/authorized_keys sudo chown -R deploy:deploy /home/deploy/.ssh
Пароль деплою для использования sudo
sudo passwd deploy
После этого можно подключаться под деплой пользователем
ssh -i C:\Users\MI.ssh\han_chat_deploy deploy@135.106.164.58
GIT не работал. Проект пришлось копировать.
tar -C HAN_chat_specification/codebase/backend -czf han-chat-backend.tar.gz . scp -i C:\Users\MI.ssh\han_chat_deploy C:\Users\MI\Documents\Assistent\han-chat-backend.tar.gz deploy@135.106.164.58:/tmp/
scp -i C:\Users\MI.ssh\hansel C:\Users\MI\Documents\Assistent\han-chat-backend.tar.gz root@135.106.164.58:/tmp/
Дальше распаковка
cd /opt/han-chat/backend tar -xzf /tmp/han-chat-backend.tar.gz
Правим переносы строк и даем права на исполнение
find . -type f ( -name '.sh' -o -name 'validate-env' ) -exec dos2unix {} + chmod +x scripts/validate-env deployment/scripts/.sh redis/scripts/.sh nginx/scripts/.sh
Разворачиваем инфраструктуру в Селектел ч2
Создаем БД
В базе подключаем pgcrypto Чтобы корректно работал PgBouncer выбираем session pooling
Копируем СА сертификат
Вариант Деплоя: mkdir -p /opt/han-chat/backend/secrets/pg scp -i C:\Users\MI.ssh\han_chat_deploy -r C:\Users\MI\Documents\job\HAN_new_life\HANapp\Production\sertificates\CA.pem deploy@135.106.164.58:/opt/han-chat/backend/secrets/pg/ca.pem chmod 644 /opt/han-chat/backend/secrets/pg/ca.pem
Вариант Селектела: mkdir -p ~/.postgresql/ wget https://storage.dbaas.selcloud.ru/CA.pem -O ~/.postgresql/root.crt chmod 0600 ~/.postgresql/root.crt
Правки DNS-маршрутизации (в прошлый раз, какие-то трабблы были с базовой маршрутизацией) - скрипт ниже создаст
sudo tee /etc/netplan/99-han-dns.yaml <<'EOF' network: version: 2 ethernets: eth0: nameservers: addresses: - 8.8.8.8 - 1.1.1.1 search: - selcloud.ru eth1: dhcp4-overrides: use-dns: false use-domains: false EOF
sudo chmod 600 /etc/netplan/99-han-dns.yaml sudo netplan apply resolvectl flush-caches
Проверка подключения к Postgers в ВМ
nc -zv 192.168.0.211 5433
sudo apt update sudo apt install postgresql-client
psql "host=master.ef54e3e4-ad3d-4b80-a6af-d63269e0895a.c.dbaas.selcloud.ru
port=5432
dbname=han_chat
user=dbAdmin
sslmode=verify-ca"
(psql "host=master.ef54e3e4-ad3d-4b80-a6af-d63269e0895a.c.dbaas.selcloud.ru port=5432 dbname=han_chat user=dbAdmin sslmode=verify-ca")
Заводим пользователей и схемы
Пользователей создаем через интерфейс селектела. Под админом даем права на создание в БД: GRANT CREATE ON DATABASE han_chat TO han_app; GRANT CREATE ON DATABASE han_chat TO bitrix_local_app; GRANT CREATE ON DATABASE han_chat TO bitrix_sync_user; GRANT CREATE ON DATABASE han_chat TO message_safety_app; GRANT CREATE ON DATABASE han_chat TO keycloak_user; GRANT CREATE ON DATABASE han_chat TO sms_user
Если создаем пользователей после того как отозвали права from public, надо давать гранты на коннект:
GRANT CONNECT ON DATABASE han_chat TO sms_user
Схемы создаем от лица пользователей, заходя каждым из них в БД.
- Запрещаем всем посторонним входить в схему han_app и др. CREATE SCHEMA IF NOT EXISTS han_app; REVOKE ALL ON SCHEMA han_app FROM PUBLIC; ALTER ROLE CURRENT_USER IN DATABASE han_chat SET search_path TO han_app; SHOW search_path; --чтобы заработало надо переподключиться (должно быть han_app)
CREATE SCHEMA IF NOT EXISTS bitrix_local; REVOKE ALL ON SCHEMA bitrix_local FROM PUBLIC; ALTER ROLE CURRENT_USER IN DATABASE han_chat SET search_path TO bitrix_local; SHOW search_path; --чтобы заработало надо переподключиться
CREATE SCHEMA IF NOT EXISTS bitrix_sync; REVOKE ALL ON SCHEMA bitrix_sync FROM PUBLIC; ALTER ROLE CURRENT_USER IN DATABASE han_chat SET search_path TO bitrix_sync; SHOW search_path; --чтобы заработало надо переподключиться
CREATE SCHEMA IF NOT EXISTS message_safety; REVOKE ALL ON SCHEMA message_safety FROM PUBLIC; ALTER ROLE CURRENT_USER IN DATABASE han_chat SET search_path TO message_safety; SHOW search_path; --чтобы заработало надо переподключиться
CREATE SCHEMA IF NOT EXISTS keycloak; REVOKE ALL ON SCHEMA keycloak FROM PUBLIC; ALTER ROLE CURRENT_USER IN DATABASE han_chat SET search_path TO keycloak; SHOW search_path; --чтобы заработало надо переподключиться
CREATE SCHEMA IF NOT EXISTS sms AUTHORIZATION sms_user; REVOKE ALL ON SCHEMA sms FROM PUBLIC; ALTER ROLE sms_user IN DATABASE han_chat SET search_path TO sms, public; SHOW search_path;
Проверка search_path SELECT r.rolname, d.datname, s.setconfig FROM pg_db_role_setting s JOIN pg_roles r ON r.oid = s.setrole JOIN pg_database d ON d.oid = s.setdatabase WHERE r.rolname in ('han_app', 'bitrix_local_app', 'bitrix_sync_user', 'message_safety_app', 'keycloak_user');
После реализации bitrix_sync (проверить, вероятно не на все таблицы права нужны): -- Даем право на чтение (SELECT) всех СУЩЕСТВУЮЩИХ таблиц в схеме GRANT SELECT, INSERT ON ALL TABLES IN SCHEMA han_app TO bitrix_sync_user; -- Настраиваем права по умолчанию для новых таблиц ALTER DEFAULT PRIVILEGES IN SCHEMA han_app GRANT SELECT ON TABLES TO bitrix_sync_user; -- Если нужно дать право на чтение и для новых последовательностей (sequences): ALTER DEFAULT PRIVILEGES IN SCHEMA han_app GRANT USAGE, SELECT ON SEQUENCES TO bitrix_sync_user;
Заводим S3 хранилища:
Создаем 2 сервисных пользователя и заводим им ключи: - API backend SELECTEL_S3_ACCESS_KEY / SELECTEL_S3_SECRET_KEY - Message Safety SELECTEL_S3_QUARANTINE_READ_ACCESS_KEY / SELECTEL_S3_QUARANTINE_READ_SECRET_KEY
Создаем 3 приватных бакета - han-chat-quarantine (политика доступа API backend RW, Message Safety R) - han-chat-attachments (политика доступа API backend RW) - han-chat-documents (политика доступа API backend RW)
RW: ListBucket GetBucketLocation PutObject GetObject DeleteObject ListBucketMultipartUploads ListMultipartUploadParts AbortMultipartUpload
R: ListBucket GetBucketLocation GetObject ListBucketMultipartUploads ListMultipartUploadParts
Политика CORS (бакет han-chat-quarantine, vHosted обязателен): Allowed origin: https://chat.han0107.ru Methods: POST, PUT, GET, HEAD Headers: * (или явно content-type; wildcard x-amz-* в Selectel не работает) Expose headers: ETag
Тест инфраструктуры
mkdir -p /opt/han-chat/infratest cd /opt/han-chat/infratest
scp -i C:\Users\MI.ssh\hansel -r "C:\Users\MI\Documents\Assistent\HAN_chat_specification\infratest*" root@135.106.164.58:/opt/han-chat/infratest
--настраиваю env на локальной машине и копирую на диск scp -i C:\Users\MI.ssh\hansel -r "C:\Users\MI\Documents\job\HAN_new_life\HANapp\Production.env" root@135.106.164.58:/opt/han-chat/infratest/
Создаем изолированное окружение: python3 -m venv .venv . .venv/bin/activate python3 -m pip install --upgrade pip python3 -m pip install -r requirements.txt
Если не работает, то надо DNS переправить на eth0 resolvectl dns eth0 1.1.1.1 8.8.8.8 resolvectl domain eth0 '~.' resolvectl flush-caches resolvectl query pypi.org
Подготовка к запуску
Сертификат рекомендуется скопировать в папку с секретами cp /root/.postgresql/root.crt /opt/han-chat/backend/secrets/pg/root.crt chmod 644 /opt/han-chat/backend/secrets/pg/root.crt
В битрикс регистрируем локальное приложение: Установка: https://chat.example.ru/bitrix/install Обработчик: https://chat.example.ru/bitrix/handler ??? (не понадобилось) Placement: https://chat.example.ru/bitrix/placement
--настраиваю env на локальной машине и копирую на диск scp -i C:\Users\MI.ssh\hansel -r "C:\Users\MI\Documents\job\HAN_new_life\HANapp\Production.env" root@135.106.164.58:/opt/han-chat/backend
--Проверка .env cd /opt/han-chat/backend chmod 600 .env ./scripts/validate-env .env docker compose --env-file .env config --quiet docker compose --env-file .env config --services python3 -m unittest discover -s tests -v
--Проверка портов (Публиковаться должны только 80 и 443 у nginx) docker compose --env-file .env config | grep -n 'published:'
--Собираем локальные образы: docker compose --env-file .env build --pull
docker compose --env-file .env images
Миграции БД и начальные настройки
cd /opt/han-chat/backend PITR_MARKER_CONFIRMED=true deployment/scripts/migrate.sh deployment/scripts/seed.sh
Запуск внутренних сервисов
Сначала запустите Redis:
docker compose --env-file .env up -d redis
docker compose --env-file .env ps redis
Затем Keycloak и OpenTelemetry:
docker compose --env-file .env up -d keycloak otel-collector
docker compose --env-file .env ps keycloak otel-collector
Первый запуск Keycloak может занять несколько минут: он создаст свои таблицы и
импортирует realm han-chat.
После готовности Keycloak:
docker compose --env-file .env up -d message-safety
docker compose --env-file .env up -d api-backend
docker compose --env-file .env up -d bitrix-local-app bitrix-sync
docker compose --env-file .env up -d \
delivery-worker safety-recovery-worker cleanup-worker
docker compose --env-file .env ps
Если сервис не становится healthy:
docker compose --env-file .env logs --tail=200 <SERVICE_NAME>
docker inspect "$(docker compose --env-file .env ps -q <SERVICE_NAME>)"
Первоначальный выпуск TLS-сертификата
Для ACME требуется работающий nginx по HTTP. В .env оставьте
NGINX_TLS_ENABLED=true, но первый nginx запустите с временным переопределением:
NGINX_TLS_ENABLED=false \
docker compose --env-file .env up -d frontend-static nginx
Проверьте HTTP:
curl -I http://chat.han0107.ru/
Сначала рекомендуется проверить Certbot через staging:
docker compose --env-file .env --profile certbot run --rm certbot certonly \
--staging \
--webroot -w /var/www/certbot \
-d chat.han0107.ru \
--cert-name chat.han0107.ru-staging \
--email ap@han.ru \
--agree-tos --no-eff-email --non-interactive
После успешного staging-теста выпустите рабочий сертификат с основным cert-name
без --staging:
docker compose --env-file .env --profile certbot run --rm certbot certonly \
--webroot -w /var/www/certbot \
-d chat.han0107.ru \
--cert-name chat.han0107.ru \
--email ap@han.ru \
--agree-tos --no-eff-email --non-interactive
Пересоздайте nginx уже с TLS:
docker compose --env-file .env up -d --force-recreate nginx
docker compose --env-file .env exec -T nginx nginx -t -c /tmp/nginx.conf
curl -I https://chat.han0107.ru/
Повторно запустите VM setup, чтобы он обнаружил проект и установил systemd-таймер продления сертификата:
sudo /opt/han-chat/backend/deployment/scripts/setup-vm.sh
systemctl status han-chat-ssl-renew.timer
## Запуск всего контура
Теперь можно привести весь проект к состоянию, описанному Compose:
```sh
cd /opt/han-chat/backend
docker compose --env-file .env up -d
docker compose --env-file .env up -d --wait \
api-backend keycloak sms-service bitrix-local-app
# Nginx разрешает имена upstream при загрузке конфигурации. Если upstream
# был пересоздан и получил новый Docker IP, без reload edge продолжит ходить
# на старый адрес и вернёт 502.
docker compose --env-file .env exec -T nginx nginx -t -c /tmp/nginx.conf
docker compose --env-file .env kill -s HUP nginx
docker compose --env-file .env ps
Проверьте, что контейнеры не перезапускаются:
docker compose --env-file .env ps
docker compose --env-file .env logs --since=10m
Публичная проверка
Запустите smoke-тест:
cd /opt/han-chat/backend
deployment/scripts/smoke.sh
Также вручную проверьте:
curl -fsS https://chat.han0107.ru/api/v1/public/app-config | jq
curl -fsS https://chat.han0107.ru/api/v1/public/content | jq
curl -fsS \
https://chat.han0107.ru/auth/realms/han-chat/.well-known/openid-configuration | jq
Если после up, build, pull, rollback или --force-recreate менялись
api-backend, keycloak, sms-service либо bitrix-local-app, всегда
повторяйте проверку /tmp/nginx.conf и docker compose kill -s HUP nginx.
Команды nginx -t/nginx -s reload без -c /tmp/nginx.conf здесь неверны:
контейнер read-only и рабочий PID расположен в /tmp/nginx.pid.
Внутренний API не должен быть опубликован:
curl -i https://chat.han0107.ru/internal/safety/v1/messages/check
Ожидаемый статус — 404.
Откройте в браузере:
https://chat.example.ru/
Для тестовой авторизации используйте значение KEYCLOAK_OTP_MOCK_CODE из .env.
Настройки keykcloack:
Для битрикса код установки приложения: docker compose --env-file .env exec -T api-backend python - <<'PY' import os import urllib.request
base = os.environ["BITRIX_LOCAL_APP_BASE_URL"] token = os.environ["BITRIX_LOCAL_APP_INTERNAL_TOKEN"]
request = urllib.request.Request( base + "/internal/openlines/v1/setup/retry", method="POST", headers={"Authorization": "Bearer " + token}, ) print(urllib.request.urlopen(request).read().decode()) PY
План включения реальной SMS-авторизации
Этот раздел — чек-лист будущего release из modules/module-11-idgtl-sms.md, а не подтверждение готовности текущего Compose. Пока отсутствуют реализованные sms-service/worker, migrations, callback route и env validation, оставлять KEYCLOAK_OTP_MOCK_ENABLED=true.
Prerequisites без placeholders:
- согласованные i-Digital sender и active approved template
auth_otpс placeholderscode,ttl_min; - выданный Direct
TOKEN_1(IDGTL_SMS_API_KEY, без повторного Base64); - отдельные random callback username/password и публичный HTTPS URL;
- повторно подтверждённый source IP callback Direct;
- фактический статический egress IP, измеренный из
sms-worker, записанный в inventory и переданный Direct для allowlist; при динамическом IP сначала настроить NAT/static IP.
Rollout:
- Seed новых
otp.phone.*в App DB. - Создать schema/role
sms, применить versioned migrations и seed template/settings. - Проверить Keycloak→
sms-service→локальный mock Direct в test environment. - Развернуть production
sms-service/worker и nginx callback route, не выключая mock. - Применить Keycloak expand migration/SPI; прежние незавершённые challenges истечь по module-11.
- Выполнить provider smoke на контролируемом номере; проверить
sms_message_id, journal, callback и redaction. - Переключить
KEYCLOAK_OTP_MOCK_ENABLED=false. - Проверить resend→
superseded, expiry snapshot, limits и то, что Direct reject/timeout после durable order не меняет verify.
Rollback: вернуть Keycloak в mock mode; не удалять schema/journal. Остановить новые real orders, дать worker завершить либо зафиксировать in-flight/uncertain. Schema downgrade только при доказанной backward compatibility, иначе forward-fix.