Keep Docker Compose 部署指南

用 Docker Compose 在 Ubuntu 22.04 服务器上部署 Keep(keephq/keep)0.54.3 三服务栈(API 后端 + Web 控制台 + 实时推送):镜像选型、持久化、环境变量表、等价 docker run 与 4 则实测排障实录。

KeepDocker监控告警2026/10/23

Keep Docker Compose 部署指南

目标:在一台 Ubuntu 22.04 服务器上,用 Docker Compose 把 Keep(keephq/keep,开源 AIOps 告警管理平台)的完整服务栈(API 后端 + Web 控制台 + 实时推送)跑起来,并完成告警注入验证。本文全部命令与回显出自 0.54.3 版本在真实服务器上的逐条实测(2026-10-23),含 4 个实际踩坑的排障实录。项目背景与选型对比见 《Keep 介绍与选型指南》。

一、环境信息

项目值说明
操作系统Ubuntu 22.04(64 位)实测服务器
Docker29.8.1(build 4a63305)docker --version 实测
Docker Composev5.5.1(v2 插件形态)docker compose version 实测
Keep 版本v0.54.3(2026-09-09 发布的最新稳定版)实测拉取的 latest 镜像即该版本
实测端口前端 8150 / 后端 API 8148 / 实时推送 8146因服务器上 3000/8080/6001 及部分备选端口已被占用,统一映射到高位端口
实测网络keep-0543-net(10.89.71.0/24)显式指定子网,避免与同机其他 Compose 栈的地址池冲突
实验产物目录/opt/data/quzuo/tmp/keep-alerts/compose 文件与持久化目录均约束在该目录内

二、镜像选型说明

Keep 官方没有发布到 Docker Hub,而是托管在 Google Artifact Registry(us-central1-docker.pkg.dev/keephq/keep/),实时推送组件直接复用 Soketi 官方镜像。0.54.3 实测用到的镜像如下(docker images 实测大小):

镜像角色实测大小
us-central1-docker.pkg.dev/keephq/keep/keep-api:latest后端 API(FastAPI),告警摄入/workflow 引擎/REST 服务1.26 GB
us-central1-docker.pkg.dev/keephq/keep/keep-ui:latest前端控制台(Next.js),内置 entrypoint 启动脚本952 MB
quay.io/soketi/soketi:1.4-16-debian实时推送服务器(Pusher 协议兼容)1.59 GB
us-central1-docker.pkg.dev/keephq/keep/keep-cli:latestCLI 工具镜像(可选,日常用 docker run 临时调用)1.48 GB

选型要点:

  1. 官方 Compose 是「两文件 + extends」结构:GitHub 仓库根目录的 docker-compose.yml 通过 extends 复用 docker-compose.common.yml 里的公共定义。extends 在依赖镜像内 entrypoint 传参时容易踩细节,本文将其摊平为单文件写法,行为等价、更适合直接照抄到自己的服务器(实测即按单文件部署验证)。
  2. 镜像 tag 用 latest 还是固定版本:latest 即当时的最新稳定版(实测为 v0.54.3)。Keep 处于 0.x 快速迭代期,生产环境建议部署后记录当前镜像摘要(docker images --digests | grep keep),后续升级时按摘要比对,避免盲目跟随 latest。
  3. 不要用 Docker Hub 上的第三方 keep 镜像:Docker Hub 存在同名/近似镜像,与 keephq 官方制品无关。

三、前置条件

  1. Docker 与 Compose v2 插件:docker compose version 能正常输出版本号(本文 v5.5.1)。
  2. 端口规划:三个服务各需一个宿主端口。默认为前端 3000、后端 8080、推送 6001;若被占用改高位端口即可(实测用 8150/8148/8146)。检查端口是否空闲:ss -ltnp | grep -E ':(8150|8148|8146)\b'(无输出即空闲)。
  3. 网络子网规划:同机跑多个 Compose 栈时,建议显式指定互不重叠的子网(实测取 10.89.71.0/24)。查看已占用网段:docker network ls --format '{{.Name}}' | xargs -n1 docker network inspect --format '{{.Name}} {{range .IPAM.Config}}{{.Subnet}}{{end}}' 2>/dev/null。
  4. 磁盘空间:三个镜像解压后约 3.8 GB(实测 docker images 合计),外加 SQLite 数据与日志预留 2 GB 以上。
  5. 出口网络:镜像仓库在 us-central1-docker.pkg.dev(Google Artifact Registry)与 quay.io,服务器需能访问这两个仓库;拉取缓慢时为 Docker 配置代理(/etc/systemd/system/docker.service.d/http-proxy.conf)再 systemctl daemon-reload && systemctl restart docker。
  6. 不需要提前安装数据库:默认 SQLite 零依赖;MySQL/PostgreSQL 可后续按需切换(见第七节)。

四、部署步骤

4.1 目录结构

mkdir -p /opt/data/quzuo/tmp/keep-alerts/stack/state/workflows
cd /opt/data/quzuo/tmp/keep-alerts/stack

最终结构:

stack/
├── docker-compose.yml
└── state/                  # 持久化目录(挂载到容器 /state)
    └── workflows/          # 预置 workflow 目录(KEEP_WORKFLOWS_DIRECTORY 指向)

4.2 编写 docker-compose.yml

name: keep-0543-stack

services:
  keep-frontend:
    image: us-central1-docker.pkg.dev/keephq/keep/keep-ui
    container_name: keep-0543-frontend
    ports:
      - "8150:3000"
    environment:
      - AUTH_TYPE=NO_AUTH
      - API_URL=http://keep-backend:8080
      - NEXTAUTH_SECRET=secret
      - NEXTAUTH_URL=http://localhost:8150
      - NEXT_PUBLIC_API_URL=http://localhost:8148
      - PUSHER_HOST=localhost
      - PUSHER_PORT=8146
      - PUSHER_APP_KEY=keepappkey
    volumes:
      - ./state:/state
    depends_on:
      - keep-backend
    networks:
      - keep-net

  keep-backend:
    image: us-central1-docker.pkg.dev/keephq/keep/keep-api
    container_name: keep-0543-backend
    ports:
      - "8148:8080"
    environment:
      - PORT=8080
      - AUTH_TYPE=NO_AUTH
      - KEEP_DEFAULT_API_KEYS=cli-demo:admin:q1w2e3r4-keep-demo-key
      - KEEP_JWT_SECRET=keep-demo-jwt-secret
      - KEEP_PROVIDERS={"console-yaml-prov":{"type":"console","authentication":{}},"bash-local":{"type":"bash","authentication":{}}}
      - KEEP_WORKFLOWS_DIRECTORY=/state/workflows
      - SECRET_MANAGER_TYPE=FILE
      - SECRET_MANAGER_DIRECTORY=/state
      - DATABASE_CONNECTION_STRING=sqlite:////state/db.sqlite3?check_same_thread=False
      - OPENAI_API_KEY=$OPENAI_API_KEY
      - PROMETHEUS_MULTIPROC_DIR=/tmp/prometheus
      - KEEP_METRICS=true
      - PUSHER_APP_ID=1
      - PUSHER_APP_KEY=keepappkey
      - PUSHER_APP_SECRET=keepappsecret
      - PUSHER_HOST=keep-websocket-server
      - PUSHER_PORT=6001
      - USE_NGROK=false
    volumes:
      - ./state:/state
    networks:
      - keep-net

  keep-websocket-server:
    image: quay.io/soketi/soketi:1.4-16-debian
    container_name: keep-0543-websocket
    ports:
      - "8146:6001"
    environment:
      - SOKETI_USER_AUTHENTICATION_TIMEOUT=3000
      - SOKETI_DEBUG=1
      - SOKETI_DEFAULT_APP_ID=1
      - SOKETI_DEFAULT_APP_KEY=keepappkey
      - SOKETI_DEFAULT_APP_SECRET=keepappsecret
    networks:
      - keep-net

networks:
  keep-net:
    name: keep-0543-net
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.71.0/24

三个环境变量按你的实际取值修改:

  • KEEP_DEFAULT_API_KEYS=cli-demo:admin:q1w2e3r4-keep-demo-key:启动时预置一个 API Key,格式为 名称:角色:密钥。这是实测中打通告警注入的关键(见第 11.3 节排障实录)。
  • NEXTAUTH_URL / NEXT_PUBLIC_API_URL / PUSHER_PORT:前端侧的三个对外地址,要与宿主端口映射一致(这里分别是 8150、8148、8146)。
  • PUSHER_APP_KEY/APP_SECRET 与 SOKETI 的三个 SOKETI_DEFAULT_APP_* 必须成对一致,否则前端实时推送连不上。

4.3 修正持久化目录属主(关键步骤)

官方 keep-api 镜像以非 root 用户 keep(uid/gid 1000)运行,而 bind mount 出来的 state/ 默认归 root 所有。先执行属主修正再启动,否则后端起不来(实测踩坑,见第 11.1 节):

chown -R 1000:1000 state

4.4 拉起服务栈

docker compose up -d

实测回显(首次部署在此前还有三个镜像的分层拉取进度,此处为栈重建时的回显,可完整展示创建过程):

 Network keep-0543-net Creating
 Network keep-0543-net Creating
 Network keep-0543-net Created
 Network keep-0543-net Created
 Container keep-0543-websocket Creating
 Container keep-0543-backend Creating
 Container keep-0543-websocket Created
 Container keep-0543-backend Created
 Container keep-0543-frontend Creating
 Container keep-0543-frontend Created
 Container keep-0543-websocket Starting
 Container keep-0543-backend Starting
 Container keep-0543-websocket Started
 Container keep-0543-backend Started
 Container keep-0543-frontend Starting
 Container keep-0543-frontend Started

4.5 验证部署

容器状态(docker compose ps 实测):

NAME                  IMAGE                                             COMMAND                   SERVICE                 CREATED          STATUS          PORTS
keep-0543-backend     us-central1-docker.pkg.dev/keephq/keep/keep-api   "/venv/lib/python3.1…"   keep-backend            13 minutes ago   Up 13 minutes   0.0.0.0:8148->8080/tcp, [::]:8148->8080/tcp
keep-0543-frontend    us-central1-docker.pkg.dev/keephq/keep/keep-ui    "/app/entrypoint.sh"      keep-frontend           28 minutes ago   Up 28 minutes   0.0.0.0:8150->3000/tcp, [::]:8150->3000/tcp
keep-0543-websocket   quay.io/soketi/soketi:1.4-16-debian               "node /app/bin/serve…"   keep-websocket-server   32 minutes ago   Up 32 minutes   0.0.0.0:8146->6001/tcp, [::]:8146->6001/tcp

后端健康检查(/healthcheck 实测,返回空 JSON 即正常):

curl -s http://localhost:8148/healthcheck
{}

API Key 与租户验证(带预置 Key 访问 /whoami 实测):

curl -s -H "x-api-key: q1w2e3r4-keep-demo-key" http://localhost:8148/whoami
{"tenant_id":"keep"}

OpenAPI 文档:浏览器打开 http://<服务器IP>:8148/docs 可见 FastAPI 自动生成的接口文档(实测 200)。

Web 控制台:浏览器打开 http://<服务器IP>:8150。实测 HTTP 层表现为 307 重定向链(/ → /incidents → /signin?callbackUrl=... → 200),进入 Keep 登录页;NO_AUTH 模式下无需输入凭据即可进入控制台(页面标题实测为 Keep)。

注入一条真实告警完成端到端验证(异步接口,返回任务名即代表受理成功,实测回显):

curl -s -X POST http://localhost:8148/alerts/event \
  -H "Content-Type: application/json" \
  -H "x-api-key: q1w2e3r4-keep-demo-key" \
  -d '{"id":"demo-alert-001","name":"disk usage on web-prod-01 above 90%","severity":"critical","status":"firing","source":["demo"],"message":"Disk usage on /var of web-prod-01 is 93%","fingerprint":"disk-web-prod-01"}'
{"task_name":"131606376315024"}

数秒后在 UI 告警台(Alerts 页)可见该条告警;CLI 侧 keep alert list 亦可见(CLI 用法见《Keep 0.54.3 常用 CLI 命令与工具详解》)。

五、数据持久化说明

实测栈将宿主 stack/state/ 目录 bind mount 到两个容器的 /state,Keep 的全部落盘数据集中在这一处(docker inspect keep-0543-backend --format '{{json .Mounts}}' 实测):

[
    {
        "Type": "bind",
        "Source": "/opt/data/quzuo/tmp/keep-alerts/stack/state",
        "Destination": "/state",
        "Mode": "rw",
        "RW": true,
        "Propagation": "rprivate"
    }
]

运行一段时间后 state/ 的实测内容与用途:

-rw-r--r-- 1 yahui yahui 794624 10月 23 09:34 db.sqlite3                                          # SQLite 数据库(告警/工作流/用户/凭据)
-rw-r--r-- 1 yahui yahui     44 10月 23 09:32 keep_bash_f92d7d3a4d754024b3c289db4b1eb43d     # SECRET_MANAGER=FILE 落盘的密钥文件
-rw-r--r-- 1 yahui yahui     22 10月 23 09:19 keep-cli-demo                                   # 预置 API Key 文件
-rw-r--r-- 1 yahui yahui     51 10月 23 09:32 keep_console_03092072a8ab409ba41974670f4e7040   # console provider 连接凭据文件
-rw-r--r-- 1 yahui yahui     46 10月 23 09:24 keep_console_09f66d0f96634ec8b9bd3c575d427ce4
drwxr-xr-x 2 yahui yahui   4096 10月 23 09:32 workflows/                                      # 预置 workflow(disk-critical-handler.yml)

持久化要点:

  1. 备份就是备份 state/ 目录:停栈 docker compose down 后整目录打包即可,恢复时原位解包再 chown -R 1000:1000 后 up -d。
  2. 权限必须保持 uid/gid 1000:任何在宿主上以 root 手工改写该目录的操作之后,都要重新 chown -R 1000:1000,否则下次启动失败。
  3. 换数据库后 db.sqlite3 不迁移:从 SQLite 切到 MySQL/PostgreSQL 是「从零开始的新库」,历史告警不会自动搬迁;切换前先 keep alert export 或调 REST API 导出需要留存的数据。

六、环境变量表

6.1 前端 keep-ui

变量实测值作用
AUTH_TYPENO_AUTH认证模式,与后端保持一致(NO_AUTH 免登录/DB 用户名密码,详见第七节)
API_URLhttp://keep-backend:8080前端服务端向Compose 网络内后端转发请求的地址(容器间用服务名)
NEXT_PUBLIC_API_URLhttp://localhost:8148浏览器直连后端的地址(要用宿主能访问的地址,故填映射端口)
NEXTAUTH_URLhttp://localhost:8150NextAuth 回调基准地址,浏览器实际访问前端的地址
NEXTAUTH_SECRET任意字符串NextAuth 会话签名密钥
PUSHER_HOST / PUSHER_PORTlocalhost / 8146浏览器建立实时推送 WebSocket 的地址(宿主侧映射端口)
PUSHER_APP_KEYkeepappkey推送应用 Key,与 Soketi 的 SOKETI_DEFAULT_APP_KEY 一致
POSTHOG_KEY / POSTHOG_HOST官方默认值官方产品分析埋点;内网环境可留空(为空时仅失去匿名统计功能,不影响功能)
NEXT_PUBLIC_SENTRY_DSN官方默认值前端错误上报 Sentry 地址;内网环境可留空

6.2 后端 keep-api

变量实测值作用
PORT8080后端监听端口
AUTH_TYPENO_AUTH认证模式(见第七节)
KEEP_DEFAULT_API_KEYScli-demo:admin:q1w2e3r4-keep-demo-key启动时预置 API Key,格式 名称:角色:密钥,多个用分号分隔;不预置则 NO_AUTH 下也无 Key 可用(实测坑,见 11.3)
KEEP_JWT_SECRETkeep-demo-jwt-secretJWT 签名密钥,生产环境务必换强随机值
DATABASE_CONNECTION_STRINGsqlite:////state/db.sqlite3?check_same_thread=FalseSQLAlchemy 连接串,可换 MySQL/PostgreSQL(见第七节)
SECRET_MANAGER_TYPEFILE密钥管理后端:FILE(落盘 /state)/ DB / AWS Secrets Manager 等
SECRET_MANAGER_DIRECTORY/stateFILE 模式的落盘目录(实测 keep-cli-demo 等文件即在此生成)
KEEP_PROVIDERS{"bash-local":{"type":"bash",...},...}启动时预置的 provider 连接(JSON),免去手工在 UI 上连接;完整说明见《Keep 0.54.3 配置参数详解》
KEEP_WORKFLOWS_DIRECTORY/state/workflows启动时批量加载该目录下的 workflow YAML
PUSHER_APP_ID / APP_KEY / APP_SECRET1 / keepappkey / keepappsecret后端作为推送客户端连 Soketi 的三元组,与 SOKETI_* 完全一致
PUSHER_HOST / PUSHER_PORTkeep-websocket-server / 6001后端连 Soketi 的容器网络地址(注意:与前端 PUSHER_HOST=localhost 含义不同,前端是给浏览器用的)
KEEP_METRICStrue开启 Prometheus 指标端点(/metrics)
PROMETHEUS_MULTIPROC_DIR/tmp/prometheus多进程模式下 Prometheus 指标共享目录
OPENAI_API_KEY(留空)AI 功能的 OpenAI Key;不配置不影响核心功能(实测零 AI Key 跑通全链路)
USE_NGROKfalse官方 compose 的快速演示开关,保持 false

6.3 实时推送 keep-websocket-server(Soketi)

变量实测值作用
SOKETI_DEFAULT_APP_ID1应用 ID,与后端 PUSHER_APP_ID 一致
SOKETI_DEFAULT_APP_KEYkeepappkey与前端/后端的 PUSHER_APP_KEY 一致
SOKETI_DEFAULT_APP_SECRETkeepappsecret与后端 PUSHER_APP_SECRET 一致
SOKETI_USER_AUTHENTICATION_TIMEOUT3000用户认证超时(毫秒)
SOKETI_DEBUG1调试日志开关

七、配置参数说明

三个关键配置块的展开说明(完整环境变量清单与 providers/workflow 配置详见《Keep 0.54.3 配置参数详解》):

认证模式 AUTH_TYPE:NO_AUTH(默认,免登录,但调用 API 仍需 API Key)、DB(用户名密码入库,内置 RBAC 预定义角色)、OAUTH2PROXY(反代层统一认证)、Okta/OneLogin(开源版可用);Keycloak/Auth0/AzureAD 属企业目录功能。前后端必须配置同一 AUTH_TYPE。

数据库切换 DATABASE_CONNECTION_STRING:默认 SQLite 适合单机与评估;多人团队换成 MySQL/PostgreSQL,例如 mysql+pymysql://keep:密码@mysql-host:3306/keep。应用层由 SQLAlchemy 屏蔽差异,Compose 里只需改这一行并为栈增加数据库服务。

推送三元组(PUSHER_APP_ID/KEY/SECRET):这是前端、后端、Soketi 三方对齐的「钥匙串」。排查实时推送故障时,用同一组值在三处比对:前端 PUSHER_APP_KEY、后端 PUSHER_APP_KEY/APP_SECRET/APP_ID、Soketi SOKETI_DEFAULT_APP_*。任一不一致,浏览器控制台会看到 WebSocket 连接 401。

八、配置文件挂载

实测栈只挂载一个 bind mount:宿主 ./state → 两个容器的 /state。它同时承担四种角色:

  1. SQLite 数据库文件:/state/db.sqlite3(由 DATABASE_CONNECTION_STRING 指定)。
  2. 密钥落盘目录:/state(SECRET_MANAGER_TYPE=FILE + SECRET_MANAGER_DIRECTORY=/state),provider 凭据以 keep_<名称> 文件存放。
  3. 预置 workflow 目录:/state/workflows(KEEP_WORKFLOWS_DIRECTORY),放入的 YAML 会在后端启动时批量注册,实测 disk-critical-handler.yml 在 UI Workflow 页可见且来源标记为系统预置。
  4. 官方默认行为:镜像内 VOLUME 声明的就是 /state,即使不写 volumes 也会生成匿名卷——但匿名卷不利于备份与查看,务必显式 bind mount 或命名卷。

workflow YAML 编辑流程:宿主上直接改 state/workflows/*.yml,docker compose restart keep-backend 后生效;或在 UI Workflow 编辑器内改(保存后入库 SQLite)。两种方式不要对同一 workflow 混用,避免覆盖。

九、等价 docker run

不用 Compose 时,以下三条命令与上述 compose 文件等价(先建网络,注意按依赖顺序启动;变量值与 compose 一致):

# 1. 创建网络
docker network create --driver bridge --subnet 10.89.71.0/24 keep-0543-net

# 2. 实时推送(Soketi)
docker run -d --name keep-0543-websocket \
  --network keep-0543-net \
  -p 8146:6001 \
  -e SOKETI_USER_AUTHENTICATION_TIMEOUT=3000 \
  -e SOKETI_DEBUG=1 \
  -e SOKETI_DEFAULT_APP_ID=1 \
  -e SOKETI_DEFAULT_APP_KEY=keepappkey \
  -e SOKETI_DEFAULT_APP_SECRET=keepappsecret \
  quay.io/soketi/soketi:1.4-16-debian

# 3. 后端 API(注意先 chown 1000:1000 /opt/data/quzuo/tmp/keep-alerts/stack/state)
docker run -d --name keep-0543-backend \
  --network keep-0543-net \
  -p 8148:8080 \
  -v /opt/data/quzuo/tmp/keep-alerts/stack/state:/state \
  -e PORT=8080 -e AUTH_TYPE=NO_AUTH \
  -e KEEP_DEFAULT_API_KEYS=cli-demo:admin:q1w2e3r4-keep-demo-key \
  -e KEEP_JWT_SECRET=keep-demo-jwt-secret \
  -e 'KEEP_PROVIDERS={"console-yaml-prov":{"type":"console","authentication":{}},"bash-local":{"type":"bash","authentication":{}}}' \
  -e KEEP_WORKFLOWS_DIRECTORY=/state/workflows \
  -e SECRET_MANAGER_TYPE=FILE -e SECRET_MANAGER_DIRECTORY=/state \
  -e 'DATABASE_CONNECTION_STRING=sqlite:////state/db.sqlite3?check_same_thread=False' \
  -e PROMETHEUS_MULTIPROC_DIR=/tmp/prometheus -e KEEP_METRICS=true \
  -e PUSHER_APP_ID=1 -e PUSHER_APP_KEY=keepappkey -e PUSHER_APP_SECRET=keepappsecret \
  -e PUSHER_HOST=keep-websocket-server -e PUSHER_PORT=6001 \
  -e USE_NGROK=false \
  us-central1-docker.pkg.dev/keephq/keep/keep-api

# 4. 前端控制台
docker run -d --name keep-0543-frontend \
  --network keep-0543-net \
  -p 8150:3000 \
  -v /opt/data/quzuo/tmp/keep-alerts/stack/state:/state \
  -e AUTH_TYPE=NO_AUTH \
  -e API_URL=http://keep-backend:8080 \
  -e NEXTAUTH_SECRET=secret \
  -e NEXTAUTH_URL=http://localhost:8150 \
  -e NEXT_PUBLIC_API_URL=http://localhost:8148 \
  -e PUSHER_HOST=localhost -e PUSHER_PORT=8146 -e PUSHER_APP_KEY=keepappkey \
  us-central1-docker.pkg.dev/keephq/keep/keep-ui

生产建议仍用 Compose 管理:环境变量集中、依赖顺序由 depends_on 保证、down/up 一条命令完成。

十、常用运维命令

以下均在实测栈上执行(工作目录 stack/):

# 服务状态
docker compose ps

# 查看后端日志(JSON 结构化日志,追踪 HTTP 请求与 workflow 执行)
docker compose logs --tail 2 keep-backend

实测日志回显(一行 JSON + 一行访问日志,可看到请求路径与状态码):

keep-0543-backend  | {"worker_type": "uvicorn", "asctime": "2026-10-23 01:46:56,387", "message": "Request finished: GET /whoami 200 in 0.00s", "levelname": "INFO", "name": "keep.api.middlewares", ..., "tenant_id": "keep", "status_code": 200}
keep-0543-backend  | 2026-10-23 01:46:56,387 - 7df515622c0158b2f5847eaf279b7306 - MainThread - 10.89.71.1:58484 - "GET /whoami HTTP/1.1" 200
# 资源占用(实测空载水平:后端约 500MB、前端约 190MB、推送 41MB)
docker stats --no-stream --format 'table {{.Name}}\t{{.MemUsage}}\t{{.CPUPerc}}' | grep keep

# 重启单个服务(修改环境变量或 workflow YAML 后)
docker compose restart keep-backend

# 停栈(实测回显:逐容器 Stopping/Stopped/Removing/Removed,最后移除网络;state/ 数据保留)
docker compose down

# 再启动(保留数据的重复部署)
docker compose up -d

docker compose down 实测回显:

 Container keep-0543-websocket Stopping
 Container keep-0543-frontend Stopping
 Container keep-0543-frontend Stopped
 Container keep-0543-frontend Removing
 Container keep-0543-frontend Removed
 Container keep-0543-backend Stopping
 Container keep-0543-backend Stopped
 Container keep-0543-backend Removing
 Container keep-0543-backend Removed
 Container keep-0543-websocket Stopped
 Container keep-0543-websocket Removing
 Container keep-0543-websocket Removed
 Network keep-0543-net Removing
 Network keep-0543-net Removed

升级流程:docker compose pull && docker compose up -d(Compose 会仅重建镜像变化的服务);回滚则把 image 固定到旧摘要后重新 up -d。

十一、常见问题(实测排障实录)

11.1 后端启动即退出:unable to open database file

现象:up -d 后后端容器反复退出,docker compose logs keep-backend 中出现:

sqlalchemy.exc.OperationalError: (sqlite3.OperationalError): unable to open database file

根因:官方镜像以非 root 用户 keep(uid/gid 1000)运行,bind mount 的 state/ 目录归宿主 root 所有,SQLite 无权在该目录创建 db.sqlite3。

解决:chown -R 1000:1000 state 后 docker compose up -d。实测一次通过。判定技巧:只要换宿主目录/迁移数据后出现同类报错,先查属主:ls -ln state | head 看第三、四列是否为 1000。

11.2 端口冲突:Bind for 0.0.0.0:xxxx failed

现象:up -d 报 Error starting userland proxy: listen tcp4 0.0.0.0:8147: bind: address already in use 之类。

根因:共享服务器上高位端口同样可能被其他栈占用。实测部署过程中连续三次换端口(3000/8080/6001 及 8128、8144、8147 均被同机其他服务占用),最终落位 8150/8148/8146。

解决:ss -ltnp | grep ':<端口>\b' 找到占用者;或干脆选更冷门的端口。注意三处联动:compose 端口映射、前端 NEXTAUTH_URL/NEXT_PUBLIC_API_URL/PUSHER_PORT 中的端口都要同步改,浏览器才能正确连通。

11.3 NO_AUTH 模式下调用 API 仍返回 401 Missing API Key

现象:NO_AUTH 部署后,注入告警 curl -X POST .../alerts/event 返回 401:

{"detail":"Missing API Key"}

根因:AUTH_TYPE=NO_AUTH 只是不要求「用户登录」,但 API 调用(含告警上报)仍然必须有 API Key;而数据库里默认没有任何 Key 可用。

解决:用 KEEP_DEFAULT_API_KEYS=名称:角色:密钥 在启动时预置(如本栈的 cli-demo:admin:q1w2e3r4-keep-demo-key),随后所有 API/CLI 调用带 x-api-key: q1w2e3r4-keep-demo-key 即可。实测验证通过(/whoami 返回 {"tenant_id":"keep"})。

11.4 拉起时提示 OPENAI_API_KEY 变量未设置

现象:每次执行 docker compose up -d/ps/down,第一行都出现:

time="..." level=warning msg="The \"OPENAI_API_KEY\" variable is not set. Defaulting to a blank string."

根因:compose 文件里 - OPENAI_API_KEY=$OPENAI_API_KEY 会从宿主 shell 环境取值,未设置时 Compose 发 warning 并置空。

影响与处理:无功能影响(实测零 AI Key 全链路可用)。不想看到 warning 可删掉该行;需要 AI 功能时在宿主 export OPENAI_API_KEY=sk-... 或写入 .env 文件。

11.5 CLI 镜像直接跑报 exec not found

现象:docker run --rm us-central1-docker.pkg.dev/keephq/keep/keep-cli version 报 exec: "version": executable file not found。

根因:keep-cli 镜像的启动命令是裸 python3,没有把 CLI 设为 entrypoint。

解决:命令需带上 keep 前缀,如 docker run --rm keep-cli keep version(实测通过)。CLI 完整用法见《Keep 0.54.3 常用 CLI 命令与工具详解》。

11.6 浏览器打开前端一直跳 /signin

现象:curl -sIL http://localhost:8150/ 实测为 307 链:/ → /incidents → /signin?callbackUrl=... → 200。

说明:这是正常行为而非故障——NextAuth 框架下即便 NO_AUTH 模式,首次进入也会先渲染登录页;在登录页点击 Sign in(无需输入凭据)即可进入控制台。若希望完全跳过该页,需改用 DB 认证模式并创建用户。

十二、参考链接