Перейти к содержанию

Контейнерные стеки dix.su

Контейнерный стек — готовый docker-compose.yml, который поднимает пользовательский сервис вместе с туннельным клиентом в одном контейнере. Пользователь запускает стек у себя на машине или VPS — туннель поднимается автоматически.

Гайд по запуску — guides/container-stacks.md.


Доступные стеки

Стек Репозиторий Назначение Мин. RAM
dixsu-shop / django_shop gitlab.com/dixsu/market/django_shop Django интернет-магазин (Oscar Commerce) 512 МБ
dixsu-git gitlab.com/dixsu/market/dixsu-git Git-хостинг на базе Forgejo + встроенный Docker/OCI-реестр и реестры пакетов (PyPI, Maven, npm, ...) — см. guides/forgejo-registry.md 512 МБ
dixsu-plausible gitlab.com/dixsu/market/dixsu-plausible Аналитика посещений (Plausible CE v3.2.1) 2 ГБ ¹
dixsu-test gitlab.com/dixsu/market/dixsu-test Тестовый стек (веб-сервер-фикстура для QA) 128 МБ

¹ Требование ClickHouse: CPU с поддержкой SSE 4.2 (x86) или NEON (ARM). Подходит: RPi 4 4GB+, RPi 5, мини-ПК. Не подходит: Redmi 4, Redmi Note 8.

Все стеки используют одну и ту же структуру: сервис(ы) приложения + сервис tunnel (контейнер ramanzes/dixsu-tunnel:latest).


Архитектура

docker-compose.yml
├── app      ← пользовательский сервис (Django, Forgejo, ...)
│              слушает на внутреннем порту (обычно 8000 или 3000)
└── tunnel   ← ramanzes/dixsu-tunnel:latest
               DIXSU_SLUG  = ваш slug из личного кабинета
               DIXSU_TOKEN = ваш токен из личного кабинета
               LOCAL_PORT  = порт, на котором слушает app
               TUNNEL_SOURCE = "docker"
               └── подключается к dix.su через WSS
                   передаёт трафик от https://slug.dix.su
                   на app:LOCAL_PORT внутри docker-сети

Контейнеры общаются через docker-сеть — tunnel обращается к app по имени сервиса (не через localhost).


Docker-образ туннельного клиента

ramanzes/dixsu-tunnel:latest — минимальный образ на базе elixir:1.19-slim.

Dockerfile

FROM elixir:1.19-slim
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \
    && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY device_client.exs .
ENTRYPOINT ["elixir", "device_client.exs"]

CA-сертификаты необходимы — без них Mix.install не может скачать зависимости с hex.pm по HTTPS.

Конфигурация через env

Образ поддерживает два режима запуска:

1. Env-переменные (Docker-режим)

environment:
  DIXSU_SLUG: your-slug
  DIXSU_TOKEN: your-token
  LOCAL_PORT: "8000"
  TUNNEL_SOURCE: "docker"

2. CLI-аргументы (прямой режим)

elixir device_client.exs your-slug your-token 8000
Переменная CLI-эквивалент Default Описание
DIXSU_SLUG $1 обязательно Slug туннеля
DIXSU_TOKEN $2 обязательно Токен туннеля
LOCAL_PORT $3 80 Порт, на котором слушает приложение
TUNNEL_SOURCE "direct" "docker" для контейнерных стеков
SERVER_HOST 127.0.0.1 Хост прокси-сервера
SERVER_PORT 4001 Порт прокси-сервера
CA_CERT_PATH Путь к CA-сертификату (для self-signed)

Репозиторий образа: gitlab.com/dixsu/elixir/websocket-client


Идентификация источника туннеля (TUNNEL_SOURCE)

При подключении к серверу клиент передаёт ?source=direct|docker в WebSocket-URL:

/socket/websocket?slug=...&token=...&vsn=2.0.0&source=docker

Phoenix-сервер сохраняет значение в Registry-метаданных:

# dixu_proxy: channel join
{:ok, socket} = Socket.assign(socket, :source, source_param)
Registry.register(DeviceRegistry, slug, %{source: source})

Внутренний API /internal/tunnel/:slug возвращает поле source вместе со статусом активности. Личный кабинет читает его через /api/profile/tunnel/status.

Отображение в UI (dix.su/auth/protected#tunnel)

Источник Иконка Метка
direct активен · прямой
docker 📦 активен · docker-стек
туннель не активен не подключён

Ограничения для docker-стеков

Ряд функций отключён для контейнерных туннелей (источник "docker"):

Заблокированные TCP-порты

Протокол Порт Причина
SSH 22 недоступен изнутри контейнера
RDP 3389 не имеет смысла для web-сервисов
VNC 5900 не имеет смысла для web-сервисов

Тумблеры этих протоколов в UI не активны, при наведении — подсказка с объяснением.

Блокировка настроек при активном туннеле

Когда туннель активен (любого типа — прямой или docker), в разделе «Туннель» личного кабинета блокируются:

  • Кнопки выбора платформы (Linux/macOS/Windows/ARM)
  • Кнопка скачивания бинарника
  • Поле порта
  • Кнопка копирования команды установки
  • Форма ввода email
  • Кнопка копирования E2E-ссылки

Отображается жёлтый баннер с предупреждением. Это предотвращает попытку создать новый туннель, пока один уже активен.


Пример: структура docker-compose.yml стека

services:
  app:
    image: ...
    expose:
      - "8000"
    env_file: .env
    networks:
      - dixsu

  tunnel:
    image: ramanzes/dixsu-tunnel:latest
    restart: unless-stopped
    depends_on:
      - app
    environment:
      DIXSU_SLUG: ${DIXSU_SLUG}
      DIXSU_TOKEN: ${DIXSU_TOKEN}
      LOCAL_PORT: "8000"
      SERVER_HOST: dix.su
      SERVER_PORT: "443"
      TUNNEL_SOURCE: "docker"
    networks:
      - dixsu

networks:
  dixsu:
    driver: bridge

Заметки: - expose, а не ports для app — порт не публикуется наружу, только в docker-сети. - restart: unless-stopped на tunnel — обеспечивает автоматическое переподключение. - depends_on: app — tunnel стартует после app, но TCP-relay всё равно работает если app временно упал (клиент переподключается).


Деплой стеков через git

Каждый стек хранится в отдельном репозитории на GitLab:

# Первый запуск
git clone git@gitlab.com:dixsu/market/django_shop.git
cd django_shop
cp .env.template .env
# Заполнить .env → DIXSU_SLUG, DIXSU_TOKEN, ...
docker compose up -d

# Обновление
git pull
docker compose up -d --build app  # только приложение, без пересборки tunnel

Образ ramanzes/dixsu-tunnel:latest обновляется отдельно — rebuild нужен только при смене логики клиента (новый TUNNEL_SOURCE, env-fallback и т.д.).


Архитектура стека dixsu-plausible (3 сервиса + tunnel)

Plausible CE состоит из трёх компонентов, каждый в отдельном контейнере:

docker-compose.yml
├── plausible_db         ← PostgreSQL 16 (пользователи, сайты, настройки)
├── plausible_events_db  ← ClickHouse 24.12 (события посещений)
├── plausible            ← ghcr.io/plausible/community-edition:v3.2.1
│                          порт 8000, зависит от обеих БД (condition: healthy)
└── tunnel               ← ramanzes/dixsu-tunnel:latest → LOCAL_PORT=8000

plausible стартует только после того, как обе БД перешли в healthy. При первом старте выполняет миграции автоматически.

Конфигурация .env

# ── dix.su туннель ────────────────────────────────────────────────
DIXSU_SLUG=myanalytics          # ← ваш slug
DIXSU_TOKEN=abc123xyz           # ← ваш токен

# ── Plausible ─────────────────────────────────────────────────────
BASE_URL=https://myanalytics.dix.su   # публичный URL инстанса
SECRET_KEY_BASE=<openssl rand -base64 64>   # мин. 64 байта

# ── PostgreSQL ────────────────────────────────────────────────────
POSTGRES_PASSWORD=сильный-пароль
# DATABASE_URL по умолчанию строится из POSTGRES_PASSWORD автоматически

# ── ClickHouse ────────────────────────────────────────────────────
# CLICKHOUSE_DATABASE_URL по умолчанию http://plausible_events_db:8123/plausible_events

Полный список env-переменных (SMTP, Google OAuth, TOTP, геолокация) — в GitHub Wiki проекта.

Интеграция сайта

Plausible работает на стороне браузера. Добавить в <head> отслеживаемого сайта:

<script defer data-domain="myshop.dix.su"
        src="https://myanalytics.dix.su/js/script.js"></script>

Бэкенд (Django/Node/любой) этот обмен не видит — он просто один раз отдаёт HTML со скриптом.

Дополнительно: - Events API (POST /api/event) — события с сервера без JS (конверсии, регистрации) - Stats API — чтение агрегированной статистики из своего приложения


Известные ограничения

  • Один туннель на slug: нельзя запустить два стека с одним slug одновременно — второй соединится и вытолкнет первый.
  • TCP-порты недоступны: docker-стек не форвардит SSH/RDP/VNC — только HTTP/HTTPS.
  • Email: по умолчанию EMAIL_BACKEND=django.core.mail.backends.dummy.EmailBackend — письма игнорируются. Для отправки сменить на smtp.EmailBackend и задать SMTP-параметры.
  • dixsu-plausible требует мощного железа: ClickHouse не запустится без SSE 4.2 / NEON и менее 2 ГБ RAM. На Redmi 4 / Redmi Note 8 стек не работоспособен.