InvenioRDM Docker Compose 部署指南
InvenioRDM 14.0.6 Docker Compose 全栈部署实测指南:Ubuntu 26.04 + Docker 29.9.2/Compose v5.6.1 上经 invenio-cli 1.13.1 生成实例项目、构建应用镜像、编排 10 服务栈(双 uWSGI 应用 + PostgreSQL 17.10 + OpenSearch 2.19.5 + Redis 7 + RabbitMQ + Celery worker/scheduler + flower + nginx 8138:443 自签),完成数据库/索引/fixtures/文件 location 初始化与管理员创建,含 scheduler 崩溃循环与建草稿 500 等排障实录。
InvenioRDM Docker Compose 部署指南
目标:在真实 Ubuntu 26.04 服务器上,用官方 invenio-cli 生成 InvenioRDM 14.0.6 实例项目,经 Docker Compose 部署完整 10 服务栈(双 uWSGI 应用 + PostgreSQL + OpenSearch + Redis + RabbitMQ + Celery worker/scheduler + flower + nginx 前端),并完成数据库初始化、管理员创建与端到端验证。全部命令与回显均在 Ubuntu 26.04.1 LTS / Docker 29.9.2 / Compose v5.6.1 环境实测通过;项目目录统一为
/opt/invenio-rdm/my-site。产品定位与选型判断见 《InvenioRDM 介绍与选型指南》。
一、环境信息
本篇实测环境的完整可复现信息如下:
| 项目 | 实测值 |
|---|---|
| 操作系统 | Ubuntu 26.04.1 LTS(/etc/os-release VERSION_ID 26.04) |
| 内核 | Linux 7.0.0-38-generic x86_64(uname -a) |
| CPU / 内存 | 6 核(nproc)/ 15Gi(free -h) |
| Docker | 29.9.2, build a82e528 |
| Docker Compose | v5.6.1 |
| invenio-cli | 1.13.1(宿主 pip 安装) |
| InvenioRDM | 14.0.6(容器内 python -c "import invenio_app_rdm" 核实 invenio-app-rdm 14.0.6) |
| 应用运行时 | Python 3.14.10 / Flask 3.1.3 / Werkzeug 3.1.9(记录模块 invenio-rdm-records 32.2.2) |
| 项目目录 | /opt/invenio-rdm/my-site(invenio-cli 生成) |
| Compose 项目名 | invenio-rdm-1406-stack(docker-compose.full.yml 中 name: invenio-rdm-14.0.6-stack,容器名前缀被 Compose 归一化去掉点号) |
| 对外端口 | frontend 映射 8138:443(自签证书 HTTPS) |
| 专用网络 | invenio-rdm-1406-stack_default,子网 10.89.57.0/24 |
| 数据持久化 | 6 个命名卷(db_data/mq_data/search_data/static_data/uploaded_data/archived_data) |
| 服务数 | 10 个:db、mq、cache、search、web-api、web-ui、worker、scheduler、flower、frontend |
| 演示账号 | root@my-site.com / q1w2e3r4(admin + system 角色,superuser-access) |
| 访问地址 | https://127.0.0.1:8138/(自签证书,浏览器需绕过警告) |
💡 说明:InvenioRDM 是「实例项目」形态的软件——官方 CLI 用 cookiecutter 模板生成一份属于你的项目目录(含 Dockerfile、compose 文件、invenio.cfg),再构建出你自己的应用镜像,官方不提供预构建的应用镜像。因此本篇的"镜像"分两类:应用镜像自建、基础设施镜像拉取,见下一章。
二、镜像选型说明
2.1 应用镜像:基于官方基础镜像自建
应用镜像 my-site:latest 由项目 Dockerfile 构建,基座是官方发布的 ghcr.io/inveniosoftware/invenio:14-debian(Dockerfile 首行 FROM 原文),前端镜像 my-site-frontend 基于官方 nginx 镜像叠加本项目的 nginx 配置与自签证书。构建完成后实测镜像清单(docker compose -f docker-compose.full.yml images,2026-11-01 采集):
| 镜像 | 标签 | 大小 | 用途 |
|---|---|---|---|
| my-site | latest | 3.16GB | web-api / web-ui / worker / scheduler 四服务共用 |
| my-site-frontend | latest | 241MB | nginx 反向代理 + TLS 终结 |
| postgres | 17.10 | 639MB | 关系数据库 |
| opensearchproject/opensearch | 2.19.5 | 2.54GB | 检索与聚合引擎 |
| redis | 7 | 167MB | 缓存 / 会话 / Celery 结果后端 / 限流 |
| rabbitmq | 3-management | 389MB | Celery 消息队列(management 版含管理 UI) |
| mher/flower | 2.0.1 | 136MB | Celery 任务监控面板 |
2.2 版本对应关系
| 组件 | 版本 | 指定位置 |
|---|---|---|
| InvenioRDM 应用外壳 | invenio-app-rdm[opensearch2]~=14.0.0(实装 14.0.6) | pyproject.toml dependencies |
| Python | ~= 3.14.0(实装 3.14.10) | pyproject.toml requires-python |
| 应用基座镜像 | ghcr.io/inveniosoftware/invenio:14-debian | Dockerfile FROM |
| cookiecutter 模板 | cookiecutter-invenio-rdm checkout v14.0 | .invenio 文件 _checkout |
| OpenSearch | 2.19.5 | docker-services.yml search.image |
| PostgreSQL | 17.10 | docker-services.yml db.image |
💡 技术拓展:应用容器化交付形态对比——
形态 特点 适用 官方预构建应用镜像(如 bitnami 系) 拉取即用,版本由发行方定 快速试用、不可深度定制 官方基础镜像 + 本地 Dockerfile 自建(本文 InvenioRDM) 基座官方维护,应用层配置/资产随自己项目构建 需要定制元数据、主题、配置的正式实例 纯源码自写 Dockerfile 自由度最大、维护成本最高 特殊发行版需求 InvenioRDM 走第二种:
invenio:14-debian基座内置 Python 环境与 invenio 通用依赖,实例项目把invenio.cfg、主题资产、app_data 词汇 COPY 进镜像,invenio collect+invenio webpack buildall在构建期完成前端资产打包。
三、前置条件
3.1 硬件与磁盘
实测单机跑通全栈的资源水位:6 核 / 15Gi 内存主机上,OpenSearch 按 compose 定义 mem_limit: 2g、堆 -Xms512m -Xmx512m,应用镜像构建峰值需要额外约 4GB 磁盘(my-site 3.16GB + opensearch 2.54GB + 构建缓存)。建议预留 20GB 以上磁盘;生产参照官方口径至少 4GB 内存起步,本篇实测主机 15Gi 余量充足。
3.2 软件与网络
宿主机需具备 Docker 与 Compose v2+、Python3 + pip。实测版本回显:
$ docker version --format '{{.Server.Version}}'
29.9.2
$ docker compose version
Docker Compose version v5.6.1
$ python3 --version
Python 3.x # 宿主发行版自带即可,应用运行在镜像内(Python 3.14.10)
$ invenio-cli --version
invenio-cli, version 1.13.1
invenio-cli 经 pip 安装到宿主(仅用于生成项目与驱动构建,不参与应用运行):
pip install invenio-cli
⚠️ 网络注意:构建过程需访问
ghcr.io(拉取 invenio:14-debian 基座)、Docker Hub(postgres/redis/rabbitmq/opensearch/flower 镜像)、PyPI 与 npm registry(uv 依赖解析与 pnpm 前端资产)。本次实测在直连网络下完成,未使用代理;若你的网络访问上述源缓慢或失败,需先配置镜像加速或代理再构建。
3.3 端口与网段规划
frontend 对外端口选定 8138(避开同机其他服务占用的 8137/8139-8141),专用 bridge 网段选定 10.89.57.0/24。部署前先确认无冲突:
$ ss -ltn | grep 8138 # 应无输出(端口空闲)
$ docker network ls | grep invenio # 应无输出(无残留网络)
四、部署步骤
4.1 生成实例项目
在规划好的项目根目录下用官方 CLI 初始化 InvenioRDM 14.0.6 实例项目:
mkdir -p /opt/invenio-rdm && cd /opt/invenio-rdm
invenio-cli init rdm -c 14.0.6
命令交互式询问 cookiecutter 参数,本次实测填写(完整答案已被 CLI 记录进项目内 .invenio 文件,可复查):
| 提示项 | 填写值 | 说明 |
|---|---|---|
| project_name | My Site | 站点名,生成目录名取短名 |
| project_shortname | my-site | 决定目录名与镜像名 |
| package_name | my_site | Python 包名 |
| project_site | my-site.com | 站点域名(OAI 前缀等派生自此值) |
| file_storage | local | 文件存储后端(可选 s3) |
| database | postgresql | 关系数据库 |
| search | opensearch2 | 检索引擎(可选 elasticsearch7) |
| base_image | debian | 应用基座发行版 |
生成结果验证——项目目录 my-site/ 出现,且 .invenio 记录了模板 checkout 与参数:
$ ls /opt/invenio-rdm/my-site
Dockerfile docker-services.yml pyproject.toml templates
README.md invenio.cfg site translations
app_data logs static uv.lock
assets docker docker-compose.full.yml docker-compose.yml
$ grep -E 'project_shortname|_checkout|search|database' /opt/invenio-rdm/my-site/.invenio
project_shortname = my-site
_checkout = v14.0
database = postgresql
search = opensearch2
生成的 pyproject.toml 锁定应用版本线(实测内容节选):
[project]
name = "my-site-instance"
version = "14.0.0"
requires-python = "~= 3.14.0"
dependencies = [
"invenio-app-rdm[opensearch2]~=14.0.0",
"uwsgi>=2.0",
...(输出节选,实测首尾)
]
4.2 定制 docker-compose.full.yml
生成的 docker-compose.full.yml 是完整基础设施栈定义(frontend 默认直接映射 443)。按 3.3 节规划改三处:项目名、对外端口、专用网段。修改后实测全文如下:
name: invenio-rdm-14.0.6-stack
services:
cache:
extends:
file: docker-services.yml
service: cache
db:
extends:
file: docker-services.yml
service: db
mq:
extends:
file: docker-services.yml
service: mq
flower:
extends:
file: docker-services.yml
service: flower
depends_on:
mq:
condition: service_started
search:
extends:
file: docker-services.yml
service: search
# Frontend
frontend:
extends:
file: docker-services.yml
service: frontend
volumes:
- static_data:/opt/invenio/var/instance/static
depends_on:
- web-ui
- web-api
ports:
- "8138:443"
# UI Application
web-ui:
extends:
file: docker-services.yml
service: app
command: ["uwsgi /opt/invenio/var/instance/uwsgi_ui.ini"]
depends_on:
search:
condition: service_healthy
image: my-site:latest
volumes:
- static_data:/opt/invenio/var/instance/static
- uploaded_data:/opt/invenio/var/instance/data
- archived_data:/opt/invenio/var/instance/archive
# API Rest Application
web-api:
extends:
file: docker-services.yml
service: app
command: ["uwsgi /opt/invenio/var/instance/uwsgi_rest.ini"]
depends_on:
search:
condition: service_healthy
image: my-site:latest
volumes:
- uploaded_data:/opt/invenio/var/instance/data
- archived_data:/opt/invenio/var/instance/archive
# Worker
worker:
extends:
file: docker-services.yml
service: app
command: ["celery -A invenio_app.celery worker --beat --loglevel=INFO"]
image: my-site:latest
volumes:
- uploaded_data:/opt/invenio/var/instance/data
depends_on:
search:
condition: service_healthy
cache:
condition: service_started
db:
condition: service_started
mq:
condition: service_started
# Jobs scheduler
scheduler:
extends:
file: docker-services.yml
service: app
command: ["celery -A invenio_app.celery beat --scheduler invenio_jobs.services.scheduler:RunScheduler --loglevel=INFO"]
image: my-site:latest
depends_on:
mq:
condition: service_started
db:
condition: service_started
volumes:
db_data:
mq_data:
search_data:
static_data:
uploaded_data:
archived_data:
networks:
default:
ipam:
config:
- subnet: 10.89.57.0/24
要点解读:
- 所有服务经
extends: docker-services.yml复用同一份服务定义(见第七章),docker-compose.full.yml只负责组合、命令差异与端口/卷; - web-ui 与 web-api 是同一镜像的两种启动命令(
uwsgi_ui.ini/uwsgi_rest.ini),分别承载 UI 与 REST API,由 nginx 按路径分流; - web-api、web-ui、worker 三者共享
uploaded_data卷挂到/opt/invenio/var/instance/data——上传文件落盘位置必须一致,否则 API 写入的文件 UI/worker 读不到; depends_on: search: condition: service_healthy让应用服务等待 OpenSearch 健康检查通过再启动;- scheduler 是 Celery beat +
invenio_jobs调度器,承担定时作业(索引清理、embargo 解禁等)。
4.3 构建应用镜像
用 invenio-cli 驱动构建(内部调用 docker bake,同时构建应用镜像与 frontend 镜像):
cd /opt/invenio-rdm/my-site
invenio-cli containers build
构建日志(实测存为 build.log,约 48KB,此处首尾节选):
Building images... Pull newer versions True, use cache True
Checking if dependencies are locked.
Dependencies are locked
Building images...
Image my-site:latest Building
Image my-site:latest Building
Image my-site:latest Building
Image my-site-frontend Building
Image my-site:latest Building
#1 [internal] load local bake definitions
...(输出节选,实测首尾)
#46 [scheduler] resolving provenance for metadata file
#46 DONE 0.1s
Image my-site:latest Built
Image my-site:latest Built
Image my-site:latest Built
Image my-site-frontend Built
Image my-site:latest Built
Images built successfully.
验证镜像产出(第二章 2.1 表即此命令实测回显):
$ docker compose -f docker-compose.full.yml images
⚠️ 首次构建耗时较长:Dockerfile 内
uv sync --frozen解析安装全部 Python 依赖,invenio collect+invenio webpack buildall用 pnpm + rspack 打包 React/LESS 前端资产,两条链路都要访问外部源。构建失败先排查网络(见 3.2 网络注意)。
4.4 启动基础设施与应用服务
先启动除 frontend、worker 之外的 8 个服务(frontend 依赖应用初始化产物、worker 待初始化完成后再消费任务):
docker compose -f docker-compose.full.yml up -d db cache mq search web-api web-ui scheduler flower
实测现象:db/cache/mq/search/web-api/web-ui/flower 正常拉起,scheduler 进入崩溃重启循环。日志回显(docker compose -f docker-compose.full.yml logs scheduler 节选):
sqlalchemy.exc.ProgrammingError: (psycopg2.errors.UndefinedTable)
relation "jobs_job" does not exist
定位:Celery beat 启动即查询 invenio-jobs 的 jobs_job 表,而新库尚未建表——这不是镜像或 compose 问题,是 InvenioRDM 首次部署必须先做数据库初始化(见 4.5)。初始化完成前该报错会随重启反复出现,属预期。
4.5 初始化数据库、索引与数据(关键步骤)
进入 web-api 服务执行官方初始化命令序列(容器内工作目录 /opt/invenio/src,实例目录 /opt/invenio/var/instance):
docker compose -f docker-compose.full.yml exec web-api bash
# 进入后:/opt/invenio/src
(1)建库建表:
invenio db init create
实测回显:
Creating database postgresql+psycopg2://my-site:***@db/my-site
Creating all tables!
Created all tables!
(2)创建检索索引与模板:
invenio index init
实测回显(节选):
Putting templates...
Putting component templates...
Putting index templates...
Creating indexes...
验证(invenio index list 输出节选,别名按 SEARCH_INDEX_PREFIX="my-site-" 前缀隔离):
users-user-v3.0.0
vocabularies-vocabulary-v1.0.0
...(实测首尾节选)
(3)载入必需基础数据(fixtures):
invenio rdm fixtures
实测回显:
Creating required fixtures...
Created required fixtures!
该命令导入 app_data/vocabularies.yaml 声明的 subjects/names/licenses/languages 词汇表。(4)载入记录内置词表(resource_type 等,缺了它建草稿会 400,见 FAQ 问题 3):
invenio rdm-records fixtures
(5)自定义字段与静态页:
invenio rdm-records custom-fields init
# 实测回显:No custom fields were configured. Exiting...
invenio rdm pages create
# 实测回显:Creating static pages... / Created static pages!
(6)创建文件存储 location(实测坑,见 FAQ 问题 2——v14 不会自动建默认 location,缺了它上传文件时建草稿直接 500):
invenio files location create local /opt/invenio/var/instance/data --default
实测回显:
Location local /opt/invenio/var/instance/data as default True stored in database
(7)创建管理员并授权(演示账号直接落实际值):
invenio users create root@my-site.com --password q1w2e3r4 -a -c -u root
# 实测回显:User created successfully.
invenio roles create admin
invenio roles create system
invenio roles add root@my-site.com admin
invenio roles add root@my-site.com system
invenio access allow superuser-access role admin
-a -c -u 分别表示激活账号、邮箱已确认、设置用户名;superuser-access 角色授权后该用户可进 Web 管理后台。
4.6 启动 frontend 与 worker,恢复 scheduler
初始化全部完成后,补齐剩余两个服务并重启 scheduler:
docker compose -f docker-compose.full.yml up -d frontend worker
docker compose -f docker-compose.full.yml restart scheduler
验证 scheduler 脱离崩溃循环(docker compose -f docker-compose.full.yml logs scheduler 尾部):
beat: Starting...
4.7 整栈验证
(1)服务状态——docker compose -f docker-compose.full.yml ps 实测回显(10 服务全 Up,search 带 healthy):
NAME IMAGE SERVICE STATUS
invenio-rdm-1406-stack-cache-1 redis:7 cache Up 45 minutes
invenio-rdm-1406-stack-db-1 postgres:17.10 db Up 45 minutes
invenio-rdm-1406-stack-flower-1 mher/flower:2.0.1 flower Up 45 minutes
invenio-rdm-1406-stack-frontend-1 my-site-frontend frontend Up 30 minutes 0.0.0.0:8138->443/tcp
invenio-rdm-1406-stack-mq-1 rabbitmq:3-management mq Up 45 minutes
invenio-rdm-1406-stack-scheduler-1 my-site:latest scheduler Up 30 minutes
invenio-rdm-1406-stack-search-1 opensearchproject/opensearch:2.19.5 search Up 45 minutes (healthy)
invenio-rdm-1406-stack-web-api-1 my-site:latest web-api Up 45 minutes
invenio-rdm-1406-stack-web-ui-1 my-site:latest web-ui Up 45 minutes
invenio-rdm-1406-stack-worker-1 my-site:latest worker Up 30 minutes
(2)HTTP 入口:
$ curl -sk -o /dev/null -w '%{http_code}' https://127.0.0.1:8138/
200
$ curl -sk https://127.0.0.1:8138/ | grep title
<title>My Site</title>
(3)登录:浏览器访问 https://127.0.0.1:8138/(自签证书,Chrome/Edge 在警告页窗口直接键入 thisisunsafe 绕过),用 root@my-site.com / q1w2e3r4 登录成功,页面出现该邮箱与 Logout 入口;实测 curl 走 POST /login/ 返回 302 回首页。
(4)匿名 API:
$ curl -sk https://127.0.0.1:8138/api/records | head -c 200
{"hits": {"buckets": ..., "hits": [], "total": 0}, ...(输出节选,实测首尾)
返回标准 JSON 列表结构(hits/aggregations),REST API 就绪。记录创建/文件上传/发布的完整 API 实测流程见本专栏 API 使用详解篇。
五、数据持久化说明
5.1 命名卷清单
docker volume ls 实测回显:
DRIVER VOLUME NAME
local invenio-rdm-1406-stack_archived_data
local invenio-rdm-1406-stack_db_data
local invenio-rdm-1406-stack_mq_data
local invenio-rdm-1406-stack_search_data
local invenio-rdm-1406-stack_static_data
local invenio-rdm-1406-stack_uploaded_data
各卷用途与挂载点:
| 卷名 | 容器内挂载点 | 归属服务 | 内容 |
|---|---|---|---|
| db_data | /var/lib/postgresql/data | db | 全部结构化数据:记录元数据、草稿、文件清单、账号角色、词汇表 |
| uploaded_data | /opt/invenio/var/instance/data | web-api / web-ui / worker | 上传文件实体(files location 落盘处,见 4.5(6)) |
| archived_data | /opt/invenio/var/instance/archive | web-api / web-ui | 长期保存归档区 |
| search_data | /usr/share/opensearch/data | search | 检索索引 |
| static_data | /opt/invenio/var/instance/static | frontend / web-ui | collect 产出的静态资产(nginx 直接服务 /static) |
| mq_data | /var/lib/rabbitmq | mq | RabbitMQ 队列持久化 |
💡 说明:备份时 db_data 与 uploaded_data 必须同批一致——记录元数据在 PostgreSQL、文件实体在 uploaded_data,二者时间点不一致会导致记录引用不到文件(校验和告警)。检索索引(search_data)可从数据库重建(
invenio index init+ 重建任务),非唯一事实源。
5.2 数据落盘验证
上传文件实测路径:API 上传的 sample_data.csv 落在卷内 /opt/invenio/var/instance/data 下(invenio-files 的 Bucket 目录结构),经 GET /api/records/<pid>/files/<key>/content 下载返回 200、61 字节、内容与源文件一致(md5 c4bd016fbc881f705105924379a66b93)。
六、环境变量表
6.1 应用侧 INVENIO_* 变量(实测全量)
Invenio 的 Flask 配置项可用 INVENIO_<配置键名> 环境变量覆盖。docker compose -f docker-compose.full.yml exec web-api env | grep INVENIO_ 实测回显(排序后 16 项,全部来自 docker-services.yml 注入):
INVENIO_ACCOUNTS_SESSION_REDIS_URL=redis://cache:6379/1
INVENIO_BROKER_URL=amqp://guest:guest@mq:5672/
INVENIO_CACHE_REDIS_URL=redis://cache:6379/0
INVENIO_CACHE_TYPE=RedisCache
INVENIO_CELERY_BROKER_URL=amqp://guest:guest@mq:5672/
INVENIO_CELERY_RESULT_BACKEND=redis://cache:6379/2
INVENIO_COMMUNITIES_IDENTITIES_CACHE_REDIS_URL=redis://cache:6379/4
INVENIO_INSTANCE_PATH=/opt/invenio/var/instance
INVENIO_PROXYFIX_CONFIG={'x_for': 1, 'x_proto': 1}
INVENIO_RATELIMIT_STORAGE_URL=redis://cache:6379/3
INVENIO_SEARCH_HOSTS=['search:9200']
INVENIO_SECRET_KEY=CHANGE_ME
INVENIO_SITE_API_URL=https://127.0.0.1/api
INVENIO_SITE_UI_URL=https://127.0.0.1
INVENIO_SQLALCHEMY_DATABASE_URI=postgresql+psycopg2://my-site:my-site@db/my-site
INVENIO_USER_ID=1000
逐项作用:
| 变量 | 作用 | 生产改动 |
|---|---|---|
| INVENIO_SQLALCHEMY_DATABASE_URI | 数据库连接串(服务名 db,账密与 db 服务 POSTGRES_* 一致) | 换强密码、独立库 |
| INVENIO_BROKER_URL / INVENIO_CELERY_BROKER_URL | Celery 消息代理(RabbitMQ) | 建专用 vhost 与账号,弃用 guest |
| INVENIO_CELERY_RESULT_BACKEND | Celery 结果后端(Redis db2) | 与缓存分实例更佳 |
| INVENIO_CACHE_REDIS_URL / INVENIO_CACHE_TYPE | 应用缓存(Redis db0),RedisCache | — |
| INVENIO_ACCOUNTS_SESSION_REDIS_URL | 会话存储(Redis db1) | — |
| INVENIO_RATELIMIT_STORAGE_URL | API 限流计数(Redis db3) | — |
| INVENIO_COMMUNITIES_IDENTITIES_CACHE_REDIS_URL | 社区身份缓存(Redis db4) | — |
| INVENIO_SEARCH_HOSTS | OpenSearch 地址(服务名 search:9200) | 多节点改列表 |
| INVENIO_SECRET_KEY | Flask 会话签名密钥 | 必须改(CHANGE_ME 落明文在 compose 里,见 FAQ 问题 5) |
| INVENIO_SITE_UI_URL / INVENIO_SITE_API_URL | 站点对外 URL,应用生成绝对链接用 | 改真实域名;注意端口一致性(FAQ 问题 4) |
| INVENIO_PROXYFIX_CONFIG | 信任 nginx 传递的 X-Forwarded-For/Proto 各 1 跳 | 前置层数变化时调整 |
| INVENIO_INSTANCE_PATH | 实例目录(配置/数据/静态资产根) | 不动 |
| INVENIO_USER_ID | 容器内运行用户 UID | 不动 |
6.2 基础设施服务变量
| 服务 | 变量 | 实测值 | 说明 |
|---|---|---|---|
| db | POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB | my-site / my-site / my-site | 与 INVENIO_SQLALCHEMY_DATABASE_URI 必须一致;生产改强密码 |
| search | discovery.type | single-node | 单节点模式,不触发集群引导 |
| search | OPENSEARCH_JAVA_OPTS | -Xms512m -Xmx512m | 堆内存(compose 注释明确"仅开发用,生产勿照抄") |
| search | OPENSEARCH_INITIAL_ADMIN_PASSWORD | CHANGE_me_0! | 演示配置,配合下两项禁用安全插件 |
| search | DISABLE_INSTALL_DEMO_CONFIG / DISABLE_SECURITY_PLUGIN | true / true | 开发栈关闭 security 插件;生产开启鉴权 |
| search | bootstrap.memory_lock | true | 配合 ulimits memlock -1 锁内存不换出 |
| mq | hostname | mq | RabbitMQ 以主机名定位 Mnesia 数据目录,改名会"失忆"队列(compose 内官方注释原文说明) |
💡 技术拓展:Invenio 配置注入三层机制——
层级 载体 优先级 实例配置文件 invenio.cfg(打进镜像或挂载) 基线 环境变量 INVENIO_* 前缀覆盖同名配置键 高于 cfg 代码扩展 site 包内 Python 覆盖 定制逻辑 同一键(如 SITE_UI_URL)cfg 与 INVENIO_ 变量并存时,环境变量生效——本实测中 cfg 写
https://127.0.0.1:5000而容器内实际生效https://127.0.0.1,即 compose 环境变量覆盖所致。逐参数详解见本专栏配置参数详解篇。
七、配置参数说明(docker-services.yml)
docker-compose.full.yml 各服务 extends 的公共定义文件 docker-services.yml 是镜像与连接参数的单一来源。实测核心段落全文:
services:
app:
build:
context: ./
args:
- ENVIRONMENT=DEV
image: my-site
restart: "unless-stopped"
environment:
- "INVENIO_ACCOUNTS_SESSION_REDIS_URL=redis://cache:6379/1"
- "INVENIO_BROKER_URL=amqp://guest:guest@mq:5672/"
- "INVENIO_CACHE_REDIS_URL=redis://cache:6379/0"
- "INVENIO_CACHE_TYPE=RedisCache"
- "INVENIO_CELERY_BROKER_URL=amqp://guest:guest@mq:5672/"
- "INVENIO_CELERY_RESULT_BACKEND=redis://cache:6379/2"
- "INVENIO_COMMUNITIES_IDENTITIES_CACHE_REDIS_URL=redis://cache:6379/4"
- "INVENIO_SEARCH_HOSTS=['search:9200']"
- "INVENIO_SECRET_KEY=CHANGE_ME"
- "INVENIO_SQLALCHEMY_DATABASE_URI=postgresql+psycopg2://my-site:my-site@db/my-site"
- "INVENIO_PROXYFIX_CONFIG={'x_for': 1, 'x_proto': 1}"
- "INVENIO_RATELIMIT_STORAGE_URL=redis://cache:6379/3"
- "INVENIO_SITE_UI_URL=https://127.0.0.1"
- "INVENIO_SITE_API_URL=https://127.0.0.1/api"
frontend:
build: ./docker/nginx/
image: my-site-frontend
restart: "unless-stopped"
cache:
image: redis:7
restart: "unless-stopped"
read_only: true
db:
image: postgres:17.10
restart: "unless-stopped"
environment:
- "POSTGRES_USER=my-site"
- "POSTGRES_PASSWORD=my-site"
- "POSTGRES_DB=my-site"
volumes:
- db_data:/var/lib/postgresql/data
mq:
image: rabbitmq:3-management
restart: "unless-stopped"
hostname: mq
ulimits:
nofile:
soft: 65536
hard: 65536
volumes:
- mq_data:/var/lib/rabbitmq
flower:
image: mher/flower:2.0.1
restart: "unless-stopped"
command: celery --broker=amqp://guest:guest@mq:5672/ flower
depends_on:
mq:
condition: service_started
search:
image: opensearchproject/opensearch:2.19.5
restart: "unless-stopped"
environment:
- bootstrap.memory_lock=true
- "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m"
- "OPENSEARCH_INITIAL_ADMIN_PASSWORD=CHANGE_me_0!"
- "DISABLE_INSTALL_DEMO_CONFIG=true"
- "DISABLE_SECURITY_PLUGIN=true"
- "discovery.type=single-node"
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536
hard: 65536
mem_limit: 2g
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://localhost:9200 >/dev/null || exit 1"]
interval: 5s
timeout: 5s
retries: 30
start_period: 30s
volumes:
- search_data:/usr/share/opensearch/data
volumes:
db_data:
mq_data:
search_data:
关键参数逐项说明:
- app 段的
restart: "unless-stopped":全栈服务统一策略,宿主机重启后自动拉起(scheduler 崩溃循环期间该策略导致日志反复滚动,属预期行为); ENVIRONMENT=DEV构建参数:Dockerfile 保留 args 位,dev 与 prod 的差别主要在资产是否带 source map、依赖是否含 dev 工具;- cache
read_only: true:Redis 容器根文件系统只读加固(本栈未开 RDB 持久化,缓存/会话可丢); - search
mem_limit: 2g+ healthcheck:限制 OpenSearch 内存上限,健康检查每 5s 探测 9200,retries: 30+start_period: 30s给足冷启动时间;web-api/web-ui/worker 的depends_on: condition: service_healthy依赖此检查; - mq
hostname: mq+ ulimits nofile 65536:前者固定 Mnesia 节点名(见 6.2),后者满足 RabbitMQ 文件描述符基线; - flower 未映射端口:任务监控面板默认只在内部网络可达,需要浏览器访问时在
docker-compose.full.yml的 flower 段补ports: ["127.0.0.1:5555:5555"]后up -d flower。
同文件还定义了 pgadmin、opensearch-dashboards 两个开发辅助服务(映射 5050/5601 到 127.0.0.1),只出现在 docker-compose.yml(开发后端栈)中,full 栈不含,生产部署勿引入。
八、配置文件挂载
8.1 实测挂载机制:配置打进镜像
InvenioRDM 容器化栈的实例配置不是运行时挂载,而是构建期 COPY 进镜像。Dockerfile 实测相关行:
COPY ./docker/uwsgi/ ${INVENIO_INSTANCE_PATH}
COPY ./invenio.cfg ${INVENIO_INSTANCE_PATH}
COPY ./templates/ ${INVENIO_INSTANCE_PATH}/templates/
COPY ./app_data/ ${INVENIO_INSTANCE_PATH}/app_data/
COPY ./translations/ ${INVENIO_INSTANCE_PATH}/translations/
验证——进入 web-api 查看实例目录中的配置文件(与项目源文件同尺寸):
$ docker compose -f docker-compose.full.yml exec web-api ls -l /opt/invenio/var/instance/invenio.cfg
-rw-r--r-- 1 root root 8426 Nov 1 13:15 /opt/invenio/var/instance/invenio.cfg
uwsgi 配置(uwsgi_ui.ini / uwsgi_rest.ini)同样 COPY 进 /opt/invenio/var/instance,由 compose 的 command 引用启动;nginx 站点配置、自签证书(test.key/test.crt)在 docker/nginx/Dockerfile 里 COPY 进 frontend 镜像。
8.2 运行时挂载的三类卷
compose 运行时真正挂载的是数据类卷(第五章):uploaded_data → /opt/invenio/var/instance/data、archived_data → /opt/invenio/var/instance/archive、static_data → /opt/invenio/var/instance/static(frontend 与 web-ui 共享,nginx location /static 直接 alias 该目录)。
8.3 修改配置后的生效路径
改 invenio.cfg 后需重建镜像并滚动更新(实测生效方式):
cd /opt/invenio-rdm/my-site
# 编辑 invenio.cfg 中需要变更的参数
invenio-cli containers build
docker compose -f docker-compose.full.yml up -d web-api web-ui worker scheduler
⚠️ 注意:cfg 改动不影响
INVENIO_*环境变量覆盖的同名键(见第六章技术拓展的优先级机制)——改 SITE_UI_URL 若只改 cfg 不会生效,必须改 docker-services.yml 的环境变量。逐参数含义与生产模板见本专栏配置参数详解篇。
九、等价 docker run
compose 栈拆成单服务手工拉起时的等价命令(供无 Compose 环境参考;服务名需换成同一自定义网络内的别名,否则 db/cache/mq/search 主机名解析不到):
# 自定义网络(等价 compose default 网络)
docker network create --subnet 10.89.57.0/24 invenio-net
# db(等价 db 服务;卷与三个 POSTGRES_* 变量一致)
docker run -d --name db --network invenio-net \
-e POSTGRES_USER=my-site -e POSTGRES_PASSWORD=my-site -e POSTGRES_DB=my-site \
-v db_data:/var/lib/postgresql/data \
--restart unless-stopped postgres:17.10
# cache(等价 cache 服务)
docker run -d --name cache --network invenio-net \
--read-only --restart unless-stopped redis:7
# mq(等价 mq 服务;--hostname mq 必须保留,否则 Mnesia 队列"失忆")
docker run -d --name mq --network invenio-net --hostname mq \
--ulimit nofile=65536:65536 -v mq_data:/var/lib/rabbitmq \
--restart unless-stopped rabbitmq:3-management
# search(等价 search 服务;healthcheck 无法用 docker run 表达,需自行确保就绪后再起应用)
docker run -d --name search --network invenio-net \
-e bootstrap.memory_lock=true \
-e OPENSEARCH_JAVA_OPTS="-Xms512m -Xmx512m" \
-e OPENSEARCH_INITIAL_ADMIN_PASSWORD=CHANGE_me_0! \
-e DISABLE_INSTALL_DEMO_CONFIG=true -e DISABLE_SECURITY_PLUGIN=true \
-e discovery.type=single-node \
--ulimit memlock=-1:-1 --ulimit nofile=65536:65536 \
--memory 2g -v search_data:/usr/share/opensearch/data \
--restart unless-stopped opensearchproject/opensearch:2.19.5
# web-api(等价 web-api 服务;16 个 INVENIO_* 变量整段写入 invenio.env 文件后注入,
# 文件内容即第六章 6.1 实测回显的 KEY=VALUE 全集,一行一个)
docker run -d --name web-api --network invenio-net \
--env-file invenio.env \
-v invenio-rdm-1406-stack_uploaded_data:/opt/invenio/var/instance/data \
-v invenio-rdm-1406-stack_archived_data:/opt/invenio/var/instance/archive \
--restart unless-stopped my-site:latest \
"uwsgi /opt/invenio/var/instance/uwsgi_rest.ini"
💡 说明:应用四服务(web-api/web-ui/worker/scheduler)共用
my-site:latest镜像、只差 command 与卷;frontend 需-p 8138:443并挂 static 卷。单容器方式失去了depends_on: service_healthy编排能力,生产仍建议用 compose 全栈。
十、常用运维命令
10.1 compose 层
cd /opt/invenio-rdm/my-site
# 查看栈状态(实测回显见 4.7)
docker compose -f docker-compose.full.yml ps
# 跟踪某服务日志(排障首选:scheduler/worker/web-api)
docker compose -f docker-compose.full.yml logs -f --tail=100 web-api
# 进入应用容器执行 CLI(cwd 即 /opt/invenio/src)
docker compose -f docker-compose.full.yml exec web-api bash
# 滚动重启应用服务(改环境变量后)
docker compose -f docker-compose.full.yml up -d web-api web-ui worker
# 停止(保留数据卷)
docker compose -f docker-compose.full.yml down
# 彻底清理(连卷一起删,数据全丢,慎用)
docker compose -f docker-compose.full.yml down -v
10.2 invenio CLI 层(容器内)
# 检索索引别名清单(实测输出节选)
invenio index list
users-user-v3.0.0
vocabularies-vocabulary-v1.0.0
...(实测首尾)
# 重建索引(search 卷数据损坏/换引擎版本后;命令序列同 4.5 的 (2)(4) 步)
invenio index init
invenio rdm-records fixtures
# 签发 API 令牌(实测回显:末行输出令牌明文,一次性)
invenio tokens create -n deploy-test -u root@my-site.com
# 用户/角色管理(完整实测命令与参数见 4.5(7))
invenio users create root@my-site.com --password q1w2e3r4 -a -c -u root
invenio roles add root@my-site.com admin
⚠️ 注意:v14 镜像内
invenio tokens list不存在(实测报Error: No such command 'list'.),令牌清单只能到 Web 管理后台查看。
10.3 任务与队列监控
worker 的 Celery 任务(索引更新、DOI 注册等)经 RabbitMQ 异步执行,实测发布记录后检索约 20 秒才可见,即该异步链路延迟。flower 面板默认不对外,需要时在 full 栈给 flower 加 ports 映射后访问 /(basic auth 未配置,仅限内网)。
十一、常见问题
11.1 scheduler 启动即崩溃循环,报 relation "jobs_job" does not exist
现象:首次 up -d 后 scheduler 反复重启,日志刷 sqlalchemy.exc.ProgrammingError: (psycopg2.errors.UndefinedTable) relation "jobs_job" does not exist。
定位:Celery beat 启动即查 invenio-jobs 的 jobs_job 表,新库未执行 invenio db init create 时表不存在。
解决:按 4.5 完成初始化后 docker compose -f docker-compose.full.yml restart scheduler,日志出现 beat: Starting... 即恢复。首次部署必须先初始化再等 scheduler 正常,属流程顺序问题而非故障。
11.2 建草稿 POST /api/records 返回 500:AttributeError 'NoneType' object has no attribute 'id'
现象:API 创建草稿 500,日志 Bucket.create 处 default_location 为 None。
根因:v14 初始化流程不会自动创建文件存储 location。
解决:invenio files location create local /opt/invenio/var/instance/data --default(回显见 4.5(6)),重试即 201。
11.3 建草稿 400 "Invalid value dataset."
根因:resource_type 等内置词表未载入。
解决:invenio rdm-records fixtures 后重试返回 201。注意与 invenio rdm fixtures(app_data 外部词汇)是两条不同命令,都要执行。
11.4 登录后被跳转到不带端口的地址
现象:访问 https://127.0.0.1:8138/login 时 308 跳到 https://127.0.0.1/login/(端口丢失)。
根因:INVENIO_SITE_UI_URL=https://127.0.0.1 未含 :8138,应用生成绝对 URL 按它拼。经标准 443 反代部署时无感,本机非 443 端口调试时踩坑。
解决:调试期把 docker-services.yml 中两个变量改为 https://127.0.0.1:8138 与 https://127.0.0.1:8138/api 后 up -d 重建应用服务;生产按真实域名配置。
11.5 SECRET_KEY=CHANGE_ME 能直接上生产吗
不能。compose 环境变量与 invenio.cfg 里该值均为占位符(实测容器内 INVENIO_SECRET_KEY=CHANGE_ME),它签发会话 cookie 与令牌签名。生产改为随机强密钥注入(如 INVENIO_SECRET_KEY=<openssl rand -hex 32 输出>),并避免把真实值提交进版本库。
11.6 invenio vocabularies import 报 AttributeError: 'list' object has no attribute 'get'
根因:该命令的参数是 pipeline 配置文件而非数据清单,直接喂 YAML 数据文件即此报错。v14 灌词汇的正确入口是 invenio rdm fixtures(app_data/vocabularies.yaml 声明的 subjects/names/licenses/languages)与 invenio rdm-records fixtures(内置词表),实测用前者即可,无需手工 import。
11.7 浏览器提示证书不受信任
frontend 用官方模板自带的自签 test.crt(nginx 配置注释原文:"must never be used in production environment")。调试期浏览器警告页键入 thisisunsafe 绕过,curl 加 -k;生产替换 docker/nginx/ 内证书文件并重建 frontend 镜像。
11.8 会话 Cookie 调 API 被 CSRF 拦截
实测链路:POST /api/records 带登录 cookie 先报 Referer checking failed - no Referer.,补 Referer 后又报 CSRF token missing or incorrect.(登录后旧 token 失效)。脚本化调用 API 不要走会话,改用个人访问令牌 Bearer(10.2 的 invenio tokens create),完整 API 实测见本专栏 API 使用详解篇。
11.9 首次构建极慢或卡死
构建含 uv sync --frozen(数百 Python 包)与 invenio webpack buildall(pnpm + rspack 前端资产)两段外网依赖下载;my-site:latest 成品 3.16GB。卡死先 docker compose -f docker-compose.full.yml logs 区分是基座拉取(ghcr.io)、PyPI 还是 npm 段,按 3.2 网络注意处理。
11.10 down -v 后重建,队列消息莫名丢失
RabbitMQ 以 hostname 定位 Mnesia 数据目录(docker-services.yml 官方注释原文说明),随机主机名的新容器会"忘记"旧队列消息。compose 已固定 hostname: mq;拆成 docker run 时也必须带 --hostname mq(见第九章)。
十二、参考链接
- InvenioRDM 官方安装文档(Build, setup & run,容器化方式出处):https://inveniordm.docs.cern.ch/install/build-setup-run/ (查询日期 2026-11-01)
- 官方部署定制文档(Configuration/Production checklist):https://inveniordm.docs.cern.ch/operate/ (查询日期 2026-11-01)
- cookiecutter 模板仓库(生成物结构出处):https://github.com/inveniosoftware/cookiecutter-invenio-rdm (checkout v14.0)
- 应用基座镜像:ghcr.io/inveniosoftware/invenio:14-debian(Dockerfile FROM 实测引用)
- invenio-app-rdm 版本线(14.0.6):https://pypi.org/pypi/invenio-app-rdm/json (查询日期 2026-11-01)
- OpenSearch 单节点与安全插件配置:https://docs.opensearch.org/latest/security/configuration/demo-configuration/ (docker-services.yml 注释指向)
- RabbitMQ 主机名与节点数据目录:https://www.rabbitmq.com/docs/configure (docker-services.yml 官方注释指向)
- 站内姊妹篇:《InvenioRDM 介绍与选型指南》