From d2dac031c4a076a80c6ccf0d1ef613741eef9766 Mon Sep 17 00:00:00 2001 From: djerom Date: Wed, 3 Dec 2025 16:52:20 +0500 Subject: [PATCH] migrate from cc --- .example.env | 8 ++ .gitignore | 3 + dev/docker-compose.yml | 202 +++++++++++++++++++++++++++++++++++ dev/login.sh | 2 + setup-alpine.sh | 233 +++++++++++++++++++++++++++++++++++++++++ test.txt | 1 - 6 files changed, 448 insertions(+), 1 deletion(-) create mode 100644 .example.env create mode 100644 .gitignore create mode 100644 dev/docker-compose.yml create mode 100644 dev/login.sh create mode 100644 setup-alpine.sh delete mode 100644 test.txt diff --git a/.example.env b/.example.env new file mode 100644 index 0000000..81459b1 --- /dev/null +++ b/.example.env @@ -0,0 +1,8 @@ +# --- Infrastructure +DOMAIN= + +REG=git.sys.smsynergy.ru +REG_USER= +REG_PASS= + +# --- Databases \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6130c02 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.vscode +.env +temp diff --git a/dev/docker-compose.yml b/dev/docker-compose.yml new file mode 100644 index 0000000..d2768f4 --- /dev/null +++ b/dev/docker-compose.yml @@ -0,0 +1,202 @@ +services: + # --- Infrastructure services --- + traefik: + image: traefik:v2.9 + restart: always + ports: + - "80:80" + - "443:443" + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ./traefik/acme:/acme + command: + - --api.dashboard=true + - --providers.docker=true + - --providers.docker.defaultRule=Host(`app.${DOMAIN}`) && PathPrefix(`/{{ index .Labels "com.docker.compose.service" }}/`) + - --entrypoints.web.address=:80 + # Применяем middleware auth автоматически ко всем сервисам через entrypoint + - --entrypoints.web.http.middlewares=auth@docker,error-401@docker + + - --entrypoints.websecure.address=:443 + - --entrypoints.websecure.http.tls=true + - --entrypoints.websecure.http.tls.certresolver=le + + - --certificatesresolvers.le.acme.email=admin@mail.${DOMAIN} + - --certificatesresolvers.le.acme.storage=/acme/acme.json +# - --certificatesresolvers.le.acme.tlschallenge=true + - --certificatesresolvers.le.acme.httpchallenge=true + - --certificatesresolvers.le.acme.httpchallenge.entrypoint=web + labels: + - "traefik.enable=true" + - "traefik.http.routers.traefik.rule=Host(`traefik.${DOMAIN}`)" + - "traefik.http.routers.traefik.entrypoints=web" + - "traefik.http.routers.traefik.service=api@internal" + + # Forward auth middleware + - "traefik.http.middlewares.auth.forwardauth.address=http://sah/api/forward/check" + - "traefik.http.middlewares.auth.forwardauth.trustForwardHeader=true" + - "traefik.http.middlewares.auth.forwardauth.authResponseHeaders=Authorization,Cookie" + + # Error handler для перехвата 401 ошибок от сервисов + - "traefik.http.middlewares.error-401.errors.status=401-401" + - "traefik.http.middlewares.error-401.errors.service=sah" + - "traefik.http.middlewares.error-401.errors.query=/api/forward/handle-401" + + # Middleware для stripprefix + - "traefik.http.middlewares.global-stripprefix.replacepathregex.regex=^/([^/]+)/(.*)" + - "traefik.http.middlewares.global-stripprefix.replacepathregex.replacement=/$$2" + - traefik.http.middlewares.global-stripprefix2.replacepathregex.regex=^/([^/]+)/[^/]+/(.*) + - traefik.http.middlewares.global-stripprefix2.replacepathregex.replacement=/$$2 + + sah: + image: ${REG}/cc/sah:latest + volumes: + - ./sah/data:/data + environment: + - COOKIE_DOMAIN=${DOMAIN} + - SAH_DOMAIN=sah.${DOMAIN} + labels: + - "traefik.http.routers.sah.rule=Host(`sah.${DOMAIN}`)" + - "traefik.http.services.sah.loadbalancer.server.port=80" + + portainer: + image: portainer/portainer-ce:latest + environment: + - PORTAINER_HTTP_ENABLED=true + volumes: + - /var/run/docker.sock:/var/run/docker.sock + labels: + - "traefik.http.routers.portainer.rule=Host(`portainer.${DOMAIN}`)" + - "traefik.http.services.portainer.loadbalancer.server.port=9000" + + watchtower: + image: containrrr/watchtower + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /root/.docker/config.json:/config.json + environment: + - WATCHTOWER_CLEANUP=true + - WATCHTOWER_DEBUG=true + - WATCHTOWER_LABEL_ENABLE=true + - WATCHTOWER_POLL_INTERVAL=30 + - WATCHTOWER_SSL_VERIFY=false + labels: + - traefik.enable=false + + portagent: + image: portainer/agent:2.19.1 + restart: always + ports: + - 9001:9001 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /var/lib/docker/volumes:/var/lib/docker/volumes + labels: + - traefik.enable=false + + api: + image: tecnativa/tcp-proxy + restart: unless-stopped + tty: true + environment: + - LISTEN=:80 :443 + - TALK=traefik:80 traefik:443 + - TIMEOUT_TUNNEL=360s + labels: + - traefik.http.routers.api.middlewares=global-stripprefix + - traefik.http.services.api.loadbalancer.server.port=80 + + # --- Database services --- + + mongo: + image: mongo:4.0.9 + ports: + - "27017:27017/tcp" + restart: "always" + volumes: + - ./db/mongo:/data/db + - ./dump/mongo:/dump + labels: + - traefik.enable=false + + mongoadmin: + image: mongo-express:1.0-20-alpine3.19 + environment: + - ME_CONFIG_MONGODB_ENABLE_ADMIN=true + - ME_CONFIG_MONGODB_URL=mongodb://mongo:27017/ + - ME_CONFIG_BASICAUTH=false # Отключает базовую аутентификацию + labels: + - traefik.http.routers.mongoadmin.rule=Host(`mongoadmin.${DOMAIN}`) + - traefik.http.services.mongoadmin.loadbalancer.server.port=8081 + + # --- Application services --- + + app: + image: ${REG}/cc/front3:latest + restart: "always" + labels: + - "traefik.http.routers.app.rule=Host(`app.${DOMAIN}`) && PathPrefix(`/`)" + - "traefik.http.services.app.loadbalancer.server.port=80" + + ma: + image: ${REG}/cc/cc_ma + environment: + - "DB_BIG=big" + - "DB_HOST=mongo" + - "MA_PREFIX=p1" + - "DB_NAME=master" + - "MA_NO_SYNC=master" + - "DB=" + - "PROCESSES=4" + - "MA_SEARCH_LIMIT=500" + restart: "always" + labels: + - com.centurylinklabs.watchtower.enable=true + # - traefik.http.routers.ma.middlewares=global-stripprefix + + # --- Plugins services --- + + metabase: + image: metabase/metabase:latest + restart: always + environment: + - MB_JETTY_PORT=3000 + - MB_DB_TYPE=h2 # Используем встроенную H2 БД (не для продакшена!) + - MB_DB_FILE=/metabase-data/metabase.db + - MB_SITE_NAME=SMSynergy Analytics + volumes: + - ./metabase:/metabase-data # Сохраняем данные между перезапусками + labels: + - "traefik.http.routers.metabase.rule=Host(`metabase.${DOMAIN}`)" + - "traefik.http.services.metabase.loadbalancer.server.port=3000" + + n8n: + image: n8nio/n8n:1.121.2 + container_name: n8n + restart: unless-stopped + environment: + - N8N_PORT=5678 + - N8N_SECURE_COOKIE=false + - DB_TYPE=sqlite + - DB_SQLITE_POOL_SIZE=64 + - N8N_RUNNERS_ENABLED=true + - N8N_GIT_NODE_DISABLE_BARE_REPOS=true + - N8N_BLOCK_ENV_ACCESS_IN_NODE=false + - N8N_DIAGNOSTICS_ENABLED=false + - N8N_PERSONALIZATION_ENABLED=false + labels: + - "traefik.http.routers.n8n.rule=Host(`n8n.${DOMAIN}`)" + - "traefik.http.services.n8n.loadbalancer.server.port=5678" + + jupyter: + image: jupyter/datascience-notebook:latest + command: > + start-notebook.sh + --NotebookApp.token='' + --NotebookApp.password='' + --ServerApp.disable_check_xsrf=True + volumes: + - ./jupyter_data:/home/jovyan/work + labels: + - "traefik.http.routers.jupyter.rule=Host(`jupyter.${DOMAIN}`)" + - "traefik.http.services.jupyter.loadbalancer.server.port=8888" diff --git a/dev/login.sh b/dev/login.sh new file mode 100644 index 0000000..28d6fc9 --- /dev/null +++ b/dev/login.sh @@ -0,0 +1,2 @@ +. ./.env +docker login $REG -u $REG_USER -p $REG_PASS \ No newline at end of file diff --git a/setup-alpine.sh b/setup-alpine.sh new file mode 100644 index 0000000..86084bd --- /dev/null +++ b/setup-alpine.sh @@ -0,0 +1,233 @@ +#!/bin/sh + +REG=git.sys.smsynergy.ru +VERSION=v0.0.1 + +. ./.env + +echo $DOMAIN + +set -e + +echo "==========================================" +echo " Установка системы Panorama Analytics" +echo "==========================================" +echo "" + +# Проверка наличия необходимых утилит +if ! command -v docker >/dev/null 2>&1; then + echo "Установка Docker..." + apk add --no-cache docker docker-compose + rc-update add docker boot + service docker start + sleep 2 +fi + +# Запрос DOMAIN +if [ -z "$DOMAIN" ]; then + echo -n "Введите домен для запуска системы Panorama Analytics (например: example.com): " + read DOMAIN + + if [ -z "$DOMAIN" ]; then + echo "Ошибка: DOMAIN обязателен для продолжения" + exit 1 + fi +else + echo "DOMAIN: $DOMAIN" +fi + +# Запрос токена (для доступа к git registry) +if [ -z "$REG_USER" ]; then + echo -n "Введите логинт для доступа к git registry: " + read REG_USER + + if [ -z "$REG_USER" ]; then + echo "Ошибка: логин обязателен для продолжения" + exit 1 + fi +fi + +if [ -z "$REG_PASS" ]; then + echo -n "Введите пароль для доступа к git registry: " + read REG_PASS + + if [ -z "$REG_PASS" ]; then + echo "Ошибка: пароль обязателен для продолжения" + exit 1 + fi +fi + +echo "" +echo "Настройка окружения..." + +# Создаем .env файл +cat > .env </dev/null || printf "%s:%s" "${REG_USER}" "${REG_PASS}" | base64) + +for COMPOSE_URL in \ + "https://${REG}/cc/deploy/raw/tag/${VERSION}/dev/docker-compose.yml" \ + "https://${REG}/cc/deploy/src/tag/${VERSION}/dev/docker-compose.yml" \ + "https://${REG}/cc/deploy/-/raw/${VERSION}/dev/docker-compose.yml" \ + "https://${REG}/cc/deploy/-/blob/${VERSION}/dev/docker-compose.yml?format=raw"; do + echo "Попытка скачать: ${COMPOSE_URL}" + + # Пробуем с базовой HTTP аутентификацией через заголовок (для BusyBox wget) + echo "Попытка с Basic Auth..." + if wget --header="Authorization: Basic ${AUTH_BASE64}" -O docker-compose.yml "${COMPOSE_URL}" 2>&1 | tee /tmp/wget_output.log; then + if [ -f "docker-compose.yml" ] && [ -s "docker-compose.yml" ]; then + # Проверяем, что это не HTML страница с ошибкой + if head -1 docker-compose.yml | grep -q "services:"; then + echo "docker-compose.yml успешно скачан с URL: ${COMPOSE_URL}" + DOWNLOADED=1 + break + else + echo "Предупреждение: скачанный файл не похож на docker-compose.yml" + cat docker-compose.yml | head -5 + rm -f docker-compose.yml + fi + fi + else + HTTP_CODE=$(grep -o "HTTP/[0-9.]* [0-9]*" /tmp/wget_output.log 2>/dev/null | tail -1 | awk '{print $2}' || echo "unknown") + if [ "$HTTP_CODE" != "unknown" ]; then + echo "HTTP код: ${HTTP_CODE}" + fi + if [ -f /tmp/wget_output.log ]; then + echo "Вывод wget:" + cat /tmp/wget_output.log | tail -3 + fi + fi + + # Пробуем с токеном в заголовке + echo "Попытка с токеном в заголовке..." + if wget --header="Authorization: token ${REG_PASS}" -O docker-compose.yml "${COMPOSE_URL}" 2>&1 | tee /tmp/wget_output.log; then + if [ -f "docker-compose.yml" ] && [ -s "docker-compose.yml" ]; then + if head -1 docker-compose.yml | grep -q "services:"; then + echo "docker-compose.yml успешно скачан с токеном: ${COMPOSE_URL}" + DOWNLOADED=1 + break + else + rm -f docker-compose.yml + fi + fi + fi + + # Пробуем с Bearer токеном + echo "Попытка с Bearer токеном..." + if wget --header="Authorization: Bearer ${REG_PASS}" -O docker-compose.yml "${COMPOSE_URL}" 2>&1 | tee /tmp/wget_output.log; then + if [ -f "docker-compose.yml" ] && [ -s "docker-compose.yml" ]; then + if head -1 docker-compose.yml | grep -q "services:"; then + echo "docker-compose.yml успешно скачан с Bearer токеном: ${COMPOSE_URL}" + DOWNLOADED=1 + break + else + rm -f docker-compose.yml + fi + fi + fi + + # Пробуем с URL содержащим credentials (для некоторых систем) + COMPOSE_URL_WITH_AUTH="https://${REG_USER}:${REG_PASS}@${REG}/cc/deploy/raw/tag/${VERSION}/dev/docker-compose.yml" + if [ "$COMPOSE_URL" = "https://${REG}/cc/deploy/raw/tag/${VERSION}/dev/docker-compose.yml" ]; then + echo "Попытка с credentials в URL..." + if wget -O docker-compose.yml "${COMPOSE_URL_WITH_AUTH}" 2>&1 | tee /tmp/wget_output.log; then + if [ -f "docker-compose.yml" ] && [ -s "docker-compose.yml" ]; then + if head -1 docker-compose.yml | grep -q "services:"; then + echo "docker-compose.yml успешно скачан с credentials в URL" + DOWNLOADED=1 + break + else + rm -f docker-compose.yml + fi + fi + fi + fi + + echo "" +done + +if [ $DOWNLOADED -eq 0 ]; then + echo "" + echo "==========================================" + echo "ОШИБКА: не удалось скачать docker-compose.yml" + echo "==========================================" + echo "Проверьте:" + echo "1. Правильность версии: ${VERSION}" + echo "2. Доступность репозитория: ${REG}/cc/deploy" + echo "3. Правильность логина и пароля" + echo "4. Существование файла: dev/docker-compose.yml в указанной версии" + echo "" + echo "Попробуйте вручную:" + AUTH_B64=$(printf "%s:%s" "${REG_USER}" "${REG_PASS}" | base64 -w 0 2>/dev/null || printf "%s:%s" "${REG_USER}" "${REG_PASS}" | base64) + echo "wget --header=\"Authorization: Basic ${AUTH_B64}\" -O docker-compose.yml \"https://${REG}/cc/deploy/raw/tag/${VERSION}/dev/docker-compose.yml\"" + echo "или:" + echo "wget -O docker-compose.yml \"https://${REG_USER}:${REG_PASS}@${REG}/cc/deploy/raw/tag/${VERSION}/dev/docker-compose.yml\"" + exit 1 +fi + +# Проверка наличия docker-compose.yml +if [ ! -f "docker-compose.yml" ]; then + echo "Ошибка: docker-compose.yml не найден" + exit 1 +fi + +# Запуск docker-compose +echo "" +echo "Запуск контейнеров..." +docker-compose up -d + +if [ $? -ne 0 ]; then + echo "Ошибка: не удалось запустить контейнеры" + exit 1 +fi + +# Ожидание запуска сервисов +echo "" +echo "Ожидание запуска сервисов..." +sleep 5 + +# Проверка статуса +echo "" +echo "Статус контейнеров:" +docker-compose ps + +echo "" +echo "==========================================" +echo " Установка завершена!" +echo "==========================================" +echo "" +echo "Зайдите на http://sah.${DOMAIN} для продолжения" +echo "" + diff --git a/test.txt b/test.txt deleted file mode 100644 index b0d597e..0000000 --- a/test.txt +++ /dev/null @@ -1 +0,0 @@ -test file can be downloaded by anyone \ No newline at end of file