Ubuntu 22.04 源码部署 Apache Kibble 指南

在 Ubuntu 22.04 上从零源码部署 Apache Kibble 1.x 完整服务端的实操指南,涵盖 Elasticsearch 8 安装调优、Python 依赖、数据库初始化、API 服务启动与卸载清理。

Apache KibbleKibbleApache安装部署Ubuntu 22.04Python2026/10/15

Ubuntu 22.04 源码部署 Apache Kibble 指南

前提说明:Apache Kibble 已于 2025 年 10 月退役并迁入 Apache Attic,官方不再提供发行包与技术支持。本文以历史归档源码为对象,在 Ubuntu 22.04 服务器上完整实测了 1.x 服务端(kibble-1 仓库 master 分支)从零部署到 API 可用的全过程,含四处真实排障记录。文中全部命令与回显均来自实测执行结果。供学习研究使用,不建议用于生产环境。

一、部署目标与总体思路

1.1 目标

在一台 Ubuntu 22.04 服务器上完成以下部署,最终做到:

  • Elasticsearch 8.17.9 单机实例运行,堆内存固定为 512MB;
  • Kibble 1.x 数据库完成初始化(三十余个索引 + 管理员账户);
  • Kibble API 服务在 8080 端口运行,可用 HTTP 请求登录并管理组织与数据源;
  • 全程记录每一步的真实回显与遇到的问题。

1.2 部署策略:冻结环境里的复现工程

给归档项目做部署,策略与给活跃项目部署有明显不同,三条原则贯穿全文。第一是版本冻结:Elasticsearch 锁定 8.17.9(与 kibble-1 最后一次兼容修补的验证环境一致)、Python 用发行版自带 3.10、依赖包按实测版本列表安装,任何组件都追求"当时能跑"的最近似复现,而不是"今天最新"。第二是最小面暴露:Elasticsearch 只监听 127.0.0.1、关闭安全插件的认证层、API 服务绑定本机端口——归档代码不再有安全修复,缩小暴露面是补偿性的必要动作,本文环境仅限本机回环使用。第三是如实记录缺陷:部署中踩到的每一个坑都按"现象、根因、修复"三段记录在案,这些记录本身就是复现工程最有价值的产出——下一个部署者可以按图索骥。

1.3 组件与版本规划

组件版本获取方式
操作系统Ubuntu 22.04.5 LTS系统自带
Python3.10.12(系统自带)apt
Elasticsearch8.17.9(tar 包,自带 JDK)artifacts.elastic.co 官方下载
Kibble 服务端kibble-1 仓库 master 分支git clone
Kibble 采集器kibble-scanners 仓库 master 分支git clone(实战篇使用)
外部工具git、cloc、curlapt

服务端只依赖 Elasticsearch,不依赖关系型数据库;采集器需要 git 命令行与 cloc。整个系统可以单机部署。

二、系统准备

2.1 安装基础工具

系统初始状态较为干净(实测 Python 3.10.12 已内置,git 与 curl 缺失),先补齐基础工具:

apt-get update
apt-get install -y git python3-pip python3-venv curl wget cloc

验证版本:

git --version
curl --version | head -1
cloc --version

实测回显:

git version 2.34.1
curl 7.81.0 (x86_64-pc-linux-gnu) libcurl/7.81.0 OpenSSL/3.0.2 zlib/1.2.11 brotli/1.0.9 zstd/1.4.3 libidn2/2.3.2 libpsl/0.21.0 (+libidn2/2.3.4 openssl/3.0.2) libssh2/1.10.0 nghttp2/1.43.0 librtmp/2.3 OpenLDAP/2.5.18
1.90

六个包的作用与取舍:python3-pip 与 python3-venv 是搭建虚拟环境的前提(Ubuntu 把 pip 拆成独立包);cloc 是采集器 git-sloc 插件的外部依赖,属于运行期必需,提前装好;wget 为通用下载工具备用。回显中 cloc 输出 1.90 是其版本号(该工具以纯数字形式报版本)。Ubuntu 22.04 自带的 Python 3.10.12 满足 kibble-1 的要求——仓库最后一次提交明确完成了 Python 3 化改造,这是选择发行版 Python 而非另装其他版本的依据。

2.2 规划目录

本指南统一使用以下目录布局(均为真实服务器路径):

/opt/elasticsearch          Elasticsearch 安装目录
/opt/es-data                Elasticsearch 数据目录
/opt/kibble-venv            Python 虚拟环境
/opt/kibble-1               Kibble 1.x 服务端源码
/opt/kibble-scanners        Kibble 采集器源码

布局的三个原则:所有程序与数据都收在 /opt 下、互不嵌套(Elasticsearch 程序目录与数据目录分开、Kibble 服务端与采集器分开),便于按目录粒度备份与卸载;虚拟环境独立于两个源码目录,重建依赖不影响源码;采集器目录虽属实战篇内容,但路径在部署阶段就统一规划,避免两篇文档的路径约定出现分叉。

三、安装并配置 Elasticsearch 8

3.1 下载与解压

cd /opt
curl -sSL -o es.tar.gz https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.17.9-linux-x86_64.tar.gz
tar -xzf es.tar.gz
mv elasticsearch-8.17.9 elasticsearch

实测下载完成后的文件大小为 636425612 字节(约 607MB)。Elasticsearch 8 的 tar 包自带 JDK,无需单独安装 Java。 选择 tar 包而非 apt 仓库安装,延续了冻结策略:tar 包的版本永远固定在 8.17.9,而 apt 源会随 elastic 官方仓库滚动更新;同时 tar 包解压即用的形态让整个安装落在 /opt 下,卸载就是删目录。自带 JDK 是 ES 8.x 的默认形态,它绑定了 Elasticsearch 团队验证过的 JDK 构建,避免系统 Java 版本漂移引入的变量——对"不再有人修复兼容问题"的归档生态而言,每减少一个变量就少一类不可解的坑。下载后建议与官方发布的校验值比对再解压,生产化部署时这一步不可省略。

3.2 创建运行用户与数据目录

Elasticsearch 出于安全考虑拒绝以 root 运行,创建专用用户:

useradd -m -s /bin/bash esuser
mkdir -p /opt/es-data
chown -R esuser:esuser /opt/elasticsearch /opt/es-data

3.3 固定堆内存为 512MB

Elasticsearch 默认按物理内存自动分配堆,长驻服务建议显式固定。在 /opt/elasticsearch/config/jvm.options.d/ 目录下新建 heap.options 文件,写入两行:

-Xms512m
-Xmx512m

512MB 的定值来自小规模验证场景的实测:Kibble 的索引量级在数十万文档以内,ES 8 的默认自动分配(物理内存的一半)在 4GB 机器上会给到 2GB,对小数据集纯属浪费,还会挤压操作系统页缓存——ES 的查询性能同样依赖页缓存。定值配置的另一层意义是行为可预测:堆固定的服务在内存监控图上是一条水平线,异常增长一眼可辨。heap.options 放在 jvm.options.d 目录意味着它叠加在主 jvm.options 之后,ES 启动时按目录内文件名的字典序依次应用,Xms 与 Xmx 相同可避免运行期堆扩容的开销与抖动。

3.4 写入主配置

将 /opt/elasticsearch/config/elasticsearch.yml 覆盖为以下内容(单机、仅本机监听、关闭安全插件,便于本机测试):

cluster.name: kibble-cluster
node.name: kibble-node
path.data: /opt/es-data
path.logs: /opt/elasticsearch/logs
network.host: 127.0.0.1
http.port: 9200
discovery.type: single-node
xpack.security.enabled: false
xpack.security.enrollment.enabled: false

八行配置每行都有明确意图:cluster/node 名自定以便日志辨识;path.data 指到独立目录 /opt/es-data 让数据与程序分离(升级或重装程序目录不动数据);network.host 收紧到 127.0.0.1 是回环部署的核心防线,服务不对外网暴露;discovery.type: single-node 跳过集群发现(多节点部署才需要 zen/pki 发现配置);两项 xpack.security 关闭的是 ES 8 默认开启的 TLS 与认证——生产环境绝不可关,本指南环境因只绑定回环接口才安全地简化。若要在暴露网络上运行,必须保留安全插件并按官方文档配置证书与口令。

3.5 启动与验证

su esuser -c "nohup /opt/elasticsearch/bin/elasticsearch >/tmp/es.log 2>&1 &"

等待约 30 秒后访问本机 9200 端口:

curl -s http://127.0.0.1:9200/

实测回显:

{
  "name" : "kibble-node",
  "cluster_name" : "kibble-cluster",
  "cluster_uuid" : "u1Rt80iMSkmm2ciEC_12vw",
  "version" : {
    "number" : "8.17.9",
    "build_flavor" : "default",
    "build_type" : "tar",
    "build_hash" : "df00e82fe908c499d28fe75db8454b62a05f7b02",
    "build_date" : "2025-07-16T22:07:47.065021813Z",
    "build_snapshot" : false,
    "lucene_version" : "9.12.0",
    "minimum_wire_compatibility_version" : "7.17.0",
    "minimum_index_compatibility_version" : "7.0.0"
  },
  "tagline" : "You Know, for Search"
}

回显携带的信息足够完成一次启动体检:version.number 与目标版本一致,说明跑起来的确实是解压的这份;build_date 2025-07-16 是 8.17.9 的官方构建日期;minimum_wire/index_compatibility_version 两行声明它对旧集群的兼容下限,单机场景用不到但值得认识;lucene_version 9.12.0 是其底层检索引擎的版本。启动耗时在 30 秒量级(JVM 启动加索引恢复),期间 curl 会连接被拒,属正常现象;若远超一分钟仍不就绪,按 FAQ 5 的路径查日志。

3.6 确认堆内存配置生效

curl -s "http://127.0.0.1:9200/_nodes/jvm?filter_path=nodes.*.jvm.mem"

实测回显(heap_init 与 heap_max 均为 536870912 字节,即 512MB):

heap_init 与 heap_max 相等且等于 536870912(即 512 × 1024 × 1024 字节),证明 heap.options 精确生效;direct_max 268435456 是网络缓冲的直接内存上限,属默认值。节点 ID(N9csMi0xSPq1loxQeUZDmw)是 ES 首次启动随机生成的,每次部署都会不同。这个检查步骤建议纳入所有 ES 部署的标准动作——堆配置写错位置(比如写进了主 jvm.options 的错误段落)是常见失误,用 API 验证比翻日志找启动参数可靠得多。

四、获取 Kibble 源码

4.1 克隆服务端仓库

cd /opt
git clone https://github.com/apache/kibble-1.git

克隆后确认版本信息:

cd kibble-1
git log -1 --format="%H %ad %s"

实测回显(最后一条提交正是 Elasticsearch 8 兼容修补,这是能配合 ES 8 运行的关键):

这条提交信息本身就是一份重要的部署情报:它声明了三件事——修复了 handler.py 的会话 PUT 处理、完成了 Elasticsearch v8 兼容、完成了 Python 3 化。换句话说,克隆该仓库的最新 master 得到的代码,已经是社区针对"现代环境"打磨过的最后一个状态,本文的部署建立在这个前提上;若检出更早的历史版本,将面临 ES 客户端协议不兼容与 Python 2 残留的双重问题。固定到这个提交(3920681)是可复现部署的推荐做法。

4.2 目录结构速览

/opt/kibble-1/
├── api/            API 服务(WSGI 应用 handler.py、页面模块 pages/、配置 yaml/)
├── setup/          数据库初始化脚本 setup.py、账户工具 makeaccount.py、索引映射 mappings.json
├── docs/           Sphinx 文档源码
└── ui/             Vue 前端

4.3 创建 Python 虚拟环境并安装依赖

python3 -m venv /opt/kibble-venv
/opt/kibble-venv/bin/pip install elasticsearch==8.17.2 bcrypt pyyaml python-dateutil ndicts requests psutil certifi

安装完成后核对关键包版本:

/opt/kibble-venv/bin/pip list | grep -iE "elasticsearch|bcrypt|pyyaml|ndicts"

实测回显:

bcrypt             5.0.0
elasticsearch      8.17.2
ndicts             0.3.0
PyYAML             6.0.3

依赖清单的来源是 api/handler.py 与 pages/ 各模块的 import 语句,逐个核对后安装,版本选择的原则是"与 ES 8.17 服务端配套的客户端主版本 + 其他包的最新可用版"。elasticsearch 8.17.2 与服务端 8.17.9 的兼容性在第五节的初始化与第七节的采集全流程中得到验证;bcrypt 5.0.0 是引发实录一缺陷的版本(行为与旧版不同);ndicts 是 handler.py 用于嵌套字典访问的小众工具库,报缺失时按同名单独安装即可。虚拟环境隔离了这套依赖与系统 Python 的相互影响,卸载时删除 /opt/kibble-venv 即净。

五、初始化 Kibble 数据库

5.1 运行初始化脚本

setup 目录下的 setup.py 支持非交互参数(原本为交互式设计,参数化便于脚本化部署):

cd /opt/kibble-1/setup
/opt/kibble-venv/bin/python setup.py -e localhost -p 9200 -d kibble -s 1 -r 0 -m localhost:25 -a -k

参数含义:-e 指定 Elasticsearch 主机,-p 指定端口,-d 指定索引前缀,-s 指定分片数,-r 指定副本数,-m 指定邮件服务器(本地无邮件服务,填 localhost:25 占位即可),-a 表示自动生成管理员账户(admin@kibble / kibbleAdmin),-k 表示索引已存在时跳过创建。 几个参数的取值逻辑值得交代:-d kibble 决定了全部索引的前缀(kibble_code_commit 等),也决定了后续清理时按 kibble_* 通配删除的边界;-s 1 -r 0 是单机部署的标准选择——单分片无副本,避免副本分片因无第二节点可分配而呈现黄色健康状态;-a 自动建账户省去单独运行 makeaccount.py 的步骤,默认口令 kibbleAdmin 仅限本机验证使用。参数化执行是本次实测对原脚本做的唯一调用方式调整(原设计为交互问答),脚本本体未改,这与只修改必要之处的原则一致。

5.2 排障实录一:bcrypt 序列化失败

首次执行,索引全部创建成功,但在创建管理员账户时失败,真实报错如下:

Creating administrator account
Index creation failed: Unable to serialize to JSON: {'email': 'admin@kibble', 'password': b'$2b$12$p/DypHrz0.5GQkYRkSLuG.cqholkrhUKN8W7dxXT5Lf1QRLxVV.Tu', 'displayName': 'Administrator', 'organisations': [], 'ownerships': [], 'defaultOrganisation': None, 'verified': True, 'userlevel': 'admin'} (type: dict)

原因:新版 bcrypt 库的 hashpw() 返回 bytes 对象,而 elasticsearch 8 客户端的 JSON 序列化器不接受 bytes。查看 setup.py 第 263 行:

pwd = bcrypt.hashpw(adminPass.encode('utf-8'), salt)

修复:把哈希结果显式解码为字符串:

pwd = bcrypt.hashpw(adminPass.encode("utf-8"), salt).decode("ascii")

重跑初始化(-k 参数保证已建索引不会冲突):

/opt/kibble-venv/bin/python setup.py -e localhost -p 9200 -d kibble -s 1 -r 0 -m localhost:25 -a -k

实测回显:

Creating administrator account
Account created!
Writing Kibble config (../api/yaml/kibble.yaml)
All done, Kibble should...work now :)

脚本同时自动生成了服务端配置文件 /opt/kibble-1/api/yaml/kibble.yaml。初始化共创建三十余个索引(kibble_api、kibble_code_commit、kibble_organisation、kibble_source、kibble_useraccount 等),可用以下命令抽查: 回看这个缺陷的因果链很有代表性:bcrypt 4.x 时代的 hashpw 已经返回 bytes,但 1.x 代码写作时依赖的旧版行为或中间封装把它变成了 str,elasticsearch 8 客户端的序列化器则严格执行"只接受字符串"——两端的假设在归档状态下各自成立,组合起来即爆。报错文本把完整的待写入文档连同类型标注一并打印(type: dict),这种"把现场全盘托出"的报错风格让定位几乎不需要猜测。修复选用 .decode("ascii") 而非 str():bcrypt 哈希字符集严格限于 bcrypt 字母表,ascii 解码语义最精确。

curl -s "http://127.0.0.1:9200/_cat/indices/kibble_*?h=index,docs.count" | head -8

六、启动 API 服务

6.1 排障实录二:API 服务没有自带启动入口

kibble-1 的 api/handler.py 是标准 WSGI 应用(暴露 application 对象),但没有附带独立的 HTTP 服务器启动脚本(其 main 分支仅打印 OpenAPI 文档 HTML)。最简单的办法是用 Python 标准库 wsgiref 包一层。在 /opt/kibble-1/api/ 下创建 serve.py:

#!/usr/bin/env python3
# Minimal WSGI launcher for the Kibble API service.
from wsgiref.simple_server import make_server
from handler import application

if __name__ == "__main__":
    httpd = make_server("0.0.0.0", 8080, application)
    print("Kibble API listening on port 8080")
    httpd.serve_forever()

6.2 启动与探活

注意必须在 /opt/kibble-1/api 目录下启动,因为 handler.py 以相对路径读取 yaml/kibble.yaml:

cd /opt/kibble-1/api
setsid nohup /opt/kibble-venv/bin/python serve.py > /tmp/kibble-api.log 2>&1 < /dev/null &

探活:请求一个不存在的端点,应返回 JSON 格式的 404:

curl -s http://127.0.0.1:8080/api/foo

实测回显:

{
    "code": 404,
    "reason": "API endpoint not found"
}

探活设计成"期待 404"是一次反向验证:它同时证明了三件事——WSGI 服务进程存活、handler.py 的路由分发在工作(能区分存在与不存在的端点)、错误响应遵循 JSON 格式(服务端的消息规范统一)。比探活根路径更好的原因是:Kibble API 没有 /health 之类的健康端点,根路径会返回 OpenAPI 文档 HTML,解析起来反而不如一个固定的 JSON 404 干脆。把这条 curl 写进部署验证清单(第七节)的同一行,任何环境重放都能得到同样的判据。

6.3 排障实录三:登录报 Wrong username or password

尝试登录管理员账户:

curl -s -X PUT http://127.0.0.1:8080/api/session/login \
  -H "Content-Type: application/json" \
  -d '{"email":"admin@kibble","password":"kibbleAdmin"}'

实测报错:

{
    "code": 403,
    "reason": "Wrong username or password supplied!"
}

分析 api/pages/session.py 第 120 至 128 行:登录逻辑先用 bcrypt 对输入密码做哈希,再与库中存储的哈希比对:

hp = doc['_source']['password']
x = bcrypt.hashpw(p.encode('utf-8'), hp.encode('utf-8'))
if x == hp:

x 是 bytes 而 hp 是 str,二者永远不相等,属于归档代码中的类型缺陷(与排障实录一同源)。修复为:

if x.decode("ascii") == hp:

重启 API 服务后重试登录:

{"message": "Logged in OK!"}

这次修复没有动存储层或密码体系,只是把比较两端统一成 str——与实录一相同的技术根源(bytes/str 混用)在另一处代码再次显形。两处缺陷的组合症状很有迷惑性:setup.py 修好了(账户能建),session.py 没修(登录永远 403),从用户视角看是"账户创建功能正常但密码错误",极易误判为密码问题反复重置。排查的突破口在于直接读数据库:用 curl 查 kibble_useraccount 索引确认哈希存在且格式正常,说明写入侧没问题,问题必然在比对侧。这类"分属两个文件的同源缺陷"在长生命周期代码库中相当常见,修复时应全文检索同模式代码一次清干净。

### 6.4 排障实录四:Cookie 作用域导致后续请求提示未登录

登录成功后立刻创建组织却收到 403。用 curl 的 cookie jar 观察,Set-Cookie 的 Path 是 /api/session/,即客户端只会在访问 /api/session/ 路径时回传该 Cookie,访问 /api/org/list 等其它端点时不会携带。实测确认服务端会话确实写入了 kibble_uisession 索引,问题只在客户端 Cookie 回传范围。解决办法是在请求头中显式携带 Cookie:

curl -s -H "Cookie: kibble_session=9eb22542-896d-4df1-9873-005b98afe824"
-X PUT http://127.0.0.1:8080/api/org/list
-H "Content-Type: application/json"
-d '{"name":"Demo Org","desc":"Local demo organisation","id":"demoorg"}'


实测回显:

{"okay": true, "message": "Organisation created!"}


后续再把该组织设为当前用户的默认组织(PATCH /api/account 必须带 email 字段):

curl -s -H "Cookie: kibble_session=9eb22542-896d-4df1-9873-005b98afe824"
-X PATCH http://127.0.0.1:8080/api/account
-H "Content-Type: application/json"
-d '{"email":"admin@kibble","defaultOrganisation":"demoorg"}'


实测回显:

{"message": "Account updated!"}


至此,服务端部署完成。数据源登记与数据采集见本系列实战篇。
实录四的修复选择的是"客户端配合"而非"服务端改 Path":把 Cookie 的 Path 改为 /api/ 是一行代码的修改,且更符合 Web 应用的常规语义。两种解法各有立场——改服务端是修正规范符合性,带 Cookie 头是绕开历史行为保持代码零改动。本指南在 API 操作篇选择了后者(请求头显式携带),因为文档目标是验证既有行为并给后续章节提供稳定可复现的调用方式;若读者计划长期本地使用,建议采用服务端修改,一次修复永久生效。无论哪种方式,理解 Set-Cookie 的 Path 属性如何约束浏览器与 curl 的自动回传行为,才是这个坑留下的真正知识。


## 七、部署验证清单

| 检查项 | 命令 | 预期 |
| ---- | ---- | ---- |
| Elasticsearch 存活 | curl -s http://127.0.0.1:9200/ | version.number = 8.17.9 |
| 堆内存 512MB | curl -s http://127.0.0.1:9200/_nodes/jvm | heap_max_in_bytes = 536870912 |
| 索引就绪 | curl -s "http://127.0.0.1:9200/_cat/indices/kibble_*?h=index" | 列出三十余个索引 |
| 管理员账户存在 | curl -s http://127.0.0.1:9200/kibble_useraccount/_doc/admin@kibble | _source.userlevel = admin |
| API 服务存活 | curl -s http://127.0.0.1:8080/api/foo | JSON 404 |
| 登录 | PUT /api/session/login | {"message": "Logged in OK!"} |

## 八、日常运维

### 8.1 启停顺序

停止:先停 API 服务(结束 serve.py 进程),再停 Elasticsearch。Elasticsearch 标准停止方式是向进程发送 SIGTERM:

pkill -u esuser -f elasticsearch


启动顺序相反:先起 Elasticsearch(等待 9200 就绪),再起 API 服务。若机器重启,两进程都不会自动拉起(本文未配置 systemd 单元),可按上述顺序手动执行。
对需要开机自启的部署,建议为两个服务各写一个 systemd 单元:Elasticsearch 单元的 ExecStart 指向 /opt/elasticsearch/bin/elasticsearch,User=esuser,Environment 提供堆配置文件已就位的前提;API 服务单元的 WorkingDirectory 必须设为 /opt/kibble-1/api(实录二与 FAQ 6 的相对路径约束在 systemd 下同样成立),ExecStart 调用虚拟环境内的 python。两个单元配置 Restart=on-failure 与依赖关系(API 单元 After=elasticsearch.service),即可获得与原生服务一致的运维体验。本文出于保持实测环境最小化未落盘单元文件,配置要点如上,读者按需取用。


### 8.2 日志位置

- Elasticsearch 日志:/opt/elasticsearch/logs/kibble-cluster.log(由 elasticsearch.yml 的 path.logs 决定)
- API 服务日志:/tmp/kibble-api.log(由启动命令重定向决定)
- 采集器输出:前台打印,重定向文件自行指定

## 九、卸载方式

按依赖逆序清理,以下命令可完整移除本指南部署的全部内容:

1. 停止 API 服务

pkill -f "python serve.py"

2. 停止 Elasticsearch

pkill -u esuser -f elasticsearch

3. 删除程序与数据目录

rm -rf /opt/elasticsearch /opt/es-data /opt/es.tar.gz rm -rf /opt/kibble-1 /opt/kibble-scanners rm -rf /opt/kibble-venv rm -rf /tmp/demoorg

4. 删除运行用户(可选)

userdel -r esuser


执行后系统回到部署前状态。若只是重置 Kibble 数据而保留 Elasticsearch,删除全部 kibble_ 前缀索引即可:

curl -s -X DELETE "http://127.0.0.1:9200/kibble_*"

按 kibble_* 通配删除是索引前缀设计的直接红利:一条命令回到"空白库"状态,随后重跑第五节的初始化(含 -k 改为首次全量创建)即可重新部署数据层。这个重置动作在反复实验时极其常用——每次采集策略调整后都值得从干净状态重来,避免上一轮的残留文档干扰对比。删除是即时生效的物理删除,执行前确认前缀拼写无误。


## 十、常见问题(FAQ)

**1. setup.py 报 Unable to serialize to JSON 且 password 是 b 开头的字符串怎么办?**
即排障实录一:新版 bcrypt 返回 bytes。将 setup.py 中 pwd = bcrypt.hashpw(adminPass.encode('utf-8'), salt) 改为末尾追加 .decode("ascii") 后重跑(加 -k 参数跳过已建索引)。

**2. 登录一直提示 Wrong username or password?**
即排障实录三:session.py 中 bcrypt 结果(bytes)与库中哈希(str)直接比较恒为假。把 if x == hp: 改为 if x.decode("ascii") == hp: 并重启 API 服务。注意 setup.py 修复过而 session.py 未修复时,就会出现账户创建成功但登录失败的现象。

**3. 明明登录成功,创建组织却提示未登录?**
即排障实录四:会话 Cookie 的 Path 被服务端设置为 /api/session/,请求其它端点时客户端不会自动回传。解决方式是显式携带请求头 Cookie: kibble_session=<会话ID>。

**4. 启动 Elasticsearch 报 can not run elasticsearch as root?**
必须用非 root 用户运行。按 3.2 节创建 esuser 并 chown 目录,用 su esuser -c 方式启动。

**5. 启动后 curl 9200 连接被拒?**
常见原因是 JVM 还在启动或启动失败。先查看 /tmp/es.log;若报 low disk watermark,说明磁盘使用率超过 90%,需要清理磁盘;若报 max virtual memory areas vm.max_map_count 太小,执行 sysctl -w vm.max_map_count=262144 并写入 /etc/sysctl.conf。

**6. API 启动报 FileNotFoundError: yaml/kibble.yaml?**
handler.py 用相对路径加载配置,必须先进入 /opt/kibble-1/api 目录再启动 serve.py;或者修改 handler.py 用绝对路径加载。

**7. 可以部署成多节点 Elasticsearch 集群吗?**
可以,但 Kibble 只是普通客户端,集群化是 Elasticsearch 层面的事。单机验证时用 discovery.type: single-node 最简单;生产级集群需去掉该配置并配置集群发现,同时把 setup.py 的分片/副本数按集群规模调整。

**8. 为什么用 wsgiref 而不是 gunicorn?**
wsgiref 是标准库,零额外依赖,适合功能验证与小规模使用;并发性能有限。若要承载更大流量,pip install gunicorn 后执行 gunicorn -b 0.0.0.0:8080 handler:application 即可,handler 模块无需改动。

**9. 邮件服务器参数 -m localhost:25 填错了有影响吗?**
仅在开启邮箱验证注册或找回密码功能时才会真正发信。本地验证环境在 kibble.yaml 中将 accounts.verifyEmail 设为 false,该参数无实际作用。

**10. Ubuntu 22.04 自带 Python 3.10 兼容吗?**
兼容。kibble-1 最后一次提交明确标注 python 3 based,实测在 Python 3.10.12 上全部依赖安装正常、服务运行正常,无需额外安装其它 Python 版本。

**11. elasticsearch Python 客户端为什么装 8.17.2 而服务端是 8.17.9?**
客户端与服务端的小版本不必严格对齐,只要处于同一主版本的兼容矩阵内即可。8.17.2 是实测时 pip 解析到的稳定版本,与 8.17.9 服务端的 REST API 完全兼容(实测全部索引与文档操作正常)。追求绝对对齐可以 pip install elasticsearch==8.17.9,行为等价。

**12. 能跳过排障实录中的代码修改吗?**
不能。四处修改对应的缺陷都是"必然触发"型:bcrypt 序列化在创建账户时必触发,会话比对在登录时必触发,配置加载在启动时必触发。归档代码不会自己修好,跳过修改的部署在对应环节必然失败。反过来,全部四处修改都只是单行级别,总改动量极小,按实录逐条执行即可。

## 十一、参考链接

- Apache Attic - Kibble(退役说明与时间线):https://attic.apache.org/projects/kibble.html
- kibble-1 服务端仓库:https://github.com/apache/kibble-1
- kibble-scanners 采集器仓库:https://github.com/apache/kibble-scanners
- Elasticsearch 8.17 官方下载页:https://www.elastic.co/downloads/past-releases/elasticsearch-8-17-9
- Elasticsearch JVM 堆配置文档:https://www.elastic.co/guide/en/elasticsearch/reference/8.17/advanced-configuration.html
- Python wsgiref 标准库文档:https://docs.python.org/3.10/library/wsgiref.html
- bcrypt PyPI 页面:https://pypi.org/project/bcrypt/
- elasticsearch-py 8.x 文档:https://www.elastic.co/guide/en/elasticsearch/client/python-api/8.17/index.html