DSpace 9 у Docker: самодостатній стек однією командою
Інституційний репозиторій — обов'язковий атрибут сучасного університету чи наукової установи. Найпопулярніша платформа для цього — DSpace, і з виходом 7-ї, а тепер і 9-ї версії її архітектура кардинально змінилась: тепер це REST-бекенд на Java та окремий Angular-фронтенд із серверним рендерингом (SSR). Встановлювати все це вручну — заняття на кілька днів із граблями на кожному кроці: PostgreSQL із pgcrypto, Solr із специфічними cores, Ant, Maven, Node.js...
Я зібрав власний самодостатній Docker-стек для DSpace 9 і виклав його на GitHub: PechenkiUA/dspace-9-docker. Одна команда docker compose up -d — і за пів години у вас працює повноцінний репозиторій з Angular UI, REST API, Solr та PostgreSQL. У цій статті розповім, як він влаштований і які проблеми довелося розв'язати.
Чим цей стек відрізняється від офіційного
DSpace має офіційні Docker-образи, але вони орієнтовані передусім на розробку самого DSpace. Мій стек робить кілька речей інакше:
- Збірка з сирців. Backend збирається Maven'ом із конкретної версії (задається в
.env), тож можна накладати власні патчі та кастомізації ще на етапі збірки. - Єдина точка входу. Nginx на порту 80 маршрутизує
/serverна REST API, а решту — на Angular SSR. Назовні дивиться один порт. - Повністю автоматична ініціалізація. Перший запуск сам генерує
local.cfg, виконуєant fresh_install, міграції бази та створює адміністратора — без жодної ручної команди. - Розв'язана проблема SSR + REST URL — про неї детально нижче, бо саме на ній ламається більшість Docker-розгортань DSpace.
Архітектура
Browser / curl
│
▼
┌──────────────────────────────────────────────┐
│ nginx :80 (єдина точка входу) │
│ ├── /server ──► DSpace REST API :8080 │
│ └── / ──► Angular SSR :4000 │
└──────────────────────────────────────────────┘
│ │
▼ ▼
PostgreSQL 16 Apache Solr 9.7
П'ять сервісів у docker-compose:
nginx(nginx:alpine) — reverse proxy;dspace— REST API, кастомний образ на Eclipse Temurin 17 JRE;dspace-ui— Angular SSR, кастомний образ на Node.js 20;db— postgres:16-alpine із розширенням pgcrypto;solr— Solr 9.7 із DSpace-конфігураціями cores.
Головна проблема DSpace у Docker — і як її розв'язано
Класична пастка, у яку потрапляють усі, хто загортає DSpace 7+ у Docker: Angular SSR виконується на сервері (у Node.js), але той самий код потім виконується у браузері користувача. І обидва мають звертатись до REST API за однаковим URL — інакше отримуємо або зламаний SSR, або зламану гідратацію на клієнті.
Типові «рішення» — прописувати host.docker.internal, правити hosts-файл усередині контейнера чи городити extra_hosts. Усе це крихко й не переноситься між середовищами.
У моєму стеку застосований прийом із Kubernetes: контейнер dspace-ui запускається з network_mode: "service:nginx" — тобто ділить мережевий простір із nginx, як контейнери в одному поді. У результаті:
- SSR у Node.js викликає
http://localhost/server→ запит потрапляє в nginx (він на тому ж localhost) → далі на backend; - браузер користувача викликає той самий
http://localhost/server→ той самий nginx на порту 80 → backend.
Однаковий URL для сервера й клієнта, жодних DNS-хаків. Побічний ефект: nginx і dspace-ui треба перезапускати разом (docker compose restart nginx dspace-ui), бо вони живуть в одному network namespace.
І ще один нюанс, який коштував мені кількох годин дебагу: Node.js 18+ за замовчуванням біндиться на ::1 (IPv6 loopback), а не на 127.0.0.1. Тому в nginx.conf апстрім для Angular прописаний як [::1]:4000 — із 127.0.0.1:4000 ви отримаєте вічний 502.
Швидкий старт
git clone https://github.com/PechenkiUA/dspace-9-docker.git
cd dspace-9-docker
Мінімальні правки у .env:
# Версія DSpace
DSPACE_VERSION=9.0
# Публічні URL
DSPACE_SERVER_URL=http://localhost/server
DSPACE_UI_URL=http://localhost
# Назва репозиторію
DSPACE_NAME=My DSpace Repository
# Адміністратор (створюється автоматично)
[email protected]
DSPACE_ADMIN_PASS=changeme
# JVM
JAVA_OPTS=-Xmx2g -Xms512m -Dfile.encoding=UTF-8
Запуск:
docker compose up -d
docker compose logs -f dspace
Перший запуск триває 15–30 хвилин — Maven завантажує близько 500 МБ залежностей і збирає DSpace, потім Ant виконує fresh_install. Наступні запуски — лічені секунди: збірка кешується через BuildKit cache mount для .m2.
Коли все піднялося:
http://localhost— Angular UI;http://localhost/server— REST API (HAL Browser);http://localhost:8983/solr— Solr Admin (debug-порт).
Що відбувається під капотом при першому старті
Уся магія — в entrypoint-скрипті бекенда:
1. Генерується /dspace/config/local.cfg з env-змінних
2. ant fresh_install — розкладає файли, підключається до БД
3. dspace database migrate — Flyway-міграції схеми
4. dspace create-administrator — створює адмін-акаунт
5. java -jar server-boot.jar — стартує Spring Boot
Щоб кроки 2–4 не виконувались при кожному рестарті, у persistent volume створюється файл-маркер .initialized. Побачив маркер — одразу переходимо до запуску сервера. Простий прийом, який варто взяти на озброєння для будь-яких «важких» ініціалізацій у Docker.
Корисні команди
# Логи окремих сервісів
docker compose logs -f dspace
docker compose logs -f dspace-ui
# DSpace CLI всередині контейнера
docker exec -it dspace-backend bash
/dspace/bin/dspace index-discovery -b # переіндексація Solr
/dspace/bin/dspace database migrate # міграції БД
# Перезбірка після зміни конфігів збірки
docker compose up -d --build dspace
# Повне скидання (видаляє ВСІ дані!)
docker compose down -v
Що зробити перед виходом у продакшн
- замінити всі паролі в
.env(особливоDSPACE_ADMIN_PASSтаDB_PASS); - налаштувати HTTPS — Certbot поверх nginx або зовнішній балансувальник; як варіант — Cloudflare Tunnel, якщо сервер без білого IP;
- закрити debug-порти 8080 (backend) та 8983 (Solr) у compose-файлі;
- збільшити
JAVA_OPTSдля великих колекцій (-Xmx4g -Xms1g); - налаштувати SMTP (
mail.*уlocal.cfg) — без нього не працюватимуть реєстрація та сповіщення; - поставити на розклад бекапи volumes
pgdata(база) таdspace_home(assetstore з файлами документів). Solr бекапити не потрібно — індекс відновлюється командоюindex-discovery -b.
Типові проблеми
502 Bad Gateway після перезапуску nginx. Nginx і dspace-ui ділять network namespace, тому перезапускати їх треба разом: docker compose restart nginx dspace-ui.
Перший старт «завис». Це нормально: Maven тягне ~500 МБ залежностей. Дивіться прогрес у docker compose logs -f dspace.
local.cfg: No such file or directory. У volume залишився маркер від невдалої попередньої установки. Повне скидання: docker compose down -v && docker compose up -d.
Solr healthcheck не проходить. Дайте йому 2–3 хвилини після першого старту — cores ініціалізуються повільно.
Висновок
Docker перетворює розгортання DSpace 9 з багатоденного квесту на одну команду. Ключові рішення цього стеку — спільний network namespace для nginx та Angular SSR (однаковий URL API для сервера й браузера без DNS-хаків), автоматична ініціалізація через entrypoint із маркером .initialized та збірка з сирців, яка дозволяє кастомізувати DSpace під себе.
Репозиторій відкритий: github.com/PechenkiUA/dspace-9-docker — issues та pull requests вітаються. У наступних матеріалах розглянемо міграцію контенту зі старих версій DSpace (5/7) через SAF-пакети та публікацію репозиторію через Cloudflare Tunnel без білого IP.