docs+build: Dockerfile 降级为仅本地开发用,并记录 hoststack.yaml 保留/移除决策
用户要求两件事:把 Dockerfile 明确降级成「仅本地开发用」并说清边界;
以及若日后决定移除 hoststack.yaml,把理由写进相关文档。
先核了一遍决定性的技术前提——删掉 YAML 到底会发生什么。读 HostStack agent
源码,优先级是三层,而且和我上一轮写的说法不一样:
1. 控制台 services 表已存的值 -> payload
2. YAML 声明的字段无条件覆盖(guard 只是 `if (svcConfig.start?.command)`,
没有 `&& !payload.x` 短路)
3. YAML 省略的字段沿用控制台值;框架默认值只在仍为空时才填,而 agent 自己的
注释写着 "trigger-payload always populates payload.x from the DB",
所以对已建好的服务这条分支实际是死代码
因此删掉 hoststack.yaml **不会**回落到 cargo fetch / app 那套默认值,而是直接
采用控制台现存的值——而那恰好就是坏掉的 ./target/release/app。"UI 更方便管理"
这个前提成立,但必须先改 UI 再删文件。据此写了决策记录,当前决定是保留。
Dockerfile / docs/deployment.md §3 / docs/design.md / README.md
- 统一标为仅本地开发 / 本地容器调试路径,生产不执行
- 写清边界后果:诊断工具、ENTRYPOINT tini、EXPOSE 8080 在线上都不生效
- 给出判定方法:容器内看 /proc/1/cmdline 与 /etc/os-release
- 顺带修掉 design.md 里把 hoststack.yaml 描述成"网关编排清单"的错误——
文件里没有任何 gateway,只有 services.web
docs/deployment.md §5.1
- 三层优先级替换掉原先"YAML 省略则回落框架默认值"的错误说法
- 新增决策记录:保留的理由(进 git、可 review 可回滚)与代价(双写);
若日后移除,必须在控制台先把 install/build/start 三条改对再删,并把本段
移到 docs/retired.md 记下移除 commit
验证:GATE-DOCS PASS 6/6(SECTION-REF 226 refs / 0,PATH AUDIT 376 refs / 0)。
This commit is contained in:
parent
978288f8d7
commit
cf252a9f06
12
Dockerfile
12
Dockerfile
|
|
@ -1,3 +1,15 @@
|
||||||
|
# ============================================================================
|
||||||
|
# 仅本地开发 / 本地容器调试用。生产不执行本文件。
|
||||||
|
#
|
||||||
|
# 线上部署走仓库根目录 hoststack.yaml + runtime: rust,由 HostStack 自己的
|
||||||
|
# agent 在 rust:slim-trixie 里构建、拷进 debian:trixie-slim 的 runner 容器运行
|
||||||
|
# ——完全不经过本 Dockerfile(连 Debian 版本都不同:bookworm vs trixie)。
|
||||||
|
# 因此本文件里的诊断工具、ENTRYPOINT tini、EXPOSE 8080 在线上都不生效。
|
||||||
|
# 判定当前跑的是哪条路径:容器内看 /proc/1/cmdline 与 /etc/os-release。
|
||||||
|
# 生产启动命令的真源是 hoststack.yaml 的 start.command,见 docs/deployment.md
|
||||||
|
# §5.1;本文件只服务于 `docker run` / 本地起一个容器来验证行为。
|
||||||
|
# ============================================================================
|
||||||
|
#
|
||||||
# C-DEBIAN-SLIM: Debian builder and runtime; no Alpine/musl dependencies.
|
# C-DEBIAN-SLIM: Debian builder and runtime; no Alpine/musl dependencies.
|
||||||
FROM rust:1-slim-bookworm AS builder
|
FROM rust:1-slim-bookworm AS builder
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,12 @@ State is an architectural boundary here, not a preference.
|
||||||
|
|
||||||
Requires a reachable Redis 7 instance and a JMAP session plus a Telegram bot token.
|
Requires a reachable Redis 7 instance and a JMAP session plus a Telegram bot token.
|
||||||
|
|
||||||
|
**This is the local/dev path.** The repo's `Dockerfile` is for local `docker run` only --
|
||||||
|
production does not execute it. Live deploys run from `hoststack.yaml` with
|
||||||
|
`runtime: rust`, which HostStack builds in its own `rust:slim-trixie` image and runs in its
|
||||||
|
own `debian:trixie-slim` runner, so the Dockerfile's diagnostic tools, `ENTRYPOINT` and
|
||||||
|
`EXPOSE` do not apply there. See `docs/deployment.md` §3 and §5.1 for the boundary.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cp .env.example .env # fill REDIS_URL and CONFIG_ENCRYPTION_KEY
|
cp .env.example .env # fill REDIS_URL and CONFIG_ENCRYPTION_KEY
|
||||||
docker build -t messageweave:latest .
|
docker build -t messageweave:latest .
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,14 @@
|
||||||
|
|
||||||
## 3. Rust 多阶段构建与运行
|
## 3. Rust 多阶段构建与运行
|
||||||
|
|
||||||
|
> **边界:本章与仓库根目录的 `Dockerfile` 是「仅本地开发 / 本地容器调试」路径,
|
||||||
|
> 生产不执行它。** 线上走 §5.1 的 `hoststack.yaml` + `runtime: rust`,由 HostStack 自带
|
||||||
|
> agent 在 `rust:slim-trixie` 构建、拷进 `debian:trixie-slim` runner 运行,完全不经过
|
||||||
|
> 本 Dockerfile(连 Debian 版本都不同)。因此本章的 `ENTRYPOINT`(tini)、
|
||||||
|
> `EXPOSE 8080`、内置诊断工具在生产容器中一律不生效。生产启动命令的真源是
|
||||||
|
> `hoststack.yaml` 的 `start.command`。本地用 `docker build` + `docker run` 验证行为时
|
||||||
|
> 才走本章。
|
||||||
|
|
||||||
### 3.1 构建阶段
|
### 3.1 构建阶段
|
||||||
(保留既有 rust:bookworm 构建 + 缓存分层约定;产物为单个静态编译二进制 `message-weave`)
|
(保留既有 rust:bookworm 构建 + 缓存分层约定;产物为单个静态编译二进制 `message-weave`)
|
||||||
|
|
||||||
|
|
@ -136,14 +144,31 @@ HostStack 使用仓库根目录 `hoststack.yaml`:`runtime: rust` 的 agent 在
|
||||||
或代码仓库。**本仓库的 Dockerfile 不由该路径执行**——runner 镜像是 HostStack 自带的
|
或代码仓库。**本仓库的 Dockerfile 不由该路径执行**——runner 镜像是 HostStack 自带的
|
||||||
`debian:trixie-slim`,不是本文件的 `debian:bookworm-slim`,见 §3 与 §8.1 的说明。
|
`debian:trixie-slim`,不是本文件的 `debian:bookworm-slim`,见 §3 与 §8.1 的说明。
|
||||||
|
|
||||||
**该 YAML 是运行时配置的真源,且不能删除**:YAML 声明的字段**覆盖**控制台已存的同名
|
**配置优先级(三层,从 HostStack agent 源码实测确认)**:部署时 agent 先把控制台
|
||||||
配置,YAML 省略的字段**回落到运行时框架默认值**——Rust 的默认值是 `cargo fetch` /
|
services 表里已存的值填进 payload,再用 `hoststack.yaml` 声明的值覆盖,最后才用运行时
|
||||||
`cargo build --release` / `./target/release/app`。最后那个默认值由 HostStack 自带、
|
框架默认值补缺。源码判定条件是 `if (svcConfig.install?.command)` 才覆盖,且没有
|
||||||
与本仓库包名无关:`Cargo.toml` 只声明一个 `[[bin]]`(名字 `message-weave`,
|
`&& !payload.x` 之类的短路——**YAML 声明了就以 YAML 为准、无条件覆盖控制台值;YAML
|
||||||
路径 `src/main.rs`),仓库里既没有 `app` 这个二进制目标也没有对应源文件。删掉本文件后
|
省略的字段则沿用控制台已存的那个值**。框架默认值只有在前两者都为空时才填,而 agent 的
|
||||||
Start Command 回落为 `./target/release/app`,进程在绑定端口前就以 `not found` 退出。
|
注释明确写着 *"trigger-payload always populates payload.x from the DB"*,所以对已建好的
|
||||||
线上实测印证了此行为:控制台 UI 显示的是默认值 `./target/release/app`,而实际 PID 1 是
|
服务这条默认值分支实际上是**死代码**。这也意味着删掉本文件**不会**退回安全的默认值——
|
||||||
`./target/release/message-weave`——说明在生效的是 YAML,不是控制台。
|
它直接采用控制台现存的那个值。
|
||||||
|
|
||||||
|
**因此目前不能删**。控制台里现存的 Start Command 是 `./target/release/app`,
|
||||||
|
这是 HostStack 为 Rust runtime 自带的默认值、与本仓库包名无关:`Cargo.toml` 只声明
|
||||||
|
一个 `[[bin]]`(名字 `message-weave`,路径 `src/main.rs`),仓库里既没有 `app` 这个
|
||||||
|
二进制目标也没有对应源文件。直接删掉 `hoststack.yaml` 后,下一次部署会以
|
||||||
|
`./target/release/app` 启动,进程在绑定端口前就以 `not found` 退出。
|
||||||
|
线上实测印证:控制台 UI 显示 `./target/release/app`,而实际 PID 1 是
|
||||||
|
`./target/release/message-weave`——说明现在生效的是 YAML,不是控制台。
|
||||||
|
|
||||||
|
> **决策记录**:最初倾向是"从控制台 UI 管理更方便,把 `hoststack.yaml` 拿掉"。
|
||||||
|
> 但按上面的优先级,**这个前提不成立**——直接删 YAML 服务起不来。**当前决定:保留
|
||||||
|
> `hoststack.yaml`。** 保留的代价是双写(在控制台改命令不会生效,必须改 YAML);
|
||||||
|
> 保留的理由是部署命令进入 git 历史、可 review 可回滚,而控制台值散在平台侧、改动无痕迹。
|
||||||
|
> **若日后确要移除**,必须**先在控制台把三条命令改对**(install = `cargo fetch --locked`、
|
||||||
|
> build = `cargo build --release --locked`、start = `./target/release/message-weave`),
|
||||||
|
> 再删本文件并重新部署一次验证;同时把本段连同该前提移到 `docs/retired.md` 并记下
|
||||||
|
> 移除 commit。**不要**只删文件就以为控制台值会自动变对。
|
||||||
|
|
||||||
**校验是宽松的,不是拒绝式的**:agent 的部署路径不应用 zod schema 校验,
|
**校验是宽松的,不是拒绝式的**:agent 的部署路径不应用 zod schema 校验,
|
||||||
`install` / `build` / `start` 等都是可选对象,**未知键会被静默丢弃而不是报错**,
|
`install` / `build` / `start` 等都是可选对象,**未知键会被静默丢弃而不是报错**,
|
||||||
|
|
|
||||||
|
|
@ -304,8 +304,8 @@ message-weave/
|
||||||
├── Cargo.toml # jmap-client 0.4.2 / redis 0.27 / reqwest 0.13;不含 teloxide
|
├── Cargo.toml # jmap-client 0.4.2 / redis 0.27 / reqwest 0.13;不含 teloxide
|
||||||
├── .env.example # 仅 2 个必填项的占位样例(REDIS_URL / CONFIG_ENCRYPTION_KEY),不含业务配置
|
├── .env.example # 仅 2 个必填项的占位样例(REDIS_URL / CONFIG_ENCRYPTION_KEY),不含业务配置
|
||||||
├── .gitignore
|
├── .gitignore
|
||||||
├── Dockerfile # debian:bookworm-slim + tini + 只读诊断工具(C-DEBIAN-SLIM)
|
├── Dockerfile # 仅本地开发用;生产不执行(C-DEBIAN-SLIM,边界见 deployment.md §3)
|
||||||
├── hoststack.yaml # 网关编排清单,非应用代码
|
├── hoststack.yaml # 生产部署真源(runtime/build/start/healthCheck),非网关清单,见 §5.1
|
||||||
├── src/
|
├── src/
|
||||||
│ ├── main.rs # tokio main:读 PORT/REDIS_URL/CONFIG_ENCRYPTION_KEY 后启动 webhook HTTP 入口(单入口,无 CLI 子命令)
|
│ ├── main.rs # tokio main:读 PORT/REDIS_URL/CONFIG_ENCRYPTION_KEY 后启动 webhook HTTP 入口(单入口,无 CLI 子命令)
|
||||||
│ ├── config.rs # Redis 业务配置反序列化(serde → BusinessConfigWire → Config);CONFIG_ENCRYPTION_KEY 解析
|
│ ├── config.rs # Redis 业务配置反序列化(serde → BusinessConfigWire → Config);CONFIG_ENCRYPTION_KEY 解析
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,30 @@
|
||||||
# chat allowlist, LLM settings) is NOT read from environment variables at all.
|
# chat allowlist, LLM settings) is NOT read from environment variables at all.
|
||||||
# It is written to Redis via /api/bootstrap or admin PUT on /api/config.
|
# It is written to Redis via /api/bootstrap or admin PUT on /api/config.
|
||||||
#
|
#
|
||||||
# Precedence (verified in the HostStack agent source, deploy-executor.ts):
|
# Precedence (verified in the HostStack agent source, deploy-executor.ts).
|
||||||
# a value declared here wins; a field omitted here falls back to the
|
# Three layers, in order:
|
||||||
# per-runtime framework default. For runtime: rust those defaults are
|
# 1. the values already stored on the dashboard services row -> payload
|
||||||
# `cargo fetch` / `cargo build --release` / `./target/release/app`.
|
# 2. a field DECLARED here unconditionally overwrites that (guard is
|
||||||
|
# `if (svcConfig.start?.command)`, with no `&& !payload.x`), so this
|
||||||
|
# file wins over the dashboard for the fields it declares
|
||||||
|
# 3. a field OMITTED here keeps the dashboard value; the per-runtime
|
||||||
|
# framework default then fills in only if that is still empty, which
|
||||||
|
# for an existing service it never is -- the agent's own comment reads
|
||||||
|
# "trigger-payload always populates payload.x from the DB", so that
|
||||||
|
# branch is effectively dead code
|
||||||
#
|
#
|
||||||
# That last default is why start.command MUST be declared below: `app` is not
|
# For runtime: rust those framework defaults are `cargo fetch` /
|
||||||
# this package's binary name. Cargo.toml declares [[bin]] name = "message-weave"
|
# `cargo build --release` / `./target/release/app`.
|
||||||
# with path = "src/main.rs"; there is no `app` target and no src/app.rs.
|
#
|
||||||
# Deleting this file reverts the service to a start command that exits with
|
# Consequence: deleting this file does NOT fall back to safe defaults. It
|
||||||
# `./target/release/app: not found` before it can bind the port.
|
# makes the dashboard's stored values take effect as-is, and that stored
|
||||||
|
# Start Command is HostStack's Rust default `./target/release/app` -- which
|
||||||
|
# is not this package's binary name. Cargo.toml declares
|
||||||
|
# [[bin]] name = "message-weave" with path = "src/main.rs"; there is no
|
||||||
|
# `app` target and no app.rs. The next deploy would exit with
|
||||||
|
# `./target/release/app: not found` before binding the port.
|
||||||
|
# To drop this file, first correct all three commands in the dashboard UI,
|
||||||
|
# then delete and redeploy to verify. See docs/deployment.md §5.1.
|
||||||
#
|
#
|
||||||
# Do NOT add --debug here for production. See the note above start.command.
|
# Do NOT add --debug here for production. See the note above start.command.
|
||||||
services:
|
services:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue