Fess 15.9.0 Docker Compose 部署指南
在一台 Linux 服务器上用官方编排文件完成 Fess 15.9.0 + OpenSearch 双容器部署,覆盖镜像选型、数据持久化、环境变量、限堆配置与常用运维操作。已在 Ubuntu 22.04.4 LTS + Docker 29.8.1(Compose v5.5.1)环境实测通过:双容器 healthy、健康检查接口 GREEN、首次爬取并搜索命中。
Fess 15.9.0 Docker Compose 部署指南
目标:在一台 Linux 服务器上用官方编排文件完成 Fess 15.9.0 + OpenSearch 双容器部署,覆盖镜像选型、数据持久化、环境变量、限堆配置与常用运维操作。已在 Ubuntu 22.04.4 LTS + Docker 29.8.1(Compose v5.5.1)环境实测通过:双容器 healthy、健康检查接口 GREEN、首次爬取并搜索命中。
一、环境信息
- 操作系统:Ubuntu 22.04.4 LTS(x86_64)
- Docker:29.8.1(含 Compose v5.5.1 插件)
- 软件版本:Fess 15.9.0(ghcr.io/codelibs/fess)+ fess-opensearch 3.9.0(ghcr.io/codelibs/fess-opensearch)
- 编排文件:官方
compose.yaml+compose-opensearch3.yaml,本文另加一个覆盖文件compose-override.yml(端口改映射 + JVM 限堆) - 项目名:
fess-15.9.0-stack;服务:fess01(Web/管理)+search01(搜索引擎) - 实测端口:宿主机 8149 → fess01 容器 8080;宿主机 8152 → search01 容器 9200(本机 8080/9200 已被占用,故用覆盖文件改映射;端口空闲的服务器可直接用官方默认 8080/9200)
- 内核参数:
vm.max_map_count = 1048576(满足 OpenSearch ≥ 262144 要求) - 版本背景:Fess 15.9.0 已移除内嵌 OpenSearch,「Fess + 独立 OpenSearch」是唯一标准拓扑(见 《Fess 15.9.0 介绍与选型指南》3.3 节)
二、镜像选型说明
官方在 codelibs/docker-fess 仓库(GitHub Container Registry)发布两类镜像:
| 镜像 | 标签 | 内容 | 选型结论 |
|---|---|---|---|
ghcr.io/codelibs/fess | 15.9.0 | Fess 应用 + 内置 Java 21 + 基础插件 | Fess 侧唯一官方镜像,直接用 |
ghcr.io/codelibs/fess | 15.9.0-noble | 在 15.9.0 基础上加装 ImageMagick / poppler-utils / unoconv / LibreOffice | 需要生成文档缩略图( thumbnails)时选它,体积更大 |
ghcr.io/codelibs/fess-opensearch | 3.9.0 | OpenSearch 3.9.0 + Fess 必需的 CodeLibs 插件(分析器、词典同步) | 搜索引擎侧唯一官方镜像;不能用社区原版 opensearch 镜像替代(缺插件会导致中文分词与词典功能异常) |
💡 说明:
fess-opensearch镜像是官方基于 OpenSearch 3.9.0 定制的发行版,内置 Fess 所需的 analysis 插件;官方 compose 默认OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g,小内存服务器请按第七章压到 512m。OCR 能力(Tesseract)与 Playwright 动态页渲染由官方提供独立构建目录(compose.yaml中的./playwright、./tesseract注释项),需要时自行构建。
三、前置条件
- 服务器已安装 Docker 24+ 与 Compose v2.24+ 插件(
!override语法需要;更老版本请直接改官方文件端口行)。 - 内核参数
vm.max_map_count不低于 262144(OpenSearch 要求),查看并临时/永久设置:
sysctl vm.max_map_count # 查看当前值,实测:1048576
sudo sysctl -w vm.max_map_count=262144 # 临时设置
echo 'vm.max_map_count=262144' | sudo tee /etc/sysctl.d/99-fess.conf # 永久写入
sudo sysctl --system # 重载生效
- 端口规划:默认占用宿主机 8080(Web/管理)与 9200(搜索引擎 HTTP);被占用时按第四章覆盖文件改映射。
- 磁盘余量:两镜像合计约 3.5 GB,另预留索引用磁盘(约为原始文本量的 1–2 倍)。
四、部署步骤
4.1 下载官方编排文件
mkdir -p /opt/fess && cd /opt/fess
curl -o compose.yaml https://raw.githubusercontent.com/codelibs/docker-fess/v15.9.0/compose/compose.yaml
curl -o compose-opensearch3.yaml https://raw.githubusercontent.com/codelibs/docker-fess/v15.9.0/compose/compose-opensearch3.yaml
compose.yaml 定义 fess01 服务(端口 8080:8080),compose-opensearch3.yaml 定义 search01 服务(端口 9200:9200)与两个数据卷(search01_data、search01_dictionary),两文件通过同名网络 search_net 联动。
4.2 编写覆盖文件(端口与限堆,可选)
宿主机 8080/9200 已被占用或需要限堆时,在 /opt/fess/compose-override.yml 写入:
# 端口改映射 + 两个 JVM 堆压到 512m
# 注意:ports 用 !override 标签整体替换官方默认映射(Compose v2.24+ 语法)
name: fess-15.9.0-stack
services:
fess01:
ports: !override
- "8149:8080"
environment:
- "FESS_HEAP_SIZE=512m"
search01:
ports: !override
- "8152:9200"
environment:
- "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m"
⚠️ 注意:Compose 对
ports/environment列表默认是「合并追加」而不是覆盖——直接写第二个ports会同时保留官方的8080:8080与你的新映射,导致 8080 冲突启动失败(实测报address already in use)。必须加!override标签。
4.3 启动
cd /opt/fess
docker compose -f compose.yaml -f compose-opensearch3.yaml -f compose-override.yml up -d
实测回显(首次启动,镜像已拉取后约 60 秒内完成):
Network fess-1590-stack_search_net Created
Volume fess-1590-stack_search01_data Created
Volume fess-1590-stack_search01_dictionary Created
Container search01 Creating
Container search01 Created
Container fess01 Creating
Container fess01 Created
Container search01 Starting
Container search01 Started
Container search01 Waiting
Container search01 Healthy
Container fess01 Starting
Container fess01 Started
search01 通过 healthcheck(_cluster/health)后才放行 fess01 启动,首个启动约需 1–2 分钟(start_period: 120s)。
4.4 验证部署
# 1. 容器状态(两容器均应 healthy)
docker compose -f compose.yaml -f compose-opensearch3.yaml -f compose-override.yml ps
实测回显:
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
fess01 ghcr.io/codelibs/fess:15.9.0 "/usr/share/fess/run…" fess01 About a minute ago Up About a minute (healthy) 0.0.0.0:8149->8080/tcp, [::]:8149->8080/tcp
search01 ghcr.io/codelibs/fess-opensearch:3.9.0 "./opensearch-docker…" search01 About a minute ago Up About a minute (healthy) 9300/tcp, 9600/tcp, 9650/tcp, 0.0.0.0:8152->9200/tcp, [::]:8152->9200/tcp
# 2. Fess 健康检查(engine.status 应为 GREEN)
curl -s http://localhost:8149/api/v2/health
# 实测回显:
{"response":{"status":0,"engine":{"status":"GREEN","ping_status":0}}}
# 3. 搜索引擎集群状态
curl -s http://localhost:8152/_cluster/health | head -c 120
# 实测回显:
{"cluster_name":"fess-search","status":"green","timed_out":false,"number_of_nodes":1,"number_of_data_nodes":1,
# 4. 监听确认
ss -ltn | grep -E ':(8149|8152) '
# 实测回显:
LISTEN 0 4096 0.0.0.0:8152 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:8149 0.0.0.0:*
LISTEN 0 4096 [::]:8152 [::]:*
LISTEN 0 4096 [::]:8149 [::]:*
4.5 管理后台初始化(首次登录强制改密)
- 浏览器打开
http://<服务器IP>:8149/admin/(端口空闲直连时为:8080),用默认账号admin/admin登录。 - Fess 15.9 首次登录强制要求修改默认密码,API 登录可看到标记(实测回显):
POST /api/v2/auth/login {"username":"admin","password":"admin"}
→ {"response":{"status":0,"csrf_token":"...","password_change_required":true,...}}
- 立即改为强密码(本文示例密码
q1w2e3r4,生产请替换)。实测请求与回显:
curl -b cookies.txt -X POST http://localhost:8149/api/v2/auth/password \
-H "Content-Type: application/json" -H "X-Fess-CSRF-Token: <登录返回的csrf_token>" \
-d '{"current_password":"admin","new_password":"q1w2e3r4","confirm_password":"q1w2e3r4"}'
→ {"response":{"status":0,"ok":true,"re_login_required":true}}
改密成功后旧会话立即失效,需用新密码重新登录;再次登录时 password_change_required 不再出现。
💡 说明:管理后台(SPA)界面登录后同样会弹出强制改密向导,与 API 行为一致。密码策略可按官方文档调整最小长度等规则。
4.6 配置第一条爬取并验证搜索
- 用管理员账号登录后台 →「爬虫」→「Web」→ 新建,名称随意(如「企业搜索知识库测试站」),URL 填目标站点(本文实测
http://crawl-target:8001/的内网测试站),权限默认{role}guest(游客可见),保存。 - 「系统」→「调度器」→ 找到
Default Crawler→ 启动。等价的 API 操作(Bearer 令牌在「系统 → 访问令牌」创建):
# 启动爬取任务(实测回显)
curl -X PUT http://localhost:8149/api/admin/scheduler/default_crawler/start \
-H "Authorization: Bearer <API访问令牌>"
→ {"response":{"job_log_id":"7f03645a866040179e915385c0d25d0f","version":"15.9","status":0}}
- 约 1 分钟后验证搜索(本文测试站含 6 个中文页面):
curl -s "http://localhost:8149/api/v2/search?q=OpenSearch&num=10"
{"response":{"version":"15.9","record_count":4,"exec_time":0.2,"data":[
{"title":"OpenSearch 搜索集群部署指南","score":0.07762065},
{"title":"Elasticsearch 与企业搜索选型对比","score":0.027356368},
{"title":"企业搜索引擎知识库 - 首页","score":0.024728492},
{"title":"Fess 全文搜索服务器介绍","score":0.007118387}]}}
五、数据持久化说明
| 数据 | 位置 | 载体 |
|---|---|---|
| 索引与全部业务数据(用户/配置/爬取结果/日志索引) | search01 容器 /usr/share/opensearch/data | 具名卷 search01_data |
| 分词词典(Fess 与引擎自动同步) | search01 容器 /usr/share/opensearch/config/dictionary | 具名卷 search01_dictionary |
| fess01 容器 | 无业务状态 | 无卷(重建容器零损失) |
要点:
- fess01 无状态,search01 是唯一事实源:删掉 fess01 容器重建不影响任何数据;因此 Fess 升级只需重建 fess01。
- 卷实际名称带项目前缀:
fess-1590-stack_search01_data/fess-1590-stack_search01_dictionary,docker volume ls可见。 - 备份即备份这两个卷(见第十章)。
- 若需把数据落到宿主机目录,在覆盖文件中把卷声明改为 bind mount(如
./data:/usr/share/opensearch/data),注意宿主机目录属主 UID 1000。
六、环境变量表
Fess 镜像(fess01):
| 变量 | 默认值 | 说明 |
|---|---|---|
SEARCH_ENGINE_HTTP_URL | 无(必填) | 搜索引擎 HTTP 地址,官方 compose 指向 http://search01:9200 |
SEARCH_ENGINE_TYPE | default | 引擎类型;可填 vanilla(社区原版 OpenSearch)或 aws(AWS OpenSearch 托管服务) |
SEARCH_ENGINE_USERNAME / SEARCH_ENGINE_PASSWORD | 无 | 引擎开启鉴权时的账号密码 |
FESS_HEAP_SIZE | 512m | Fess JVM 堆上限,本文实测设为 512m |
FESS_JAVA_OPTS | 无 | 追加 JVM 系统属性,如 -Dfess.config.xxx=yyy 覆盖任意配置项 |
FESS_PLUGINS | 无 | 空格分隔的 FESS 插件列表,启动时自动安装(如 fess-ds-wikipedia:15.9.0) |
FESS_DICTIONARY_PATH | /usr/share/opensearch/config/dictionary/ | 词典目录 |
FESS_PORT | 8080 | 容器内监听端口(healthcheck 也引用它) |
FESS_CONTEXT_PATH | / | 应用上下文路径 |
搜索引擎镜像(search01):
| 变量 | 默认值 | 说明 |
|---|---|---|
OPENSEARCH_JAVA_OPTS | -Xms1g -Xmx1g(官方 compose 设定) | 引擎 JVM 堆;本文实测覆盖为 -Xms512m -Xmx512m |
DISABLE_SECURITY_PLUGIN | true(官方 compose) | 关闭引擎自带安全插件(由 Fess 统一鉴权,内网部署可维持默认) |
DISABLE_INSTALL_DEMO_CONFIG | true | 不装演示证书/用户 |
bootstrap.memory_lock=true | compose 内设定 | 锁定内存防交换,配合 ulimits.memlock: -1 |
node.roles=cluster_manager,data,ingest,ml | compose 内设定 | 单节点全角色 |
七、配置参数说明
- JVM 堆(重点):两个 JVM 进程各自独立设堆。Fess 侧改
FESS_HEAP_SIZE(如-e FESS_HEAP_SIZE=512m),引擎侧改OPENSEARCH_JAVA_OPTS(如-Xms512m -Xmx512m,Xms 与 Xmx 保持一致)。4 GB 内存服务器建议 Fess 512m + 引擎 512m;2 GB 内存建议各 256m 并搭配 1 GB swap。实测验证方式:
# Fess 进程堆(实测回显)
docker exec fess01 sh -c "ps -ef | grep -o 'Xms[0-9]*[a-z] -Xmx[0-9]*[a-z]' | head -1"
Xms512m -Xmx512m
# 引擎堆(实测回显,536870912 字节 = 512m)
curl -s "http://localhost:8152/_nodes/jvm?filter_path=nodes.*.jvm.mem.heap_init_in_bytes,nodes.*.jvm.mem.heap_max_in_bytes"
{"nodes":{"GzbCCWa7TWih6k8JCNyKSQ":{"jvm":{"mem":{"heap_init_in_bytes":536870912,"heap_max_in_bytes":536870912}}}}}
- 连接引擎的方式:换引擎地址只改
SEARCH_ENGINE_HTTP_URL;把SEARCH_ENGINE_TYPE设为vanilla可对接未定制插件的原版 OpenSearch(此时词典功能受限),设为aws对接托管服务。 - 容器内配置文件:Fess 主配置在容器
/etc/fess/fess_config.properties,持久化优先用FESS_JAVA_OPTS=-Dfess.config.<键>=<值>或挂载覆盖,避免重建容器丢配置(详见第八章;参数含义逐条解析见本专栏《Fess 15.9.0 配置参数详解》)。
八、配置文件挂载
官方镜像把配置目录排在 classpath 前部的 /opt/fess,覆盖配置有两种挂法:
- 挂载整个 classpath 覆盖目录(官方推荐,支持
fess_config.properties等全部文件):
# compose-override.yml 中 fess01 增加
volumes:
- ./app:/opt/fess # 宿主机目录优先于镜像内置 classpath
在宿主机 ./app/ 下放自定义的 fess_config.properties(可只放要覆盖的键)即可。
- 单键覆盖(免挂载):只改一两个参数时用环境变量追加系统属性,不触碰文件:
environment:
- "FESS_JAVA_OPTS=-Dfess.config.crawler.document.ocr.enabled=true -Duser.timezone=Asia/Shanghai"
⚠️ 注意:不要把单个文件 bind mount 成
/etc/fess/fess_config.properties之外的路径就期望生效——Fess 15.x 运行时读取的是 classpath 内的/etc/fess/,单文件挂载该路径可行,但整目录挂/opt/fess是官方文档明确支持的方式,优先采用。
九、等价 docker run
不使用 Compose 时,以下两条命令与本文部署等价(先建网络):
docker network create fess-net
# 搜索引擎(先启动)
docker run -d --name search01 --network fess-net \
-p 8152:9200 \
-e node.name=search01 \
-e discovery.seed_hosts=search01 \
-e cluster.initial_cluster_manager_nodes=search01 \
-e cluster.name=fess-search \
-e bootstrap.memory_lock=true \
-e node.roles=cluster_manager,data,ingest,ml \
-e "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" \
-e "DISABLE_INSTALL_DEMO_CONFIG=true" \
-e "DISABLE_SECURITY_PLUGIN=true" \
-e "FESS_DICTIONARY_PATH=/usr/share/opensearch/config/dictionary" \
--ulimit memlock=-1:-1 --ulimit nofile=65535:65535 \
-v fess-search01_data:/usr/share/opensearch/data \
-v fess-search01_dictionary:/usr/share/opensearch/config/dictionary \
ghcr.io/codelibs/fess-opensearch:3.9.0
# Fess 应用(引擎健康后启动)
docker run -d --name fess01 --network fess-net \
-p 8149:8080 \
-e "SEARCH_ENGINE_HTTP_URL=http://search01:9200" \
-e "FESS_DICTIONARY_PATH=/usr/share/opensearch/config/dictionary/" \
-e "FESS_HEAP_SIZE=512m" \
ghcr.io/codelibs/fess:15.9.0
日常部署仍推荐 Compose:healthcheck 依赖(引擎健康后才启动应用)、重启策略与日志轮转已在编排文件中声明,手工 docker run 需自行维护。
十、常用运维命令
# 状态与日志
docker compose -f compose.yaml -f compose-opensearch3.yaml -f compose-override.yml ps
docker logs -f fess01 # 应用日志(启动完成以 "Fess started" 为准)
docker logs -f search01 # 引擎日志
# 重启单个服务
docker compose -f compose.yaml -f compose-opensearch3.yaml -f compose-override.yml restart fess01
# 停止与启动(保留数据)
docker compose -f compose.yaml -f compose-opensearch3.yaml -f compose-override.yml stop
docker compose -f compose.yaml -f compose-opensearch3.yaml -f compose-override.yml up -d
# 数据卷备份(冷备:停 fess01 后打 tar)
docker run --rm -v fess-1590-stack_search01_data:/data -v $PWD:/backup \
ubuntu tar czf /backup/fess_data_$(date +%F).tar.gz -C /data .
# Fess 版本升级(fess01 无状态,直接换镜像重建)
docker compose -f compose.yaml -f compose-opensearch3.yaml -f compose-override.yml pull fess01
docker compose -f compose.yaml -f compose-opensearch3.yaml -f compose-override.yml up -d fess01
# 彻底删除(连数据卷一起)
docker compose -f compose.yaml -f compose-opensearch3.yaml -f compose-override.yml down -v
十一、常见问题
Q1:fess01 起不来,报 failed to bind host port ... address already in use?
宿主机 8080/9200 被占用。用 4.2 节覆盖文件改映射;若用旧版 Compose 不支持 !override,直接编辑官方文件中的 ports 行。
Q2:vm.max_map_count [65530] is too low 启动失败?
OpenSearch 要求 ≥262144,执行第三章的 sysctl 设置后 restart search01。
Q3:首次登录后台提示改密码,能否跳过?
不能。15.x 强制首次改密(API 层同样拦截 password_change_required),这是默认安全策略;改密接口要求 current_password、new_password、confirm_password 三字段齐全。
Q4:搜索接口返回 record_count: 0?
先确认爬取任务已运行且完成(调度器页面看 Job 状态),再确认爬取配置的「权限」包含目标用户角色——爬取配置默认权限为空时文档归属 guest 角色,游客可搜;若手动设成 {role}admin 则只有管理员搜索可见。
Q5:内存不足被 OOM Kill?
两进程都是 JVM,2 GB 内存机器必须显式限堆(Fess FESS_HEAP_SIZE=256m + 引擎 -Xms256m -Xmx256m)并留 swap;docker inspect fess01 | grep -A3 OOMKilled 可确认。
Q6:中文分词不生效或报缺插件?
必须用官方 fess-opensearch 定制镜像(内置 CodeLibs 分析插件);用原版 opensearch 镜像时把 SEARCH_ENGINE_TYPE 改为 vanilla 才能启动,但词典与多语言分词能力受限。
十二、参考链接
- 官方 Docker 部署指南:https://fess.codelibs.org/stable/install/install-docker.html (查询日期 2026-10-24)
- 官方编排文件仓库(v15.9.0):https://github.com/codelibs/docker-fess/tree/v15.9.0/compose (查询日期 2026-10-24)
- Compose
!override语法说明:https://docs.docker.com/compose/multiple-compose-files/merge/ (查询日期 2026-10-24) - 本专栏姊妹篇:《Fess 15.9.0 介绍与选型指南》(认知篇)、《Fess 15.9.0 配置参数详解》(配置篇)、《Fess 15.9.0 查询语言详解》(查询语法篇)。