diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..7b203c8 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +_build/ +build/ +deps/ +logs/ +Mnesia.* +.git/ +.gitignore +*.log +*.dump +.DS_Store \ No newline at end of file diff --git a/Makefile b/Makefile index 5b3dadc..afded40 100644 --- a/Makefile +++ b/Makefile @@ -192,20 +192,64 @@ release: ## Собрать релиз @$(REBAR3) as prod release @echo "✓ Релиз собран в _build/prod/rel/eventhub/" +# ============================================================================ +# DOCKER +# ============================================================================ docker-build: ## Собрать Docker образ @echo "Сборка Docker образа..." - @docker build -t eventhub:latest . - @echo "✓ Docker образ собран" + @docker build -f docker/Dockerfile -t eventhub:latest . + @echo "✅ Docker образ собран" -docker-run: ## Запустить Docker контейнер +docker-build-debug: ## Собрать Docker образ + @echo "Сборка Docker образа..." + @docker build -f docker/Debug.Dockerfile -t eventhub-debug:latest . + @echo "✅ Docker образ собран" + + +docker-run: ## Запустить Docker контейнер (одиночный) @echo "Запуск Docker контейнера..." - @docker run -p 8080:8080 -p 8445:8445 --name eventhub eventhub:latest + @docker run -d \ + --name eventhub \ + -p 8080:8080 \ + -p 8081:8081 \ + -p 8445:8445 \ + -p 8446:8446 \ + -v eventhub-data:/app/data \ + eventhub:latest + @echo "✅ Контейнер запущен на http://localhost:8080" docker-stop: ## Остановить Docker контейнер @echo "Остановка Docker контейнера..." - @docker stop eventhub || true - @docker rm eventhub || true - @echo "✓ Контейнер остановлен" + @docker stop eventhub 2>/dev/null || true + @docker rm eventhub 2>/dev/null || true + @echo "✅ Контейнер остановлен" + +docker-logs: ## Показать логи Docker контейнера + @docker logs -f eventhub + +docker-shell: ## Зайти в Docker контейнер + @docker exec -it eventhub sh + +docker-compose-up: ## Запустить кластер (3 ноды) + @echo "Запуск кластера EventHub (3 ноды)..." + @docker-compose -f docker/docker-compose.yml up -d + @echo "✅ Кластер запущен" + @echo "Node 1: http://localhost:8080" + @echo "Node 2: http://localhost:8082" + @echo "Node 3: http://localhost:8084" + +docker-compose-down: ## Остановить кластер + @echo "Остановка кластера..." + @docker-compose -f docker/docker-compose.yml down + @echo "✅ Кластер остановлен" + +docker-compose-logs: ## Показать логи кластера + @docker-compose -f docker/docker-compose.yml logs -f + +docker-clean: docker-stop ## Очистить Docker образы и volumes + @docker rmi eventhub:latest 2>/dev/null || true + @docker volume rm eventhub-data 2>/dev/null || true + @echo "✅ Docker очищен" # ============================================================================ # UTILITIES diff --git a/docker/Debug.Dockerfile b/docker/Debug.Dockerfile new file mode 100644 index 0000000..a58d275 --- /dev/null +++ b/docker/Debug.Dockerfile @@ -0,0 +1,38 @@ +# ============================================================ +# Одноэтапный Dockerfile (сборка и рантайм в одном образе) +# ============================================================ +FROM erlang:28-alpine + +# Устанавливаем инструменты для сборки и runtime-зависимости +RUN apk add --no-cache \ + # для сборки + git curl make gcc musl-dev \ + rust cargo openssl-dev libsodium-dev \ + # для рантайма + openssl libstdc++ libgcc ncurses-libs + +# Рабочая директория +WORKDIR /app + +# Копируем конфигурацию и исходники +COPY rebar.config ./ +COPY include/ include/ +COPY src/ src/ +COPY src/config/sys.config ./config/sys.config + +# Собираем зависимости и релиз +RUN rebar3 get-deps && \ + rebar3 as prod release + +# Создаём директорию для данных +RUN mkdir -p /app/data && chmod 777 /app/data + +# Настраиваем порты +EXPOSE 8080 8081 8445 8446 + +# Переменные окружения +ENV RELX_REPLACE_OS_VARS=true +ENV MNESIA_DIR=/app/data + +# Запускаем приложение из собранного релиза +CMD ["/app/_build/prod/rel/eventhub/bin/eventhub", "foreground"] \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..fde0ffe --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,44 @@ +# ============================================================ +# Этап 1: Сборка +# ============================================================ +FROM erlang:28-alpine AS builder + +RUN apk add --no-cache \ + git curl make gcc musl-dev \ + rust cargo openssl-dev libsodium-dev + +WORKDIR /app +COPY rebar.config ./ +RUN rebar3 lock || true +RUN rebar3 get-deps + +COPY include/ include/ +COPY src/ src/ + +# Копируем sys.config из src/config/ в config/ +COPY src/config/sys.config ./config/sys.config + +RUN rebar3 as prod release +RUN rebar3 as prod tar +RUN mkdir -p /app/release && \ + tar -xzf _build/prod/rel/eventhub/eventhub-*.tar.gz -C /app/release + +# ============================================================ +# Этап 2: Финальный образ +# ============================================================ +FROM alpine:3.20 + +RUN apk add --no-cache openssl libstdc++ libgcc ncurses-libs libsodium +COPY --from=builder /app/release /app +COPY --from=builder /usr/lib/libcrypto.* /usr/lib/ +COPY --from=builder /usr/lib/libssl.* /usr/lib/ + +RUN mkdir -p /app/data && chmod 777 /app/data + +WORKDIR /app +EXPOSE 8080 8081 8445 8446 + +ENV RELX_REPLACE_OS_VARS=true +ENV MNESIA_DIR=/app/data + +CMD ["/app/bin/eventhub", "foreground"] \ No newline at end of file diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..20efa3d --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,86 @@ +version: '3.8' + +services: + eventhub-node1: + build: + context: .. + dockerfile: docker/Dockerfile + container_name: eventhub-node1 + ports: + - "8080:8080" + - "8081:8081" + - "8445:8445" + - "8446:8446" + environment: + - NODE_NAME=eventhub-node1@eventhub-node1 + - HTTP_PORT=8080 + - WS_PORT=8081 + - ADMIN_HTTP_PORT=8445 + - ADMIN_WS_PORT=8446 + - MNESIA_DIR=/app/data + volumes: + - eventhub-node1-data:/app/data + networks: + - eventhub-net + restart: unless-stopped + + eventhub-node2: + build: + context: .. + dockerfile: docker/Dockerfile + container_name: eventhub-node2 + ports: + - "8082:8080" + - "8083:8081" + - "8447:8445" + - "8448:8446" + environment: + - NODE_NAME=eventhub-node2@eventhub-node2 + - HTTP_PORT=8080 + - WS_PORT=8081 + - ADMIN_HTTP_PORT=8445 + - ADMIN_WS_PORT=8446 + - MNESIA_DIR=/app/data + - JOIN_NODES=eventhub-node1@eventhub-node1 + volumes: + - eventhub-node2-data:/app/data + networks: + - eventhub-net + depends_on: + - eventhub-node1 + restart: unless-stopped + + eventhub-node3: + build: + context: .. + dockerfile: docker/Dockerfile + container_name: eventhub-node3 + ports: + - "8084:8080" + - "8085:8081" + - "8449:8445" + - "8450:8446" + environment: + - NODE_NAME=eventhub-node3@eventhub-node3 + - HTTP_PORT=8080 + - WS_PORT=8081 + - ADMIN_HTTP_PORT=8445 + - ADMIN_WS_PORT=8446 + - MNESIA_DIR=/app/data + - JOIN_NODES=eventhub-node1@eventhub-node1 + volumes: + - eventhub-node3-data:/app/data + networks: + - eventhub-net + depends_on: + - eventhub-node1 + restart: unless-stopped + +volumes: + eventhub-node1-data: + eventhub-node2-data: + eventhub-node3-data: + +networks: + eventhub-net: + driver: bridge \ No newline at end of file diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh new file mode 100644 index 0000000..a4c35d8 --- /dev/null +++ b/docker/docker-entrypoint.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -e + +# Создаём директорию Mnesia, если не существует +mkdir -p ${MNESIA_DIR} + +# Запускаем приложение +exec /app/bin/eventhub foreground \ No newline at end of file diff --git a/rebar.config b/rebar.config index f973f04..cdc751c 100644 --- a/rebar.config +++ b/rebar.config @@ -18,11 +18,19 @@ {relx, [ {release, {eventhub, "0.0.1"}, [eventhub, sasl]}, {dev_mode, true}, - {include_erts, false}, + {include_erts, true}, {extended_start_script, true} ]}. {profiles, [ + {prod, [ + {relx, [ + {release, {eventhub, "0.0.1"}, [eventhub, sasl]}, + {include_erts, true}, + {extended_start_script, true}, + {sys_config, "./src/config/sys.config"} + ]} + ]}, {test, [ {erl_opts, [debug_info, {i, "include"}, {d, 'TEST'}]}, {src_dirs, ["src", "test/unit"]}, diff --git a/src/config/sys.config b/src/config/sys.config index 7d3270f..128d631 100644 --- a/src/config/sys.config +++ b/src/config/sys.config @@ -4,11 +4,10 @@ {ws_port, 8081}, {admin_http_port, 8445}, {admin_ws_port, 8446}, - {jwt_secret, <<"my-super-secret-key-for-jwt-32-bytes!">>}, - {argon2_params, #{t_cost => 2, m_cost => 19, parallelism => 1}} + {jwt_secret, <<"${JWT_SECRET:-change_me_in_production}">>} ]}, {mnesia, [ - {dir, "Mnesia.${NODE}"} + {dir, "${MNESIA_DIR:-Mnesia.${NODE}}"} ]}, {kernel, [ {logger_level, info},