From a37acfaaa7c88cddce940c5a57908c6a05561ff5 Mon Sep 17 00:00:00 2001 From: Liosha84 Date: Fri, 6 Nov 2026 02:58:44 +0100 Subject: [PATCH] =?UTF-8?q?Docker-=D0=BE=D0=B1=D1=80=D0=B0=D0=B7=20=D0=B2?= =?UTF-8?q?=20=D1=80=D0=B5=D0=B5=D1=81=D1=82=D1=80=D0=B5=20Gitea=20(Drone,?= =?UTF-8?q?=20=D0=BA=D0=B0=D0=BA=20=D1=83=20htop-web)=20=D0=B8=20docker-co?= =?UTF-8?q?mpose.image.yml=20=D0=BD=D0=B0=20=D0=BD=D1=91=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .dockerignore | 9 +++++ .drone.yml | 47 ++++++++++++++++++++++ README.md | 17 ++++++++ docker-compose.image.yml | 84 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 157 insertions(+) create mode 100644 .dockerignore create mode 100644 .drone.yml create mode 100644 docker-compose.image.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..cd8edf0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +.git +.idea +.vscode +**/node_modules +**/build +**/dist +**/.gradle +**/.angular +*.log diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..0cf3d90 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,47 @@ +# Docker image of reactive-mail-server — like htop-web: Drone builds backend/Dockerfile, +# master pushes it to the Gitea container registry of jambotron.com. +# pull request -> build only (image is not pushed) +# push master -> gitea.jambotron.com/jambotron_gang/reactive-mail-server:latest and : +# Drone secrets of this repo: REGISTRY_USERNAME, REGISTRY_PASSWORD (Gitea user with write:package in jambotron_gang) +kind: pipeline +type: docker +name: build-test + +trigger: + event: + - pull_request + +steps: + - name: build-test + image: plugins/docker + settings: + dockerfile: backend/Dockerfile + context: . + repo: gitea.jambotron.com/jambotron_gang/reactive-mail-server + dry_run: true +--- +kind: pipeline +type: docker +name: build-and-push + +trigger: + event: + - push + branch: + - master + +steps: + - name: build-and-push + image: plugins/docker + settings: + dockerfile: backend/Dockerfile + context: . + registry: gitea.jambotron.com + repo: gitea.jambotron.com/jambotron_gang/reactive-mail-server + tags: + - latest + - ${DRONE_COMMIT_SHA:0:8} + username: + from_secret: REGISTRY_USERNAME + password: + from_secret: REGISTRY_PASSWORD diff --git a/README.md b/README.md index aa7a9af..70c6112 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,23 @@ cd frontend && npm install && npx ng serve # http://loca Образ собирается из `backend/Dockerfile` в три этапа: Angular → Spring Boot (веб-клиент внутри jar) → JRE не от root. +### Готовый Docker-образ + +Как у htop-web: Drone (`.drone.yml`) собирает `backend/Dockerfile`. На PR образ только собирается, на `master` уходит в реестр Gitea: + +``` +gitea.jambotron.com/jambotron_gang/reactive-mail-server:latest +gitea.jambotron.com/jambotron_gang/reactive-mail-server:<первые 8 символов коммита master> +``` + +Реестр публичный: `docker pull` без логина. + +`docker-compose.image.yml` — тот же стенд, что `docker-compose.jambotron.yml`, но сервис `mail` берётся из образа, на ВМ ничего не собирается (деплой быстрее, Node и Gradle на ВМ не нужны). В мастере деплоя Jambotron выбрать этот файл; домен — сервису `mail`, порт `8080`. По умолчанию `latest`, он скачивается заново при каждом деплое (`pull_policy: always`); закрепить сборку — переменная `MAIL_IMAGE_TAG=<8 символов коммита>`. + +```bash +docker compose -f docker-compose.image.yml up -d +``` + ## Не сделано - [ ] Сертификат Let's Encrypt для настоящей почты (75.6): сейчас — свой файл или самоподписанный. diff --git a/docker-compose.image.yml b/docker-compose.image.yml new file mode 100644 index 0000000..dd28002 --- /dev/null +++ b/docker-compose.image.yml @@ -0,0 +1,84 @@ +# Same stand as docker-compose.jambotron.yml, but from the ready image — nothing is built on the VM: +# gitea.jambotron.com/jambotron_gang/reactive-mail-server (Drone builds it from master, see .drone.yml). +# Pin a build: MAIL_IMAGE_TAG=; default latest (pulled again on every deploy). +# Test deploy on Jambotron (v0.2.3 75.3): pick this file in the deploy wizard. +# Outside, only the web client + API (service "mail", port 8080) through Jambotron's Caddy — give it a domain. +# SMTP (3025) stays inside the VM's docker network; nothing is sent outside (OUTBOUND_MODE=off). +# Secrets are generated on the first start into the "secrets" volume — nothing to type in, nothing in git. +# Mailboxes admin@ and trap@mail.test (catch-all): passwords in the "mail" log on the first start +# (Jambotron: project → Services → mail → Logs) and in /app/data/initial-passwords.txt +# of the "mail" container (docker exec cat /app/data/initial-passwords.txt). +name: reactive-mail + +services: + secrets-init: + image: alpine:3.20 + restart: "no" + command: + - sh + - -c + - | + umask 077 + for name in DB_PASSWORD JWT_SECRET; do + [ -s /secrets/$$name ] || head -c 48 /dev/urandom | base64 | tr -dc 'A-Za-z0-9' > /secrets/$$name + done + # postgres reads it as uid 70, the app as uid 10001 — readable inside these containers only + chmod 644 /secrets/DB_PASSWORD /secrets/JWT_SECRET + volumes: + - secrets:/secrets + + db: + # Debian, not -alpine: on Jambotron staging VMs (Alpine 3.24, kernel 6.18) the Alpine build of postgres + # dies with "Bus error" even on --version; the Debian build works (2026-11-05) + image: postgres:17 + restart: unless-stopped + depends_on: + secrets-init: + condition: service_completed_successfully + environment: + POSTGRES_DB: mailserver + POSTGRES_USER: mail + POSTGRES_PASSWORD_FILE: /run/mail-secrets/DB_PASSWORD + volumes: + - pgdata:/var/lib/postgresql/data + - secrets:/run/mail-secrets:ro + healthcheck: + test: ["CMD-SHELL", "pg_isready -U mail -d mailserver"] + interval: 10s + timeout: 5s + retries: 10 + mem_limit: 256m + + mail: + image: gitea.jambotron.com/jambotron_gang/reactive-mail-server:${MAIL_IMAGE_TAG:-latest} + pull_policy: always + restart: unless-stopped + depends_on: + db: + condition: service_healthy + expose: + - "8080" # web client + API → domain in Jambotron + - "3025" # SMTP, inside the network only + - "3143" # IMAP (STARTTLS), inside the network only — open it in Jambotron as an internal TCP port + environment: + DB_HOST: db + MAILSERVER_SECRETS_DIR: /run/mail-secrets + MAIL_DOMAINS: ${MAIL_DOMAINS:-mail.test} + MAIL_HOSTNAME: ${MAIL_HOSTNAME:-mail.test} + DKIM_DOMAIN: ${MAIL_DOMAINS:-mail.test} + # mail trap: mail to any address without its own mailbox here (also gmail.com etc.) goes into this mailbox + MAIL_CATCH_ALL: ${MAIL_CATCH_ALL:-trap@mail.test} + MAIL_REGISTRATION: ${MAIL_REGISTRATION:-true} # test stand: anyone can create a mailbox in MAIL_DOMAINS (nothing goes outside) + OUTBOUND_MODE: "off" + SMTP_AUTH_WITHOUT_TLS: "true" # SMTP is not reachable from outside the VM + SPF_ENABLED: "false" + DMARC_ENABLED: "false" + volumes: + - data:/app/data + - secrets:/run/mail-secrets:ro + mem_limit: 512m + +volumes: + secrets: + pgdata: + data: -- 2.54.0