chore: add env.cfg.example templates and clean up config files

This commit is contained in:
cnphpbb
2026-08-13 17:59:23 +08:00
parent c75c9884c8
commit c6eccd687b
88 changed files with 4264 additions and 88 deletions
+73
View File
@@ -0,0 +1,73 @@
# ========== 经典模式 ==========
# 如果为 true,不使用任何颜色或图标,与标准 ls 行为类似
classic: false
# ========== 分块(Blocks ==========
# 定义每行显示哪些信息块,按顺序排列
blocks:
- permission # 文件权限 (rwxr-xr-x)
- user # 文件所有者
- group # 用户组
- size # 文件大小
- date # 日期/时间
- name # 文件名(必须包含)
# ========== 颜色主题 ==========
color:
# 何时使用颜色:always(总是)、auto(自动)、never(从不)
when: auto
# 自定义文件类型颜色(覆盖默认值)
theme: custom
# ========== 日期格式 ==========
date: "+%Y-%m-%d %H:%M:%S" # 可选:relative(相对时间)、isoISO格式)
# ========== 跟随符号链接 ============
dereference: false
# ========== 图标 ==========
icons:
# 何时显示图标:always、auto、never
when: auto
# 图标主题(需要对应的 Nerd Font)
theme: fancy # 或 unicode
# 分隔符(图标与文件名之间的字符)
separator: " "
# ========== 忽略选项 ==========
ignore-globs:
- "*.bak" # 忽略所有 .bak 文件
- ".git" # 忽略 .git 目录
- "__pycache__" # 忽略 Python 缓存
# ========== 布局 ==========
layout: grid # grid(网格)| tree(树状)| oneline(单行)
# ========== 递归深度(--tree 时有效) ==========
recursion:
enabled: false
depth: 3 # 递归显示的最大深度
# ========== 排序 ==========
sorting:
column: name # 排序字段:name、size、time、version、extension
# reverse: false # 是否倒序
# dir-grouping: first # 目录排序:first(靠前)、last(靠后)、none
# ========== 符号链接 ==========
# 是否显示符号链接的目标路径
no-symlink: false
symlink-arrow: " ➜ "
# ========== 总大小 ==========
total-size: false # 是否显示总大小(在目录底部)
# ========== 截断所有者名 ==========
truncate-owner:
after: 8 # 用户名超过多少字符截断
# ========== 超链接 ==========
hyperlink: never # always | auto | never 是否生成终端超链接
+32
View File
@@ -0,0 +1,32 @@
# 文件类型颜色
file-type:
directory:
foreground: cyan
bold: true
symlink:
foreground: magenta
italic: true
executable:
foreground: green
bold: true
socket:
foreground: red
pipe:
foreground: yellow
block-device:
foreground: red
char-device:
foreground: yellow
# 文件扩展名颜色(覆盖文件类型颜色)
extensions:
rs:
foreground: white
bold: true
py:
foreground: yellow
js:
foreground: green
md:
foreground: cyan
italic: true
+13
View File
@@ -0,0 +1,13 @@
# 强制 LF 行尾(防止 Windows 工具引入 CRLF 导致 bash 报错)
* text=auto eol=lf
# 显式标记常见脚本/配置类型
*.sh eol=lf
*.bash eol=lf
*.py eol=lf
*.md eol=lf
*.yml eol=lf
*.yaml eol=lf
*.cfg eol=lf
*.example eol=lf
Dockerfile eol=lf
+11 -1
View File
@@ -1,8 +1,11 @@
# Docker相关
.env
*.env
*.env.*
*.env.cfg.*
docker-compose.override.yml
# 开发环境
.idea/
.vscode/
@@ -25,8 +28,15 @@ crontab/debug_*.py
crontab/test_*.py
crontab/test*.py
g-b/*
.gitignore
mcptime/go.sum
mcpTimeServer/go.sum
mcp_server_go/config.toml
compare_images.py
env.cfg
env.cfg.*
stack.yml.*
# 例外:env 模板(不视为敏感配置)允许提交
!**/*.env.example
!**/*.env.cfg.example
!**/env.cfg.example
+262 -3
View File
@@ -7,9 +7,9 @@
```
<service>/
├── stack.yml / compose.yml / <env>.stack.yml / <env>.yml # Docker Compose 文件
├── env.cfg # 环境变量(含敏感信息,应 gitignore)
├── readme.md # 服务说明文档(可选)
└── config/ # 服务配置文件(可选)
├── env.cfg.example # 环境变量(含敏感信息,应 gitignore)
├── readme.md # 服务说明文档(可选)
└── config/ # 服务配置文件(可选)
```
顶层目录按职责划分:
@@ -132,6 +132,7 @@ docker compose -p <名称> --env-file ./builder/golang/env.cfg -f ./builder/gola
- **国内镜像源**Dockerfile 和 apt 配置默认使用国内 CDN 镜像(中科大、阿里云、华为),部署在其他地区需修改。
- **Portainer Docker 兼容性**Portainer CE LTS < 2.36.0 不兼容 Docker >= 29.0.0,需设置 `DOCKER_MIN_API_VERSION=1.24`。详见 `portainer-ce/readme.md`
- **i2c.py 需要硬件**OLED 显示脚本需要树莓派 I2C 硬件、`adafruit_ssd1306` 库和中文字体(`fonts-wqy-microhei`)。
- **shell 脚本禁用 CRLF**:仓库根目录的 `.gitattributes` 已强制 `*.sh` / `*.bash` / `*.py` / `*.md` / `*.yml` / `*.cfg` 等文本 LF 行尾。Windows 工具(IDE agent、记事本、PowerShell)默认写 CRLF,会导致 Linux 上 `'\r': 未找到命令``function xxx() {` 语法错误。**新建或修改脚本后必须 `file <script>` 验证输出不含 `CRLF line terminators`**,如果命中立即 `sed -i 's/\r$//' <script>` 修正。
## 系统配置(etc/
@@ -141,3 +142,261 @@ docker compose -p <名称> --env-file ./builder/golang/env.cfg -f ./builder/gola
- 连接队列大小(somaxconn、syn backlog
- 内存管理(swappiness、脏页阈值)
- 安全加固(ICMP 重定向拒绝、反向路径过滤、kptr_restrict
## Git 提交规范
### Commit Message 格式
本仓库 commit message **优先使用 Conventional Commits** 规范,与历史风格保持一致。格式:
```
<type>(<scope>): <subject> # 中文
<type>(<scope>): <subject> # 英文
```
### Type 类型
| Type | 用途 | 示例 |
| ---------- | ------------------------------------ | --------------------------------------------- |
| `feat` | 新增服务/新功能 | `feat: add adminer docker deployment files` |
| `fix` | 修复 bug | `fix(gitea): 修复备份脚本权限问题` |
| `docs` | 文档变更(readme、AGENTS.md 等) | `docs: 添加git提交信息规则文件` |
| `refactor` | 重构(不改功能) | `refactor(status.py): 重构OLED状态显示代码` |
| `perf` | 性能优化 | `perf(redis): 调整内存淘汰策略` |
| `build` | 构建相关(Dockerfile、compose、依赖)| `build(moltbot): 添加生产环境docker compose` |
| `chore` | 杂项(版本号、配置、镜像标签) | `chore: 更新 Joplin 服务器镜像版本至 3.6.1` |
| `style` | 格式调整(不影响代码逻辑) | `style: 统一 yaml 缩进为 2 空格` |
| `test` | 测试相关 | `test: 添加 memos 部署验证脚本` |
### Scope 范围(可选)
- 服务名(目录名小写):`gitea``memos``haproxy``postgres``portainer-ce`
- 顶层目录:`builder``crontab``etc``shell``i2c.py`
- 省略:当改动跨多个服务或为全局性变更
### Subject 主题规则
1. **中文项目**用中文描述,**英文项目**用英文,统一保持
2. **首字母小写**(中文不受影响)
3. **不超过 50 个字符**,尽量精炼
4. **不要句末加句号**
5. **动词开头**:添加/更新/修复/重构/删除 或 add/update/fix/refactor/remove
6. **写明对象**:要让人一眼看出改了什么
### Body 与 Footer(可选)
需要时换行后空一行写正文:
```
feat(gitea): 添加 lfs 存储后端配置
- 使用 minio 作为 lfs 对象存储
- 调整 gitea app.ini 路径映射
- 备份脚本需同步调整
Refs: #123
```
### 提交前自检
```bash
# 1. 查看变更文件
git status
# 2. 检查 diff 大小(避免误提交敏感文件)
git diff --stat
# 3. 确认无 env.cfg / 凭据被误提交
git diff | grep -iE "password|secret|token|key" # 应无敏感输出
# 4. 暂存并提交
git add <files>
git commit -m "<type>(<scope>): <subject>"
# 5. 推送
git push origin main
```
### 提交粒度
- **一个 commit 只做一件事**:不要把无关改动混在一起
- **服务级别独立提交**:新增一个服务(如 `gitea/`)应该是独立 commit
- **版本号更新单独 commit**`chore(<service>): bump image tag to x.y.z`
- **不要 commit 内容**
- `env.cfg`(含敏感信息,已 gitignore
- 编译产物(`*.bin``main``__pycache__/` 等)
- IDE 配置(`.vscode/``.idea/`
- 系统级配置(`/data/volumes/` 下的实际数据)
### 历史风格兼容性
仓库早期 commit 有少量非 Conventional 风格(如 `Add lsd config and color theme`),
**新增 commit 一律遵循本规范**,历史风格不强制改写。
### AI 生成 commit message 的硬约束
本小节是 `## AI 协作系统级约束` 章节在 commit 场景的具体执行规则。被要求生成 commit message 时,**只输出 commit text 本身**,严禁夹带任何元评论、解释或代码块包裹。
**输出黑名单(出现即视为违规):**
- 前置说明:`我看了你的改动…``建议使用下面的 commit message``以下为 commit`
- 元评论词汇:`分析``考虑``由于``因为``建议``注意``这里``本次``因为需要`
- 复述 diff:把 `git diff` 的关键行直接放进 commit
- 解释"为什么改"commit 只描述"改了什么",不写动机
- 多版本候选/对比:不要 `Option 1 / Option 2``---begin--- / ---end---`
- markdown 代码块包裹:直接给纯文本,除非用户明确要求
**输出前自检清单:**
- [ ] 只包含 commit text,无任何前后缀
- [ ] subject ≤ 50 字符、无句末标点、动词开头、首字母小写
- [ ] type 在 `{feat, fix, docs, refactor, perf, build, chore, style, test}`
- [ ] scope 准确(服务名小写或顶层目录名)
- [ ] body 每行 ≤ 72 字符、不重复 subject
- [ ] 中英文不混用、保持与项目历史一致
- [ ] 未泄露 `password|secret|token|key` 等敏感词
**正确示例:**
```
feat(gitea): bump image to 1.25.2
```
**错误示例(AI 常见错误):**
```
我看了你的改动,主要是更新了镜像版本,建议使用:
feat(gitea): bump image to 1.25.2
这次改动把版本从 1.24 升到 1.25.2,主要修复了…
```
> **注意**:thinking 块属于模型内部推理,**无法在 commit 场景关闭**,但它对最终 commit 输出无污染。用户的关注点应放在“最终输出是否干净”。
## AI 协作系统级约束
本章定义 AI 助手在仓库内的系统级行为约束。`## Git 提交规范 > ### AI 生成 commit message 的硬约束` 是本约束在 commit 场景的具体执行规则,本章则覆盖所有 AI 协作场景的通用原则。
### 约束生效机制
AGENTS.md 全文在 **系统提示层级注入** AI 上下文(类似 CLAUDE.md / AGENTS.md 类机制的底层原理)。AI 收到本仓库相关请求时**必须先读取并应用**本约束——这是不可协商的,不是“参考文档”。
### 通用输出原则
任何场景下 AI 输出必须遵守:
- **不夹带元评论**`我看了你的改动…``建议使用:…``以下为…``分析你的…`
- **不复述 diff**:把 `git diff` 关键行直接复述到回复里
- **不解释“为什么”**:commit 只描述“改了什么”,不写动机
- **不多版本候选**`Option 1 / Option 2``---begin--- / ---end---`
- **不擅自 commit / push**:用户未明确要求时不执行 `git commit``git push`
- **不修改敏感文件**`env.cfg`、Harbor `compose.yaml``/data/volumes/` 数据
- **不修改本文件**AGENTS.md 由人类维护,AI 不得自行编辑
### thinking 块边界
- **thinking 块是模型内部推理**,在系统提示注入层之上,AI 无法控制是否生成
- **Zed UI 折叠显示**属于编辑器行为,不在 AGENTS.md 约束范围
- **判断“是否干净”看最终 commit text 本身**,不是 UI 渲染的折叠块
- commit 文本里出现元评论 = AI 没遵守本约束,**不是 thinking 泄漏**
### 违规处理
- **轻微违规**(输出含元评论/复述 diff):让 AI 重做,提示“严格按 AGENTS.md 系统级约束”
- **严重违规**(擅自 commit/push、修改 env.cfg):撤销操作 + 立即报告用户
- **持续违规**:考虑切换非 reasoning 模型或降低 reasoning effort
## AGENTS.md 维护规则
AGENTS.md 是给 AI 助手(Claude Code、Codex、Hermes 等)和协作者阅读的项目宪法。
它**不是写完就不动的文档**,而是要跟着仓库演进持续更新。
### 何时更新 AGENTS.md
出现以下情况之一,**必须**同步更新本文件(在同一个 PR/commit 或紧随其后):
1. **新增/删除服务**:增减顶层服务目录(如新增 `gitea/`、下线 `flame/`
2. **架构变更**:目录结构、命名约定、文件组织方式发生调整
3. **新增通用约定**:跨多个服务复用的规则(如新的环境变量命名、新的备份策略)
4. **重大操作变更**:升级 Docker 版本、Compose 规范变更、镜像源切换
5. **AI 助手踩坑**:发现 AI 反复犯同样的错误(如改 Harbor compose.yaml、提交 env.cfg
6. **私有仓库/凭据变动**:新增私有镜像仓库、凭据管理方式变化
### 何时**不**更新 AGENTS.md
- 单个服务的局部配置变更(写到该服务的 `readme.md`
- 镜像版本小版本号 bump(写到该服务 `chore` commit
- 一次性的 bug 修复
- 与项目规范无关的个人偏好
### 内容质量要求
写给 AI 看的规则必须**明确、可执行、有上下文**:
-**好的写法**
> `harbor/compose.yaml` 是 `./prepare` 生成的输出文件,**不要手动修改**。
-**差的写法**
> 注意 Harbor 的 compose 文件。
好的写法具备三要素:
1. **是什么**(明确对象/文件/命令)
2. **为什么**(背景/原因,让 AI 理解)
3. **怎么办**(具体操作/替代方案)
### 章节组织
新增章节时遵循现有结构:
```markdown
# 标题 + 简介
## 架构与组织
## 服务规范
## 部署与运维
## 重要注意事项
## 系统配置(etc/
## Git 提交规范
## AGENTS.md 维护规则 ← 新章节放在最后
```
- 章节顺序按 **"项目结构 → 规范 → 注意事项 → 工具/脚本 → 治理"** 排列
- 新章节优先放在文档末尾,避免大改章节编号
- 章节标题用 `##` 一级章节,**保持中文**(与全文风格一致)
### 更新流程建议
```bash
# 1. 修改 AGENTS.md
$EDITOR AGENTS.md
# 2. 单独提交(不要和服务代码改动混在一起)
git add AGENTS.md
git commit -m "docs: <本次更新的内容>"
# 3. 推送到远程
git push origin main
```
典型 commit 消息:
- `docs: 补充 git 提交规范章节`
- `docs: 新增 postgres 服务的部署注意事项`
- `docs: 更新 AGENTS.md 维护规则`
- `docs: 修正 harbor compose.yaml 描述`
### 验证清单
每次更新后过一遍:
- [ ] 章节顺序合理,编号未乱
- [ ] 链接、命令路径、文件名拼写正确
- [ ] 中英文混用风格与现有章节一致
- [ ] 没有把敏感信息(密码、token)写进文档
- [ ] 涉及 AI 助手的提醒**具体到文件/命令**,不空泛
- [ ] 改动反映在 `git status` 中**只**包含 `AGENTS.md`
### 同步与传播
- 复制到其他 stack 仓库时**只复制骨架**(章节标题),不要直接复制内容
- 不同 stack 的"重要注意事项"差异很大,混用会导致 AI 误判
- 如果有 fork/PR 流程,AGENTS.md 变更要在 PR 描述里说明理由
+22
View File
@@ -0,0 +1,22 @@
# ============================================================
# VaultWarden 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p VaultWarden --env-file ./env.cfg -f ./stack.yml up -d
#
# ============================================================
# ⚠️ 敏感值(必填,env.cfg 不提交)
# ============================================================
IMAGE_TAG_VER=1.36.0-alpine
IMAGE_TAG=vaultwarden/server:${IMAGE_TAG_VER}
Volumes_Path=/data/volumes/VaultWarden
Domain=https://passd.6t7.net
ADMIN_TOKEN=<your-token>
+22
View File
@@ -0,0 +1,22 @@
# ============================================================
# wg-easy 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p wg-easy --env-file ./env.cfg -f ./stack.yml up -d
#
# ============================================================
# ⚠️ 敏感值(必填,env.cfg 不提交)
# ============================================================
IMAGE_TAG_VER=15.2.2
IMAGE_TAG=hub.6t7.net/base/wg-easy:${IMAGE_TAG_VER}
Volumes_Path=/data/volumes/wg-easy
WG_HOST=man.tp229.com
WG_PASSWORD="<your-strong-password>"
+15
View File
@@ -0,0 +1,15 @@
# ============================================================
# adminer 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p adminer --env-file ./env.cfg -f ./stack.yml up -d
#
IMAGE_TAG_VER=5.4.2
IMAGE_TAG=adminer:${IMAGE_TAG_VER}
Volumes_Path=/data/volumes/adminer
+42
View File
@@ -0,0 +1,42 @@
# Adminer 部署说明
[Adminer](https://www.adminer.org/) 是一个轻量级的数据库管理工具,支持 MySQL、PostgreSQL、SQLite、MS SQL、Oracle 等多种数据库,单文件 PHP 应用,适合日常数据库运维操作。
## 目录结构
| 文件 | 说明 |
|------|------|
| `stack.yml` | Docker Compose 部署配置 |
| `env.cfg` | 环境变量(镜像版本) |
## 部署命令
```bash
# 拉取镜像
docker compose -p adminer --env-file ./adminer/env.cfg -f ./adminer/stack.yml pull
# 启动服务
docker compose -p adminer --env-file ./adminer/env.cfg -f ./adminer/stack.yml up -d
# 停止服务
docker compose -p adminer --env-file ./adminer/env.cfg -f ./adminer/stack.yml down
```
## 环境变量说明
| 变量 | 说明 | 示例 |
|------|------|------|
| `IMAGE_TAG_VER` | Adminer 镜像版本 | `5.4.2` |
| `IMAGE_TAG` | 完整镜像标签 | `adminer:5.4.2` |
| `Volumes_Path` | 数据卷宿主机路径 | `/data/volumes/adminer` |
## 访问服务
- 默认端口:`8080`
- 访问地址:`http://<主机IP>:8080`
## 注意事项
- Adminer 本身无持久化数据,容器重建不影响使用
- 连接数据库时使用数据库容器的服务名或内网 IP,而非 localhost
- 建议仅在内部网络使用,不要直接暴露到公网
+15
View File
@@ -0,0 +1,15 @@
# pull:: docker compose -p adminer --env-file ./adminer/env.cfg -f ./adminer/stack.yml pull
# run:: docker compose -p adminer --env-file ./adminer/env.cfg -f ./adminer/stack.yml up -d
services:
adminer:
image: ${IMAGE_TAG}
container_name: adminer
restart: unless-stopped
ports:
- '8080:8080'
environment:
- TZ=Asia/Shanghai
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
+14
View File
@@ -0,0 +1,14 @@
# ============================================================
# alist 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 视情况填入真实值
# docker compose -p alist --env-file ./alist/env.cfg -f ./alist/compose.yml up -d
#
IMAGE_TAG=xhofe/alist:v3.32.0-ffmpeg
DATA_PATH=/data/volumes/alist/data
LOCALFS_PATH=/data/volumes/alist/localFS
+24
View File
@@ -0,0 +1,24 @@
# ============================================================
# anqicms 部署 — 公共环境变量模板
# 复制为 env.cfg 后填入真实值,env.cfg 已在 .gitignore 中忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 按需修改端口和镜像版本
# docker compose -p anqicms --env-file ./env.cfg -f ./stack.yml up -d
# ---------- 镜像 ----------
# anqicms 版本号
IMAGE_TAG_VER=latest
# 完整镜像引用(默认 Docker Hub 官方镜像)
IMAGE_TAG=anqicms/anqicms:${IMAGE_TAG_VER}
# ---------- 数据卷 ----------
# 宿主机持久化目录(存放应用配置、上传文件、缓存、主题、插件等)
# 部署前手动创建:mkdir -pv /data/volumes/anqicms
Volumes_Path=/data/volumes/anqicms
# ---------- 服务端口 ----------
# 宿主机端口(容器内固定 8001)
ANQICMS_PORT=8001
+125
View File
@@ -0,0 +1,125 @@
# anqicms 部署栈
[anqicms](https://github.com/anqicms/anqicms) 是一个轻量、SEO 友好的中文内容管理系统,面向博客、自媒体和中小型企业建站场景,基于 Go 开发,开箱即用。
## 目录结构
| 文件 | 说明 |
|------|------|
| `stack.yml` | Docker Compose 编排文件 |
| `env.cfg.example` | 环境变量模板(不含敏感信息,可提交) |
| `env.cfg` | **实际环境变量(gitignore,不提交)** — 部署时从 example 复制 |
| `readme.md` | 本文档 |
## 服务端口
| 宿主机 | 容器 | 说明 |
|--------|------|------|
| `${ANQICMS_PORT}`(默认 `8001` | `8001` | Web 服务(含前台 + 后台) |
宿主机端口可通过 `env.cfg` 中的 `ANQICMS_PORT` 自定义。
## 数据卷映射
| 容器路径 | 宿主机路径 | 说明 |
|----------|------------|------|
| `/app` | `${Volumes_Path}` | 应用数据根(配置、上传、缓存、主题、插件等) |
| `/etc/timezone` | `/etc/timezone` | 时区(只读) |
| `/etc/localtime` | `/etc/localtime` | 本地时间(只读) |
## 环境变量
### 公共变量(`env.cfg.example`
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `IMAGE_TAG_VER` | `latest` | anqicms 镜像版本号 |
| `IMAGE_TAG` | `anqicms/anqicms:${IMAGE_TAG_VER}` | 完整镜像引用 |
| `Volumes_Path` | `/data/volumes/anqicms` | 宿主机持久化目录 |
| `ANQICMS_PORT` | `8001` | 宿主机端口 |
## 部署步骤
### 首次部署
```bash
# 1. 准备数据目录
mkdir -pv /data/volumes/anqicms
# 2. 复制 env 模板并按需修改
cp env.cfg.example env.cfg
$EDITOR env.cfg
# 3. 拉取镜像
docker compose -p anqicms --env-file ./anqicms/env.cfg -f ./anqicms/stack.yml pull
# 4. 启动服务
docker compose -p anqicms --env-file ./anqicms/env.cfg -f ./anqicms/stack.yml up -d
```
### 首次访问
容器启动后,浏览器访问 `http://<host-ip>:${ANQICMS_PORT}/`,按安装向导完成:
- 设置管理员账号 / 密码
- 选择数据库(默认 SQLite,零外部依赖;如需 MySQL 在向导里填连接信息)
- 完成初始化
### 验证
```bash
# 容器状态
docker ps -f name=anqicms
# 端口监听
ss -tlnp | grep ${ANQICMS_PORT}
# 容器日志
docker logs -f anqicms
# 健康检查
curl -sI http://localhost:${ANQICMS_PORT}/
```
### 停止 / 重启
```bash
# 停止(保留数据)
docker compose -p anqicms --env-file ./anqicms/env.cfg -f ./anqicms/stack.yml stop
# 完全销毁(**数据不删**,bind 挂载保留在宿主机)
docker compose -p anqicms --env-file ./anqicms/env.cfg -f ./anqicms/stack.yml down
# 重启
docker compose -p anqicms --env-file ./anqicms/env.cfg -f ./anqicms/stack.yml restart
```
## 升级
```bash
# 1. 备份数据(先停服)
docker compose -p anqicms --env-file ./anqicms/env.cfg -f ./anqicms/stack.yml stop
sudo rsync -a /data/volumes/anqicms/ /data/backups/anqicms/$(date +%Y%m%d)/
# 2. 修改 IMAGE_TAG_VER 后拉取
$EDITOR env.cfg
docker compose -p anqicms --env-file ./anqicms/env.cfg -f ./anqicms/stack.yml pull
# 3. 重建并启动
docker compose -p anqicms --env-file ./anqicms/env.cfg -f ./anqicms/stack.yml up -d
```
## 故障排查
| 症状 | 排查命令 |
|------|----------|
| 容器起不来 | `docker logs -f anqicms` |
| 端口冲突 | `ss -tlnp \| grep ${ANQICMS_PORT}` |
| 无法访问 | 检查防火墙/安全组、`curl -sI http://localhost:${ANQICMS_PORT}/` |
| 数据丢失 | 确认 bind 挂载路径正确,不要用 anonymous volume |
## 相关链接
- GitHub: <https://github.com/anqicms/anqicms>
- Docker Hub: <https://hub.docker.com/r/anqicms/anqicms>
- 文档: <https://www.anqicms.com/docs>
+24
View File
@@ -0,0 +1,24 @@
# path:: mkdir -pv /data/volumes/anqicms
# pull:: docker compose -p anqicms --env-file ./anqicms/env.cfg -f ./anqicms/stack.yml pull
# run:: docker compose -p anqicms --env-file ./anqicms/env.cfg -f ./anqicms/stack.yml up -d
# disc::
# - /app 是容器内应用数据根目录(配置、上传、缓存、主题、插件等)
# - bind 挂载到 ${Volumes_Path} 实现持久化
# - 首次启动后通过 http://<host>:8001 进入安装向导
services:
anqicms:
image: ${IMAGE_TAG}
container_name: anqicms
restart: unless-stopped
environment:
- TZ=Asia/Shanghai
ports:
- "${ANQICMS_PORT}:8001"
volumes:
- ${Volumes_Path}:/app
- ${Volumes_Path}/public:/app/public
- ${Volumes_Path}/template:/app/template
- ${Volumes_Path}/data:/app/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
+11
View File
@@ -0,0 +1,11 @@
# ============================================================
# ansible 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p ansible --env-file ./env.cfg -f ./stack.yml up -d
#
IMAGE_TAG=hub.tp229.com:3500/ansible-alpine:py3.13-rootless
+13
View File
@@ -0,0 +1,13 @@
# ============================================================
# base 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p base --env-file ./env.cfg -f ./stack.yml up -d
#
Cadvisor_Tag_Ver=v0.53.0
Cadvisor_Image=hub.6t7.net/base/cadvisor:${Cadvisor_Tag_Ver}
+11
View File
@@ -0,0 +1,11 @@
# ============================================================
# alpine 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p alpine --env-file ./env.cfg -f ./stack.yml up -d
#
IMAGE_TAG=alpine:3
+11
View File
@@ -0,0 +1,11 @@
# ============================================================
# debian 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p debian --env-file ./env.cfg -f ./stack.yml up -d
#
IMAGE_TAG=debian:13
+16
View File
@@ -0,0 +1,16 @@
# ============================================================
# golang 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p golang --env-file ./env.cfg -f ./stack.yml up -d
#
IMAGE_TAG_BASH=golang:1.25-trixie
IMAGE_TAG_ASH=golang:1.25-alpine
Volumes_Path=/data/volumes
+15
View File
@@ -0,0 +1,15 @@
# ============================================================
# nodejs 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p nodejs --env-file ./env.cfg -f ./stack.yml up -d
#
IMAGE_TAG_BASH=node:22.18.0-bookworm
IMAGE_TAG_ASH=node:22.18.0-alpine
Volumes_Path=/data/volumes
+6
View File
@@ -0,0 +1,6 @@
{
"$schema": "https://charm.land/crush.json",
"options": {
"context_paths": ["."]
}
}
+22
View File
@@ -0,0 +1,22 @@
# ============================================================
# couchdb 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p couchdb --env-file ./env.cfg -f ./stack.yml up -d
#
# ============================================================
# ⚠️ 敏感值(必填,env.cfg 不提交)
# ============================================================
IMAGE_TAG=couchdb:3.5
COUCHDB_PORT=5984
COUCHDB_USER=your_custom_user
COUCHDB_PASSWORD=<your-db-password>
Volumes_Path=/data/volumes/couchdb
+13
View File
@@ -0,0 +1,13 @@
# ============================================================
# loki 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p loki --env-file ./env.cfg -f ./stack.yml up -d
#
IMAGE_TAG=3.6.2
IMAGE_NAME=grafana/loki:${IMAGE_TAG}
+26
View File
@@ -0,0 +1,26 @@
# ============================================================
# mysql 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p mysql --env-file ./env.cfg -f ./stack.yml up -d
#
# ============================================================
# ⚠️ 敏感值(必填,env.cfg 不提交)
# ============================================================
# 密码为生成的密码,请使用时修改后再部署
IMAGE_TAG=mysql:8.4
IMAGE_TAG_V9=mysql:9.7
IMAGE_TAG_V8=mysql:8.0
IMAGE_TAG_LTS=mysql:8.4
MYSQL_ROOT_PASSWORD=<your-strong-password>
Volumes_Path=/data/volumes
+30
View File
@@ -0,0 +1,30 @@
# ============================================================
# postgres 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p postgres --env-file ./env.cfg -f ./stack.yml up -d
#
# ============================================================
# ⚠️ 敏感值(必填,env.cfg 不提交)
# ============================================================
# 密码为生成的密码,请使用时修改后再部署
#IMAGE_TAG=postgres:14.11-bookworm
#IMAGE_TAG=postgres:15.6-bookworm
IMAGE_TAG=postgres:16.14
IMAGE_TAG_V17=postgres:17.10
IMAGE_TAG_V18=postgres:18.4
IMAGE_TAG_V15=postgres:15.18
POSTGRES_PASSWORD=<your-strong-password>
POSTGRES_HOST_AUTH_METHOD=scram-sha-256
+26
View File
@@ -0,0 +1,26 @@
# ============================================================
# redis 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p redis --env-file ./env.cfg -f ./stack.yml up -d
#
# ============================================================
# ⚠️ 敏感值(必填,env.cfg 不提交)
# ============================================================
IMAGE_TAG_REDISV6=redis:6.2.21-alpine
IMAGE_TAG_REDISV7=redis:7.4.7-alpine
IMAGE_TAG_REDISV8=redis:8.4.0-alpine
IMAGE_TAG_VALKEY_Latest=valkey/valkey:9.0.0
IMAGE_TAG_VALKEY_V8=valkey/valkey:8.1.5
Volumes_Path=/data/volumes/redis
REDIS_PASSWORD=<your-strong-password>
+15
View File
@@ -0,0 +1,15 @@
# ============================================================
# domain-admin 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p domain-admin --env-file ./env.cfg -f ./stack.yml up -d
#
IMAGE_TAG_VER=v1.6.78
IMAGE_TAG=mouday/domain-admin:${IMAGE_TAG_VER}
Volumes_Path=/data/volumes/domain-admin
+13
View File
@@ -0,0 +1,13 @@
# ============================================================
# drawnix 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p drawnix --env-file ./env.cfg -f ./stack.yml up -d
#
IMAGE_TAG_VER=v0.4.1
IMAGE_TAG=pubuzhixing/drawnix:${IMAGE_TAG_VER}
+23
View File
@@ -0,0 +1,23 @@
# ============================================================
# Dufs 部署 — 公共环境变量
# 复制为 env.cfg 后填入真实值,env.cfg 已在 .gitignore 中忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# docker compose -p dufs --env-file ./env.cfg -f ./stack.yml up -d
# ---------- 镜像 ----------
# Dufs 版本号
IMAGE_TAG_VER=v0.46.0
# 完整镜像引用
IMAGE_TAG=sigoden/dufs:${IMAGE_TAG_VER}
# ---------- 数据卷 ----------
# 宿主机持久化目录(存放服务文件数据)
# 部署前手动创建:mkdir -pv /data/volumes/dufs
Volumes_Path=/data/volumes/dufs
# ---------- 配置路径 ----------
# 宿主机配置文件/脚本目录
Config_Path=/data/mygit/config
+27
View File
@@ -0,0 +1,27 @@
{
"storage-driver": "overlay2",
"data-root": "/data/docker",
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3",
},
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Hard": 65535,
"Soft": 65535,
},
},
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://docker.1ms.run",
"https://docker.1panel.dev",
"https://docker.367231.xyz",
"https://dockerproxy.net",
"https://hub.rat.dev",
],
"_comment_20260618173842": "\"insecure-registries\": [\"hub.tp229.com:3500\"]",
"_comment_20260619173657": "按实际情况填写",
"_comment_20260619173708": "\"bip\": \"172.18.0.1/16\"",
}
+18
View File
@@ -0,0 +1,18 @@
# ============================================================
# flame 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p flame --env-file ./env.cfg -f ./stack.yml up -d
#
# ============================================================
# ⚠️ 敏感值(必填,env.cfg 不提交)
# ============================================================
IMAGE_TAG=pawelmalak/flame:multiarch2.3.1
Volumes_Path=/data/volumes/flame
PASSWORD="<your-strong-password>"
+30
View File
@@ -0,0 +1,30 @@
# ============================================================
# Gitea 部署 — 公共环境变量
# 复制为 env.cfg 后填入真实值,env.cfg 已在 .gitignore 中忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# docker compose -p gitea --env-file ./env.cfg -f ./lky-prod.yml up -d
#
# 数据库等敏感配置在容器 /etc/gitea/app.ini 中配置(挂载自 config/ 目录)
# ---------- 镜像 ----------
# Gitea 版本号(rootless 镜像带 -rootless 后缀)
IMAGE_TAG_VER=1.26.2-rootless
# 完整镜像引用(私有仓库示例:hub.tp229.com:3500/gitea/gitea:${IMAGE_TAG_VER}
IMAGE_TAG=gitea/gitea:${IMAGE_TAG_VER}
# ---------- 数据卷 ----------
# 宿主机持久化目录
# 部署前手动创建并授权:
# mkdir -pv /data/volumes/gitea/{data,config}
# chown -R 1000:1000 /data/volumes/gitea/{data,config} # rootless 镜像必须
Volumes_Path=/data/volumes/gitea
# ---------- SSH 端口 ----------
# 容器内 SSH 端口固定 2222;按部署主机二选一:
# LAY_SSH_HOST_PORT —— 宿主 22 空闲的机器(lky-prod.yml 引用)
# SSH_HOST_PORT —— 宿主 22 被占用的机器(rpi-prod.yml 引用,如树莓派)
LAY_SSH_HOST_PORT=22
SSH_HOST_PORT=2222
+128
View File
@@ -0,0 +1,128 @@
# ============================================================
# goofish 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 .env 后填入真实值,.env 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp .env.example .env
# $EDITOR .env # 填入真实密码/密钥后保存
# docker compose -p goofish --env-file ./.env -f ./stack.yml up -d
#
# ============================================================
# ⚠️ 敏感值(必填,.env 不提交)
# ============================================================
# --- AI 模型相关配置 ---
# 模型的API Key。
OPENAI_API_KEY="<your-api-key>"
# 模型的API接口地址。这里需要填写服务商提供的、兼容OpenAI格式的API地址,基本所有模型都有提供OpenAI格式兼容的接口
# 可查阅你使用的大模型API文档,如格式为 https://xx.xx.com/v1/chat/completions 则OPENAI_BASE_URL只需要填入前半段 https://xx.xx.com/v1/
OPENAI_BASE_URL="https://api.siliconflow.cn/v1/chat/completions"
# 使用的模型名称,模型需要支持图片上传。
OPENAI_MODEL_NAME="zai-org/GLM-4.5V"
# (可选) 为AI请求配置HTTP/S代理。支持 http 和 socks5。例如: http://127.0.0.1:7890 或 socks5://127.0.0.1:1080
PROXY_URL=""
# ntfy 通知服务配置
NTFY_TOPIC_URL="https://ntfy.sh/your-topic-name" # 替换为你的 ntfy 主题 URL
# (可选) Gotify 通知服务配置
GOTIFY_URL="" # 你的 Gotify 服务地址, 例如: https://push.example.de
GOTIFY_TOKEN="<your-token>" # 你的 Gotify 应用的 Token
# (可选) Bark 通知服务配置
BARK_URL="" # 你的 Bark 推送地址, 例如: https://api.day.app/your_key
# 企业微信机器人通知配置 如果无则不用配置
WX_BOT_URL="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxx"
# (可选) 通用 Webhook 通知配置
WEBHOOK_URL="" # 你的 Webhook URL, 例如: https://foo.bar.com/quz?a=b
WEBHOOK_METHOD="POST" # 请求方法: "GET" 或 "POST"
WEBHOOK_HEADERS='{"X-API-TOKEN":"your-secret-token"}' # 自定义请求头 (JSON格式)
WEBHOOK_CONTENT_TYPE="JSON" # POST请求内容类型: "JSON" 或 "FORM"
WEBHOOK_QUERY_PARAMETERS='{"title":"{{title}}","content":"{{content}}"}' # GET请求的查询参数 (JSON格式, 支持 {{title}}, {{content}} 占位符)
WEBHOOK_BODY='{"title":"{{title}}","content":"{{content}}"}' # POST请求的请求体 (JSON格式, 支持 {{title}}, {{content}} 占位符)
# 是否使用edge浏览器 默认使用chrome浏览器
LOGIN_IS_EDGE=false
# 是否开启电脑链接转换为手机链接
PCURL_TO_MOBILE=true
# 爬虫是否以无头模式运行 (true/false)。
# 本地运行时遇到滑动验证码时,可设为 false 手动进行滑动验证,如果出现风控建议停止运行。
# 使用docker部署不支持GUI,设置 RUN_HEADLESS=true 否则无法运行。
RUN_HEADLESS=true
# (可选) AI调试模式 (true/false)。开启后会在控制台打印更多用于排查AI分析问题的日志。
AI_DEBUG_MODE=false
# 是否启用enable_thinking参数 (true/false)。某些AI模型需要此参数,而有些则不支持。
ENABLE_THINKING=false
# 服务端口自定义 不配置默认8000
SERVER_PORT=8000
# Web服务认证配置
WEB_USERNAME=cnphpbb
WEB_PASSWORD=<your-strong-password>
+13
View File
@@ -0,0 +1,13 @@
# ============================================================
# grafana 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p grafana --env-file ./env.cfg -f ./stack.yml up -d
#
IMAGE_TAG_VER=13.0.2
IMAGE_TAG=grafana/grafana:${IMAGE_TAG_VER}
+68 -3
View File
@@ -1,4 +1,69 @@
## Grafana
# Grafana 部署说明
image: grafana/grafana:12.2.0
ports: 3000
[Grafana](https://grafana.com/) 是一个开源的可观测性平台,支持 Prometheus、InfluxDB、Loki、Elasticsearch 等多种数据源,提供丰富的仪表盘和告警能力,适用于基础设施监控、应用性能分析和日志可视化等场景。
## 目录结构
| 文件 | 说明 |
|------|------|
| `compose.yml` | Docker Compose 部署配置 |
| `env.cfg` | 环境变量(镜像版本) |
| `env.cfg.example` | 环境变量模板(不含敏感信息) |
## 部署命令
```bash
# 拉取镜像
docker compose -p grafana --env-file ./grafana/env.cfg -f ./grafana/compose.yml pull
# 启动服务
docker compose -p grafana --env-file ./grafana/env.cfg -f ./grafana/compose.yml up -d
# 停止服务
docker compose -p grafana --env-file ./grafana/env.cfg -f ./grafana/compose.yml down
# 查看日志
docker compose -p grafana --env-file ./grafana/env.cfg -f ./grafana/compose.yml logs -f
```
## 环境变量说明
| 变量 | 说明 | 示例 |
|------|------|------|
| `IMAGE_TAG_VER` | Grafana 镜像版本 | `13.0.2` |
| `IMAGE_TAG` | 完整镜像标签 | `grafana/grafana:13.0.2` |
## 访问服务
- 默认端口:`3000`
- 访问地址:`http://<主机IP>:3000`
- 默认登录凭据:`admin / admin`(首次登录后建议修改密码)
## 持久化
当前配置使用无状态模式,容器重启后仪表盘和配置会丢失。如需持久化,建议在 `compose.yml` 中添加卷挂载:
```yaml
services:
grafana:
volumes:
- ${Volumes_Path:-/data/volumes/grafana}:/var/lib/grafana # 数据持久化
```
并确保宿主机目录权限正确:
```bash
# 创建数据目录
mkdir -pv /data/volumes/grafana
# 设置目录属主为 Grafana 容器用户(UID 472
chown -R 472:472 /data/volumes/grafana
```
## 注意事项
- Grafana 和 Gitea 默认都使用 3000 端口,部署在同一宿主机时需通过 `env.cfg` 或 compose 调整端口映射避免冲突
- 容器使用 `restart: unless-stopped` 策略,宿主机重启后自动拉起
- 默认使用匿名用户访问,如需接入 LDAP/OAuth 等认证,需挂载自定义 `grafana.ini` 配置文件
- 生产环境建议配置数据源和告警渠道的持久化存储
- 当前 `env.cfg.example` 版本为 `13.0.2`,实际部署版本以 `env.cfg` 为准
+23
View File
@@ -0,0 +1,23 @@
# ============================================================
# haproxy 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p haproxy --env-file ./env.cfg -f ./stack.yml up -d
#
IMAGE_TAG_VER=3.3.0
IMAGE_TAG_DEV=3.4-dev
IMAGE_TAG=haproxy:${IMAGE_TAG_VER}
Volumes_Path=/data/configs/haproxy
REDIS_PORT=6379
MYSQL_TEST_PORT=3306
MYSQL_DEV_PORT=3308
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash
# ============================================================
# Hindsight 备份任务(每日 02:30 跑,cron 部署参考 crontab/
# - 热备份:docker exec pg_dump 落盘到 bind 挂载的 backups/
# - 保留 7 天的 .sql.gz
# ============================================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=/dev/null
source "${SCRIPT_DIR}/env.cfg"
BACKUP_DIR="${Volumes_Path}/backups"
KEEP_DAYS=7
TS=$(date +%Y%m%d_%H%M%S)
FNAME="hindsight_db_${TS}.sql.gz"
mkdir -p "${BACKUP_DIR}"
docker exec hindsight-db pg_dump \
-U hindsight_user \
-d hindsight_db \
--no-owner --no-privileges \
| gzip > "${BACKUP_DIR}/${FNAME}"
# 清理超过 KEEP_DAYS 天的旧备份
find "${BACKUP_DIR}" -name "hindsight_db_*.sql.gz" -mtime +${KEEP_DAYS} -delete
echo "[backup] ok: ${FNAME} ($(du -h "${BACKUP_DIR}/${FNAME}" | cut -f1))"
+43
View File
@@ -0,0 +1,43 @@
# ============================================================
# Hindsight 部署 — 公共环境变量(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
# 镜像版本
HINDSIGHT_VERSION=latest
HINDSIGHT_DB_VERSION=18
# 镜像源
HINDSIGHT_DB_IMAGE=pgvector/pgvector
HINDSIGHT_APP_IMAGE=ghcr.nju.edu.cn/vectorize-io/hindsight
# 数据库账号/库名
HINDSIGHT_DB_USER=hindsight_user
HINDSIGHT_DB_NAME=hindsight_db
POSTGRES_HOST_AUTH_METHOD=scram-sha-256
# 宿主机数据卷路径(**必须 WSL/Linux ext4 原生 fs**,不能放 /mnt/9P
# bind 挂载 PostgreSQL 数据;放在 ~/hindsight/pgdata 而非 /mnt/d/mydata/
# 因为 9P drvfs 的 fsync 不可靠,会导致 PG 数据损坏。
Volumes_Path=/data/Volumes/hindsight
# 服务端口(宿主机:容器)
HINDSIGHT_DB_PORT=5432
HINDSIGHT_API_PORT=8888
HINDSIGHT_ADMIN_PORT=9999
HINDSIGHT_CP_DATAPLANE_API_URL=http://0.0.0.0:8888
# LLMMiniMax OpenAI-compatible 协议)
HINDSIGHT_API_LLM_PROVIDER=minimax
HINDSIGHT_API_LLM_MODEL=MiniMax-M3
HINDSIGHT_API_LLM_BASE_URL=https://api.minimaxi.com/v1
# 日志
HINDSIGHT_API_LOG_LEVEL=info
# ============================================================
# 敏感值(必填,env.cfg 不提交)— 在 .gitignore 已忽略
# PostgreSQL 密码 最好不要带特殊字符
# ============================================================
# HINDSIGHT_DB_PASSWORD=<强密码>
# HINDSIGHT_API_LLM_API_KEY=<你的 MiniMax key>
+210
View File
@@ -0,0 +1,210 @@
# Hindsight 部署栈
[vectorize-io/hindsight](https://github.com/vectorize-io/hindsight) 是一个面向 LLM Agent 的长期记忆后端,采用 PostgreSQL/pgvector 存储。Hermes Agent 用它做跨会话长期记忆。
本目录是 **方案三:DB + Hindsight 分离部署** 迁移到 `deploy.stack` 仓库的版本。
## 目录结构
| 文件 | 说明 |
|------|------|
| `stack.yml` | Docker Compose 主文件(2 服务:db + hindsight |
| `env.cfg.example` | 公共环境变量模板(不含敏感信息,可提交) |
| `env.cfg` | **敏感配置(gitignore,不提交)** — 实际部署时从 example 复制后填密码/API Key |
| `backup.job` | 每日 `pg_dump` 热备份脚本,保留 7 天 |
| `readme.md` | 本文档 |
## 架构
```
┌──────────────────────┐ ┌──────────────────────┐
│ hindsight-app 容器 │ │ hindsight-db 容器 │
│ (ghcr.nju.edu.cn/ │ :5432 内网 │ (pgvector/pgvector │
│ vectorize-io/ │ ────────────► │ :pg18) │
│ hindsight) │ │ │
│ │ :8888 API │ :5432 对外暴露 │
│ │ :9999 Admin │ (供外部工具连接) │
└──────────────────────┘ └──────────────────────┘
│ │
▼ bind mount ▼ bind mount
${Volumes_Path}/backups ${Volumes_Path}/pgdata
(pg_dump 落盘目录) (PG 数据,原生 ext4)
```
## 端口
| 端口 | 服务 | 用途 |
|------|------|------|
| `5432` | db | PostgreSQL 对外访问(DBeaver、pgAdmin 等工具连接) |
| `8888` | hindsight | API 服务 |
| `9999` | hindsight | Admin UI |
宿主机端口可通过 `env.cfg` 中的 `HINDSIGHT_DB_PORT``HINDSIGHT_API_PORT``HINDSIGHT_ADMIN_PORT` 自定义。
## 环境变量
### 公共变量(`env.cfg.example`
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `HINDSIGHT_VERSION` | `latest` | Hindsight App 镜像标签 |
| `HINDSIGHT_DB_VERSION` | `18` | PostgreSQL 大版本号 |
| `HINDSIGHT_DB_IMAGE` | `pgvector/pgvector` | DB 镜像仓库 |
| `HINDSIGHT_APP_IMAGE` | `ghcr.nju.edu.cn/vectorize-io/hindsight` | App 镜像仓库(南大 ghcr 镜像) |
| `HINDSIGHT_DB_USER` | `hindsight_user` | PostgreSQL 用户名 |
| `HINDSIGHT_DB_NAME` | `hindsight_db` | PostgreSQL 数据库名 |
| `POSTGRES_HOST_AUTH_METHOD` | `scram-sha-256` | PG 认证方式,同步用于 `INITDB_ARGS --auth-host` |
| `Volumes_Path` | `/data/Volumes/hindsight` | 宿主机持久化数据根路径 |
| `HINDSIGHT_DB_PORT` | `5432` | PostgreSQL 宿主机端口 |
| `HINDSIGHT_API_PORT` | `8888` | API 宿主机端口 |
| `HINDSIGHT_ADMIN_PORT` | `9999` | Admin UI 宿主机端口 |
| `HINDSIGHT_CP_DATAPLANE_API_URL` | `http://0.0.0.0:8888` | 数据面 API 地址 |
| `HINDSIGHT_API_LLM_PROVIDER` | — | LLM 提供商 |
| `HINDSIGHT_API_LLM_MODEL` | — | LLM 模型名 |
| `HINDSIGHT_API_LLM_BASE_URL` | — | LLM API Base URL |
| `HINDSIGHT_API_LOG_LEVEL` | `info` | 日志级别 |
### 敏感变量(`env.cfg`,不提交)
| 变量 | 说明 |
|------|------|
| `HINDSIGHT_DB_PASSWORD` | PostgreSQL 密码(**最好不要带特殊字符**,避免 URI 解码问题) |
| `HINDSIGHT_API_LLM_API_KEY` | LLM API Key |
## 部署步骤
### 首次部署
```bash
# 1. 准备数据目录(必须原生 ext4,不能放 9P drvfs
sudo mkdir -pv /data/Volumes/hindsight/{pgdata,backups}
sudo chown -R 999:999 /data/Volumes/hindsight/pgdata
# 2. 复制 env 模板并填入真实值
cp env.cfg.example env.cfg
$EDITOR env.cfg
# 必填:HINDSIGHT_DB_PASSWORD, HINDSIGHT_API_LLM_API_KEY
# 3. 拉镜像
docker compose --env-file ./hindsight/env.cfg -f ./hindsight/stack.yml pull
# 4. 启动
docker compose -p hindsight --env-file ./hindsight/env.cfg -f ./hindsight/stack.yml up -d
```
### 验证
```bash
# 容器状态
docker ps -f name=hindsight
# DB 连接与建表
docker exec -it hindsight-db psql -U hindsight_user -d hindsight_db -c '\dt'
# 端口监听
ss -tlnp | grep -E '5432|8888|9999'
# API 健康检查
curl -s http://localhost:8888/health
```
### 停止/重启
```bash
# 停止(保留数据)
docker compose -p hindsight --env-file ./hindsight/env.cfg -f ./hindsight/stack.yml stop
# 完全销毁(**数据不删**,bind 挂载保留在宿主机)
docker compose -p hindsight --env-file ./hindsight/env.cfg -f ./hindsight/stack.yml down
# 重启
docker compose -p hindsight --env-file ./hindsight/env.cfg -f ./hindsight/stack.yml restart
```
## 关键设计决策
| 决策点 | 决定 | 原因 |
|--------|------|------|
| 数据卷方案 | bind 挂载到 `${Volumes_Path}/pgdata` | 直观、可直接 `rsync`/`pg_dump`、跨机迁移用 `tar` 整个目录即可 |
| 端口绑定 | 0.0.0.0,含 DB 5432 | PVE LAN 上其他 VM 也可访问;DB 端口暴露方便外部工具(DBeaver 等)连接 |
| 镜像源 | `ghcr.nju.edu.cn/vectorize-io/hindsight` | 南京大学 ghcr 镜像,国内拉得快 |
| LLM | MiniMax-M3 via api.minimaxi.com | 用 MiniMax 的 OpenAI-compatible 协议 |
| PG 认证 | `scram-sha-256` | 比 md5 更安全,`POSTGRES_INITDB_ARGS` 同步设置 `--auth-host` |
| 旧数据迁移 | 见 `docs-hermes-Hindsight-记忆系统部署指南.md` 决策表 | 走 A(zip 导入) 或 B(重置新 DB) 路径 |
## 备份与恢复
### 自动备份
`backup.job``pg_dump` 热备份脚本(不停服),落盘到 bind 挂载的 `backups/`。接入 cron
```bash
# /etc/cron.d/hindsight-backup 或 crontab -e
30 2 * * * /path/to/deploy.stack/hindsight/backup.job >> /var/log/hindsight-backup.log 2>&1
```
或参考 `crontab/` 目录的统一任务管理方式(`shell/up.bash` 会处理 `chmod +x`)。
### 手动备份
```bash
# 热备份(推荐)
docker exec hindsight-db pg_dump -U hindsight_user -d hindsight_db | gzip > /data/Volumes/hindsight/backups/manual_$(date +%Y%m%d).sql.gz
# 冷备份(停服时,更彻底)
docker compose -p hindsight --env-file ./hindsight/env.cfg -f ./hindsight/stack.yml stop
sudo rsync -a /data/Volumes/hindsight/pgdata/ /data/Volumes/hindsight/backups/pgdata-cold/
docker compose -p hindsight --env-file ./hindsight/env.cfg -f ./hindsight/stack.yml start
```
### 恢复
```bash
# 从 pg_dump 恢复
gunzip -c /data/Volumes/hindsight/backups/hindsight_db_20260607_023000.sql.gz \
| docker exec -i hindsight-db psql -U hindsight_user -d hindsight_db
# 从冷备份恢复(停服 + 替换 bind 目录)
docker compose -p hindsight --env-file ./hindsight/env.cfg -f ./hindsight/stack.yml down
sudo rm -rf /data/Volumes/hindsight/pgdata/*
sudo rsync -a /data/Volumes/hindsight/backups/pgdata-cold/ /data/Volumes/hindsight/pgdata/
sudo chown -R 999:999 /data/Volumes/hindsight/pgdata
docker compose -p hindsight --env-file ./hindsight/env.cfg -f ./hindsight/stack.yml up -d
```
## 故障排查
| 症状 | 排查命令 |
|------|----------|
| 容器起不来 | `docker logs -f hindsight-app` / `docker logs -f hindsight-db` |
| DB 连不上(容器内) | `docker exec -it hindsight-db psql -U hindsight_user -d hindsight_db` |
| DB 连不上(外部工具) | 确认 `HINDSIGHT_DB_PORT` 已映射、`ss -tlnp` 检查 5432 监听 |
| 慢查询 | 在 psql 里 `SELECT * FROM pg_stat_activity;` |
| 端口冲突 | `ss -tlnp \| grep -E '5432\|8888\|9999'` |
| 端口未对外 | `ss -tlnp` 看是不是只监听 `127.0.0.1`,确认 `ports:` 没加 IP 前缀 |
| 密码含特殊字符导致连接失败 | `HINDSIGHT_DB_PASSWORD` 最好不要带特殊字符,避免 URI 编码问题 |
| glibc 错误 | 0.7.2 内嵌 pg0 需 glibc 2.38,方案三已用独立容器避开 |
## 与 Hermes 集成
Hermes plugin 通过 HTTP 调用本服务的 API(`localhost:8888`),不直连 DB。配置在 `~/.hermes/config.yaml`
```yaml
memory:
provider: hindsight
hindsight:
api_url: http://localhost:8888
bank_id: hermes
memory_mode: hybrid
auto_recall: true
auto_retain: true
retain_async: true
```
切换命令:`hermes config set memory.provider hindsight`
## 相关文档
- `~/Obsibian/MyNotes/DevOps/04-AI工具/docs-hermes-Hindsight-记忆系统部署指南.md` — 完整部署指南(700+ 行,含方案对比、旧数据迁移决策表)
- 仓库根 `AGENTS.md``deploy.stack` 项目规范
- `crontab/` — 定时任务集成参考
+77
View File
@@ -0,0 +1,77 @@
# Hindsight 部署栈
# ============================================================
# 部署前准备(仅首次):
# mkdir -pv ${Volumes_Path}/{pgdata,backups,huggingface}
# sudo chown -R 999:999 ${Volumes_Path}/pgdata
# cp env.cfg.example env.cfg && $EDITOR env.cfg # 填入密码/API Key
#
# pull:: docker compose --env-file ./hindsight/env.cfg -f ./hindsight/stack.yml pull
# RUN:: docker compose -p hindsight --env-file ./hindsight/env.cfg -f ./hindsight/stack.yml up -d
# disc::
# - DB 数据 bind 挂到 /home/geng/hindsight/pgdataWSL 原生 fs,避 9P fsync 风险)
# - 复用宿主 HF 缓存(bge + ms-marco 不重下)
# - 镜像走南京大学 ghcr 镜像,国内拉得快
# - 端口 8888=API, 9999=Admin UI, 5432=PostgreSQL,绑定 0.0.0.0 供 LAN VM 访问
# ============================================================
services:
db:
image: ${HINDSIGHT_DB_IMAGE}:pg${HINDSIGHT_DB_VERSION:-18}
container_name: hindsight-db
restart: unless-stopped
environment:
- TZ=Asia/Shanghai
- POSTGRES_USER=${HINDSIGHT_DB_USER:-hindsight_user}
- POSTGRES_PASSWORD=${HINDSIGHT_DB_PASSWORD:?set HINDSIGHT_DB_PASSWORD}
- POSTGRES_DB=${HINDSIGHT_DB_NAME:-hindsight_db}
- POSTGRES_HOST_AUTH_METHOD=${POSTGRES_HOST_AUTH_METHOD}
- POSTGRES_INITDB_ARGS=--encoding=UTF8 --locale=C --auth-host=${POSTGRES_HOST_AUTH_METHOD}
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- ${Volumes_Path}/pgdata:/var/lib/postgresql/${HINDSIGHT_DB_VERSION:-18}/docker
ports:
- "${HINDSIGHT_DB_PORT:-5432}:5432"
healthcheck:
test:
[
"CMD-SHELL",
"pg_isready -U ${HINDSIGHT_DB_USER:-hindsight_user} -d ${HINDSIGHT_DB_NAME:-hindsight_db}",
]
interval: 10s
timeout: 5s
retries: 10
networks:
- hindsight-net
hindsight:
image: ${HINDSIGHT_APP_IMAGE}:${HINDSIGHT_VERSION:-latest}
container_name: hindsight-app
restart: unless-stopped
depends_on:
db:
condition: service_healthy
ports:
- "${HINDSIGHT_API_PORT:-8888}:8888"
- "${HINDSIGHT_ADMIN_PORT:-9999}:9999"
environment:
- TZ=Asia/Shanghai
- HINDSIGHT_API_LLM_PROVIDER=${HINDSIGHT_API_LLM_PROVIDER}
- HINDSIGHT_API_LLM_API_KEY=${HINDSIGHT_API_LLM_API_KEY:?set HINDSIGHT_API_LLM_API_KEY}
- HINDSIGHT_API_LLM_MODEL=${HINDSIGHT_API_LLM_MODEL}
- HINDSIGHT_API_LLM_BASE_URL=${HINDSIGHT_API_LLM_BASE_URL}
- HINDSIGHT_API_DATABASE_URL=postgresql://${HINDSIGHT_DB_USER}:${HINDSIGHT_DB_PASSWORD}@db:5432/${HINDSIGHT_DB_NAME}
- HINDSIGHT_API_LOG_LEVEL=${HINDSIGHT_API_LOG_LEVEL:-info}
- HINDSIGHT_CP_DATAPLANE_API_URL=${HINDSIGHT_CP_DATAPLANE_API_URL}
- HF_HUB_OFFLINE=1
- TRANSFORMERS_OFFLINE=1
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- ${Volumes_Path}/backups:/home/hindsight/backups
networks:
- hindsight-net
networks:
hindsight-net:
driver: bridge
+126
View File
@@ -0,0 +1,126 @@
#!/usr/bin/env bash
#
# create-multi-keys.sh — 在 vm61 (10.8.0.9, 备用/测试) honcho 上批量创建 3 个客户端的 API key
#
# 用法:
# 1. 在 vm61 上: ./create-multi-keys.sh
# 2. 输出 3 个 key 文件到 ~/.honcho-keys/chmod 600
# 3. scp 到对应客户端
#
# 前置:
# - honcho-api 容器跑着(监听 10.8.0.9:18000
# - AUTH_USE_AUTH=true + 管理员 tokenhoncho init 时生成)
# - workspace "home" 已存在
#
# ⚠️ 不要 echo key 到聊天。文件 chmod 600scp 时 -p 保留权限。
set -euo pipefail
WORKSPACE="${HONCHO_WORKSPACE:-home}"
HONCHO_API="${HONCHO_API_URL:-http://10.8.0.9:18000}"
ADMIN_TOKEN_FILE="${HONCHO_ADMIN_TOKEN_FILE:-/root/.honcho-keys/admin.key}"
# 客户端清单: name:peer:device_hint
CLIENTS=(
"alpha:laodeng-mypc03:mypc03"
"beta:laodeng-govpc:gov-pc"
"gamma:laodeng-mac:macbook"
)
OUT_DIR="${OUT_DIR:-/root/.honcho-keys}"
mkdir -p "$OUT_DIR"
chmod 700 "$OUT_DIR"
if [[ ! -f "$ADMIN_TOKEN_FILE" ]]; then
echo "❌ 找不到 admin token: $ADMIN_TOKEN_FILE" >&2
echo " 把 honcho init 时生成的 admin token 写到该文件,chmod 600" >&2
exit 1
fi
ADMIN_TOKEN="$(cat "$ADMIN_TOKEN_FILE")"
echo "==> honcho: $HONCHO_API"
echo "==> workspace: $WORKSPACE"
echo "==> 输出目录: $OUT_DIR"
echo ""
for entry in "${CLIENTS[@]}"; do
IFS=':' read -r slug peer device <<< "$entry"
key_file="$OUT_DIR/${device}.key"
info_file="$OUT_DIR/${device}.json"
echo "--- [$device] peer=$peer ---"
# 检查 peer 是否已存在(幂等)
existing="$(curl -fsS -H "Authorization: Bearer *** "$HONCHO_API/v2/workspaces/$WORKSPACE/peers/$peer" 2>/dev/null || true)"
if [[ -n "$existing" && "$existing" != *"404"* ]]; then
echo " peer 已存在,跳过创建"
else
# 创建 peer
curl -fsS -X POST \
-H "Authorization: Bearer *** \
-H "Content-Type: application/json" \
-d "{\"name\":\"$peer\"}" \
"$HONCHO_API/v2/workspaces/$WORKSPACE/peers" \
| tee "$info_file.tmp" > /dev/null
mv "$info_file.tmp" "$info_file"
chmod 600 "$info_file"
echo " ✅ peer 已创建"
fi
# 创建 API key(每次新建,旧的不 revoke)
# honcho 的 key 创建 API 名称可能因版本不同,下面给常见两种尝试
key_resp="$(curl -fsS -X POST \
-H "Authorization: Bearer *** \
-H "Content-Type: application/json" \
-d "{\"name\":\"$peer\",\"scope\":\"peer:$peer\"}" \
"$HONCHO_API/v2/workspaces/$WORKSPACE/api-keys" 2>/dev/null || true)"
if [[ -z "$key_resp" ]]; then
# 备选 endpoint
key_resp="$(curl -fsS -X POST \
-H "Authorization: Bearer *** \
-H "Content-Type: application/json" \
-d "{\"name\":\"$peer\"}" \
"$HONCHO_API/v2/auth/keys 2>/dev/null || true)"
fi
if [[ -z "$key_resp" ]]; then
echo " ❌ 创建 key 失败,请检查 honcho API 版本" >&2
echo " 临时手动: honcho create user --workspace $WORKSPACE --name $peer" >&2
continue
fi
# 提取 token(兼容多种字段名)
token="$(echo "$key_resp" | python3 -c '
import json, sys
try:
d = json.load(sys.stdin)
print(d.get("token") or d.get("api_key") or d.get("key") or d.get("value") or "")
except Exception:
print("")
')"
if [[ -z "$token" ]]; then
echo " ❌ 无法从响应提取 token:"
echo "$key_resp" | head -c 500
echo ""
continue
fi
echo -n "$token" > "$key_file"
chmod 600 "$key_file"
echo " ✅ key 已写入: $key_file ($(wc -c < "$key_file") bytes)"
done
echo ""
echo "==> 完成。当前 keys:"
ls -la "$OUT_DIR"
echo ""
echo "下一步:"
echo " scp $OUT_DIR/mypc03.key mypc03:~/.honcho-keys/"
echo " scp $OUT_DIR/govpc.key govpc:~/.honcho-keys/"
echo " scp $OUT_DIR/mac.key macbook:~/.honcho-keys/"
echo ""
echo "⚠️ 不要 echo 这些 key 到聊天/日志/screenshot"
+85
View File
@@ -0,0 +1,85 @@
# ============================================================
# Honcho Self-Hosted 部署 — 公共环境变量(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
# ============================================================
# 镜像版本
# ============================================================
# Honcho 主仓库 git ref(源码 build 用,可填 tag / branch / SHA
HONCHO_VERSION=main
# PostgreSQL 大版本号(pgvector 镜像 tag 后缀,必须与镜像仓库支持的 tag 对应)
HONCHO_DB_VERSION=18
# Redis 大版本号
HONCHO_REDIS_VERSION=8
# 镜像仓库(默认公共镜像;自建私有 hub 可改 hub.tp229.com:3500/...
HONCHO_DB_IMAGE=pgvector/pgvector
HONCHO_REDIS_IMAGE=redis
# ============================================================
# PostgreSQL 数据库
# ============================================================
HONCHO_DB_USER=honcho
HONCHO_DB_NAME=honcho
# scram-sha-256 比 md5 安全;与 POSTGRES_INITDB_ARGS --auth-host 同步
POSTGRES_HOST_AUTH_METHOD=scram-sha-256
# ============================================================
# 宿主机数据卷路径(必须 WSL/Linux ext4 原生 fs,不能放 /mnt/9P
# 9P drvfs 的 fsync 不可靠,会导致 PG 数据损坏
# ============================================================
Volumes_Path=/data/volumes/honcho
# ============================================================
# 服务端口(宿主机:容器,全部绑定 0.0.0.0 供 LAN 访问)
# 5432 与 hindsight/dbSer 撞车,多服务共存时改 HONCHO_DB_PORT 区分
# ============================================================
HONCHO_API_PORT=8000
HONCHO_DB_PORT=5432
HONCHO_REDIS_PORT=6379
# ============================================================
# Honcho 应用配置
# ============================================================
HONCHO_LOG_LEVEL=INFO
# 本地 LAN 自用可设 false;公网暴露必须改 true
HONCHO_AUTH_USE_AUTH=false
HONCHO_NAMESPACE=honcho
# 关闭可省 embedding 费用(但失去语义检索能力)
HONCHO_EMBED_MESSAGES=true
# Embedding 模型(默认走 SiliconFlow 兼容端点,独立 base URL
HONCHO_EMBEDDING_MODEL=Qwen/Qwen3-Embedding-8B
HONCHO_EMBEDDING_BASE_URL=https://api.siliconflow.cn/v1
# 替代方案 A:本地 Ollama(零成本、隐私优先)
# HONCHO_EMBEDDING_MODEL=qwen3-embedding:0.6b
# HONCHO_EMBEDDING_BASE_URL=http://host.docker.internal:11434/v1
# EMBEDDING_API_KEY 可留空(Ollama 默认无 key;如配了 OLLAMA_API_KEY 环境变量再填)
# 前置:宿主机 `ollama pull qwen3-embedding:0.6b`
# docker compose 中 host.docker.internal 在 Linux compose v2 默认可用,Windows/WSL2 自动转发
# Embedding 维度(Qwen3-Embedding-8B 默认 4096,但通过 SiliconFlow 的 dimensions 参数强制输出 1536
# 必须与已有 DB schema 的 public.documents.embedding dim 一致;切换模型必须先 migrate 旧数据
# 若 Honcho 自动推断失败,需在 stack.yml 同步设置 EMBEDDING_VECTOR_DIMENSIONS=1536
# deriver 必须用支持 tool calling 的模型
HONCHO_DERIVER_MODEL=deepseek-ai/DeepSeek-V4-Flash
# dreamer 的 deduction/induction specialist(默认走 OpenAI SDK + gpt-5.4-mini
# 会请求 api.openai.com/v1 在国内超时);必须显式覆盖到国内可达端点
HONCHO_DEDUCTION_MODEL=deepseek-ai/DeepSeek-V4-Flash
HONCHO_INDUCTION_MODEL=deepseek-ai/DeepSeek-V4-Flash
HONCHO_DERIVER_WORKERS=1
# ============================================================
# LLMOpenAI 兼容协议;可换 OpenRouter / vLLM / Ollama 等端点)
# deriver 启动时强校验 API Key 非空,占位符会反复重启
# 注意:必须同时被 deriver/dreamer/embedding 复用,否则 specialist 会回落 openai.com
# ============================================================
HONCHO_LLM_BASE_URL=https://api.siliconflow.cn/v1
# ============================================================
# 敏感值(必填,env.cfg 不提交)— 在 .gitignore 已忽略
# 占位符约定:REPLACE_ME_XXX → 替换为真实值(避免使用 <...> 占位符,patch 工具链会吞)
# ============================================================
# HONCHO_LLM_API_KEY=REPLACE_ME_LLM_KEY # 你的 OpenAI key 或兼容端点 key
# HONCHO_EMBEDDING_API_KEY=REPLACE_ME_EMBEDDING_KEY # SiliconFlow keyhttps://cloud.siliconflow.cn/account/ak
# HONCHO_DB_PASSWORD=REPLACE_ME_DB_PASSWORD # 强密码,不要带特殊字符,避免 URI 编码问题
# HONCHO_AUTH_JWT_SECRET=REPLACE_ME_JWT_SECRET # 仅 AUTH_USE_AUTH=true 时需要;用 python scripts/generate_jwt_secret.py 生成
+374
View File
@@ -0,0 +1,374 @@
# Honcho Self-Hosted 部署栈
[plastic-labs/honcho](https://github.com/plastic-labs/honcho) 是 Plastic Labs 开源的 **AI Agent 长期记忆后端**v3.0.xAGPL-3.0)。Hermes Agent 用它做跨会话的用户上下文管理(peer-centric 记忆、推理、检索)。本目录部署的是 **honcho-self-hosted**:基于 [elkimek/honcho-self-hosted](https://github.com/elkimek/honcho-self-hosted) 配置的本地化版本,集成到 `deploy.stack` 仓库规范下。
与官方 `docker-compose.yml.example` 的差异:本目录用 **PostgreSQL 18 + Redis 8**(非官方默认的 pg15/redis 8.2)、`${Volumes_Path}` bind 挂载(非 docker volume)、固定到内网可访问的端口。
## 目录结构
| 文件 | 说明 |
|------|------|
| `stack.yml` | Docker Compose 主文件(4 服务:api + deriver + database + redis |
| `env.cfg.example` | 公共环境变量模板(不含敏感信息,可提交) |
| `env.cfg` | **敏感配置(gitignore,不提交)** — 实际部署时从 example 复制后填密码/API Key |
| `readme.md` | 本文档 |
## 架构
```mermaid
flowchart TB
subgraph src["源码构建期(无运行时 bind"]
source["${Volumes_Path}/honcho/source<br/>git clone plastic-labs/honcho<br/>api/deriver Dockerfile build context"]
end
subgraph run["运行时 — honcho-net bridge"]
api["<b>honcho-api</b><br/>FastAPI 主服务<br/>:8000 (0.0.0.0)<br/>build from source"]
deriver["<b>honcho-deriver</b><br/>后台推理 worker<br/>消息→观察→peer representation<br/>build from source"]
database["<b>honcho-database</b><br/>pgvector/pgvector:pg18<br/>:5432 (0.0.0.0)<br/>scram-sha-256"]
redis["<b>honcho-redis</b><br/>redis:8<br/>:6379<br/>CACHE_URL"]
end
subgraph host["宿主机持久化 (WSL ext4)"]
pgdata["${Volumes_Path}/pgdata<br/>(bind mount)"]
redisdata["${Volumes_Path}/redis<br/>(bind mount)"]
end
subgraph external["外部调用方"]
hermes["Hermes Agent<br/>memory.provider=honcho"]
sdk["Python / TS SDK<br/>(honcho-ai / @honcho-ai/sdk)"]
tools["DBeaver / psql<br/>PG 5432 直连"]
siliconflow["SiliconFlow<br/>api.siliconflow.cn/v1<br/>Qwen3-Embedding-8B<br/>dimensions=1536<br/>(云端默认)"]
ollama["本地 Ollama<br/>host.docker.internal:11434<br/>qwen3-embedding:0.6b<br/>(备选,零成本)"]
end
source -.->|docker build<br/>首次 5-10 min| api
source -.->|docker build| deriver
hermes -->|HTTP :8000| api
sdk -->|HTTP :8000| api
tools -->|:5432| database
api -->|:5432| database
deriver -->|:5432| database
api -->|:6379| redis
deriver -->|:6379| redis
deriver -.->|轮询<br/>pending messages| api
api -->|HTTPS embedding<br/>Qwen3-8B 1536d| siliconflow
deriver -->|HTTPS embedding<br/>Qwen3-8B 1536d| siliconflow
database --- pgdata
redis --- redisdata
classDef ext fill:#e1f5ff,stroke:#01579b,color:#000
classDef build fill:#fff3e0,stroke:#e65100,color:#000
classDef data fill:#f3e5f5,stroke:#4a148c,color:#000
classDef host fill:#e8f5e9,stroke:#1b5e20,color:#000
class hermes,sdk,tools,siliconflow,ollama ext
class source build
class api,deriver,database,redis ext
class pgdata,redisdata data
```
> **源码不在运行时 bind mount**api/deriver 通过 `build: context: .` 从 `git clone` 的源码目录(`${Volumes_Path}/honcho/source`)构建,**不**挂源码进运行容器。`source/` 仅作为构建缓存,方便后续 `git pull` 后增量 rebuild。
**四个服务的职责:**
| 服务 | 角色 | 关键依赖 |
|------|------|----------|
| `api` | FastAPI 主服务,对外提供 REST API(端口 8000 | database、redis |
| `deriver` | **后台推理 worker**:消息→观察→peer representation→session 摘要 | api(健康后才起)、database、redis |
| `database` | PostgreSQL + pgvector 扩展,存所有 messages、peers、workspaces | — |
| `redis` | 缓存层(`CACHE_URL=redis://redis:6379/0` | — |
**⚠️ deriver 不可省**:没它,消息能写入 DB,但不会生成记忆、不会提取观察、不会做 session 摘要。Honcho 退化成纯 KV 存储。
## 端口
| 端口 | 服务 | 用途 |
|------|------|------|
| `8000` | api | Honcho REST API(供 Hermes / Python SDK / TypeScript SDK 调用) |
| `5432` | database | PostgreSQL 对外访问(DBeaver、psql 等工具连接) |
| `6379` | redis | Redis 对外访问(一般无需直连) |
宿主机端口可通过 `env.cfg` 中的 `HONCHO_API_PORT``HONCHO_DB_PORT``HONCHO_REDIS_PORT` 自定义。
## 环境变量
### 公共变量(`env.cfg.example`
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `HONCHO_VERSION` | `main` | Honcho 主仓库 git reftag / branch / SHA 都可;源码 build 用) |
| `HONCHO_BRANCH` | `main` | 源码 checkout 分支(`HONCHO_VERSION` 不是 branch 时生效) |
| `HONCHO_DB_VERSION` | `18` | PostgreSQL 大版本号(pgvector 镜像 tag 后缀) |
| `HONCHO_REDIS_VERSION` | `8` | Redis 大版本号 |
| `HONCHO_DB_IMAGE` | `pgvector/pgvector` | DB 镜像仓库 |
| `HONCHO_REDIS_IMAGE` | `redis` | Redis 镜像仓库 |
| `HONCHO_DB_USER` | `honcho` | PostgreSQL 用户名 |
| `HONCHO_DB_NAME` | `honcho` | PostgreSQL 数据库名 |
| `HONCHO_DB_PASSWORD` | `honcho` | PostgreSQL 密码(**部署时必须改** |
| `POSTGRES_HOST_AUTH_METHOD` | `scram-sha-256` | PG 认证方式 |
| `Volumes_Path` | `/data/Volumes/honcho` | 宿主机持久化数据根路径 |
| `HONCHO_API_PORT` | `8000` | API 宿主机端口 |
| `HONCHO_DB_PORT` | `5432` | PostgreSQL 宿主机端口 |
| `HONCHO_REDIS_PORT` | `6379` | Redis 宿主机端口 |
| `HONCHO_LOG_LEVEL` | `INFO` | Honcho 日志级别 |
| `HONCHO_AUTH_USE_AUTH` | `false` | 是否启用 JWT 鉴权(生产建议 `true` |
| `HONCHO_NAMESPACE` | `honcho` | 全局命名空间前缀 |
| `HONCHO_EMBED_MESSAGES` | `true` | 是否对消息做向量化(关闭可省 embedding 费用) |
| `HONCHO_EMBEDDING_MODEL` | `Qwen/Qwen3-Embedding-8B` | Embedding 模型名(默认 SiliconFlow,可换本地 Ollama |
| `HONCHO_EMBEDDING_BASE_URL` | `https://api.siliconflow.cn/v1` | Embedding API Base URLOpenAI 兼容;Ollama 改 `http://host.docker.internal:11434/v1` |
| `HONCHO_DERIVER_MODEL` | `gpt-5.4-mini` | Deriver 用的 LLM 模型(必须支持 tool calling |
| `HONCHO_LLM_BASE_URL` | `https://api.openai.com/v1` | LLM API Base URLOpenAI 兼容端点即可) |
| `HONCHO_DERIVER_WORKERS` | `1` | Deriver worker 数 |
### 敏感变量(`env.cfg`,不提交)
| 变量 | 说明 |
|------|------|
| `HONCHO_LLM_API_KEY` | LLM API Key**必填**server 启动检查,没它起不来) |
| `HONCHO_EMBEDDING_API_KEY` | Embedding API Key**必填**仅当走云端:SiliconFlow 在 [cloud.siliconflow.cn/account/ak](https://cloud.siliconflow.cn/account/ak) 生成;走 Ollama 时留空) |
| `HONCHO_DB_PASSWORD` | PostgreSQL 密码(**必填**,用强密码) |
| `HONCHO_AUTH_JWT_SECRET` | JWT 密钥(仅在 `HONCHO_AUTH_USE_AUTH=true` 时需要;用 `python scripts/generate_jwt_secret.py` 生成) |
**关键约束**Honcho 启动时会**校验** `HONCHO_LLM_API_KEY``HONCHO_EMBEDDING_API_KEY` 都非空 — 缺失则立刻退出。这意味着 `env.cfg` 里这两项**不能用占位符**(如 `***`),必须填真实值,否则 `docker compose up -d` 后 deriver/api 容器会反复重启。
## 部署步骤
### 首次部署
```bash
# 1. 准备数据目录(必须原生 ext4,不能放 9P drvfs
sudo mkdir -pv /data/Volumes/honcho/{pgdata,redis,source}
sudo chown -R 999:999 /data/Volumes/honcho/pgdata
sudo chown -R 999:999 /data/Volumes/honcho/redis
# 2. 复制 env 模板并填入真实值
cp honcho/env.cfg.example honcho/env.cfg
$EDITOR honcho/env.cfg
# 必填:HONCHO_LLM_API_KEY(不能用空值/默认值,否则 deriver 起不来)
# 必填:HONCHO_DB_PASSWORD(改成强密码)
# 推荐改:HONCHO_DB_USER / HONCHO_DB_NAME(默认值 honcho 仅 dev 可用)
# 3. 拉镜像(PG + Redis 是预构建;api/deriver 从源码 build
docker compose --env-file ./honcho/env.cfg -f ./honcho/stack.yml pull
# 4. 构建 + 启动(首次 api/deriver 构建需 5-10 分钟,依赖网络)
docker compose -p honcho --env-file ./honcho/env.cfg -f ./honcho/stack.yml up -d --build
```
### 验证
```bash
# 容器状态(必须 4 个都 healthy)
docker ps -f name=honcho
# API 健康检查
curl -s http://localhost:8000/health
# 数据库连接
docker exec -it honcho-database psql -U honcho -d honcho -c '\dt'
# pgvector 扩展确认
docker exec -it honcho-database psql -U honcho -d honcho \
-c "SELECT extname FROM pg_extension WHERE extname='vector';"
# Redis 连接
docker exec -it honcho-redis redis-cli ping
# 期望输出:PONG
# deriver 日志(看是否成功处理消息)
docker logs -f honcho-deriver --tail 50
# 端口监听
ss -tlnp | grep -E '8000|5432|6379'
```
### 停止/重启
```bash
# 停止(保留数据)
docker compose -p honcho --env-file ./honcho/env.cfg -f ./honcho/stack.yml stop
# 完全销毁(**数据不删**,bind 挂载保留在宿主机)
docker compose -p honcho --env-file ./honcho/env.cfg -f ./honcho/stack.yml down
# 重启
docker compose -p honcho --env-file ./honcho/env.cfg -f ./honcho/stack.yml restart
```
## 关键设计决策
| 决策点 | 决定 | 原因 |
|--------|------|------|
| 镜像源 | api/deriver 从 [plastic-labs/honcho](https://github.com/plastic-labs/honcho) **源码 build** | 官方**没有预构建镜像**,Docker Hub 也没 — 必须 build |
| DB 版本 | PostgreSQL 18pgvector 镜像 `pg18` tag) | 用户指定,比官方默认 pg15 新 |
| Redis 版本 | Redis 8 | 用户指定 |
| 数据卷方案 | bind 挂载到 `${Volumes_Path}/{pgdata,redis,source}` | 直观、可直接 `rsync`/`pg_dump`、跨机迁移用 `tar` 整个目录即可 |
| 端口绑定 | 0.0.0.0,含 DB 5432 | PVE LAN 上其他 VM 也可访问;DB 端口暴露方便外部工具(DBeaver 等)连接 |
| 认证 | 默认 `HONCHO_AUTH_USE_AUTH=false`(dev 模式) | 本地 LAN 自用,跳过 JWT 校验;公网暴露必须改 `true` |
| LLM 默认 | `gpt-5.4-mini`(主 LLMOpenAI 兼容);embedding 默认 `Qwen/Qwen3-Embedding-8B`dimensions=1536 强制输出,兼容已有 1536 维 schema)走 SiliconFlow**备选** 本地 Ollama `qwen3-embedding:0.6b`(零成本、隐私优先) | LLM 走 OpenAI 兼容;embedding 双轨:SiliconFlow(云端,独立 base URL / Ollama(本地 `host.docker.internal:11434/v1` |
| Honcho 版本 | 默认 `main`(源码 build),可锁 tag 如 `v3.0.10` | 当前最新稳定版 v3.0.x,支持 peer card、dialectic、dream consolidation |
| deriver 必须 | api + deriver 双容器 | deriver 是后台推理 worker,没它记忆功能不工作 |
| PG 认证 | `scram-sha-256` | 比 md5 更安全,`POSTGRES_INITDB_ARGS` 同步设置 `--auth-host` |
## 关键陷阱(必读)
### 1. 首次构建时间长且需要 BuildKit
```bash
# 必须开启 BuildKit(默认 20.10+ 已启用,但确认下)
DOCKER_BUILDKIT=1 docker compose ...
```
- 首次 build api/deriver 镜像需 **5-10 分钟**(拉源码 + uv sync Python 依赖 + pip install ~300MB
- 后续增量构建快(除非 Honcho 自身依赖变更)
- 构建失败常见原因:网络拉 `astral-sh/uv` 慢、Python wheel 编译失败 — 配置 Docker 镜像加速
### 2. deriver 必须健康,否则记忆不工作
如果只看到 `honcho-api``healthy``honcho-deriver` 反复重启或卡在 startup,**消息会写入但不会产生任何记忆**。检查:
```bash
docker logs honcho-deriver --tail 100 | grep -iE "error|fatal"
```
deriver 起不来最常见原因:
- `HONCHO_LLM_API_KEY` 为空/无效 — server 启动立即校验
- LLM 模型不支持 tool calling — deriver 强依赖 function calling
- LLM API base URL 配错(如少了 `/v1` 后缀)
### 3. AGPL-3.0 传染条款
Honcho 是 **AGPL-3.0**,对网络服务部署有"用户可获取源码"的要求。本地 LAN 自用不触发,但若对外提供服务(哪怕只是 API 反代),需考虑:
- 对外服务时 `AUTH_USE_AUTH=true` + 公开 `AUTH_JWT_SECRET` 之外,**还需要向用户公开 Honcho 源码修改**
- 详见 [AGPL-3.0 §13](https://www.gnu.org/licenses/agpl-3.0.html)
### 4. WSL 9P `fsync` 陷阱
**PG/Redis 数据绝不能放 `/mnt/c/`、`/mnt/d/` 等 9P drvfs 路径**`${Volumes_Path}` 必须指向 WSL 原生 ext4`/data/Volumes/honcho`),否则可能静默数据损坏。
### 5. `POSTGRES_HOST_AUTH_METHOD=trust` 已删除
honcho-self-hosted 相对官方 compose 的一个改进:**移除了**`trust` 认证(官方默认的 dev 用法),改用强密码 + scram-sha-256。生产环境务必保留这个设置。
### 6. 容器端口 vs 宿主机端口不可混用
**容器之间互访必须用容器内端口(5432/6379/8000**,不能用宿主机映射端口(如 `HONCHO_DB_PORT=25432`)。错误示例:
```yaml
# ❌ 错:${HONCHO_DB_PORT} 是宿主机端口 25432,容器内根本没监听这个
DB_CONNECTION_URI=postgresql+psycopg://honcho:pw@database:${HONCHO_DB_PORT}/honcho
# ✅ 对:硬编码容器内端口 5432
DB_CONNECTION_URI=postgresql+psycopg://honcho:pw@database:5432/honcho
```
同样 healthcheck 也用容器内端口 `localhost:8000`**不是** `localhost:18000`(宿主映射端口)。
## 故障排查
| 症状 | 排查命令 |
|------|----------|
| api 容器起不来 | `docker logs -f honcho-api` — 通常是 `LLM_OPENAI_API_KEY is required` 或 DB 连接失败 |
| deriver 反复重启 | `docker logs -f honcho-deriver` — 检查 LLM key、模型名、base URL |
| DB 连不上(容器内) | `docker exec -it honcho-database psql -U honcho -d honcho` |
| DB 连不上(外部工具) | 确认 `HONCHO_DB_PORT` 已映射、`ss -tlnp` 检查 5432 监听;密码含特殊字符试改简单 |
| API `/health` 502/超时 | 检查 deriver 是否 healthy`depends_on` 会卡住 api |
| pgvector 扩展缺失 | `docker exec -it honcho-database psql -U honcho -d honcho -c "CREATE EXTENSION IF NOT EXISTS vector;"` |
| 端口冲突 | `ss -tlnp \| grep -E '8000\|5432\|6379'` — 检查是否与 hindsight/dbSer 等服务撞车 |
| 慢查询 / DB 锁 | `docker exec -it honcho-database psql -U honcho -d honcho -c "SELECT * FROM pg_stat_activity;"` |
| 密码含特殊字符连不上 | `HONCHO_DB_PASSWORD` 最好只用字母数字,避免 URI 编码问题;如必须用,把 URL 里特殊字符 percent-encode |
## 与 Hermes Agent 集成
Hermes plugin 通过 HTTP 调用 Honcho 的 REST API`localhost:8000`),不直连 DB。配置在 `~/.hermes/config.yaml`
```yaml
memory:
provider: honcho
honcho:
api_url: http://localhost:8000
# api_key 不需要(本地自托管,AUTH_USE_AUTH=false
workspace_id: hermes
auto_recall: true
auto_retain: true
```
切换命令:`hermes config set memory.provider honcho`
**与 Hindsight 的差异**
- Hindsight 强调 **跨会话长期记忆**(基于时序、bi-temporal
- Honcho 强调 **peer-centric 用户画像**(多 peer 互观察、推理、deduction
- 两者可并存;Hermes 当前用 Honcho 做主记忆层,Hindsight 作为补充
## 备份与恢复
### 自动备份(推荐)
`backup.job`(待补)— 用 `pg_dump` 热备份 PG + `redis-cli BGSAVE` 备份 Redis。接入 cron
```bash
# /etc/cron.d/honcho-backup
30 3 * * * /path/to/deploy.stack/honcho/backup.job >> /var/log/honcho-backup.log 2>&1
```
或参考 `crontab/` 目录的统一任务管理方式。
### 手动备份
```bash
# PG 热备份
docker exec honcho-database pg_dump -U honcho -d honcho | \
gzip > /data/Volumes/honcho/backups/honcho_$(date +%Y%m%d).sql.gz
# Redis 快照
docker exec honcho-redis redis-cli BGSAVE
# 触发后 /data/Volumes/honcho/redis/dump.rdb 是最新快照
```
### 恢复
```bash
# 从 pg_dump 恢复(需要先创建空 DB)
gunzip -c /data/Volumes/honcho/backups/honcho_20260619.sql.gz | \
docker exec -i honcho-database psql -U honcho -d honcho
# 从 Redis dump 恢复
docker compose -p honcho --env-file ./honcho/env.cfg -f ./honcho/stack.yml stop honcho-redis
sudo cp /data/Volumes/honcho/backups/dump.rdb /data/Volumes/honcho/redis/dump.rdb
sudo chown 999:999 /data/Volumes/honcho/redis/dump.rdb
docker compose -p honcho --env-file ./honcho/env.cfg -f ./honcho/stack.yml start honcho-redis
```
## 升级
```bash
# 1. 拉取最新源码(在 host 上或临时容器)
cd /data/Volumes/honcho/source && git pull
# 2. 重建 api + deriver 镜像
docker compose -p honcho --env-file ./honcho/env.cfg -f ./honcho/stack.yml build api deriver
# 3. 滚动重启
docker compose -p honcho --env-file ./honcho/env.cfg -f ./honcho/stack.yml up -d
# 4. 跑迁移(honcho entrypoint.sh 会自动执行;若需手动跑)
docker exec -it honcho-api sh -c "cd /app && .venv/bin/alembic upgrade head"
```
**PG 大版本升级**(如未来 18→19)需 `pg_dump` → 新版本空 DB → restore**不能直接换 tag**。
## 相关文档
- [plastic-labs/honcho](https://github.com/plastic-labs/honcho) — 上游仓库
- [elkimek/honcho-self-hosted](https://github.com/elkimek/honcho-self-hosted) — 一键安装器,本目录配置基于此
- [Honcho 自托管官方文档](https://honcho.dev/docs/v3/contributing/self-hosting)
- `hindsight/readme.md` — 同仓库姊妹服务,部署模式高度相似
- 仓库根 `AGENTS.md``deploy.stack` 项目规范
- `crontab/` — 定时任务集成参考
## 已知限制
详见上文 [关键陷阱](#关键陷阱必读) — 主要为:无预构建镜像(必须源码构建)/ AGPL-3.0 传染 / deriver 强依赖 LLM。
+285
View File
@@ -0,0 +1,285 @@
# Honcho 多 Hermes 客户端共享方案
> 状态: 📝 草案 (2026-06-20 创建, 06-26 拓扑修正 + v3.0.10 schema 探针重写)
> 适用: vm61 (10.8.0.9, 备用/测试环境) 部署 honcho v3.0.10 + 3 个 hermes 客户端共享同一份记忆
---
## 1. 场景
| 客户端 | 位置 | WireGuard IP | 网络到 vm61 | peer 名 |
|---|---|---|---|---|
| mypc03 (WSL Debian) | 🏠 家里 LAN | `10.8.0.112` | WireGuard → vm61 | `laodeng-mypc03x` |
| gov-pc (公司机器) | 🏢 公司 | `10.8.0.143` | WireGuard → vm61 | `laodeng-govpcxxxx` |
| macbook | ☕ 任意位置 | `10.8.0.144` | WireGuard → vm61 | `laodeng-macxxxxxxx` |
**3 个客户端都是老登本人**用,所以希望:
-**共享 workspace**`home`):在 A 上聊的事,B 上能召回
-**peer 独立**(带设备后缀):方便审计"在哪台设备说的"
-**AUTH 关闭(DEV 模式)**vm61 honcho v3.0.10 实例 `USE_AUTH=False`JWT_SECRET 留空,peer 不强制鉴权;接入稳后再开
- ❌ 不共享 LLM 凭据:每个客户端用自己的 hermes LLM key
---
## 2. 架构
```mermaid
flowchart LR
subgraph Clients["3 个 Hermes 客户端"]
A[mypc03<br/>peer: laodeng-mypc03x]
B[gov-pc<br/>peer: laodeng-govpcxxxx]
C[macbook<br/>peer: laodeng-macxxxxxxx]
end
subgraph Vm61["vm61 (192.168.10.61 / WG 10.8.0.9, 备用)"]
WG[WireGuard<br/>10.8.0.9 入口]
API[honcho-api:8000<br/>AUTH OFF (v3 DEV)]
DB[(postgres+pgvector)]
DERIVER[honcho-deriver<br/>~2 done/min]
REDIS[(redis)]
end
A ==wg==> WG
B ==wg==> WG
C ==wg==> WG
WG --> API
API --> DB
API --> REDIS
DERIVER <--> REDIS
DERIVER <--> DB
classDef client fill:#e1f5ff,stroke:#0066cc
classDef svc fill:#fff4e1,stroke:#cc6600
classDef store fill:#e8f5e9,stroke:#2e7d32
class A,B,C client
class WG,API,DERIVER svc
class DB,REDIS store
```
**网络**3 客户端 (10.8.0.112/143/144) → WireGuard 隧道 (10.8.0.x) → vm61 (10.8.0.9) 8000
---
## 3. 为什么这样设计
| 决策 | 选择 | 替代方案 | 理由 |
|---|---|---|---|
| **AUTH 关闭(DEV** | ✅ OFF | ON | 当前 vm61 honcho v3.0.10 `USE_AUTH=False`;3 客户端同源(同老登)+ WireGuard 内网隔离已足够;生产或多人共用必须开 |
| **Workspace** | `home` 一个共用 | 每个 peer 独立 ws | 老登本人用,跨设备回忆体验更好 |
| **Peer** | 带设备后缀 | 统一 `laodeng` | 审计需要知道"哪台设备写的"peer 概念在 honcho 里是身份,不影响记忆语义 |
| **API URL** | `http://10.8.0.9:18000`vm61 WireGuard IP | 公网 IP / 域名 | WireGuard 已加密 + 认证,没必要再绕公网 |
| **LLM 凭据** | 客户端本地保留 | 共享到 honcho | deriver 已经持有全局 LLM key;客户端 hermes 自己的对话 LLM 不应上送 honcho |
| **deriver 后台** | 共享 vm61 上现有 | 每客户端本地跑 | honcho deriver 设计就是单实例后台,集中推理更省 API 配额 |
---
## 4. 实施步骤
### Phase 1: vm61 上 honcho 准备
```bash
# 1.1 确认 4 个容器 Up
docker ps | grep -E "honcho-(api|deriver|database|redis)"
# 1.2 确认 AUTH 状态(v3.0.10 实际为 DEV OFF2026-06-26 探针实测)
ssh yong@10.8.0.9 'docker exec honcho-api python3 -c "from src.config import settings; print(\"USE_AUTH:\", settings.AUTH.USE_AUTH)"'
# 期望: USE_AUTH: False(当前状态)
# 1.3 确认 honcho-api 监听 0.0.0.0(不是 127.0.0.1
docker inspect honcho-api | grep -A2 -E "HostPort|NetworkMode"
# 或直接看启动日志
docker logs honcho-api 2>&1 | grep -i "listening"
# 1.4 从 WireGuard 网卡验证
curl -s http://10.8.0.9:18000/health
# 期望: {"status":"ok"}
```
**如果 1.3 / 1.4 失败**
- stack.yml 里 `ports:` 必须暴露 `0.0.0.0:18000:18000`
- 6-19 修过的 `DB_CONNECTION_URI` / `CACHE_URL` 已稳定,但确认 WireGuard 网卡没被 firewall 拦
### Phase 2: v3 鉴权模式 + peer 创建
**v3.0.10 实测 schema2026-06-26 探针)**
| 关键事实 | 说明 |
|---|---|
| 端点前缀 | **/v3/**(不是 /v2/ |
| 鉴权 | `HTTPBearer(auto_error=False)` — 未带 token 放行(DEV 模式表现) |
| `AUTH.USE_AUTH` 开关 | False 时所有 peer 操作无需 tokenTrue 时需 admin JWT |
| 创建 JWT 端点 | `POST /v3/keys?workspace_id=&peer_id=&session_id=`admin auth 强制) |
| 创建 workspace | `POST /v3/workspaces` body `{"id":"home"}` |
| 创建 peer | `POST /v3/workspaces/{ws_id}/peers` body `{"id":"<peer_id>"}` |
| Peer ID 格式 | text 21 字符,`^[A-Za-z0-9_-]+$`**不是 UUID** |
| DB schema | `peers(id PK text, name text, workspace_name FK→workspaces(name))` |
| DB schema | **无 `api_keys` 表**(v3 用 JWT,无独立 key 表) |
**当前 DEV 模式(USE_AUTH=False)下,3 客户端接入步骤**
```bash
# 2.1 确认 workspace 已存在
ssh yong@10.8.0.9 'curl -s -X POST http://127.0.0.1:18000/v3/workspaces \
-H "Content-Type: application/json" -d "{\"id\":\"home\"}"'
# 期望: {"id":"home",...} 或已存在返回
# 2.2 创建 3 个 peerpeer_id 必须 21 字符,格式 ^[A-Za-z0-9_-]+$
for PEER in laodeng-mypc03x laodeng-govpcxxxx laodeng-macxxxxxxx; do
ssh yong@10.8.0.9 "curl -s -X POST http://127.0.0.1:18000/v3/workspaces/home/peers \
-H 'Content-Type: application/json' -d '{\"id\":\"$PEER\"}'"
echo "--- $PEER done ---"
done
# 期望: 3 个 200 OK 返回 peer 对象
```
**未来开 AUTHUSE_AUTH=True)时**:每个 peer 创建后用 `POST /v3/keys?workspace_id=home&peer_id=<id>` 拿 scoped JWTscp 到客户端 `~/.honcho-keys/<peer>.key`hermes config `memory.honcho.api_key` 填 JWT。
### Phase 3: 3 客户端配置 hermes
每台机器执行(**当前 DEV 模式不填 api_keyAUTH 开后填 JWT**):
```bash
# 通用(3 台都跑)
hermes config set memory.provider honcho
hermes config set memory.honcho.api_url http://10.8.0.9:18000
hermes config set memory.honcho.workspace home
# mypc03
hermes config set memory.honcho.peer laodeng-mypc03x
# gov-pc
hermes config set memory.honcho.peer laodeng-govpcxxxx
# macbook
hermes config set memory.honcho.peer laodeng-macxxxxxxx
```
### Phase 4: 交叉验证(关键!)
**Step A: 网络 + honcho 响应**3 台机器各跑)
```bash
# DEV 模式不带 token 也能访问
curl -s -o /dev/null -w "HTTP %{http_code}\n" \
http://10.8.0.9:18000/v3/workspaces/home/peers
# 期望: HTTP 200
```
**未来 AUTH 开启后改用**
```bash
curl -s -o /dev/null -w "HTTP %{http_code}\n" \
-H "Authorization: Bearer $(cat ~/.honcho-keys/xxx.key)" \
http://10.8.0.9:18000/v3/workspaces/home/peers
```
**Step B: 跨端召回**
```bash
# mypc03 写一条
hermes memory retain "2026-06-26 mypc03 测试 honcho v3 跨设备共享"
# gov-pc 召回
hermes memory recall "honcho v3 跨设备共享"
# 期望: 看到 mypc03 写的那条
```
**Step C: 隔离验证(确认 peer 字段)**
```bash
# honcho 管理(vm61 上)— 注意 v3 列名是 workspace_name 不是 workspace
ssh yong@10.8.0.9 'docker exec honcho-database psql -U honcho -d honcho -c \
"SELECT name, created_at FROM peers WHERE workspace_name = '\''home'\'';"'
# 期望: 3 行,name 各不相同(带设备后缀)
```
### Phase 5: deriver 容量观察
接入第 3 个客户端后,前 2 天重点观察:
```bash
# vm61 看 deriver 进度
docker logs honcho-deriver --since 1h | grep -E "queue|processed|done"
# 队列深度(v3 端点)
ssh yong@10.8.0.9 'curl -s http://127.0.0.1:18000/v3/workspaces/home/queue/status' | jq
```
**告警阈值**
| 指标 | 正常 | 警告 | 干预 |
|---|---|---|---|
| 队列深度 | < 500 | 500-2000 | > 2000 持续 1h → 考虑错峰 |
| deriver done/min | 2-4 | 4-8 | > 8 持续 → 加 worker |
| Postgres 连接 | < 50 | 50-100 | > 100 → 调大 max_connections |
---
## 5. 故障排查
| 症状 | 排查 | 修复 |
|---|---|---|
| `curl HTTP 401` | AUTH 已开但缺 token / token 过期 | 重新创建 JWT`POST /v3/keys?workspace_id=home` |
| `curl HTTP 422` | peer_id 格式不符(必须 21 字符 `^[A-Za-z0-9_-]+$` | 调整 peer_id 长度/字符 |
| `curl HTTP 404` | workspace 名错 | `hermes config get memory.honcho.workspace` 必须 = `home` |
| `curl timeout` | WireGuard 没拨上 | `wg show` 看握手时间;`ping 10.8.0.9` |
| 召回不到 mypc03 写的 | peer 不在同一 workspace | 三台 `hermes config get memory.honcho.workspace` 都该是 `home` |
| deriver 队列暴涨 | LLM 限流(SiliconFlow | 临时调低 deriver 并发;或加备份 LLM endpoint |
| honcho-api OOM | 写入太频繁 | `docker stats honcho-api`;考虑加 limit |
---
## 6. 撤销 / 隔离某个客户端
需要把 gov-pc 从共享里踢出去(换工作 / 借人):
**DEV 模式**:直接删 peerv3 REST
```bash
# v3 REST 删除 peerDEV 模式不带 token
ssh yong@10.8.0.9 'curl -s -X DELETE \
http://127.0.0.1:18000/v3/workspaces/home/peers/laodeng-govpcxxxx'
```
**AUTH 开启后**:先删 JWT,再删 peer
```bash
# 1. 服务端撤 JWTv3 没有 api_keys 表;JWT 自身无法服务端 revoke,只能靠短期 exp)
# 解决:缩短 JWT exp(如 24h),或重新生成 admin token 改 settings.AUTH
# 2. 删 peer
ssh yong@10.8.0.9 'curl -s -X DELETE \
-H "Authorization: Bearer $(cat /root/.honcho-keys/admin.key)" \
http://127.0.0.1:18000/v3/workspaces/home/peers/laodeng-govpcxxxx'
```
**清理历史消息**(如要彻底删除 gov-pc 的数据):
```sql
-- v3 schema: peer_id 是 text 不是 UUIDname 是 peer 的可读名
DELETE FROM messages WHERE peer_id = (SELECT id FROM peers WHERE name = 'laodeng-govpcxxxx');
DELETE FROM session_peers WHERE peer_id = (SELECT id FROM peers WHERE name = 'laodeng-govpcxxxx');
DELETE FROM peers WHERE name = 'laodeng-govpcxxxx';
```
---
## 7. 关联资源
| 资源 | 路径 | 说明 |
|---|---|---|
| honcho stack | `deploy.stack/honcho/` | 当前部署 |
| WireGuard 部署 | `deploy.stack/WireGuardVPN/wg-easy/` | 已有,给 3 客户端分配 10.8.0.112/143/144honcho 服务端在 10.8.0.9 |
| honcho deriver 调试 | honcho memory (2026-06-19/20) | deriver 修复历史 |
| Hermes memory provider 切换 | hermes-agent skill | `memory.provider honcho` 配法 |
| 凭据管理 | `~/.honcho-keys/` | chmod 600,绝不入 gitDEV 模式暂未启用 |
---
## 8. 后续 TODO(可选)
- [ ]`memory.honcho.api_url` 改成 hostname 而非 IPDNS 友好)
- [ ] 给 honcho-api 加 TLS(自签 CA 推 3 客户端)
- [ ] deriver 加 Prometheus exporter,接 vm61 VictoriaMetrics
- [ ] cron: 每天 pg_dump honcho DB → dufs 备份(参考 hindsight/backup.job
- [ ] 4 个客户端的 hermes 版本统一(避免 protocol 不一致)
- [ ] 评估何时开 AUTHUSE_AUTH=True+ 配 scoped JWT 体系
+173
View File
@@ -0,0 +1,173 @@
# Honcho Self-Hosted 部署栈
# ============================================================
# 部署前准备(仅首次):
# mkdir -pv ${Volumes_Path}/{pgdata,redis,source}
# sudo chown -R 999:999 ${Volumes_Path}/pgdata
# sudo chown -R 999:999 ${Volumes_Path}/redis
# git clone https://github.com/plastic-labs/honcho.git ${Volumes_Path}/honcho/source
# cp env.cfg.example env.cfg && $EDITOR env.cfg # 填入密码/API Key
#
# pull:: docker compose --env-file ./honcho/env.cfg -f ./honcho/stack.yml pull
# RUN:: docker compose -p honcho --env-file ./honcho/env.cfg -f ./honcho/stack.yml up -d --build
# disc::
# - api/deriver 从 ${Volumes_Path}/honcho/source 源码 build(不是运行时 bind)
# - DB/Redis 数据 bind 挂到 WSL/Linux 原生 ext4(不能放 /mnt/9P drvfs
# - 端口 8000=API, 5432=PG, 6379=Redis,全部绑定 0.0.0.0 供 LAN 访问
# - 必须设置 HONCHO_LLM_API_KEY(占位符会导致 deriver 反复重启)
# - deriver 不可省:没它消息能写但不会产生记忆
# - PG 5432 与 hindsight/dbSer 撞车,通过 HONCHO_DB_PORT 区分
# ============================================================
services:
api:
build:
context: ${Volumes_Path}/source
dockerfile: Dockerfile
container_name: honcho-api
restart: unless-stopped
entrypoint: ["sh", "docker/entrypoint.sh"]
depends_on:
database:
condition: service_healthy
redis:
condition: service_healthy
ports:
- "${HONCHO_API_PORT:-8000}:8000"
environment:
- TZ=Asia/Shanghai
- DB_CONNECTION_URI=postgresql+psycopg://${HONCHO_DB_USER}:${HONCHO_DB_PASSWORD}@database:5432/${HONCHO_DB_NAME}
- CACHE_URL=redis://redis:${HONCHO_REDIS_PORT}/0?suppress=true
- CACHE_ENABLED=true
- LOG_LEVEL=${HONCHO_LOG_LEVEL:-INFO}
- NAMESPACE=${HONCHO_NAMESPACE:-honcho}
- EMBED_MESSAGES=${HONCHO_EMBED_MESSAGES:-true}
- EMBEDDING_MODEL_CONFIG__TRANSPORT=openai
- EMBEDDING_MODEL_CONFIG__MODEL=${HONCHO_EMBEDDING_MODEL:-Qwen/Qwen3-Embedding-8B}
- EMBEDDING_MODEL_CONFIG__OVERRIDES__BASE_URL=${HONCHO_EMBEDDING_BASE_URL:-https://api.siliconflow.cn/v1}
- EMBEDDING_MODEL_CONFIG__OVERRIDES__DIMENSIONS=1536
- EMBEDDING_VECTOR_DIMENSIONS=1536
- EMBEDDING_API_KEY=${HONCHO_EMBEDDING_API_KEY:-}
- DERIVER_MODEL_CONFIG__TRANSPORT=openai
- DERIVER_MODEL_CONFIG__MODEL=${HONCHO_DERIVER_MODEL:-gpt-4o-mini}
- DERIVER_WORKERS=${HONCHO_DERIVER_WORKERS:-1}
- LLM_OPENAI_API_KEY=${HONCHO_LLM_API_KEY:?set HONCHO_LLM_API_KEY}
- MODEL_CONFIG__OVERRIDES__BASE_URL=${HONCHO_LLM_BASE_URL:-https://api.openai.com/v1}
- AUTH_USE_AUTH=${HONCHO_AUTH_USE_AUTH:-false}
- AUTH_JWT_SECRET=${HONCHO_AUTH_JWT_SECRET}
- VECTOR_STORE_TYPE=pgvector
- VECTOR_STORE_MIGRATED=false
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
healthcheck:
test:
- CMD
- /app/.venv/bin/python
- -c
- "import urllib.request; urllib.request.urlopen('http://localhost:8000/health', timeout=2).read()"
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
networks:
- honcho-net
deriver:
build:
context: ${Volumes_Path}/source
dockerfile: Dockerfile
container_name: honcho-deriver
restart: unless-stopped
entrypoint: ["/app/.venv/bin/python", "-m", "src.deriver"]
depends_on:
api:
condition: service_healthy
database:
condition: service_healthy
redis:
condition: service_healthy
environment:
- TZ=Asia/Shanghai
- DB_CONNECTION_URI=postgresql+psycopg://${HONCHO_DB_USER}:${HONCHO_DB_PASSWORD}@database:5432/${HONCHO_DB_NAME}
- CACHE_URL=redis://redis:${HONCHO_REDIS_PORT}/0?suppress=true
- CACHE_ENABLED=true
- NAMESPACE=${HONCHO_NAMESPACE:-honcho}
- DERIVER_MODEL_CONFIG__TRANSPORT=openai
- DERIVER_MODEL_CONFIG__MODEL=${HONCHO_DERIVER_MODEL:-deepseek-ai/DeepSeek-V4-Flash}
- DERIVER_MODEL_CONFIG__OVERRIDES__BASE_URL=${HONCHO_LLM_BASE_URL:-https://api.siliconflow.cn/v1}
- DEDUCTION_MODEL_CONFIG__TRANSPORT=openai
- DEDUCTION_MODEL_CONFIG__MODEL=${HONCHO_DEDUCTION_MODEL:-deepseek-ai/DeepSeek-V4-Flash}
- DEDUCTION_MODEL_CONFIG__OVERRIDES__BASE_URL=${HONCHO_LLM_BASE_URL:-https://api.siliconflow.cn/v1}
- INDUCTION_MODEL_CONFIG__TRANSPORT=openai
- INDUCTION_MODEL_CONFIG__MODEL=${HONCHO_INDUCTION_MODEL:-deepseek-ai/DeepSeek-V4-Flash}
- INDUCTION_MODEL_CONFIG__OVERRIDES__BASE_URL=${HONCHO_LLM_BASE_URL:-https://api.siliconflow.cn/v1}
- DERIVER_WORKERS=${HONCHO_DERIVER_WORKERS:-1}
- EMBEDDING_MODEL_CONFIG__TRANSPORT=openai
- EMBEDDING_MODEL_CONFIG__MODEL=${HONCHO_EMBEDDING_MODEL:-Qwen/Qwen3-Embedding-8B}
- EMBEDDING_MODEL_CONFIG__OVERRIDES__BASE_URL=${HONCHO_EMBEDDING_BASE_URL:-https://api.siliconflow.cn/v1}
- EMBEDDING_MODEL_CONFIG__OVERRIDES__DIMENSIONS=1536
- EMBEDDING_VECTOR_DIMENSIONS=1536
- LLM_OPENAI_API_KEY=${HONCHO_LLM_API_KEY:?set HONCHO_LLM_API_KEY}
- EMBEDDING_API_KEY=${HONCHO_EMBEDDING_API_KEY:-}
- MODEL_CONFIG__OVERRIDES__BASE_URL=${HONCHO_LLM_BASE_URL:-https://api.openai.com/v1}
- AUTH_USE_AUTH=${HONCHO_AUTH_USE_AUTH:-false}
- AUTH_JWT_SECRET=${HONCHO_AUTH_JWT_SECRET}
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
networks:
- honcho-net
database:
image: ${HONCHO_DB_IMAGE:-pgvector/pgvector}:pg${HONCHO_DB_VERSION:-18}
container_name: honcho-database
restart: unless-stopped
command: ["postgres", "-c", "max_connections=200"]
environment:
- TZ=Asia/Shanghai
- POSTGRES_USER=${HONCHO_DB_USER}
- POSTGRES_PASSWORD=${HONCHO_DB_PASSWORD:?set HONCHO_DB_PASSWORD}
- POSTGRES_DB=${HONCHO_DB_NAME}
- POSTGRES_HOST_AUTH_METHOD=${POSTGRES_HOST_AUTH_METHOD:-scram-sha-256}
- POSTGRES_INITDB_ARGS=--encoding=UTF8 --locale=C --auth-host=${POSTGRES_HOST_AUTH_METHOD:-scram-sha-256}
- PGDATA=/var/lib/postgresql/data/pgdata
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- ${Volumes_Path}/pgdata:/var/lib/postgresql/data
ports:
- "${HONCHO_DB_PORT:-5432}:5432"
healthcheck:
test:
- CMD-SHELL
- pg_isready -U ${HONCHO_DB_USER} -d ${HONCHO_DB_NAME}
interval: 10s
timeout: 5s
retries: 10
networks:
- honcho-net
redis:
image: ${HONCHO_REDIS_IMAGE:-redis}:${HONCHO_REDIS_VERSION:-8}
container_name: honcho-redis
restart: unless-stopped
command: ["redis-server", "--save", "60", "1", "--appendonly", "yes"]
environment:
- TZ=Asia/Shanghai
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- ${Volumes_Path}/redis:/data
ports:
- "${HONCHO_REDIS_PORT:-6379}:6379"
healthcheck:
test: ["CMD-SHELL", "redis-cli ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- honcho-net
networks:
honcho-net:
driver: bridge
+13
View File
@@ -0,0 +1,13 @@
# ============================================================
# hub-registry 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p hub-registry --env-file ./env.cfg -f ./stack.yml up -d
#
IMAGE_TAG=hub.tp229.com:3500/registry:3.0
UI_IMAGE_TAG=hub.tp229.com:3500/cnphpbb/registry-ui:latest
+1 -1
View File
@@ -1,4 +1,4 @@
# run:: docker compose -p memos --env-file ./it-tools/env.cfg -f ./it-tools/compose.yml up -d
# run:: docker compose -p it-tools --env-file ./it-tools/env.cfg -f ./it-tools/compose.yml up -d
services:
+11
View File
@@ -0,0 +1,11 @@
# ============================================================
# it-tools 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p it-tools --env-file ./env.cfg -f ./stack.yml up -d
#
IMAGE_TAG=hub.6t7.net/cnphpbb/it-tools:20241022
+59
View File
@@ -0,0 +1,59 @@
# IT Tools 部署栈
[IT Tools](https://github.com/CorentinTh/it-tools) 是一个面向开发者的在线工具集合,提供编码、转换、格式化、加密等多种实用功能。基于 Vue + TypeScript 构建,拥有良好的交互体验。
## 目录结构
| 文件 | 说明 |
|------|------|
| `compose.yml` | Docker Compose 部署配置 |
| `env.cfg` | 环境变量(镜像版本,已 gitignore) |
| `env.cfg.example` | 环境变量模板 |
## 部署命令
```bash
# 拉取镜像
docker compose -p it-tools --env-file ./it-tools/env.cfg -f ./it-tools/compose.yml pull
# 部署
docker compose -p it-tools --env-file ./it-tools/env.cfg -f ./it-tools/compose.yml up -d
```
## 环境变量
| 变量 | 说明 | 示例 |
|------|------|------|
| `IMAGE_TAG` | 完整镜像标签 | `hub.6t7.net/cnphpbb/it-tools:20241022` |
## 服务端口
| 宿主机 | 容器 |
|--------|------|
| `8380` | `80` |
## Docker 构建说明
上游仓库包含 `Dockerfile`,支持多阶段构建(node 构建 → nginx 运行):
```bash
git clone https://github.com/CorentinTh/it-tools.git
cd it-tools
# 构建镜像
docker buildx build --platform linux/amd64 -t hub.6t7.net/cnphpbb/it-tools:<tag> .
# 推送到仓库
docker push hub.6t7.net/cnphpbb/it-tools:<tag>
```
构建依赖:
- 构建阶段:`node:lts-alpine` + `pnpm`,执行 `pnpm build` 生成静态文件
- 运行阶段:`nginx:stable-alpine`,提供 `nginx.conf` 自定义配置
- 环境变量:`NPM_CONFIG_LOGLEVEL=warn``CI=true`
- 上游 Dockerfile 参考:[Dockerfile](https://github.com/CorentinTh/it-tools/blob/main/Dockerfile)
## 相关链接
- GitHub: <https://github.com/CorentinTh/it-tools>
- 在线体验: <https://it-tools.tech>
+32
View File
@@ -0,0 +1,32 @@
# ============================================================
# joplin 部署 — amd64 专用环境变量模板(不含敏感信息)
# 复制为 amd.env.cfg 后填入真实值,amd.env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp amd.env.cfg.example amd.env.cfg
# $EDITOR amd.env.cfg # 填入真实密码后保存
# docker compose -p joplin --env-file ./joplin/amd.env.cfg -f ./joplin/compose.yml up -d
#
# ============================================================
# ⚠️ 敏感值(必填,amd.env.cfg 不提交)
# ============================================================
IMAGE_TAG=joplin/server:3.6.1
# only amd64
#IMAGE_TAG=joplin/server:2.14.2-beta
## 实际部署时需要考虑APP_BASE_URL变量值
APP_BASE_URL=https://joplin.6t7.net
POSTGRES_USER=postgres
POSTGRES_PASSWORD=<your-strong-password>
POSTGRES_DATABASE=joplin-db
POSTGRES_PORT=5432
POSTGRES_HOST=10.0.74.3
+36
View File
@@ -0,0 +1,36 @@
# ============================================================
# joplin 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p joplin --env-file ./env.cfg -f ./stack.yml up -d
#
# ============================================================
# ⚠️ 敏感值(必填,env.cfg 不提交)
# ============================================================
#IMAGE_TAG=etechonomy/joplin-server:2.14.2
#IMAGE_TAG=florider89/joplin-server:2.14.2
IMAGE_TAG=joplin/server:3.6.1
# only amd64
#IMAGE_TAG=joplin/server:2.14.2-beta
## 实际部署时需要考虑APP_BASE_URL变量值
APP_BASE_URL=http://192.168.100.184:22300
POSTGRES_USER=postgres
POSTGRES_PASSWORD=<your-strong-password>
POSTGRES_DATABASE=joplin-db
POSTGRES_PORT=5432
POSTGRES_HOST=192.168.100.184
+1 -1
View File
@@ -3,4 +3,4 @@ DB_USER=postgres
DB_PASSWORD=Kevin0412PgSql
DB_DATANAME=memos-db
DB_HOST=192.168.100.184
DB_POST=5432
DB_POST=15432
+1 -1
View File
@@ -3,4 +3,4 @@ DB_USER=postgres
DB_PASSWORD=Kevin0412PgSql
DB_DATANAME=memos-db
DB_HOST=192.168.10.61
DB_POST=5432
DB_POST=15432
+6
View File
@@ -0,0 +1,6 @@
DB_DRIVER=postgres
DB_USER=postgres
DB_PASSWORD=Kevin0412PgSql
DB_DATANAME=memos-db
DB_HOST=10.0.74.3
DB_POST=15432
+6
View File
@@ -0,0 +1,6 @@
DB_DRIVER=postgres
DB_USER=postgres
DB_PASSWORD=Kevin0412PgSql
DB_DATANAME=memos-db
DB_HOST=pgsql
DB_POST=15432
+21
View File
@@ -0,0 +1,21 @@
# ============================================================
# Memos 部署 — 公共环境变量
# 复制为 env.cfg 后填入真实值,env.cfg 已在 .gitignore 中忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# # 数据库相关变量在 db-<env>.cnf 中独立配置(参考 db-184.cnf
# docker compose -p memos --env-file ./env.cfg --env-file ./db-61.cnf -f ./prod.stack.yml up -d
# ---------- 镜像 ----------
# Memos 版本号
IMAGE_TAG_VER=0.29.1
# 完整镜像引用(私有仓库示例:hub.tp229.com:3500/neosmemo/memos:${IMAGE_TAG_VER}
IMAGE_TAG=neosmemo/memos:${IMAGE_TAG_VER}
PGSQL_IMAGE_TAG=postgres:18.4
# ---------- 数据卷 ----------
# 宿主机持久化目录,会挂载到容器 /var/opt/memos
# 部署前手动创建:mkdir -pv /data/volumes/memos/{data,pgsql_data}
Volumes_Path=/data/volumes/memos
+39 -9
View File
@@ -1,18 +1,48 @@
# run:: docker compose -p memos -f ./memos/local.stack.yml up -d
# run:: docker compose -p memos --env-file ./memos/env.cfg --env-file ./memos/db-local.cnf -f ./memos/local.stack.yml up -d
services:
memos:
image: neosmemo/memos:0.20.0
#command: '--driver mysql --dsn root:Kevin%0412%Mysql@tcp(10.10.13.233:3306)/memos-db'
image: ${IMAGE_TAG}
volumes:
- data:/var/opt/memos
- ${Volumes_Path}/data:/var/opt/memos
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- '8230:5230'
container_name: memos
- "8230:5230"
container_name: memos_local
restart: always
environment:
- MEMOS_DRIVER=postgres
- MEMOS_DSN=postgresql://postgres:Kevin0412PgSql@192.168.100.184:5432/memos-db?sslmode=disable
- MEMOS_DRIVER=${DB_DRIVER}
- MEMOS_DSN=user=${DB_USER} password=${DB_PASSWORD} dbname=${DB_DATANAME} host=${DB_HOST} port=${DB_POST} sslmode=disable
- TZ=Asia/Shanghai
pgsql:
image: ${PGSQL_IMAGE_TAG:-postgres:18.4}
container_name: memos_local_pgsql
restart: always
shm_size: 256mb
environment:
- POSTGRES_USER=${DB_USER:-postgres}
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=${DB_DATANAME:-memos-db}
- POSTGRES_HOST_AUTH_METHOD=scram-sha-256
- TZ=Asia/Shanghai
volumes:
# Postgres 数据持久化(具名卷 pgsql_data)
- pgsql_data:/var/lib/postgresql/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "${DB_POST:-15432}:5432"
healthcheck:
test:
[
"CMD-SHELL",
"pg_isready -U ${DB_USER:-postgres} -d ${DB_DATANAME:-memos-db}",
]
interval: 10s
timeout: 5s
retries: 5
volumes:
data:
pgsql_data:
+36 -6
View File
@@ -1,7 +1,9 @@
# path:: mkdir -pv /data/volumes/memos/data
# run-61:: docker compose -p memos --env-file ./memos/env.cfg --env-file ./memos/db-61.cnf -f ./memos/prod.stack.yml up -d
# pull:: docker compose -p memos --env-file ./memos/env.cfg -f ./memos/prod.stack.yml pull
# run-61:: docker compose -p memos --env-file ./memos/env.cfg --env-file ./memos/db-61.cnf -f ./memos/prod.stack.yml up -d
# run-184:: docker compose -p memos --env-file ./memos/env.cfg --env-file ./memos/db-184.cnf -f ./memos/prod.stack.yml up -d
# run-lkc:: docker compose -p memos --env-file ./memos/env.cfg --env-file ./memos/env-lkc.cnf -f ./memos/prod.stack.yml up -d
# run-lkc:: docker compose -p memos --env-file ./memos/env.cfg --env-file ./memos/db-lkc.cnf -f ./memos/prod.stack.yml up -d
# run-local:: docker compose -p memos --env-file ./memos/env.cfg --env-file ./memos/db-local.cnf -f ./memos/prod.stack.yml up -d
services:
memos:
@@ -11,8 +13,8 @@ services:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- '5230:5230'
container_name: memos
- "5230:5230"
container_name: memos_app
restart: always
environment:
- MEMOS_DRIVER=${DB_DRIVER}
@@ -21,5 +23,33 @@ services:
#- MEMOS_DRIVER=mysql
#- MEMOS_DSN=root:password@tcp(localhost)/memos_prod
- TZ=Asia/Shanghai
#volumes:
# data:
pgsql:
image: ${PGSQL_IMAGE_TAG:-postgres:18.4}
container_name: memos_pgsql
restart: always
shm_size: 256mb
environment:
- POSTGRES_USER=${DB_USER:-postgres}
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=${DB_DATANAME:-memos-db}
- POSTGRES_HOST_AUTH_METHOD=scram-sha-256
- TZ=Asia/Shanghai
volumes:
- ${Volumes_Path}/pgsql_data:/var/lib/postgresql/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "${DB_POST:-15432}:5432"
healthcheck:
test:
[
"CMD-SHELL",
"pg_isready -U ${DB_USER:-postgres} -d ${DB_DATANAME:-memos-db}",
]
interval: 10s
timeout: 5s
retries: 5
volumes:
pgsql_data:
+13
View File
@@ -0,0 +1,13 @@
# ============================================================
# mynat 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p mynat --env-file ./env.cfg -f ./stack.yml up -d
#
IMAGE_TAG=hub.wesais.cn/cnphpbb/mynat:v250603
Volumes_Path=/data/volumes/mynat
+60
View File
@@ -0,0 +1,60 @@
# ============================================================
# n8n 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p n8n --env-file ./env.cfg -f ./stack.yml up -d
#
# ============================================================
# ⚠️ 敏感值(必填,env.cfg 不提交)
# ============================================================
IMAGE_TAG_LATEST=2.3.6
IMAGE_TAG_Pre=2.4.4
# 数据库连接配置
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_DB=n8n
POSTGRES_USER=n8n
POSTGRES_PASSWORD=<your-strong-password>
# n8n基础配置
N8N_BASIC_AUTH_ACTIVE=true
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=<your-strong-password>
# 时区
GENERIC_TIMEZONE=Asia/Shanghai
TZ=Asia/Shanghai
# Webhook配置
WEBHOOK_URL=http://localhost:5678
# 加密密钥
N8N_ENCRYPTION_KEY=<your-encryption-key>
+1 -1
View File
@@ -51,7 +51,7 @@ services:
- "--loggerTimezone=Asia/Shanghai"
nightingale:
image: flashcatcloud/nightingale:8.5.0
image: flashcatcloud/nightingale:9.0.0
container_name: nightingale
hostname: nightingale
restart: always
+2 -4
View File
@@ -2,12 +2,10 @@ networks:
nightingale:
driver: bridge
services:
redis:
image: "redis:8-alpine"
container_name: redis
container_name: n9e_redis
hostname: redis
restart: always
ports:
@@ -18,7 +16,7 @@ services:
- nightingale
nightingale:
image: flashcatcloud/nightingale:8.5.0
image: flashcatcloud/nightingale:9.0.0
container_name: nightingale
hostname: nightingale
restart: always
+13
View File
@@ -0,0 +1,13 @@
# ============================================================
# netdata 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p netdata --env-file ./env.cfg -f ./stack.yml up -d
#
IMAGE_TAG=netdata/netdata:edge-0
Volumes_Path=/data/volumes/netdata
+31
View File
@@ -0,0 +1,31 @@
# ============================================================
# nginxpulse 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p nginxpulse --env-file ./nginxpulse/env.cfg -f ./nginxpulse/stack.yml up -d
# mkdir -pv /data/volumes/{postgres,nginxpulse}
# mkdir -pv /data/volumes/nginxpulse/{logs,nginxpulse_data,configs}
#
# ============================================================
# ⚠️ 敏感值(必填,env.cfg 不提交)
# ============================================================
# --- PostgreSQL ---
PG_IMAGE_TAG_VER=18.4
PG_IMAGE_TAG_V18=postgres:${PG_IMAGE_TAG_VER}
POSTGRES_PASSWORD=<your-password>
POSTGRES_HOST_AUTH_METHOD=scram-sha-256
# --- Nginxpulse ---
NP_IMAGE_TAG_VER=v1.6.24
NP_IMAGE_TAG=magiccoders/nginxpulse:${NP_IMAGE_TAG_VER}
# --- 卷路径 ---
Volumes_Path=/data/volumes
PG_Volumes_Path=${Volumes_Path}/postgres
NP_Volumes_Path=${Volumes_Path}/nginxpulse
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -eu
VOLUMES_PATH="${1:-/data/volumes}"
mkdir -pv \
"${VOLUMES_PATH}/postgres/data" \
"${VOLUMES_PATH}/nginxpulse/logs" \
"${VOLUMES_PATH}/nginxpulse/nginxpulse_data" \
"${VOLUMES_PATH}/nginxpulse/configs"
+34
View File
@@ -0,0 +1,34 @@
services:
postgres:
image: ${PG_IMAGE_TAG_V18}
container_name: prod-postgres
shm_size: 256mb
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_HOST_AUTH_METHOD=${POSTGRES_HOST_AUTH_METHOD}
- POSTGRES_INITDB_ARGS=--auth-host=${POSTGRES_HOST_AUTH_METHOD}
- TZ=Asia/Shanghai
volumes:
- ${PG_Volumes_Path}/data:/var/lib/postgresql
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "5432:5432"
restart: unless-stopped
nginxpulse:
image: ${NP_IMAGE_TAG}
container_name: local_nginxpulse
ports:
- "8088:8088"
- "8089:8089"
volumes:
- ${NP_Volumes_Path}/logs:/share/logs
- ${NP_Volumes_Path}/nginxpulse_data:/app/var/nginxpulse_data
- ${NP_Volumes_Path}/configs:/app/configs
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
stop_grace_period: 90s
restart: unless-stopped
environment:
- TZ=Asia/Shanghai
+29
View File
@@ -0,0 +1,29 @@
# ============================================================
# ntfy 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p ntfy --env-file ./env.cfg -f ./stack.yml up -d
#
IMAGE_TAG_VER=v2.24
IMAGE_TAG=binwiederhier/ntfy:${IMAGE_TAG_VER}
Volumes_Path=/data/volumes/ntfy
NTFY_BASE_URL=http://ntfy:80
# Web推送密钥对(自行生成):
# docker run --rm binwiederhier/ntfy ntfy web push generate > keys.txt
# 或:docker exec ntfy ntfy web push generate
# 然后将 public key 和 private key 分别填入下方
NTFY_WEB_PUSH_PUBLIC_KEY=
NTFY_WEB_PUSH_PRIVATE_KEY=
# Web推送关联的邮件地址(用于通知等场景)
NTFY_WEB_PUSH_EMAIL_ADDRESS=
+16 -17
View File
@@ -9,26 +9,25 @@ services:
container_name: ntfy
#tty: true
stdin_open: true
command:
serve
command: serve
restart: unless-stopped
ports:
- '5480:80'
- "5480:80"
environment:
- TZ=Asia/Shanghai # 时区设置为上海时区
- NTFY_BASE_URL=<http://ntfy:80> # ntfy服务的基础访问URL(协议+主机+端口)
- NTFY_CACHE_FILE=/var/lib/ntfy/cache.db # 缓存数据的存储文件路径
- NTFY_AUTH_FILE=/var/lib/ntfy/auth.db # 授权信息(如用户认证数据)的存储文件路径
- NTFY_AUTH_DEFAULT_ACCESS=deny-all # 默认访问权限:禁止所有未授权访问
- NTFY_BEHIND_PROXY=true # 标识ntfy服务运行在代理服务器后方(如Nginx等)
- NTFY_ATTACHMENT_CACHE_DIR=/var/lib/ntfy/attachments # 附件资源的缓存存储目录
- NTFY_ENABLE_LOGIN=true # 启用登录功能,允许用户通过账号密码授权访问
- NTFY_WEB_PUSH_PUBLIC_KEY= <public_key> # Web推送功能的公钥(用于消息加密)
- NTFY_WEB_PUSH_PRIVATE_KEY= <private_key> # Web推送功能的私钥(与公钥配对,用于解密/签名)
- NTFY_WEB_PUSH_FILE=/var/lib/ntfy/webpush.db # Web推送相关的数据存储文件路径
- NTFY_WEB_PUSH_EMAIL_ADDRESS= <email> # Web推送关联的邮件地址(用于通知等场景)
- NTFY_ENABLE_SIGNUP=false # 禁用用户注册功能,阻止新用户自主创建账号
- NTFY_ENABLE_METRICS=true # 启用指标收集功能,用于监控和分析服务运行状态
- TZ=Asia/Shanghai # 时区设置为上海时区
- NTFY_BASE_URL=${NTFY_BASE_URL} # ntfy服务的基础访问URL(协议+主机+端口)
- NTFY_CACHE_FILE=/var/lib/ntfy/cache.db # 缓存数据的存储文件路径
- NTFY_AUTH_FILE=/var/lib/ntfy/auth.db # 授权信息(如用户认证数据)的存储文件路径
- NTFY_AUTH_DEFAULT_ACCESS=deny-all # 默认访问权限:禁止所有未授权访问
- NTFY_BEHIND_PROXY=true # 标识ntfy服务运行在代理服务器后方(如Nginx等)
- NTFY_ATTACHMENT_CACHE_DIR=/var/lib/ntfy/attachments # 附件资源的缓存存储目录
- NTFY_ENABLE_LOGIN=true # 启用登录功能,允许用户通过账号密码授权访问
- NTFY_WEB_PUSH_PUBLIC_KEY=${NTFY_WEB_PUSH_PUBLIC_KEY} # Web推送功能的公钥(用于消息加密)
- NTFY_WEB_PUSH_PRIVATE_KEY=${NTFY_WEB_PUSH_PRIVATE_KEY} # Web推送功能的私钥(与公钥配对,用于解密/签名)
- NTFY_WEB_PUSH_FILE=/var/lib/ntfy/webpush.db # Web推送相关的数据存储文件路径
- NTFY_WEB_PUSH_EMAIL_ADDRESS=${NTFY_WEB_PUSH_EMAIL_ADDRESS} # Web推送关联的邮件地址(用于通知等场景)
- NTFY_ENABLE_SIGNUP=false # 禁用用户注册功能,阻止新用户自主创建账号
- NTFY_ENABLE_METRICS=true # 启用指标收集功能,用于监控和分析服务运行状态
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
+6
View File
@@ -0,0 +1,6 @@
# IMAGE_LATEST=portainer/portainer-ce:2.19.4
# IMAGE_LATEST=portainer/portainer-ce:latest
IMAGE_TAG_LTS=2.39.4
IMAGE_TAG_STS=2.43.0
IMAGE_TAG=portainer/portainer-ce:${IMAGE_TAG_LTS}
AGENT_TAG=portainer/agent:${IMAGE_TAG_LTS}
+1 -2
View File
@@ -1,12 +1,11 @@
# Run:: docker compose -p portainer-ce --env-file ./portainer-ce/env.cfg -f ./portainer-ce/portainer-ce.yaml up -d
services:
portainer:
image: ${IMAGE_TAG}
restart: always
ports:
- "9000:9000"
- "9443:9443"
# - "9443:9443"
- "8000:8000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
+31 -30
View File
@@ -1,38 +1,39 @@
## portainer-ce
重点说明: 如果 docker 版本 <= 29.0.0
**版本说明:**
现在使用的LTS版本是2.33.4 和 STS版本是2.35.0,都不支持最新的 docker 版本 >= 29.0.0
| 版本类型 | 当前版本 | Docker >= 29.0.0 支持 |
|---------|----------|----------------------|
| LTS | 2.39.4 | ✅ 支持 |
| STS | 2.43.0 | ✅ 支持 |
**解决的方法:**
### 历史记录
##### 方案1
在 docker 的 service 文件 中添加如下:
```
## 找到 docker.service 文件 `systemctl status docker` 会显示 docker.service 文件的完整路径
### 在 [Service] 段内添加
[Service]
Environment=DOCKER_MIN_API_VERSION=1.24
### 保持文件
### 重启 docker 服务
systemctl daemon-reload
systemctl restart docker
```
- **2025-11-30 之前**LTS 2.33.4 和 STS 2.35.0 不支持 Docker >= 29.0.0,需要通过以下方式解决:
##### 方案2
也可以在 `/etc/docker/daemon.json` 中添加如下:
```
{
"min-api-version": "1.24"
}
```
最后重启 docker 服务
##### 方案1(已废弃)
在 docker 的 service 文件中添加:
```
## 找到 docker.service 文件 `systemctl status docker` 会显示 docker.service 文件的完整路径
### 在 [Service] 段内添加
[Service]
Environment=DOCKER_MIN_API_VERSION=1.24
### 保持文件
### 重启 docker 服务
systemctl daemon-reload
systemctl restart docker
```
```
systemctl restart docker
```
##### 方案2(已废弃)
在 `/etc/docker/daemon.json` 中添加:
```
{
"min-api-version": "1.24"
}
```
最后重启 docker 服务:
```
systemctl restart docker
```
### by 2025-11-30
LTS版本是2.33.5 和 STS版本是2.36.0,都支持最新的 docker 版本 >= 29.0.0
无需添加任何配置
- **2025-11-30 起**LTS 2.33.5 和 STS 2.36.0 发布,**原生支持** Docker >= 29.0.0**无需任何配置**。
+130
View File
@@ -7,3 +7,133 @@
> 1. 该脚本只是一个简单的示例,不保证任何的稳定性。
> 2. 请根据自己的需求进行修改。
## 项目结构
项目包含多个可独立部署的服务栈,每个服务栈位于单独的目录中,包含以下核心文件:
- `stack.yml` / `compose.yml` - Docker Compose 配置文件
- `env.cfg` - 环境变量配置文件(包含敏感信息,已 gitignore)
- `env.cfg.example` - 环境变量配置模板(不含敏感信息)
- `readme.md` - 服务说明文档(可选)
## 使用示例
### Gitea 部署实践事例
**场景**:要部署 Gitea 服务,但希望保持项目目录的原始配置不变,同时在独立的目录中进行定制化部署。
**1. 创建独立部署目录**
```bash
# 假设当前在 deploy.stack 项目根目录
# 创建独立的部署目录
mkdir -p ~/mydeploy
cd ~/mydeploy
# 复制项目中的 Gitea 配置(相对路径或绝对路径)
cp -r ../deploy.stack/gitea .
# 或者使用绝对路径
# cp -r /path/to/your/deploy.stack/gitea .
# 进入 Gitea 目录
cd gitea
```
**2. 准备环境变量**
```bash
# 复制环境变量模板
cp env.cfg.example env.cfg
# 编辑环境变量(根据实际需求修改)
vim env.cfg
```
**3. 修改配置(在 mydeploy 目录中)**
**修改端口映射**Gitea 默认使用 3000 端口,可能与 Grafana 冲突):
```yaml
# 在 lky-prod.yml 或 rpi-prod.yml 中添加端口映射
ports:
- "3001:3000" # 将容器内 3000 端口映射到宿主机 3001 端口
```
**调整数据卷路径**(根据实际存储位置):
```bash
# 在 env.cfg 中修改 Volumes_Path
Volumes_Path=/data/volumes/gitea
```
**更新镜像版本**
```bash
# 在 env.cfg 中修改镜像版本
IMAGE_TAG_VER=1.25.2
IMAGE_TAG=gitea/gitea:${IMAGE_TAG_VER}-rootless
```
**选择环境配置文件**
- `lky-prod.yml` - 用于生产环境(Linux 服务器)
- `rpi-prod.yml` - 用于树莓派环境
- 根据实际环境选择合适的 compose 文件
**4. 创建数据目录**
```bash
# 根据 env.cfg 中的 Volumes_Path 创建目录
mkdir -pv /data/volumes/gitea/{data,config,logs}
# 设置正确的权限(Gitea 使用 UID 1000
chown -R 1000:1000 /data/volumes/gitea/
```
**5. 部署服务**
```bash
# 拉取镜像(根据选择的配置文件)
docker compose -p gitea --env-file ./env.cfg -f ./lky-prod.yml pull
# 或使用 rpi-prod.yml
# docker compose -p gitea --env-file ./env.cfg -f ./rpi-prod.yml pull
# 启动服务
docker compose -p gitea --env-file ./env.cfg -f ./lky-prod.yml up -d
# 查看日志
docker compose -p gitea --env-file ./env.cfg -f ./lky-prod.yml logs -f
# 查看服务状态
docker compose -p gitea --env-file ./env.cfg -f ./lky-prod.yml ps
# 停止服务
docker compose -p gitea --env-file ./env.cfg -f ./lky-prod.yml down
```
**6. 优势**
- **配置隔离**:在 `mydeploy` 目录中的修改不会影响原始项目配置
- **版本管理**:可以针对不同环境维护不同的配置版本
- **快速回滚**:如需恢复原始配置,只需删除 `mydeploy` 目录并重新部署
- **测试验证**:可以在 `mydeploy` 中测试新配置,确认无误后再应用到生产环境
**7. 注意事项**
- **配置兼容性**:确保 `mydeploy` 目录中的配置文件与原始项目保持兼容,特别是 compose 文件版本
- **定期同步**:关注原始项目的更新(安全补丁、新功能等),适时同步到 `mydeploy` 目录
- **配置备份**:定期备份 `mydeploy` 目录中的自定义配置
- **敏感信息安全**`env.cfg` 包含密码、密钥等敏感信息,确保文件权限为 600
```bash
chmod 600 env.cfg
```
- **版本控制**:建议将 `mydeploy` 目录纳入版本控制(排除 `env.cfg`
- **多环境管理**:可以为不同环境创建不同的 `mydeploy` 目录,如 `mydeploy-prod``mydeploy-dev`
**8. 通用模式**
这种 "mydeploy" 模式不仅适用于 Gitea,也适用于项目中的其他服务:
```bash
# 部署 Grafana
cp -r ../deploy.stack/grafana ~/mydeploy/grafana
# 部署 Memos
cp -r ../deploy.stack/memos ~/mydeploy/memos
# 部署 Portainer
cp -r ../deploy.stack/portainer-ce ~/mydeploy/portainer-ce
# 部署 Traefik
cp -r ../deploy.stack/traefik ~/mydeploy/traefik
```
每个服务都可以在独立的目录中进行定制化配置和测试,实现真正的配置隔离和环境管理。
+26
View File
@@ -0,0 +1,26 @@
# ============================================================
# rustfs 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p rustfs --env-file ./env.cfg -f ./stack.yml up -d
#
# ============================================================
# ⚠️ 敏感值(必填,env.cfg 不提交)
# ============================================================
IMAGE_TAG=rustfs/rustfs:alpha # 最新版本
IMAGE_TAG_LTS=rustfs/rustfs:latest # 长期支持版本
Volumes_Path=/data/volumes/rustfs # 数据卷路径
RUSTFS_SERVER_DOMAINS=rustfs.example.com # 服务器域名
RUSTFS_CONSOLE_ENABLE=true # 是否启用控制台
RUSTFS_SECRET_KEY=<your-secret> # 密钥
RUSTFS_ACCESS_KEY=<your-access-key> # 访问密钥
+17
View File
@@ -0,0 +1,17 @@
# ============================================================
# searxng 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p searxng --env-file ./env.cfg -f ./stack.yml up -d
#
SearXNG_TAG=searxng/searxng:latest
Redis_TAG=valkey/valkey:9.0.0-alpine
Valkey_TAG=valkey/valkey:9.0.0
Volumes_Path=/data/volumes/searxng
+1 -2
View File
@@ -3,7 +3,6 @@
# Run:: docker compose --env-file ./searxng/env.cfg -f ./searxng/stack.yml up -d
services:
valkey:
image: ${Valkey_TAG}
container_name: redis
@@ -12,7 +11,7 @@ services:
networks:
- searxng
volumes:
- ${Volumes_Path}/redis-data:/data
- ${Volumes_Path}/redis-data:/data
searxng:
image: ${SearXNG_TAG}
+269
View File
@@ -0,0 +1,269 @@
#!/usr/bin/env bash
# ============================================================
# File: proxy.sh
# Description: 通用代理管理脚本
# Usage:
# source proxy.sh # 加载默认配置
# proxy on # 开启代理
# proxy off # 关闭代理
# proxy status # 查看代理状态
# proxy test # 测试代理连通性
# proxy set <host> <port> # 临时设置代理地址
#
# 也可直接执行: bash proxy.sh [on|off|status|test|set ...]
# ============================================================
# -------------------- 默认配置 --------------------
PROXY_HOST="${PROXY_HOST:-127.0.0.1}"
PROXY_PORT="${PROXY_PORT:-7890}"
PROXY_TYPE="${PROXY_TYPE:-http}" # http 或 socks5
NO_PROXY_LIST="localhost,127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
# -------------------- 构建代理 URL --------------------
function _build_proxy_url() {
local type="${PROXY_TYPE:-http}"
local host="${PROXY_HOST:-127.0.0.1}"
local port="${PROXY_PORT:-7890}"
case "$type" in
http|HTTP) echo "http://${host}:${port}" ;;
socks5|socks) echo "socks5://${host}:${port}" ;;
socks4) echo "socks4://${host}:${port}" ;;
*) echo "${type}://${host}:${port}" ;;
esac
}
# -------------------- 开启代理 --------------------
function proxy_on() {
local url
url="$(_build_proxy_url)"
# --- 环境变量 ---
export http_proxy="$url"
export HTTP_PROXY="$url"
export https_proxy="$url"
export HTTPS_PROXY="$url"
export all_proxy="$url"
export ALL_PROXY="$url"
export no_proxy="$NO_PROXY_LIST"
export NO_PROXY="$NO_PROXY_LIST"
export ftp_proxy="$url"
export FTP_PROXY="$url"
export rsync_proxy="$url"
# --- Git ---
git config --global http.proxy "$url" 2>/dev/null
git config --global https.proxy "$url" 2>/dev/null
# --- NPM ---
npm config set proxy "$url" 2>/dev/null
npm config set https-proxy "$url" 2>/dev/null
# --- Yarn ---
yarn config set proxy "$url" 2>/dev/null
yarn config set https-proxy "$url" 2>/dev/null
# --- Docker(需要 sudo 权限写入配置目录)---
if [ -d /etc/systemd/system/docker.service.d ]; then
local docker_conf="/etc/systemd/system/docker.service.d/http-proxy.conf"
sudo bash -c "cat > '$docker_conf' <<EOF
[Service]
Environment=\"HTTP_PROXY=$url\"
Environment=\"HTTPS_PROXY=$url\"
Environment=\"NO_PROXY=$NO_PROXY_LIST\"
EOF"
sudo systemctl daemon-reload 2>/dev/null
sudo systemctl restart docker 2>/dev/null
fi
echo -e "\033[32m[✓] 代理已开启 → \033[1m${url}\033[0m"
echo -e "\033[32m NO_PROXY → \033[1m${NO_PROXY_LIST}\033[0m"
}
# -------------------- 关闭代理 --------------------
function proxy_off() {
# --- 环境变量 ---
unset http_proxy HTTP_PROXY
unset https_proxy HTTPS_PROXY
unset all_proxy ALL_PROXY
unset no_proxy NO_PROXY
unset ftp_proxy FTP_PROXY
unset rsync_proxy
# --- Git ---
git config --global --unset http.proxy 2>/dev/null
git config --global --unset https.proxy 2>/dev/null
# --- NPM ---
npm config delete proxy 2>/dev/null
npm config delete https-proxy 2>/dev/null
# --- Yarn ---
yarn config delete proxy 2>/dev/null
yarn config delete https-proxy 2>/dev/null
# --- Docker ---
local docker_conf="/etc/systemd/system/docker.service.d/http-proxy.conf"
if [ -f "$docker_conf" ]; then
sudo rm -f "$docker_conf"
sudo systemctl daemon-reload 2>/dev/null
sudo systemctl restart docker 2>/dev/null
fi
echo -e "\033[31m[✓] 代理已关闭\033[0m"
}
# -------------------- 查看代理状态 --------------------
function proxy_status() {
echo -e "\n\033[1m========== 代理状态 ==========\033[0m"
# 环境变量
echo -e "\n\033[1m[环境变量]\033[0m"
local env_vars=("http_proxy" "https_proxy" "all_proxy" "no_proxy" "ftp_proxy")
for var in "${env_vars[@]}"; do
local val="${!var}"
if [ -n "$val" ]; then
printf " \033[32m%-14s → %s\033[0m\n" "$var" "$val"
else
printf " \033[90m%-14s → (未设置)\033[0m\n" "$var"
fi
done
# Git 配置
echo -e "\n\033[1m[Git 配置]\033[0m"
local git_http git_https
git_http=$(git config --global --get http.proxy 2>/dev/null)
git_https=$(git config --global --get https.proxy 2>/dev/null)
if [ -n "$git_http" ] || [ -n "$git_https" ]; then
[ -n "$git_http" ] && printf " \033[32mhttp.proxy → %s\033[0m\n" "$git_http"
[ -n "$git_https" ] && printf " \033[32mhttps.proxy → %s\033[0m\n" "$git_https"
else
printf " \033[90m(未配置)\033[0m\n"
fi
# NPM 配置
if command -v npm &>/dev/null; then
echo -e "\n\033[1m[NPM 配置]\033[0m"
local npm_proxy npm_https_proxy
npm_proxy=$(npm config get proxy 2>/dev/null)
npm_https_proxy=$(npm config get https-proxy 2>/dev/null)
if [ "$npm_proxy" != "null" ] && [ -n "$npm_proxy" ]; then
printf " \033[32mproxy → %s\033[0m\n" "$npm_proxy"
else
printf " \033[90mproxy → (未设置)\033[0m\n"
fi
if [ "$npm_https_proxy" != "null" ] && [ -n "$npm_https_proxy" ]; then
printf " \033[32mhttps-proxy → %s\033[0m\n" "$npm_https_proxy"
else
printf " \033[90mhttps-proxy → (未设置)\033[0m\n"
fi
fi
echo -e "\n"
}
# -------------------- 测试代理连通性 --------------------
function proxy_test() {
local url
url="$(_build_proxy_url)"
echo -e "\033[1m[测试代理] ${url}\033[0m\n"
# 测试 Google (需要代理)
echo -n " Google (proxy) → "
if curl -s -o /dev/null -w "%{http_code}" --max-time 5 --proxy "$url" https://www.google.com 2>/dev/null | grep -q "200\|301\|302"; then
echo -e "\033[32m✓ OK\033[0m"
else
echo -e "\033[31m✗ FAIL\033[0m"
fi
# 测试 GitHub (需要代理)
echo -n " GitHub (proxy) → "
if curl -s -o /dev/null -w "%{http_code}" --max-time 5 --proxy "$url" https://github.com 2>/dev/null | grep -q "200\|301\|302"; then
echo -e "\033[32m✓ OK\033[0m"
else
echo -e "\033[31m✗ FAIL\033[0m"
fi
# 测试本地直连
echo -n " Local (no proxy) → "
if curl -s -o /dev/null -w "%{http_code}" --max-time 3 --noproxy '*' https://www.baidu.com 2>/dev/null | grep -q "200\|301\|302"; then
echo -e "\033[32m✓ OK\033[0m"
else
echo -e "\033[31m✗ FAIL\033[0m"
fi
echo ""
}
# -------------------- 临时设置代理地址 --------------------
function proxy_set() {
if [ $# -lt 2 ]; then
echo -e "\033[33m用法: proxy set <host> <port> [http|socks5]\033[0m"
echo -e "\033[33m示例: proxy set 192.168.1.100 1080 socks5\033[0m"
return 1
fi
PROXY_HOST="$1"
PROXY_PORT="$2"
if [ -n "$3" ]; then
PROXY_TYPE="$3"
fi
echo -e "\033[32m[✓] 代理地址已设置为 ${PROXY_TYPE}://${PROXY_HOST}:${PROXY_PORT}\033[0m"
echo -e "\033[32m 执行 'proxy on' 启用新的代理地址\033[0m"
}
# -------------------- 总入口函数 --------------------
function proxy() {
case "$1" in
on) proxy_on ;;
off) proxy_off ;;
status) proxy_status ;;
test) proxy_test ;;
set) shift; proxy_set "$@" ;;
toggle)
if [ -n "$http_proxy" ]; then
proxy_off
else
proxy_on
fi
;;
help|*)
echo -e "
\033[1m========== proxy.sh — 通用代理管理 ==========\033[0m
\033[1m用法:\033[0m
source proxy.sh # 加载脚本(必须先 source 才能用 proxy 命令)
proxy on # 开启代理(环境变量 + git + npm + docker
proxy off # 关闭代理(清除所有配置)
proxy status # 查看当前代理状态
proxy test # 测试代理连通性
proxy toggle # 切换代理开/关
proxy set <h> <p> [type] # 设置代理地址
proxy help # 显示帮助
\033[1m默认配置(可通过环境变量覆盖):\033[0m
PROXY_HOST = ${PROXY_HOST}
PROXY_PORT = ${PROXY_PORT}
PROXY_TYPE = ${PROXY_TYPE} (http | socks5 | socks4)
\033[1m自定义示例:\033[0m
PROXY_HOST=10.0.0.5 PROXY_PORT=1080 PROXY_TYPE=socks5 source proxy.sh
proxy set 10.0.0.5 1080 socks5 # 临时修改
"
;;
esac
}
# -------------------- 执行入口 --------------------
# 如果被直接执行(非 source),解析命令行参数
if [ "${BASH_SOURCE[0]:-$0}" = "$0" ]; then
if [ $# -gt 0 ]; then
proxy "$@"
else
proxy help
fi
else
# source 方式加载,显示提示
echo -e "\033[36m[proxy.sh] 已加载。使用 'proxy help' 查看用法。\033[0m"
fi
+353
View File
@@ -0,0 +1,353 @@
<#
.SYNOPSIS
通用代理管理脚本 (PowerShell 版)
.DESCRIPTION
source/加载后使用 proxy 命令管理代理开/关/状态/测试
.NOTES
File: proxy.ps1
用法:
. .\proxy.ps1 # 加载脚本
proxy on # 开启代理
proxy off # 关闭代理
proxy status # 查看代理状态
proxy test # 测试代理连通性
proxy set <host> <port> [type] # 临时设置代理地址
proxy toggle # 切换开/关
proxy help # 显示帮助
#>
# -------------------- 默认配置 --------------------
if (-not $env:_PROXY_HOST) { $env:_PROXY_HOST = "127.0.0.1" }
if (-not $env:_PROXY_PORT) { $env:_PROXY_PORT = "7890" }
if (-not $env:_PROXY_TYPE) { $env:_PROXY_TYPE = "http" } # http | socks5 | socks4
$global:NO_PROXY_LIST = "localhost,127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
# -------------------- 辅助:构建代理 URL --------------------
function _BuildProxyUrl {
$type = $env:_PROXY_TYPE
$host_addr = $env:_PROXY_HOST
$port = $env:_PROXY_PORT
switch ($type.ToLower()) {
"http" { return "http://$host_addr`:$port" }
"socks5" { return "socks5://$host_addr`:$port" }
"socks4" { return "socks4://$host_addr`:$port" }
default { return "$type`://$host_addr`:$port" }
}
}
# -------------------- 辅助:控制台 WinINET 代理 (系统级 IE/Edge) --------------------
function _SetWinInetProxy([string]$proxyUrl) {
$regPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings"
if ($proxyUrl) {
Set-ItemProperty -Path $regPath -Name ProxyEnable -Value 1
Set-ItemProperty -Path $regPath -Name ProxyServer -Value ($proxyUrl -replace '^\w+://', '')
Set-ItemProperty -Path $regPath -Name ProxyOverride -Value $global:NO_PROXY_LIST
} else {
Set-ItemProperty -Path $regPath -Name ProxyEnable -Value 0
}
# 广播 WM_SETTINGCHANGE,让其他程序感知代理变更
$sig = @'
[DllImport("wininet.dll", SetLastError=true)]
public static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength);
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam, uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);
'@
try {
Add-Type -MemberDefinition $sig -Namespace WinAPI -Name ProxyHelper -ErrorAction SilentlyContinue
[WinAPI.ProxyHelper]::InternetSetOption([IntPtr]::Zero, 39, [IntPtr]::Zero, 0) | Out-Null # PROXY_SETTINGS_CHANGED
$HWND_BROADCAST = [IntPtr]0xffff
$WM_SETTINGCHANGE = 0x1A
$result = [UIntPtr]::Zero
[WinAPI.ProxyHelper]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE, [UIntPtr]::Zero, "Environment", 2, 2000, [ref]$result) | Out-Null
} catch {}
}
# ====================================================================
# 开启代理
# ====================================================================
function Proxy-On {
$url = _BuildProxyUrl
# --- 进程级环境变量 ---
$env:http_proxy = $url
$env:HTTP_PROXY = $url
$env:https_proxy = $url
$env:HTTPS_PROXY = $url
$env:all_proxy = $url
$env:ALL_PROXY = $url
$env:no_proxy = $global:NO_PROXY_LIST
$env:NO_PROXY = $global:NO_PROXY_LIST
$env:ftp_proxy = $url
$env:FTP_PROXY = $url
$env:rsync_proxy = $url
# --- 用户级环境变量(持久化,新开终端也生效)---
[Environment]::SetEnvironmentVariable("http_proxy", $url, "User")
[Environment]::SetEnvironmentVariable("https_proxy", $url, "User")
[Environment]::SetEnvironmentVariable("all_proxy", $url, "User")
[Environment]::SetEnvironmentVariable("no_proxy", $global:NO_PROXY_LIST, "User")
# --- Git ---
git config --global http.proxy $url 2>$null
git config --global https.proxy $url 2>$null
# --- NPM ---
npm config set proxy $url 2>$null
npm config set https-proxy $url 2>$null
# --- Yarn ---
yarn config set proxy $url 2>$null
yarn config set https-proxy $url 2>$null
# --- Windows 系统 WinINET (IE/Edge/Chrome 等) ---
_SetWinInetProxy $url
Write-Host "[✓] 代理已开启 → " -ForegroundColor Green -NoNewline
Write-Host $url -ForegroundColor White
Write-Host " NO_PROXY → " -ForegroundColor Green -NoNewline
Write-Host $global:NO_PROXY_LIST -ForegroundColor White
}
# ====================================================================
# 关闭代理
# ====================================================================
function Proxy-Off {
# --- 进程级环境变量 ---
$vars = @("http_proxy","HTTP_PROXY","https_proxy","HTTPS_PROXY","all_proxy","ALL_PROXY",
"no_proxy","NO_PROXY","ftp_proxy","FTP_PROXY","rsync_proxy")
foreach ($v in $vars) { Remove-Item -Path "Env:$v" -ErrorAction SilentlyContinue }
# --- 用户级环境变量 ---
foreach ($v in @("http_proxy","https_proxy","all_proxy","no_proxy")) {
[Environment]::SetEnvironmentVariable($v, $null, "User")
}
# --- Git ---
git config --global --unset http.proxy 2>$null
git config --global --unset https.proxy 2>$null
# --- NPM ---
npm config delete proxy 2>$null
npm config delete https-proxy 2>$null
# --- Yarn ---
yarn config delete proxy 2>$null
yarn config delete https-proxy 2>$null
# --- Windows 系统 WinINET ---
_SetWinInetProxy $null
Write-Host "[✓] 代理已关闭" -ForegroundColor Red
}
# ====================================================================
# 查看代理状态
# ====================================================================
function Proxy-Status {
Write-Host ""
Write-Host "========== 代理状态 ==========" -ForegroundColor White
# 环境变量
Write-Host "`n[环境变量]" -ForegroundColor White
$envVars = @("http_proxy", "https_proxy", "all_proxy", "no_proxy", "ftp_proxy")
foreach ($v in $envVars) {
$val = [Environment]::GetEnvironmentVariable($v, "Process")
if ($val) {
Write-Host (" {0,-14} → {1}" -f $v, $val) -ForegroundColor Green
} else {
Write-Host (" {0,-14} → (未设置)" -f $v) -ForegroundColor DarkGray
}
}
# 用户级环境变量
Write-Host "`n[用户级环境变量 (持久化)]" -ForegroundColor White
foreach ($v in @("http_proxy", "https_proxy", "all_proxy", "no_proxy")) {
$val = [Environment]::GetEnvironmentVariable($v, "User")
if ($val) {
Write-Host (" {0,-14} → {1}" -f $v, $val) -ForegroundColor Green
} else {
Write-Host (" {0,-14} → (未设置)" -f $v) -ForegroundColor DarkGray
}
}
# Git
Write-Host "`n[Git 配置] (global)" -ForegroundColor White
$gitHttp = git config --global --get http.proxy 2>$null
$gitHttps = git config --global --get https.proxy 2>$null
if ($gitHttp -or $gitHttps) {
if ($gitHttp) { Write-Host " http.proxy → $gitHttp" -ForegroundColor Green }
if ($gitHttps) { Write-Host " https.proxy → $gitHttps" -ForegroundColor Green }
} else {
Write-Host " (未配置)" -ForegroundColor DarkGray
}
# NPM
if (Get-Command npm -ErrorAction SilentlyContinue) {
Write-Host "`n[NPM 配置]" -ForegroundColor White
$npmProxy = npm config get proxy 2>$null
$npmHttps = npm config get https-proxy 2>$null
if ($npmProxy -and $npmProxy -ne "null" -and $npmProxy -ne "undefined") {
Write-Host " proxy → $npmProxy" -ForegroundColor Green
} else {
Write-Host " proxy → (未设置)" -ForegroundColor DarkGray
}
if ($npmHttps -and $npmHttps -ne "null" -and $npmHttps -ne "undefined") {
Write-Host " https-proxy → $npmHttps" -ForegroundColor Green
} else {
Write-Host " https-proxy → (未设置)" -ForegroundColor DarkGray
}
}
# Windows 系统代理
Write-Host "`n[Windows 系统 WinINET 代理]" -ForegroundColor White
$regPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings"
if (Test-Path $regPath) {
$enabled = (Get-ItemProperty -Path $regPath -Name ProxyEnable -ErrorAction SilentlyContinue).ProxyEnable
$server = (Get-ItemProperty -Path $regPath -Name ProxyServer -ErrorAction SilentlyContinue).ProxyServer
if ($enabled -eq 1) {
Write-Host " ProxyEnable → 1" -ForegroundColor Green
Write-Host " ProxyServer → $server" -ForegroundColor Green
} else {
Write-Host " ProxyEnable → 0 (已禁用)" -ForegroundColor DarkGray
}
} else {
Write-Host " (无法读取注册表)" -ForegroundColor DarkGray
}
Write-Host ""
}
# ====================================================================
# 测试代理连通性
# ====================================================================
function Proxy-Test {
$url = _BuildProxyUrl
Write-Host "[测试代理] $url`n" -ForegroundColor White
# Google (通过代理)
Write-Host " Google (proxy) → " -NoNewline
try {
$r = Invoke-WebRequest -Uri "https://www.google.com" -Proxy $url -TimeoutSec 5 -UseBasicParsing -ErrorAction Stop
if ($r.StatusCode -in 200,301,302) { Write-Host "✓ OK ($($r.StatusCode))" -ForegroundColor Green }
else { Write-Host "✗ FAIL ($($r.StatusCode))" -ForegroundColor Red }
} catch { Write-Host "✗ FAIL" -ForegroundColor Red }
# GitHub (通过代理)
Write-Host " GitHub (proxy) → " -NoNewline
try {
$r = Invoke-WebRequest -Uri "https://github.com" -Proxy $url -TimeoutSec 5 -UseBasicParsing -ErrorAction Stop
if ($r.StatusCode -in 200,301,302) { Write-Host "✓ OK ($($r.StatusCode))" -ForegroundColor Green }
else { Write-Host "✗ FAIL ($($r.StatusCode))" -ForegroundColor Red }
} catch { Write-Host "✗ FAIL" -ForegroundColor Red }
# 本地直连 (不走代理)
Write-Host " Baidu (direct) → " -NoNewline
try {
$r = Invoke-WebRequest -Uri "https://www.baidu.com" -TimeoutSec 3 -UseBasicParsing -NoProxy -ErrorAction Stop
if ($r.StatusCode -in 200,301,302) { Write-Host "✓ OK ($($r.StatusCode))" -ForegroundColor Green }
else { Write-Host "✗ FAIL ($($r.StatusCode))" -ForegroundColor Red }
} catch {
# PowerShell 5.1 不支持 -NoProxy,退而用 curl
try {
$code = (curl.exe -s -o NUL -w "%{http_code}" --max-time 3 --noproxy "*" https://www.baidu.com 2>$null)
if ($code -match "200|301|302") { Write-Host "✓ OK ($code)" -ForegroundColor Green }
else { Write-Host "✗ FAIL" -ForegroundColor Red }
} catch { Write-Host "✗ FAIL" -ForegroundColor Red }
}
Write-Host ""
}
# ====================================================================
# 临时设置代理地址
# ====================================================================
function Proxy-Set {
param(
[Parameter(Position=0)][string]$ProxyHost,
[Parameter(Position=1)][string]$ProxyPort,
[Parameter(Position=2)][string]$ProxyType = "http"
)
if (-not $ProxyHost -or -not $ProxyPort) {
Write-Host "用法: proxy set <host> <port> [http|socks5]" -ForegroundColor Yellow
Write-Host '示例: proxy set 192.168.1.100 1080 socks5' -ForegroundColor Yellow
return
}
$env:_PROXY_HOST = $ProxyHost
$env:_PROXY_PORT = $ProxyPort
$env:_PROXY_TYPE = $ProxyType
Write-Host "[✓] 代理地址已设置为 ${ProxyType}://${ProxyHost}:${ProxyPort}" -ForegroundColor Green
Write-Host " 执行 'proxy on' 启用新的代理地址" -ForegroundColor Green
}
# ====================================================================
# 主入口:proxy 命令
# ====================================================================
function proxy {
param(
[Parameter(Position=0)][string]$Action = "help",
[Parameter(Position=1, ValueFromRemainingArguments)][string[]]$Args
)
switch ($Action.ToLower()) {
"on" { Proxy-On }
"off" { Proxy-Off }
"status" { Proxy-Status }
"test" { Proxy-Test }
"toggle" {
if ($env:http_proxy) { Proxy-Off } else { Proxy-On }
}
"set" {
if ($Args -and $Args.Count -ge 2) {
$type = if ($Args.Count -ge 3) { $Args[2] } else { "http" }
Proxy-Set -ProxyHost $Args[0] -ProxyPort $Args[1] -ProxyType $type
} else {
Proxy-Set
}
}
"help" {
Write-Host @"
========== proxy.ps1 ==========
:
. .\proxy.ps1 # Dot-Source
proxy on # ( + WinINET + git + npm)
proxy off #
proxy status #
proxy test #
proxy toggle # /
proxy set <host> <port> [type]#
proxy help #
():
_PROXY_HOST = $($env:_PROXY_HOST)
_PROXY_PORT = $($env:_PROXY_PORT)
_PROXY_TYPE = $($env:_PROXY_TYPE) (http | socks5 | socks4)
:
`$env:_PROXY_HOST = "10.0.0.5" #
`$env:_PROXY_PORT = "1080" #
proxy on #
"@ -ForegroundColor Cyan
}
default {
Write-Host "未知命令: $Action (使用 'proxy help' 查看帮助)" -ForegroundColor Red
}
}
}
# -------------------- 加载提示 --------------------
if ($MyInvocation.Line -notmatch '\$\s*\(?\s*\.\s+.*proxy\.ps1') {
# 被直接执行而非 dot-source 时
if ($args.Count -eq 0) {
# 无参数直接执行,先 dot-source 自己
Write-Host "[proxy.ps1] 请使用 '. .\proxy.ps1' 来加载脚本。" -ForegroundColor Yellow
Write-Host "[proxy.ps1] 如果要直接执行命令, 请运行: .\proxy.ps1 on" -ForegroundColor Yellow
}
} else {
Write-Host "[proxy.ps1] 已加载。使用 'proxy help' 查看用法。" -ForegroundColor Cyan
}
+107
View File
@@ -0,0 +1,107 @@
#!/usr/bin/env bash
# 代理配置 - 请根据实际情况修改以下值
PROXY_HOST="127.0.0.1"
PROXY_HTTP_PORT="7890"
PROXY_SOCKS_PORT="7890"
# HTTP/HTTPS 代理地址
HTTP_PROXY_URL="http://${PROXY_HOST}:${PROXY_HTTP_PORT}"
SOCKS_PROXY_URL="socks5://${PROXY_HOST}:${PROXY_SOCKS_PORT}"
# 不使用代理的地址
NO_PROXY_VALUE="localhost,127.0.0.1,localaddress,.localdomain.com,.example.com,.hermesagent.org.cn,mcp.hermesagent.org.cn"
function proxy_on() {
export http_proxy="${HTTP_PROXY_URL}"
export https_proxy="${HTTP_PROXY_URL}"
export all_proxy="${SOCKS_PROXY_URL}"
export no_proxy="${NO_PROXY_VALUE}"
# 同时设置大写版本(某些程序需要)
export HTTP_PROXY="${HTTP_PROXY_URL}"
export HTTPS_PROXY="${HTTP_PROXY_URL}"
export ALL_PROXY="${SOCKS_PROXY_URL}"
export NO_PROXY="${NO_PROXY_VALUE}"
echo -e "\033[32m┌──────────────────────────────────────┐\033[0m"
echo "│ ✅ 代理已启用 │"
echo "├──────────────────────────────────────┤"
echo "│ http_proxy : ${HTTP_PROXY_URL}"
echo "│ https_proxy: ${HTTP_PROXY_URL}"
echo "│ all_proxy : ${SOCKS_PROXY_URL}"
echo -e "\033[32m└──────────────────────────────────────┘\033[0m"
}
function proxy_off() {
unset http_proxy https_proxy all_proxy no_proxy
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY
echo -e "\033[31m┌──────────────────────────────────────┐\033[0m"
echo "│ ❌ 代理已关闭 │"
echo -e "\033[31m└──────────────────────────────────────┘\033[0m"
}
function proxy_status() {
echo -e "\033[36m┌──────────────────────────────────────┐\033[0m"
echo "│ 📊 代理状态 │"
echo -e "├──────────────────────────────────────┤\033[0m"
if [ -z "$http_proxy" ]; then
echo -e "\033[31m│ 状态: 未启用 │\033[0m"
else
echo -e "\033[32m│ 状态: 已启用 │\033[0m"
echo "│ http_proxy : $http_proxy"
echo "│ https_proxy: $https_proxy"
echo "│ all_proxy : $all_proxy"
echo "│ no_proxy : $no_proxy"
fi
echo -e "\033[36m└──────────────────────────────────────┘\033[0m"
}
function proxy_test() {
# 检查 curl 是否可用
if ! command -v curl >/dev/null 2>&1; then
echo "curl 未安装,无法测试代理" >&2
return 1
fi
echo -e "\033[33m测试代理连通性...\033[0m"
# 1. 本地代理端口是否在监听(不依赖外网,最快的失败定位)
echo -n "代理端口 ${PROXY_HOST}:${PROXY_HTTP_PORT}: "
if (exec 3<>/dev/tcp/"${PROXY_HOST}"/"${PROXY_HTTP_PORT}") 2>/dev/null; then
exec 3<&- 3>&-
echo -e "\033[32m✓ 可达\033[0m"
else
echo -e "\033[31m✗ 不可达(请确认代理客户端已启动)\033[0m"
return 1
fi
# 2. 通过代理访问 GitHub(翻墙连通性测试),不通则试 Google
echo -n "代理访问 https://github.com: "
code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 8 \
-x "${HTTP_PROXY_URL}" https://github.com 2>/dev/null)
if [ -n "$code" ] && [ "$code" -ge 200 ] && [ "$code" -lt 400 ]; then
echo -e "\033[32m✓ HTTP ${code}\033[0m"
else
echo -e "\033[31m✗ 失败(${code:-N/A}\033[0m"
echo -n "代理访问 https://www.google.com: "
code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 8 \
-x "${HTTP_PROXY_URL}" https://www.google.com 2>/dev/null)
if [ -n "$code" ] && [ "$code" -ge 200 ] && [ "$code" -lt 400 ]; then
echo -e "\033[32m✓ HTTP ${code}\033[0m"
else
echo -e "\033[31m✗ 失败(${code:-N/A}\033[0m"
fi
fi
# 3. 显示当前出口 IP(直连)
echo -n "直连出口 IP: "
curl --max-time 5 myip.ipip.net 2>/dev/null || echo "获取失败"
# 4. 显示代理出口 IP(走代理,通过 HTTPS CONNECT 获取更稳定)
echo -n "代理出口 IP: "
curl -w "\n" --max-time 8 -x "${HTTP_PROXY_URL}" inet-ip.info 2>/dev/null || echo "获取失败"
}
+13
View File
@@ -0,0 +1,13 @@
# ============================================================
# tasks 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p tasks --env-file ./env.cfg -f ./stack.yml up -d
#
IMAGE_TAG=baldissaramatheus/tasks.md:2.5.4
Volumes_Path=/data/volumes/tasks.md
+20
View File
@@ -0,0 +1,20 @@
# path:: mkdir -pv /data/volumes/tasks.md/{tasks,config}
# pull:: docker compose -p tasks.md --env-file ./tasks.md/env.cfg -f ./tasks.md/stack.yml pull
# run:: docker compose -p tasks.md --env-file ./tasks.md/env.cfg -f ./tasks.md/stack.yml up -d
services:
tasks.md:
image: ${IMAGE_TAG}
container_name: tasks.md
environment:
- PUID=1000
- PGID=1000
- TZ=Asia/Shanghai
volumes:
- ${Volumes_Path}/tasks:/tasks
- ${Volumes_Path}/config:/config
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
restart: unless-stopped
ports:
- 5480:8080
+15
View File
@@ -0,0 +1,15 @@
# ============================================================
# traefik 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p traefik --env-file ./env.cfg -f ./stack.yml up -d
#
IMAGE_TAG_V3=traefik:v3.5.1
IMAGE_TAG_V2=traefik:v2.11.29
IMAGE_TAG_LATEST=traefik:latest
+73
View File
@@ -0,0 +1,73 @@
# ========== 经典模式 ==========
# 如果为 true,不使用任何颜色或图标,与标准 ls 行为类似
classic: false
# ========== 分块(Blocks ==========
# 定义每行显示哪些信息块,按顺序排列
blocks:
- permission # 文件权限 (rwxr-xr-x)
- user # 文件所有者
- group # 用户组
- size # 文件大小
- date # 日期/时间
- name # 文件名(必须包含)
# ========== 颜色主题 ==========
color:
# 何时使用颜色:always(总是)、auto(自动)、never(从不)
when: auto
# 自定义文件类型颜色(覆盖默认值)
theme: custom
# ========== 日期格式 ==========
date: "+%Y-%m-%d %H:%M:%S" # 可选:relative(相对时间)、isoISO格式)
# ========== 跟随符号链接 ============
dereference: false
# ========== 图标 ==========
icons:
# 何时显示图标:always、auto、never
when: auto
# 图标主题(需要对应的 Nerd Font)
theme: fancy # 或 unicode
# 分隔符(图标与文件名之间的字符)
separator: " "
# ========== 忽略选项 ==========
ignore-globs:
- "*.bak" # 忽略所有 .bak 文件
- ".git" # 忽略 .git 目录
- "__pycache__" # 忽略 Python 缓存
# ========== 布局 ==========
layout: grid # grid(网格)| tree(树状)| oneline(单行)
# ========== 递归深度(--tree 时有效) ==========
recursion:
enabled: false
depth: 3 # 递归显示的最大深度
# ========== 排序 ==========
sorting:
column: name # 排序字段:name、size、time、version、extension
# reverse: false # 是否倒序
# dir-grouping: first # 目录排序:first(靠前)、last(靠后)、none
# ========== 符号链接 ==========
# 是否显示符号链接的目标路径
no-symlink: false
symlink-arrow: " ➜ "
# ========== 总大小 ==========
total-size: false # 是否显示总大小(在目录底部)
# ========== 截断所有者名 ==========
truncate-owner:
after: 8 # 用户名超过多少字符截断
# ========== 超链接 ==========
hyperlink: never # always | auto | never 是否生成终端超链接
+32
View File
@@ -0,0 +1,32 @@
# 文件类型颜色
file-type:
directory:
foreground: cyan
bold: true
symlink:
foreground: magenta
italic: true
executable:
foreground: green
bold: true
socket:
foreground: red
pipe:
foreground: yellow
block-device:
foreground: red
char-device:
foreground: yellow
# 文件扩展名颜色(覆盖文件类型颜色)
extensions:
rs:
foreground: white
bold: true
py:
foreground: yellow
js:
foreground: green
md:
foreground: cyan
italic: true
+1
View File
@@ -1,3 +1,4 @@
# RUN:: docker compose -p prod-dbs --env-file ./victoriametrics/env.cfg -f ./victoriametrics/compose.yml up -d
services:
victoriametrics:
+13
View File
@@ -0,0 +1,13 @@
# ============================================================
# victoriametrics 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p victoriametrics --env-file ./env.cfg -f ./stack.yml up -d
#
IMAGE_TAG=victoriametrics/victoria-metrics:v1.126.0
Volumes_Path=/data/volumes/VictoriaMetrics
+23
View File
@@ -0,0 +1,23 @@
# ============================================================
# webout 部署 — 公共环境变量模板(不含敏感信息)
# 复制为 env.cfg 后填入真实值,env.cfg 已被 .gitignore 忽略
# ============================================================
#
# 使用方式:
# cp env.cfg.example env.cfg
# $EDITOR env.cfg # 填入真实密码/密钥后保存
# docker compose -p webout --env-file ./env.cfg -f ./stack.yml up -d
#
#IMAGE_TAG=hub.tp229.com:3500/caddy:2.8.4
IMAGE_TAG=caddy:2.10.0
BUILD_IMAGE_TAG=caddy:2.10.0-builder
WEBDAV_IMAGE_TAG=cnphpbb/caddy-webdav:2-alpine
TXC_DEPLOY_ROOT=/data/deploy.stack
TXC_VOLUMES_ROOT=/data/volumes
CADDY_CONFIG_ROOT=/data/configs