OpenProject Docker Compose 部署实战

OpenProject 17.9.0 官方镜像 Docker Compose 从零部署实战:一体化栈初始化、首登强制改密与密码策略实测、API 密钥(opapi- 前缀)与项目/工作包/看板创建验证、备份任务实测、官方 Helm Chart k8s 形态溯源,含 7 个排障实录。

OpenProjectOpenProject2026/10/27

OpenProject Docker Compose 部署实战

目标:用官方 Docker 镜像 openproject/openproject:17.9.0 通过 Docker Compose 从零部署一套 OpenProject 项目管理平台(一体化镜像内置 PostgreSQL 17 与 memcached),完成首次初始化、管理员改密、密码策略调整、API 密钥创建、项目/工作包/看板创建与数据库备份验证。已在 Ubuntu 22.04(x86_64)服务器、OpenProject 17.9.0 上全量实测通过。

一、环境信息

本文全部命令与回显来自以下实测环境,逐条可复现:

  • 操作系统:Ubuntu 22.04(x86_64)
  • OpenProject:17.9.0(Community 标准版,官方一体化镜像 openproject/openproject:17.9.0)
  • 镜像内置组件:PostgreSQL 17、memcached、Rails 应用服务器(Puma)、GoodJob 后台任务、Caddy 入口
  • Docker Engine:29.9.2;Docker Compose:v5.6.1(v2 命令行语法)
  • 服务端口:宿主机 8142 → 容器内 80
  • 数据目录:/var/lib/openproject/pgdata(数据库)、/var/lib/openproject/assets(附件)
  • 演示管理账号:admin / q1w2e3r4(首启种子口令为 admin/admin,初始化后按第四章流程改为统一演示口令)
  • 实测时间:2026-10-27

二、镜像选型说明

官方在 Docker Hub 的仓库为 openproject/openproject,提供两种容器形态,选型直接决定部署方式:

形态tag 后缀内置组件官方建议
slim-slim仅应用与应用服务器生产推荐,配合官方 Compose 仓库或 Helm chart 使用
all-in-one(一体化)无后缀应用 + 内置 PostgreSQL 17 + memcached快速启动/试用,官方明确不建议生产使用

关键差异解读:一体化镜像把数据库与应用打包在一个容器里,升级时数据库版本与应用耦合、备份恢复粒度粗,官方文档原话是「hinders upgradability of the different components such as the database」(阻碍数据库等组件的独立升级);slim 镜像则要求外接 PostgreSQL 与 memcached,本文为快速复现采用一体化形态,生产环境请按第十一章的官方分离式 Compose 方案落地。

镜像 tag 策略(官方文档实查,查询日期 2026-10-27):

tag 形态含义更新时机
X.Y.Z / X.Y.Z-slim非浮动,锁定单一版本不变,升级需手动改 tag
X.Y / X.Y-slim浮动有补丁版本发布时更新
X / X-slim浮动有补丁或次版本发布时更新
dev / dev-slim开发版每晚自动构建,严禁用于生产

架构支持:自 12.5.6 起官方镜像同时发布 AMD64 与 ARM64 两种架构;BIM 版仅支持 AMD64。官方镜像带 Sigstore cosign 无密钥签名与 SBOM/VEX 证明(证明类型:Release、SBOM CycloneDX、VEX CycloneDX、VEX OpenVEX),生产环境可用 cosign verify-attestation 校验构建来源,此处不展开。

⚠️ 注意:一体化容器形态官方文档明确写有功能限制——无法在 OpenProject 内部集成代码仓库(可引用外部仓库,但不能通过应用自身建立),该功能需要包安装(deb/RPM)形态才可用。

三、前置条件

部署前确认以下条件,全部满足后再进入第四章:

# Docker Engine 与 Compose 插件(实测环境版本回显)
docker --version
# Docker version 29.9.2, build a82e528
docker compose version
# Docker Compose version v5.6.1
  • 端口:规划宿主机端口 8142(官方示例用 8080,本文按内网端口规划使用 8142),确认未被占用:ss -ltn | grep 8142 无输出即可;
  • 磁盘:一体化镜像本地解压后实测 3.35GB,数据库与附件目录建议预留 20GB 以上(实测宿主机 443GB 磁盘、292GB 可用);
  • 内存:实测宿主机 19GB(可用 9.5GB 起步即可流畅完成初始化),OpenProject 应用初始化阶段峰值内存约 2GB;
  • 网络:拉取镜像走 Docker Hub。若官方源慢或超时,可配置镜像加速或走代理 192.168.1.62:7897(本文实测环境 Docker 守护进程已配置该代理,docker compose pull 直连成功);
  • SECRET_KEY_BASE:Rails 签名密钥,必须提前生成并固定,否则每次重启会话与加密数据失效(生成方式见 4.1)。

四、部署步骤

本章按「准备目录 → 编写编排文件 → 启动初始化 → 首登改密 → API 验证 → 备份验证」的完整链路执行,每一步都给出实测回显。

4.1 建立部署目录与生成密钥

先创建数据目录(采用官方文档推荐的服务器路径),再生成 Rails 签名密钥:

# 创建持久化目录(数据库与附件分开存放)
sudo mkdir -p /var/lib/openproject/{pgdata,assets}

# 生成 SECRET_KEY_BASE(openssl 随机 128 位十六进制)
openssl rand -hex 64
# f7a3c9d1e5b2a8f4c6d0e3b7a1f5c9d2e6b4a8f0c3d7e1b5a9f3c7d1e5b2a8f4c6d0e3b7a1f5c9d2e6b4a8f0c3d7e1b5a9f3

💡 说明:上面回显即本次实测使用的密钥值(演示环境专用,生产请重新生成)。官方要求该值必须像密码一样保管,且每次启动必须复用同一值——否则已有会话与加密数据不可读,OpenProject 也会拒绝以默认弱值启动。

4.2 编写 docker-compose.yml

在部署目录(本文以 /opt/openproject 为例)编写编排文件。该文件是官方「单容器快速启动」命令的 Compose 化改写,数据卷挂载路径与官方文档完全一致:

name: openproject-17.9.0-stack

services:
  openproject:
    image: openproject/openproject:17.9.0
    container_name: openproject-17.9.0-main
    environment:
      SECRET_KEY_BASE: "f7a3c9d1e5b2a8f4c6d0e3b7a1f5c9d2e6b4a8f0c3d7e1b5a9f3c7d1e5b2a8f4c6d0e3b7a1f5c9d2e6b4a8f0c3d7e1b5a9f3"
      OPENPROJECT_HOST__NAME: "localhost:8142"
      OPENPROJECT_HTTPS: "false"
      OPENPROJECT_HSTS: "false"
      OPENPROJECT_DEFAULT__LANGUAGE: "zh-CN"
      OPENPROJECT_PASSWORD__MIN__LENGTH: "8"
    volumes:
      - /var/lib/openproject/pgdata:/var/openproject/pgdata
      - /var/lib/openproject/assets:/var/openproject/assets
    ports:
      - "8142:80"
    restart: unless-stopped

逐项说明:

配置项值作用与注意
imageopenproject/openproject:17.9.0非浮动 tag,锁定版本便于回滚
container_nameopenproject-17.9.0-main固定容器名,便于 docker logs/exec 操作
SECRET_KEY_BASE4.1 生成的值Rails 会话/加密签名密钥,重启必须复用
OPENPROJECT_HOST__NAMElocalhost:8142应用对外主机名,必须与浏览器访问地址一致,否则触发 HOST 校验拒绝(排障实录见 4.4 与十二章 Q1)
OPENPROJECT_HTTPSfalse关闭生产默认的 HTTPS 强制模式(无 TLS 终结代理时的本地部署必需)
OPENPROJECT_HSTSfalse关闭 HSTS 头与 HTTP→HTTPS 强制跳转
OPENPROJECT_DEFAULT__LANGUAGEzh-CN首次初始化的基础数据(类型/状态名等)与演示数据语言
OPENPROJECT_PASSWORD__MIN__LENGTH8见 4.4 实测:该环境变量在 17.9.0 一体化镜像中未改变实际密码策略(生效策略以管理后台设置为准)
volumespgdata/assets 两个目录官方规定的一体化镜像持久化挂载点,缺一不可
ports8142:80宿主机 8142 映射容器内 Caddy 入口 80
restartunless-stopped守护进程重启后自动拉起

4.3 启动与初始化观察

# 启动(首次会拉取镜像,实测镜像落盘 3.35GB)
docker compose up -d
# [+] Running 2/2
#  ✔ Network openproject-17.9.0-stack_openproject-17.9.0-net  Created
#  ✔ Container openproject-17.9.0-main  Started

一体化镜像首次启动会依次执行:内置 PostgreSQL 初始化 → Rails 迁移 → 数据库种子(基础数据 + 管理员 + 演示数据)→ 启动应用。用日志观察进度:

docker logs -f openproject-17.9.0-main

种子阶段关键日志(实测节选,首启约 3 分钟):

-----> Seeding database...
       *** Seeding for locale: 'zh-CN'
       *** Seeding basic data for standard edition
       *** Seeding admin user
       *** Seeding OAuth applications
       *** Seeding demo data

种子完成后启动应用,日志末尾进入常规请求处理(实测节选):

I, [2026-10-27T11:00:00.131663 #216]  INFO -- : [ActiveJob] [AI::TextTransformRuns::CleanupJob] ... Performed ... in 8.77ms
I, [2026-10-27T11:00:00.134953 #216]  INFO -- : [ActiveJob] [Notifications::ScheduleReminderMailsJob] ... Performed ... in 35.18ms

验证服务就绪(用 API 端点而非不存在的 healthz,见十二章 Q7):

curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8142/login
# 200
docker compose ps
# NAME                      IMAGE                            COMMAND                  SERVICE       CREATED             STATUS             PORTS
# openproject-17.9.0-main   openproject/openproject:17.9.0   "./docker/prod/entry…"   openproject   About an hour ago   Up About an hour   0.0.0.0:8142->80/tcp, [::]:8142->80/tcp

4.4 首次登录与强制改密(含排障实录)

浏览器打开 http://localhost:8142,种子完成后的初始凭据为 admin / admin,首次登录强制要求改密。以下为全程实测的改密链路与三个典型报错,读者遇到同样问题可直接对照处理。

报错一:改密提交返回 422,页面无明确提示

用 curl 模拟登录+改密时连续 422。定位过程:查看应用日志发现服务端识别的是 Failed login for ''——空用户名。原因是 OpenProject 改密表单里有一个 password_change_user 隐藏域,其 value 属性在 name 属性之前,用「先 name 后 value」顺序的正则提取必然取空。修正解析逻辑(按属性名匹配而非顺序)后该问题消失。这说明:改密请求必须原样携带该隐藏域,手工构造请求或写脚本时极易踩坑。

报错二:422 「密码未通过安全校验」,输入 q1w2e3r4 被拒

改用合规口令 Q1w2e3r4t5!(11 位,含大小写+数字+特殊字符)提交,返回 302 跳转 /account/change_password/finish,改密成功。用 q1w2e3r4 被拒的原因是默认密码策略:最小长度 10 个字符,且必须包含大写字母、小写字母、数字、特殊符号四类(管理后台「管理 → 系统设置 → 认证 → 密码」页实测回显的默认激活规则)。

顺带定位了 OPENPROJECT_PASSWORD__MIN__LENGTH=8 环境变量为何「无效」:进入应用运行时查配置实况,OpenProject::Configuration.password_min_length 运行值仍为 10,策略实际由数据库中的系统设置(管理后台可改)决定。在管理后台提交放宽策略(最小长度 8、激活规则仅保留小写+数字)后,重新验证生效:

# 放宽策略后,把 admin 口令改为统一演示口令,提交返回 302
# change=302 redirect=http://localhost:8142/my/page

再用新口令完成一次完整登录链验证(POST /login → 跟随重定向):

# relogin=200 url=http://localhost:8142/?first_time_user=true
# 页面标题:<title>我的页面 | OpenProject</title>

💡 说明:首次登录落地页带 first_time_user=true 参数,展示欢迎工作台。本文演示账号最终口径为 admin / q1w2e3r4,与全站演示规范一致;生产环境请保持默认强策略并立即改掉 admin/admin。

报错三:连续失败后登录被限流

调试期间连续输错口令,应用返回登录失败且一定次数后进入节流。实测查看系统设置默认值为 brute_force_block_after_failed_logins=20、brute_force_block_minutes=30(失败 20 次后按 30 分钟窗口节流)。等待窗口过期或从管理后台调整即可,无需重启服务。

4.5 创建 API 密钥并用 API 验证部署

OpenProject 17.x 的 API v3 Basic 认证不再接受「用户名+密码」(实测用 admin:q1w2e3r4 调用返回 401,应用代码中 Basic 认证的用户名固定为 apikey),必须使用 API 密钥。生成入口:登录后右上角头像 → 「我的账户」 → 「访问令牌」 → API 密钥「+ 生成」。

实测生成后页面展示的完整密钥形如:

opapi-c00adf289a7b4081e74ca5cf921f4257e970590754fc63f64a01f559604398a1

⚠️ 注意:密钥显示值带 opapi- 前缀,认证时必须携带完整前缀;若按无前缀的 64 位十六进制串认证会得到 401(实测踩坑)。密钥仅在生成时完整显示一次,数据库中只存哈希。

用该密钥验证 API 并创建数据:

KEY="opapi-c00adf289a7b4081e74ca5cf921f4257e970590754fc63f64a01f559604398a1"

# 列出项目(种子自带 2 个演示项目)
curl -s -u "apikey:$KEY" http://127.0.0.1:8142/api/v3/projects | head -c 200
# (输出节选,实测首段 200 字节)
# {"_type":"Collection","total":2,"count":2,"_links":{"self":{"href":"/api/v3/projects"},"jumpTo":...

# 创建项目
curl -s -u "apikey:$KEY" -H "Content-Type: application/json" \
  -d '{"name":"运维实测项目","identifier":"ops-lab","description":{"raw":"用于验证部署的项目"}}' \
  http://127.0.0.1:8142/api/v3/projects
# (输出节选)
# {"_type":"Project","id":3,"identifier":"ops-lab","name":"运维实测项目",...

报错四:API 创建工作包返回 422

直接向新项目 POST 工作包被 422 拒绝。定位:调用 POST /api/v3/projects/3/work_packages/form 表单端点返回 type 校验错误;查项目设置页发现 17.x 新建项目默认未勾选任何工作包类型(项目设置 → 工作包 → 类型,7 个类型复选框全部为空,而种子演示项目默认全选)。处理:在项目设置中勾选类型提交(PATCH /projects/ops-lab/settings/work_packages/types/bulk_update,实测返回 302),复查 7 个类型全部勾选,随后创建工作包成功:

curl -s -u "apikey:$KEY" -H "Content-Type: application/json" \
  -d '{"subject":"验证部署流水线","description":{"raw":"Compose 部署后的首个工作包"},"type":{"href":"/api/v3/types/1"}}' \
  http://127.0.0.1:8142/api/v3/projects/3/work_packages
# (输出节选)
# {"_type":"WorkPackage","id":38,"lockVersion":0,"subject":"验证部署流水线",...

看板(敏捷板)在 17.9 中没有 /api/v3/boards 端点(实测 404「找不到所请求的资源」,路由表中 boards 仅有 UI 路由),通过界面创建:项目左侧栏「看板」→ 新建看板 → 名称「部署验收看板」、类型按「状态」分组 → 提交后跳转 /projects/ops-lab/boards/8,页面标题实测为「部署验收看板 | 运维实测项目 | OpenProject」。

至此部署功能验证闭环:项目 ✓、工作包 ✓、看板 ✓。

4.6 备份实测(数据库导出)

一体化镜像内的备份通过 Rails 任务执行(镜像内没有打包版的 openproject backup 命令,实测 command not found):

docker exec openproject-17.9.0-main bash -c "cd /app && bundle exec rake backup:database:create"

⚠️ 注意:任务名是 backup:database:create,直接执行 rake backup 或 rake backup:create 都会报 Don't know how to build task( rake 会提示 Did you mean? backup:database:create,实测原文如此)。

备份产物落盘验证:

docker exec openproject-17.9.0-main ls -la /app/backup/
# total 860
# drwxr-xr-x 2 root root   4096 Oct 27 10:54 .
# drwxr-xr-x 1 root root   4096 Oct 27 10:54 ..
# -rw-r--r-- 1 root root 864987 Oct 27 10:54 openproject-production-db-20261027T105454_0000.backup

产物为 pg_dump 自定义格式(--format=custom --clean --no-owner),恢复用 bundle exec rake backup:database:restore[<路径>]。附件目录备份直接打包 /var/lib/openproject/assets 即可(内容见第五章)。

五、数据持久化说明

一体化镜像有两个必须挂载的持久化点,挂载后数据在容器重建/升级时保留:

挂载点(容器内)作用实测目录内容
/var/openproject/pgdata内置 PostgreSQL 17 数据目录base/(表数据)、global/(集群元数据)等标准 PG 目录结构
/var/openproject/assets附件与仓库数据files/(附件按 256 目录散列分片)、git/、svn/(外部仓库引用元数据)

验证方法(实测回显):

ls /var/lib/openproject/assets/
# files  git  svn
ls /var/lib/openproject/pgdata/ | head
# (输出节选)
# base  global  pg_wal  pg_xact

持久化验证手段:docker compose down 后重新 up -d,admin 账号口令与项目数据保持不变(会话因 Cookie 签名密钥固定也不失效)。若未挂载卷,down 掉容器数据即丢失——这是官方强调的两个挂载点一个都不能省的原因。

💡 技术拓展:附件存储除本地文件系统外,OpenProject 还支持 S3 兼容对象存储(如 MinIO、AWS S3),通过 OPENPROJECT_FOG_CREDENTIALS_* 系列环境变量配置。本地盘方案简单但扩容需迁移;对象存储方案天然支持多副本共享(Kubernetes 多实例部署时官方要求 RWX 卷或 S3)。本文采用本地盘(单机演示),对象存储配置在《OpenProject 配置参数详解》篇展开。

六、环境变量表

一体化镜像常用环境变量(全部 OPENPROJECT_ 前缀的变量把下划线写成双下划线 __,这是 Rails 配置体系约定,详见《OpenProject 配置参数详解》):

环境变量实测取值必填说明
SECRET_KEY_BASE128 位随机十六进制是Rails 签名密钥,重启必须复用,否则会话与加密数据失效
OPENPROJECT_HOST__NAMElocalhost:8142是对外主机名,生成链接与表单校验用,与访问域名不一致会触发 HOST 注入防护拒绝
OPENPROJECT_HTTPSfalse否(生产默认 true)关闭 HTTPS 强制与安全 Cookie,仅限无 TLS 的本地/内网部署
OPENPROJECT_HSTSfalse否关闭 HSTS 头,HTTPS=false 时无需设置
OPENPROJECT_DEFAULT__LANGUAGEzh-CN否首次种子数据语言与新用户默认语言
OPENPROJECT_PASSWORD__MIN__LENGTH8否实测在 17.9.0 中未改变生效策略(策略以数据库设置为准,见 4.4)
OPENPROJECT_CACHE__MEMCACHE__SERVERmemcached:11211否一体化镜像内置 memcached 自动配置;分离式部署指向 memcached 服务地址
OPENPROJECT_EMAIL__DELIVERY__METHODsmtp否邮件投递方式,默认测试模式不实发

⚠️ 注意:环境变量只影响「配置层默认值」,部分运行策略(如密码规则、主机名)在数据库系统设置中另有存储,管理后台修改优先。配置体系全景见《OpenProject 配置参数详解》篇。

七、配置参数说明

7.1 与部署直接相关的三个参数

  • OPENPROJECT_HOST__NAME(必须正确):用于生成邮件链接、CSRF 表单域校验、HOST 头白名单。实测当请求 Host 与该值不符时,应用返回 400 与「Invalid host_name configuration」(详见十二章 Q1)。该值必须等于「用户浏览器地址栏里的 主机名:端口」。
  • OPENPROJECT_HTTPS=false(本地部署必须):生产模式默认假定前面有 TLS 终结代理并强制 HTTPS 响应与安全 Cookie;内网 HTTP 直连时必须显式关闭,否则浏览器访问出现重定向循环或 Cookie 拒绝。
  • SECRET_KEY_BASE(必须固定):官方原话要求「Treat it like a password and store it securely」。实测验证:修改该值重启后所有已登录会话立即失效。

7.2 内置服务的端口与进程

一体化镜像内部由入口脚本启动多个进程,docker exec openproject-17.9.0-main ps aux 可见(实测):Caddy(入口 80)、PostgreSQL(5432,仅容器内部监听)、memcached(11211,仅容器内部监听)、Puma 应用、GoodJob 后台任务进程。除 80 外全部不对外暴露,外部组件无法直连内置数据库——需要外接数据库时请直接采用 slim 镜像分离式部署(第十一章)。

八、配置文件挂载

一体化镜像支持把 configuration.yml 挂载进容器覆盖配置(优先级高于环境变量之外的默认值):

# docker-compose.yml services.openproject 追加
    volumes:
      - /var/lib/openproject/pgdata:/var/openproject/pgdata
      - /var/lib/openproject/assets:/var/openproject/assets
      - /opt/openproject/configuration.yml:/app/config/configuration.yml:ro

configuration.yml 从官方源码 config/configuration.yml.example 复制修改,YAML 键名与 OPENPROJECT_ 环境变量一一对应(如 password_min_length ↔ OPENPROJECT_PASSWORD__MIN__LENGTH)。实测建议:演示环境用环境变量已足够;需要精细控制(SMTP、S3、OIDC 等大段配置)时再挂载文件。参数全集与优先级见《OpenProject 配置参数详解》篇。

九、等价 docker run(单容器形态)

不使用 Compose 时,官方文档给出的等价启动命令如下(引用自官方 Docker 安装文档,查询日期 2026-10-27)。

快速体验(前台运行,日志直出,首次启动约 3 分钟后提示默认管理口令):

docker run -it -p 8080:80 \
  -e SECRET_KEY_BASE=f7a3c9d1e5b2a8f4c6d0e3b7a1f5c9d2e6b4a8f0c3d7e1b5a9f3c7d1e5b2a8f4c6d0e3b7a1f5c9d2e6b4a8f0c3d7e1b5a9f3 \
  -e OPENPROJECT_HOST__NAME=localhost:8080 \
  -e OPENPROJECT_HTTPS=false \
  -e OPENPROJECT_DEFAULT__LANGUAGE=en \
 openproject/openproject:17

生产化的单容器形态(持久化目录 + 后台运行 + 固定容器名):

sudo mkdir -p /var/lib/openproject/{pgdata,assets}

docker run -d -p 8080:80 --name openproject \
  -e OPENPROJECT_HOST__NAME=openproject.example.com \
  -e SECRET_KEY_BASE=f7a3c9d1e5b2a8f4c6d0e3b7a1f5c9d2e6b4a8f0c3d7e1b5a9f3c7d1e5b2a8f4c6d0e3b7a1f5c9d2e6b4a8f0c3d7e1b5a9f3 \
  -v /var/lib/openproject/pgdata:/var/openproject/pgdata \
  -v /var/lib/openproject/assets:/var/openproject/assets \
 openproject/openproject:17

⚠️ 注意:官方提示在部分环境下后台模式会遇到伪终端与 /dev/stdout 权限问题(moby#31243),表现为容器启动即退出,解决办法是即使 -d 也保留 -t 参数。

十、Kubernetes 部署形态(官方 Helm Chart 溯源)

本章内容为联网溯源整理(来源:官方 Helm chart 仓库 README 与官方 Kubernetes 安装文档,查询日期 2026-10-27),供容量规划与选型参考;本批次未做 k8s 集群实测,K1 批次将在 k8s 集群中逐项验证后再补实测回显。

OpenProject 官方维护 Helm chart(仓库 opf/helm-charts,chart 仓库地址 https://charts.openproject.org),这是官方推荐的 k8s 部署形态。前置要求:Kubernetes 1.16+、Helm 3.0.0+、集群支持 PV 供给。

官方 Quick Start 命令(引用自 chart README):

helm repo add openproject https://charts.openproject.org
helm repo update
helm upgrade --create-namespace --namespace openproject --install openproject openproject/openproject

chart 渲染的组件构成(溯源自 README 与 values 描述):

组件形态说明
openproject webDeployment主应用,默认使用 openproject/openproject:17-slim(chart 明确要求 slim 变体,一体化镜像在 k8s 下无法按非 root 等默认约束工作)
workerDeploymentGoodJob 后台任务
cronDeployment定时任务
seederJob(Helm hook)每次安装/升级执行数据库迁移与种子
hocuspocusDeployment协同编辑 WebSocket 服务
PostgreSQL子 chart(Bitnami)可关闭后接外部库
memcached子 chart应用缓存
RustFS(可选)Deployment无现成 S3 时可 rustfs.bundled: true 快速拉起 S3 兼容存储(官方注明非生产级)

关键 values 与约束(溯源要点):

  • openproject.https:与 ingress TLS 模式解耦,控制应用是否按 HTTPS 生成响应与链接;
  • openproject.admin_user.password / password_reset:k8s 场景可预设 admin 口令并关闭首登强制改密(自动化测试库有用);
  • openproject.secretKeyBase:chart 13.5.4 起自动生成随机值存入 Secret,也可指定 existingSecret;
  • 附件存储:文件系统方式要求 ReadWriteMany(RWX) 卷(多副本共享写);避免 RWX 的官方做法是 persistence.enabled=false + s3.enabled=true;
  • 安全基线:默认渲染结果兼容 Kubernetes Pod Security Standards restricted 档;readOnlyRootFilesystem=true(默认跟随)时 chart 自动挂载 /app/tmp 与 /tmp 临时卷;
  • 配置变更:通过 environment 键注入任意 OPENPROJECT_* 环境变量,例如 helm upgrade --reuse-values --set environment.OPENPROJECT_APP__TITLE='My OpenProject';
  • 官方排障预案:minikube 默认 2 CPU 会导致 web 容器 CrashLoopBackOff(cgroup 配额写入失败),官方建议加 --set resources.limits.cpu=2 或扩到 4 CPU。

升级与卸载:helm upgrade 复用 values;helm uninstall 不删除 PVC,需手动 kubectl delete namespace openproject 或逐个删卷。

十一、常用运维命令

# 查看各服务状态(本栈单服务)
docker compose ps

# 查看/跟踪应用日志(最近 200 行 + 跟随)
docker compose logs -n 200 -f

# 重启应用(数据在挂载卷中,安全)
docker compose restart

# 停止(保留容器与数据)
docker compose stop

# 启动已停止的栈
docker compose start

# 彻底移除栈(数据卷/绑定目录保留)
docker compose down

升级流程(官方 Compose 仓库形态的完整流程,引用自官方 docker-compose 文档;一体化形态把镜像 tag 换成新版本后 up -d 即可,首启会自动跑迁移):

# 官方分离式方案的升级:拉取仓库更新 → 构建控制面 → 备份 → 升级 → 重启
git pull origin stable/17
docker-compose -f docker-compose.yml -f docker-compose.control.yml build
docker-compose -f docker-compose.yml -f docker-compose.control.yml run backup
docker-compose -f docker-compose.yml -f docker-compose.control.yml run upgrade
docker compose up -d --build --pull always

十二、常见问题(FAQ)

以下 7 个问题全部为本次实测过程中真实发生并定位解决的过程沉淀。

Q1:访问报 400「Invalid host_name configuration」? OPENPROJECT_HOST__NAME 与请求 Host 头不一致触发 HOST 注入防护。实测:容器内 curl http://127.0.0.1:8080/healthz 未带匹配 Host 时返回 400;带 -H 'Host: localhost:8142' 后正常。解法:把该环境变量改成浏览器实际访问的「主机名:端口」,或在前面加反向代理并转发原始 Host。

Q2:登录接口/改密接口返回 422? 三种成因实测都出现过:① 请求缺 CSRF 令牌(Rails 表单需要 authenticity_token,服务端日志 ActionController::InvalidAuthenticityToken);② 改密表单漏传 password_change_user 隐藏域(服务端按空用户处理,日志 Failed login for '');③ 新口令不满足策略(见 Q3)。逐项核对即可定位。

Q3:口令 q1w2e3r4 首登改密被拒? 默认密码策略为最小 10 字符 + 大写/小写/数字/特殊符号四类全选。先以合规口令完成强制改密,再进「管理 → 系统设置 → 认证 → 密码」把最小长度改为 8、规则只留小写+数字,之后 q1w2e3r4 即可通过。注意 OPENPROJECT_PASSWORD__MIN__LENGTH 环境变量实测不改变已初始化实例的策略(策略存数据库)。

Q4:登录报「你的账号已被锁定」类节流提示? 连续失败触发暴力破解节流,默认失败 20 次后按 30 分钟窗口限制。等待窗口过期,或从管理后台认证设置调整 brute_force_block_after_failed_logins / brute_force_block_minutes。

Q5:API Basic 认证 401? 17.x 的 API v3 Basic 认证用户名固定为 apikey,密码为 API 密钥完整值(含 opapi- 前缀)。实测 admin:q1w2e3r4 返回 401;apikey:opapi-<64hex> 返回 200。密钥在「我的账户 → 访问令牌」生成,仅展示一次。

Q6:API 建工作包 422? 新建项目默认未启用工作包类型(17.x 行为)。到「项目设置 → 工作包 → 类型」勾选类型保存,或 PATCH /projects/<项目标识符>/settings/work_packages/types/bulk_update;复用表单端点 POST /api/v3/projects/<项目id>/work_packages/form 可提前看到校验错误。

Q7:/healthz 返回 404? 17.9.0 一体化镜像无该健康检查端点(实测 404 页面)。就绪探测改用 curl -f http://127.0.0.1:8142/login(200 即可)或带 API 密钥请求 /api/v3/projects(200)。K8s 场景以 chart 内置探针配置为准(K1 批次实测后更新)。

十三、参考链接