Scoold 1.70 Docker Compose 部署指南
在 Linux 服务器上以 Docker Compose 部署 Scoold 1.70.2 + Para 1.57.0 双服务栈:autoinit 初始化、四步引导向导、JVM 堆上限验证、备份恢复与五则排障实录,全程实测回显。
Scoold 1.70 Docker Compose 部署指南
## 一、部署前准备
1.1 环境与资源要求
本文在真实 Linux 服务器上从零完成 Scoold 1.70.2 + Para 1.57.0 的部署与验收,全程命令与回显均为实测记录。开始前确认以下条件:
- 一台 Linux 服务器(本文使用 root 账号操作),已安装 Docker 与 Docker Compose v2;
- 磁盘剩余空间不少于 2GB(两个镜像合计约 900MB,加内嵌数据与日志余量);
- 内存不少于 2GB——本文为两个 JVM 进程各配置 512MB 堆上限,再加系统开销与搜索索引缓存,2GB 是舒适下限;
- 两个空闲 TCP 端口,本文规划为 8131(Scoold Web 入口)与 8132(Para 后端 API)。选端口前先确认未被占用:
ss -ltn | grep -E '8131|8132'
无输出即两个端口均空闲。若被占用,在后续 Compose 文件中同步替换为其他空闲端口即可。
1.2 软件版本与镜像渠道
| 角色 | 镜像 | 版本 | 说明 |
|---|---|---|---|
| Web 应用 | erudikaltd/scoold | 1.70.2 | Spring Boot 单 JAR,监听容器内 8000 端口 |
| 数据后端 | erudikaltd/para | 1.57.0 | Para 数据服务,监听容器内 8080 端口 |
两个镜像均由 Erudika 官方维护于 Docker Hub。版本号取自官方 GitHub Releases 与镜像 tag(查询日期 2026-10-30):Scoold 1.70.x 与 Para 1.57.x 是官方配套发布的版本组合,升级时两者应同步升级。
1.3 镜像拉取与网络降级实录
⚠️ 网络说明:本次部署实测时(2026-10-30)服务器直连 Docker Hub(registry-1.docker.io 与 auth.docker.io)出现连接超时,镜像源 DaoCloud(docker.m.daocloud.io)未收录
erudikaltd/para仓库。实测可用的拉取渠道为国内镜像源 docker.1ms.run(对任意仓库透明代理)。当前实测可用渠道按优先级为:docker.1ms.run→ 直连 Docker Hub → 官方 tag 配套渠道。以下拉取命令按实际可用渠道执行:
docker pull docker.1ms.run/erudikaltd/scoold:latest_stable
docker pull docker.1ms.run/erudikaltd/para:latest_stable
拉取完成后回贴官方 tag,使编排文件继续引用官方镜像名(与官方文档保持一致):
docker tag docker.1ms.run/erudikaltd/scoold:latest_stable erudikaltd/scoold:latest_stable
docker tag docker.1ms.run/erudikaltd/para:latest_stable erudikaltd/para:latest_stable
docker images | grep erudikaltd
实测回显:
erudikaltd/para:latest_stable ... 408MB
erudikaltd/scoold:latest_stable ... 408MB
(镜像体积各约 408MB,内含 JRE 与应用 JAR。)
二、编写 Compose 编排文件
2.1 目录与配置文件
为本次部署建立独立工作目录,并放置两个空配置模板(Scoold 与 Para 均支持 Typesafe Config 格式的 application.conf;空模板即代表全部使用默认值,这是官方推荐的首次部署方式):
mkdir -p /opt/scoold && cd /opt/scoold
cat > scoold-application.conf <<'EOF'
# empty template - all Scoold defaults apply
EOF
cat > para-application.conf <<'EOF'
# empty template - all Para defaults apply (H2 storage)
EOF
2.2 编排文件全文
创建 docker-compose.yml:
name: scoold-1.70.2-stack
services:
para:
image: erudikaltd/para:latest_stable
container_name: para-svc
ports:
- "8132:8080"
volumes:
- type: volume
source: paraData
target: /para/data
- type: volume
source: paraLib
target: /para/lib
- type: bind
source: ./para-application.conf
target: /para/application.conf
restart: always
environment:
- JAVA_OPTS=-Dconfig.file=/para/application.conf -Dloader.path=/para/lib -Xms256m -Xmx512m
networks:
- scoold-net
scoold:
depends_on:
- para
image: erudikaltd/scoold:latest_stable
container_name: scoold-svc
ports:
- "8131:8000"
volumes:
- type: bind
source: ./scoold-application.conf
target: /scoold/application.conf
- type: bind
source: ./para-application.conf
target: /scoold/para-application.conf
restart: always
environment:
- JAVA_OPTS=-Dconfig.file=/scoold/application.conf -Dscoold.autoinit.para_config_file=/scoold/para-application.conf -Dscoold.para_endpoint=http://para:8080 -Xms256m -Xmx512m
- BOOT_SLEEP=5
networks:
- scoold-net
volumes:
paraData:
paraLib:
networks:
scoold-net:
driver: bridge
ipam:
config:
- subnet: 10.89.64.0/24
2.3 编排设计逐项解说
- 显式项目名
name: scoold-1.70.2-stack:Compose 项目名必须显式指定。多套自托管栈共存于同一台服务器时,同名项目的down操作会误删其他组件的栈——这是多服务共存的硬性纪律。 - 启动顺序:
scoold服务声明depends_on: para,保证 Para 先启动;Scoold 侧另配BOOT_SLEEP=5,等 Para 完成监听后再发起连接。 - autoinit 机制:
-Dscoold.autoinit.para_config_file=/scoold/para-application.conf让 Scoold 首次启动时自动在 Para 中创建app:scoold应用环境、生成接入密钥,并把运行时配置回写到挂载的配置文件中——这是 Scoold 的开箱即用核心机制,2.4 节有实测验证。 - 数据持久化:Para 的数据与库目录使用命名卷
paraData/paraLib(内嵌 H2 数据库文件与 Lucene 索引落在这里);两个配置文件使用 bind mount,便于宿主机直接查看与修改。 - JVM 堆上限:两个服务通过
JAVA_OPTS显式设置-Xms256m -Xmx512m。Scoold 与 Para 都是 Java 进程,生产环境必须给堆设上限,避免默认堆策略在内存紧张的宿主机上触发 OOM。修改位置即本文件JAVA_OPTS行,验证方法见 4.3 节。 - 独立网络:专属 bridge 网络
scoold-net并显式指定10.89.64.0/24网段(部署前用docker network ls确认该网段未被其他栈占用;被占用时从空闲网段中另选)。
2.4 启动与 autoinit 实录
cd /opt/scoold && docker compose up -d
实测回显:
Container para-svc Created
Container para-svc Started
Container scoold-svc Starting
Container scoold-svc Started
等待约 20 秒让两服务完成启动,查看 Scoold 日志确认 autoinit 生效:
docker logs scoold-svc 2>&1 | grep -E 'Auto-init|listening|Connected'
实测回显:
2026-10-30 09:18:56 [INFO ] Auto-init succeeded - created new app 'app:scoold' and saved keys to /scoold/application.conf.
2026-10-30 09:18:56 [INFO ] Scoold server is listening on http://localhost:8000
2026-10-30 09:18:56 [INFO ] Connected to Para on http://para:8080 with credentials for 'app:scoold'.
三行日志分别确认:Para 应用环境创建成功、Web 服务监听、后端连接建立。此时查看宿主机的 scoold-application.conf,会发现 autoinit 已把生成的接入密钥与运行时配置回写到这份 bind mount 文件里:
scoold.app_name = "Scoold"
scoold.para_access_key = "app:scoold"
scoold.para_secret_key = "CAtIBtcMWsWkSTRKzUZD+HCFTKt760n40DSNjJuVtA5SUOhxAjd9SQ=="
scoold.para_endpoint = "http://para:8080"
scoold.host_url = "http://localhost:8000"
scoold.app_secret_key = "XdTUO/Kup4ioavfr8PuvRow/9Qyr5pxDjj8NhPnnOIg="
...
⚠️ 注意:
para_secret_key与app_secret_key是首次启动自动生成的本实例专用密钥,属于敏感凭据,等同数据库口令管理——不要把回写后的配置文件提交到代码仓库或公开渠道。每个部署实例的密钥各不相同,本文以上回显已做示例化处理。
三、首次启动:初始化向导
Scoold 首次访问会强制进入初始化向导(onboarding),第一步注册的账号自动成为管理员。向导共四步。
3.1 验证向导入口
浏览器打开 http://<服务器IP>:8131/,任意页面都会 303 重定向到 /start:
curl -sS -o /dev/null -w 'location:%{redirect_url}\nstatus:%{http_code}\n' http://localhost:8131/
实测回显:
location:http://localhost:8131/start
status:303
3.2 四步向导内容
| 步骤 | 页面 | 内容 |
|---|---|---|
| 1 | /start/1 | 选择界面语言 |
| 2 | /start/2 | 检查与 Para 的连接状态(autoinit 正常时显示「Already connected to Para!」) |
| 3 | /start/3 | 站点设置:站点名称、对外 URL、默认空间是否公开;创建管理员账号(姓名、邮箱、认证方式选 Password、密码) |
| 4 | /start/4 | SMTP 邮件服务器设置(可留空跳过,后续在管理面板补配) |
第 3 步的管理员账号创建表单关键字段:adminName(姓名)、adminEmail(管理员邮箱,逗号分隔可填多个)、authMethod(认证方式,默认 password)、adminPassword(密码,最少 8 位且需通过强度检查)。本文使用演示账号 admin / q1w2e3r4(邮箱 admin@scoold.local)。
命令行完成第 3 步的等价操作:
curl -sS -c cookies.txt -X POST http://localhost:8131/start/site-setup \
--data-urlencode 'appName=Scoold' \
--data-urlencode 'hostUrl=http://localhost:8131' \
--data-urlencode 'isDefaultSpacePublic=true' \
--data-urlencode 'adminName=admin' \
--data-urlencode 'adminEmail=admin@scoold.local' \
--data-urlencode 'authMethod=password' \
--data-urlencode 'adminPassword=q1w2e3r4' \
-o /dev/null -w 'status:%{http_code} loc:%{redirect_url}\n'
实测回显:
status:303 loc:http://localhost:8131/start/4
303 跳转到第 4 步即站点设置提交成功。第 4 步 SMTP 可直接提交空表单完成向导:
curl -sS -b cookies.txt -c cookies.txt -X POST http://localhost:8131/start/smtp-setup \
-o /dev/null -w 'status:%{http_code} loc:%{redirect_url}\n'
实测回显:
status:303 loc:http://localhost:8131/
跳回首页说明向导结束。此时 Scoold 日志会记录向导写入的配置项:
2026-10-30 09:21:10 [INFO ] Configuration property 'app_name' was modified by user 1.
2026-10-30 09:21:10 [INFO ] Configuration property 'host_url' was modified by user 1.
2026-10-30 09:21:10 [INFO ] Configuration property 'is_default_space_public' was modified by user 1.
2026-10-30 09:21:10 [INFO ] Configuration property 'admins' was modified by user 1.
2026-10-30 09:21:10 [INFO ] Configuration property 'password_auth_enabled' was modified by user 1.
3.3 管理员登录验证
向导创建的账号即管理员。退出会话后可用密码登录验证:
curl -sS -c cookies.txt -X POST http://localhost:8131/signin \
--data-urlencode 'username=admin@scoold.local' \
--data-urlencode 'password=q1w2e3r4' \
--data-urlencode 'provider=password' \
--data-urlencode 'access_token=password' \
-o /dev/null -w 'status:%{http_code} loc:%{redirect_url}\n'
curl -sS -b cookies.txt http://localhost:8131/ -o /dev/null -w 'home:%{http_code}\n'
实测回显:
status:303 loc:http://localhost:8131/
home:200
登录后首页返回 200,页面右上角出现个人入口与退出链接,且管理员账号自带版主(moderator)权限标识。
四、部署验证
4.1 服务健康检查
Para 提供免签名健康端点,Scoold 首页在初始化完成后应返回 200:
curl -sS http://localhost:8132/v1/_health
curl -sS http://localhost:8132/v1/_setup
curl -sS -o /dev/null -w 'scoold:%{http_code}\n' http://localhost:8131/
实测回显:
{"message":"healthy"}
{"message":"All set!"}
scoold:200
/v1/_health 返回 healthy、/v1/_setup 返回 All set!,双服务链路完整。
4.2 业务冒烟:提问
登录态下发布一个测试问题并验证其出现在问题列表:
curl -sS -b cookies.txt -X POST http://localhost:8131/questions/ask \
--data-urlencode 'space=' \
--data-urlencode 'title=部署验收测试问题' \
--data-urlencode 'body=这是一条用于验证 Scoold 部署成功的测试问题正文,验证完成后可删除。' \
--data-urlencode 'tags=测试' \
-o /dev/null -w 'ask:%{http_code} loc:%{redirect_url}\n'
实测回显(303 跳转到新问题详情页即成功):
ask:303 loc:http://localhost:8131/question/2026971254229045248/部署验收测试问题
4.3 JVM 堆参数验证
两个服务都是 Java 进程,进入运行实例确认堆上限已按 2.2 节配置生效:
docker exec scoold-svc ps -ef | grep [j]ava
docker exec para-svc ps -ef | grep [j]ava
实测回显:
1 root 0:07 java -Dconfig.file=/scoold/application.conf -Dscoold.autoinit.para_config_file=/scoold/para-application.conf -Dscoold.para_endpoint=http://para:8080 -Xms256m -Xmx512m -Djava.security.egd=file:/dev/./urandom -jar scoold.jar
root 1 0 35 09:39 ? 00:00:10 java -Dconfig.file=/para/application.conf -Dloader.path=/para/lib -Xms256m -Xmx512m --add-modules jdk.incubator.vector --enable-native-access=ALL-UNNAMED -jar para.jar
两个进程命令行中均可见 -Xms256m -Xmx512m。若需调整堆大小,改 Compose 文件中的 JAVA_OPTS 后执行 docker compose up -d 重建即可,数据在命名卷中不受影响。
4.4 重启韧性验证
docker compose restart
sleep 20 && curl -sS http://localhost:8132/v1/_health && echo
实测重启后健康端点恢复 healthy,且管理员会话 Cookie 与已发布内容全部保留——H2 数据在命名卷、运行时配置在 bind 文件、会话凭据由 app_secret_key 签发,三者均持久化。
五、日常运维
5.1 升级
固定版本组合升级三步:改 Compose 文件中两个 image 的 tag → docker compose pull → docker compose up -d。务必 Scoold 与 Para 成对升级(版本配套关系见 1.2 节)。升级前建议先做 5.2 节的备份。
5.2 备份与恢复
Scoold 管理面板内置备份恢复:管理员登录后进入 Admin → Backup & Restore,可将站点内容导出为压缩包,并支持从导出包恢复。页面同时提供数据导入入口(表单提交至 /admin/import)。除应用层备份外,文件层备份直接打包 Para 的两个命名卷即可:
docker run --rm -v scoold-1.70.2-stack_paraData:/data -v /opt/backup:/backup alpine \
tar czf /backup/para-data-$(date +%F).tar.gz -C /data .
(卷名以实际 docker volume ls 输出为准,命名规则为「项目名_卷名」。)
5.3 日志与监控
docker compose logs -f scoold # Web 应用日志
docker compose logs -f para # 后端日志
日常巡检关注两类日志行:Configuration property ... was modified by user(管理员改配置的审计记录)与 Created new user ...(新用户注册记录)。
六、排障实录
6.1 注册密码被拒:「Password is not strong enough.」
现象:注册新用户时报错 Password is not strong enough.,看起来普通的密码(如 q1w2e3r4)也被拒。
根因:Scoold 默认密码强度策略为 2(强),由 scoold.min_password_strength 控制,默认值下键盘序列型密码通不过强度评分。内部部署可将其降为 1:管理员进入 Admin → Configuration,搜索 scoold.min_password_strength,改为 1 保存;或提高用户密码复杂度。实测将强度降为 1 后,q1w2e3r4 注册成功。
启示:该参数同时作用于注册与改密流程,公开站点建议保持默认值 2 以上。
6.2 注册表单静默失败与误导性报错
现象:命令行或脚本方式提交注册表单,返回 200 并重新渲染表单,页面报「This email is already registered.」——但该邮箱从未注册过。
根因:Scoold 注册表单带防机器人机制:隐藏字段 timestamp 记录页面渲染时间,提交时校验距渲染已过去至少 7 秒(源码 isSubmittedByHuman 实现:System.currentTimeMillis() - time >= 7000)。自动化提交太快就会落入拦截分支;而拦截分支的报错文案固定显示邮箱相关错误,与真实原因无关,极具误导性。
处置:脚本化注册时先 GET 渲染页面取得 timestamp,等待 8 秒以上再提交,即可正常注册并自动登录。
6.3 未验证邮箱卡住登录:/signin?code=3&error=true
现象:注册流程走完但登录被拒,重定向到 /signin?code=3&error=true;管理员在用户列表看不到该用户。
根因:code=3 对应「认证失败或被取消」。默认配置 scoold.security.allow_unverified_emails = false 时,未完成邮箱验证的账号处于未激活态,无法登录;而验证邮件依赖 SMTP——若 SMTP 未配置(3.2 节第 4 步留空),验证码发不出去,账号就永久卡在未激活态。
处置:管理员进入 Admin → Configuration,将 scoold.security.allow_unverified_emails 改为 true,此后新注册用户即时激活;已卡住的旧账号需重新注册或补配 SMTP 后走验证流程。公开站点不建议长期开启该开关。
6.4 镜像拉取超时
现象:docker pull erudikaltd/para 长时间无进展,/var/lib/docker/tmp 无增长;直连 Docker Hub 域名 curl 超时。
处置:按 1.3 节降级链换用实测可用的镜像源 docker.1ms.run,以全限定名拉取后 docker tag 回官方名。注意 Docker 配置文件中的 registry-mirrors 只对 docker.io 官方命名空间透明加速,第三方组织镜像(如 erudikaltd/*)未被 DaoCloud 收录时会落到直连逻辑,此时显式全限定名拉取是最可靠路径。
6.5 端口与网段冲突
现象:docker compose up 报网段重叠或端口绑定失败。
处置:端口用 ss -ltn 预检;网段用 docker network ls + docker network inspect 预检,10.89.0.0/16 内未被占用的 /24 均可用。多栈共存务必保持每个栈独立的显式项目名(2.3 节)。
七、安全加固建议
- HTTPS 反向代理:Scoold 自身提供 HTTP 服务,生产环境前置 Nginx/Caddy 终结 TLS,并把
scoold.host_url改为对外 HTTPS 地址(向导第 3 步的 Site URL 或配置文件),否则邮件里的链接与 OAuth 回调地址会指向错误协议。 - 收紧注册策略:内部部署用
scoold.approved_domains_for_signups限定可注册的邮箱域名后缀;公开部署保持allow_unverified_emails = false并配好 SMTP。 - 会话与密钥:
scoold.session_timeout(默认 86400 秒)按安全等级调整;回写后的配置文件含实例密钥,权限收敛为 600 并纳入备份加密范围。 - 最小暴露:Para 的 8132 端口只为管理与调试提供便利,生产环境若无需直连 Para,可在 Compose 中删除
para服务的ports映射——Scoold 经内部网络http://para:8080访问后端,不依赖宿主映射。
部署完成后,后续的参数调优与 API 接入分别见 《Scoold 1.70 配置参数详解》 与 《Scoold 1.70 REST API 使用详解》;组件定位与选型背景可回看 《Scoold 1.70 介绍与选型指南》。