从 0 到 1 WeKnora部署指南与深度排障

前言

在构建集成大模型与多智能体的企业级知识库系统(如 WeKnora)时,私有化部署是保障数据安全的必经之路。然而,面对包含 PostgreSQL(集成 pgvector 插件)、Qdrant 向量库、Neo4j 图数据库等庞大组件的微服务集群,环境基建往往是最容易翻车的一环。

本文将分为两大板块:**【标准部署篇】确保你能跟着步骤一次性跑通全流程;【深度排障篇】**则为你剖析那些极具隐蔽性的底层架构陷阱。


第一篇:从 0 到 1 标准部署指南

不要轻视基础环境的搭建。微服务启动失败,90% 源于宿主机指令集不支持、系统预装的 Docker 版本过旧或依赖冲突。

⚠️ 环境要求 :请准备一台纯净的 Ubuntu 24.04/22.04 服务器。如果是虚拟机,请务必在虚拟化面板中将 CPU 模式设置为 Host(直通模式),以透传 AVX2 指令集,否则后续的向量数据库将无法运行。

Step 0: 拉取源码或者下载官方源码zip/tar包

shell 复制代码
# 方式一:使用 Git 克隆(推荐)
git clone git@github.com:Tencent/WeKnora.git

# 方式二:使用 curl 下载压缩包 并制定下载后的文件名称
curl -o WeKnora-v0.6.2.tar.gz https://github.com/Tencent/WeKnora/archive/refs/tags/v0.6.2.tar.gz

# 解压下载的压缩包(根据实际文件名选择)
tar -zxvf WeKnora-v0.6.2.tar.gz

Step 1: 彻底清理旧版环境

不要信任系统自带的包管理器(如 snap),先执行外科手术式的清理,为新环境腾出干净的空间:

bash 复制代码
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove -y $pkg; done

Step 2: 安装官方 Docker 引擎

配置官方 GPG 密钥与源,确保获取的是最新且最纯正的 Docker Engine 与 Compose V2 插件:

bash 复制代码
# 1. 安装基础依赖
sudo apt update
sudo apt install -y ca-certificates curl jq

# 2. 注入官方 GPG 密钥
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL [https://download.docker.com/linux/ubuntu/gpg](https://download.docker.com/linux/ubuntu/gpg) -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# 3. 添加 Docker 稳定版仓库
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] [https://download.docker.com/linux/ubuntu](https://download.docker.com/linux/ubuntu) \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 4. 安装 Docker 引擎全家桶
sudo apt  update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Step 3: 前置规避引擎冲突与修改配置

为了防止 Docker 29.x 版本引入的新特性(Private Time Namespace)与 Ubuntu 内核产生冲突,以及确保微服务依赖启动顺畅,我们需要提前调整两处配置。

1. 关闭 Docker 时间命名空间特性:

bash 复制代码
sudo mkdir -p /etc/docker
# 如果 daemon.json 存在则安全合并,不存在则创建
if [ -f /etc/docker/daemon.json ]; then
  sudo jq '.features += {"time-namespaces": false}' /etc/docker/daemon.json > /tmp/daemon.json && sudo mv /tmp/daemon.json /etc/docker/daemon.json
else
  echo '{"features": {"time-namespaces": false}}' | sudo tee /etc/docker/daemon.json
fi
sudo systemctl restart docker

2. 优化微服务自愈能力(修改 Compose 文件):

进入 WeKnora 项目目录,打开 docker-compose.yml,找到负责数据库初始化的 langfuse-db-init 服务,为其添加失败重启策略,然后注释原有的 #restart: "no" 配置 以规避数据库启动慢导致的时序崩溃。

yaml 复制代码
  langfuse-db-init:
    image: paradedb/paradedb:v0.22.2-pg17
    container_name: WeKnora-langfuse-db-init
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      PGPASSWORD: ${DB_PASSWORD}
    restart: on-failure            # 遇到非零退出码时自动重启
    deploy:
      restart_policy:
        condition: on-failure      # 仅在失败时重启(成功 exit 0 则不重启)
        delay: 5s                  # 每次重试前等待 5 秒,避开 DB 的重启空窗期
        max_attempts: 10  
    # ${LANGFUSE_DB_NAME:-langfuse} / ${DB_USER} 由 compose 解析成字面量后再传给 shell;
    # 脚本中需要 shell 自己展开的变量(无)一律用 $$ 转义。
    entrypoint: ["sh", "-c"]
    command:
      - |
        set -e
        echo "[langfuse-db-init] ensuring database '${LANGFUSE_DB_NAME:-langfuse}' exists in WeKnora-postgres..."
        # 先刷新现有库的 collation(镜像 ICU 2.36 与宿主 2.41 不匹配时必须做),否则 CREATE DATABASE 会失败
        psql -h postgres -U ${DB_USER} -d postgres -v ON_ERROR_STOP=0 -c "ALTER DATABASE template1 REFRESH COLLATION VERSION;" >/dev/null 2>&1 || true
        psql -h postgres -U ${DB_USER} -d postgres -v ON_ERROR_STOP=0 -c "ALTER DATABASE postgres REFRESH COLLATION VERSION;" >/dev/null 2>&1 || true
        # 幂等创建:已存在则跳过;不存在则从 template0 克隆(template0 永远不会有 collation 漂移)
        if psql -h postgres -U ${DB_USER} -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname='${LANGFUSE_DB_NAME:-langfuse}'" | grep -q 1; then
          echo "[langfuse-db-init] database '${LANGFUSE_DB_NAME:-langfuse}' already exists, skipping."
        else
          psql -h postgres -U ${DB_USER} -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE \"${LANGFUSE_DB_NAME:-langfuse}\" TEMPLATE template0;"
          echo "[langfuse-db-init] database '${LANGFUSE_DB_NAME:-langfuse}' created."
        fi
        echo "[langfuse-db-init] done."
    networks:
      - WeKnora-network
    #restart: "no"
    profiles:
      - langfuse
      - full

Step 4: 配置

shell 复制代码
# 基本示例配置 复制一份配置文件
cp .env.example .env

.env 文件中,修改自定义配置,如修改前端端口 FRONTEND_PORT=8082)等

Step 5: 一键拉起微服务集群

启动集群:

bash 复制代码
# 全量拉起
docker compose --profile full up -d

# 验收服务拓扑与运行状态
docker compose --profile full ps

当所有容器状态显示 UpUp (healthy),并在 PORTS 列看到端口映射时,即可通过浏览器访问你的智能体工作台。


第二篇:深度排障与架构陷阱复盘

如果你在过去的部署中,遇到过容器不断重启、部分服务"离奇失踪"等问题,极大可能是踩中了以下三大架构级陷阱。以下是我的完整排障实录。

陷阱一:CPU 指令集阉割引发 Illegal instruction

异常现场

WeKnora-postgres 容器启动即崩溃,查看日志仅有短短几行,退出码为致命的 132

running bootstrap script ... ok

Illegal instruction (core dumped)

child process exited with exit code 132

深度剖析

退出码 132 (SIGILL) 代表 CPU 无法识别程序发出的指令。

现代 AI 业务对向量计算性能要求极高,带有向量插件的数据库(如 paradedbpgvector)在编译时,会强制要求宿主机 CPU 支持 AVX2 甚至 FMA 硬件指令集。

当我们的业务运行在虚拟机(KVM/Proxmox等)上时,如果底层的虚拟 CPU 模型被设置成了通用的 kvm64,虚拟化层就会无情地阉割掉物理机的 AVX2 指令集,导致容器引擎直接崩溃。

检测是否支持avx2指令集

shell 复制代码
grep -q avx2 /proc/cpuinfo && echo "AVX2 supported" || echo "AVX2 not supported"

破局之道

如果是虚拟机 :必须从 Hypervisor 层解决。进入虚拟化管理后台,将该虚拟机的 CPU 类型修改为 Host(直通模式) ,让其直接调用物理硬件指令集。注意:必须彻底关机后重开 方能生效。

如果还是不行建议更换支持avx2的机器

如果是物理机器: 目前看来要不降级 postgres 版本 要不更换支持avx2的机器(推荐)

另外可关注已提交的官方问题

https://github.com/Tencent/WeKnora/issues/271


陷阱二:引擎背刺 ------ Docker 29 time 命名空间冲突

异常现场

在纯正的 Ubuntu 物理节点上全新安装 Docker 后,docker compose up 遭到 Daemon 秒拒:

Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: namespace {"time" ""} does not exist

深度剖析

这是 Docker v29.x 引入的 Private Time Namespace(私有时间命名空间) 惹的祸。该特性本意是隔离容器时间,但部分 Linux 内核与底层 runc 的安全策略尚未完美兼容。当容器向内核索要 time 命名空间时遭拒,导致整个创建流程阻断。

破局之道

既然超前特性引发了兼容性灾难,最快的方法就是主动降级,显式禁用该特性。即在 /etc/docker/daemon.json 中配置 "features": { "time-namespaces": false } 并重启引擎。(详见第一篇 Step 3)


陷阱三:容器编排的"假健康"与依赖熔断

异常现场

底层组件均已就绪(Postgres 显示 Healthy),但负责初始化的容器 langfuse-db-init 却报错 Connection refused。更致命的是,排在它后面的前端 UI 和 Web 管理容器根本不启动

深度剖析

这是微服务编排中最经典的 Race Condition(时序错位)

Postgres 首次挂载空卷启动时,会先拉起一个临时实例建表,随后关闭临时实例 ,最后才拉起正式实例。

Docker 的健康探针常常在"临时实例"存活期间就被骗过了,打上了 Healthy 标记,导致 db-init 脚本过早入场,一头撞死在 Postgres 的关机空窗期。

而 Docker Compose 存在严格的依赖链保护机制:一旦初始化节点(db-init)失败,所有依赖它的前端服务全部遭遇依赖熔断,被引擎静默抛弃。

破局之道

不要试图用 sleep 脚本去对抗不确定的 I/O 延迟。用架构的思维解决:为该一次性容器赋予容错自愈能力

通过在 Compose 文件中为初始化容器配置 restart: on-failure 策略(详见第一篇 Step 3)。即使它不幸撞上空窗期失败,也会在退避 5 秒后自动重试。只要最终执行成功(exit 0),原本被熔断的前端链路将如多米诺骨牌般自动拉起。

结语

在落地私有化 AI 基础设施时,排障视野必须向下穿透:敬畏硬件算力边界(AVX2),警惕运行时引擎特性(Namespace),驾驭服务启动时序(状态机容错)。用架构思维打好地基,才能让上层的智能体跑得更加稳健。


good day!!!