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 等排障实录。

InvenioRDMInvenioRDMDockerDocker ComposePostgreSQLOpenSearchRedisRabbitMQ2026/11/01

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)
Docker29.9.2, build a82e528
Docker Composev5.6.1
invenio-cli1.13.1(宿主 pip 安装)
InvenioRDM14.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-sitelatest3.16GBweb-api / web-ui / worker / scheduler 四服务共用
my-site-frontendlatest241MBnginx 反向代理 + TLS 终结
postgres17.10639MB关系数据库
opensearchproject/opensearch2.19.52.54GB检索与聚合引擎
redis7167MB缓存 / 会话 / Celery 结果后端 / 限流
rabbitmq3-management389MBCelery 消息队列(management 版含管理 UI)
mher/flower2.0.1136MBCelery 任务监控面板

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-debianDockerfile FROM
cookiecutter 模板cookiecutter-invenio-rdm checkout v14.0.invenio 文件 _checkout
OpenSearch2.19.5docker-services.yml search.image
PostgreSQL17.10docker-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_nameMy Site站点名,生成目录名取短名
project_shortnamemy-site决定目录名与镜像名
package_namemy_sitePython 包名
project_sitemy-site.com站点域名(OAI 前缀等派生自此值)
file_storagelocal文件存储后端(可选 s3)
databasepostgresql关系数据库
searchopensearch2检索引擎(可选 elasticsearch7)
base_imagedebian应用基座发行版

生成结果验证——项目目录 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/datadb全部结构化数据:记录元数据、草稿、文件清单、账号角色、词汇表
uploaded_data/opt/invenio/var/instance/dataweb-api / web-ui / worker上传文件实体(files location 落盘处,见 4.5(6))
archived_data/opt/invenio/var/instance/archiveweb-api / web-ui长期保存归档区
search_data/usr/share/opensearch/datasearch检索索引
static_data/opt/invenio/var/instance/staticfrontend / web-uicollect 产出的静态资产(nginx 直接服务 /static)
mq_data/var/lib/rabbitmqmqRabbitMQ 队列持久化

💡 说明:备份时 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_URLCelery 消息代理(RabbitMQ)建专用 vhost 与账号,弃用 guest
INVENIO_CELERY_RESULT_BACKENDCelery 结果后端(Redis db2)与缓存分实例更佳
INVENIO_CACHE_REDIS_URL / INVENIO_CACHE_TYPE应用缓存(Redis db0),RedisCache—
INVENIO_ACCOUNTS_SESSION_REDIS_URL会话存储(Redis db1)—
INVENIO_RATELIMIT_STORAGE_URLAPI 限流计数(Redis db3)—
INVENIO_COMMUNITIES_IDENTITIES_CACHE_REDIS_URL社区身份缓存(Redis db4)—
INVENIO_SEARCH_HOSTSOpenSearch 地址(服务名 search:9200)多节点改列表
INVENIO_SECRET_KEYFlask 会话签名密钥必须改(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 基础设施服务变量

服务变量实测值说明
dbPOSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DBmy-site / my-site / my-site与 INVENIO_SQLALCHEMY_DATABASE_URI 必须一致;生产改强密码
searchdiscovery.typesingle-node单节点模式,不触发集群引导
searchOPENSEARCH_JAVA_OPTS-Xms512m -Xmx512m堆内存(compose 注释明确"仅开发用,生产勿照抄")
searchOPENSEARCH_INITIAL_ADMIN_PASSWORDCHANGE_me_0!演示配置,配合下两项禁用安全插件
searchDISABLE_INSTALL_DEMO_CONFIG / DISABLE_SECURITY_PLUGINtrue / true开发栈关闭 security 插件;生产开启鉴权
searchbootstrap.memory_locktrue配合 ulimits memlock -1 锁内存不换出
mqhostnamemqRabbitMQ 以主机名定位 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 镜像。

实测链路: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(见第九章)。

十二、参考链接

  1. InvenioRDM 官方安装文档(Build, setup & run,容器化方式出处):https://inveniordm.docs.cern.ch/install/build-setup-run/ (查询日期 2026-11-01)
  2. 官方部署定制文档(Configuration/Production checklist):https://inveniordm.docs.cern.ch/operate/ (查询日期 2026-11-01)
  3. cookiecutter 模板仓库(生成物结构出处):https://github.com/inveniosoftware/cookiecutter-invenio-rdm (checkout v14.0)
  4. 应用基座镜像:ghcr.io/inveniosoftware/invenio:14-debian(Dockerfile FROM 实测引用)
  5. invenio-app-rdm 版本线(14.0.6):https://pypi.org/pypi/invenio-app-rdm/json (查询日期 2026-11-01)
  6. OpenSearch 单节点与安全插件配置:https://docs.opensearch.org/latest/security/configuration/demo-configuration/ (docker-services.yml 注释指向)
  7. RabbitMQ 主机名与节点数据目录:https://www.rabbitmq.com/docs/configure (docker-services.yml 官方注释指向)
  8. 站内姊妹篇:《InvenioRDM 介绍与选型指南》
InvenioRDM Docker Compose 部署指南 - Quzuo