在K3s上构建信息管道:Miniflux → KaraKeep → Gotify → Telegram
目录
背景#
作为一个重度 RSS 用户,我每天通过 Miniflux 阅读大量技术文章。阅读过程中经常遇到一个问题:读到好文章想收藏,但收藏后很难再找到;想分享到 Telegram 频道,又需要手动复制链接。
之前在 Miniflux 中"starred"的文章只是静静躺在那里,既没有归档到独立的书签管理器,也没有推送到任何地方。我需要一条自动化的信息管道来解决这个问题:
- 自动存档:Miniflux 中保存的文章自动同步到 KaraKeep(书签管理器)
- 精选推送:在 KaraKeep 中给文章打上
telegram标签后,自动推送到 Telegram 频道 - 零手动操作:除了阅读和打标签,其他环节全自动
部署环境#
我的 Homelab 由两个 K3s 集群组成,通过 Tailscale 互联:
| 集群 | 位置 | 已有服务 | 新增服务 |
|---|---|---|---|
| oracle-k3s | Oracle Cloud ARM | Miniflux, Homepage, IT-Tools | KaraKeep, Redpanda Connect |
| k3s-homelab | Proxmox 虚拟机 | Grafana, ArgoCD, Vault | Gotify |
外部流量路径:Internet → Cloudflare DNS → Cloudflare Tunnel → Traefik → Services
架构设计#
最终的信息管道架构如下:
┌─────────────────────────── Oracle K3s ──────────────────────────────┐
│ │
│ ┌──────────┐ Webhook ┌──────────────────┐ KaraKeep API │
│ │ Miniflux │ ────────────► │ Redpanda Connect │ ───────────────► │
│ │ │ POST │ (webhook-to- │ │
│ │ │ /save_entry │ karakeep) │ ┌──────────┐ │
│ └──────────┘ │ │ │ KaraKeep │ │
│ │ │ │ + Chrome │ │
│ │ 每5分钟轮询 │◄──│ + Meili │ │
│ │ tag=telegram │ └──────────┘ │
│ │ 内存去重 (24h) │ │
│ └────────┬─────────┘ │
│ │ │
└───────────────────────────────────────┼─────────────────────────────┘
│ Gotify API
│ (通过 Tailscale 跨集群)
▼
┌─────────────────────────── Homelab K3s ─────────────────────────────┐
│ │
│ ┌────────┐ Telegram Plugin ┌──────────────────┐ │
│ │ Gotify │ ───────────────────► │ Telegram Channel │ │
│ └────────┘ └──────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────┘
核心数据流:
| 流程 | 触发方式 | 路径 |
|---|---|---|
| 文章存档 | Miniflux Webhook (用户保存文章时) | Miniflux → Redpanda Connect → KaraKeep API |
| 精选推送 | 定时轮询 (每 5 分钟) | KaraKeep API → Redpanda Connect → Gotify → Telegram |
技术选型#
KaraKeep — 书签管理器#
KaraKeep(前身 Hoarder)是一个自托管的书签管理器,支持:
- AI 自动标签和摘要(可选接入 OpenAI/Ollama)
- 全文搜索(内置 Meilisearch)
- 完整的 REST API
- 网页快照(内置 Headless Chrome)
为什么不用 Linkding 或 Wallabag?KaraKeep 的 API 更简洁,部署一体化(web + chrome + meilisearch 三容器),适合 K8s Sidecar 模式。
Redpanda Connect — 数据管道#
Redpanda Connect(前身 Benthos)是一个流处理引擎,用声明式 YAML 定义数据管道。选择它而非 n8n/Node-RED 的原因:
- 极轻量:单个 Go 二进制,~50MB 内存
- 声明式配置:YAML 定义输入/处理/输出,无需写代码
- 内置去重:
cache+dedupe处理器,支持内存缓存 - 双管道运行:单个 Pod 内可运行多个独立管道
Gotify — 推送通知#
Gotify 是一个轻量的自托管推送通知服务,支持通过插件将消息转发到 Telegram。部署在 Homelab 而非 Oracle 的原因:
- Gotify 使用文件存储(SQLite),NFS 持久卷更可靠
- 作为通知中枢,未来可接收更多 Homelab 内部告警
实现细节#
KaraKeep 部署 (Oracle K3s)#
KaraKeep 采用三容器 Sidecar 模式部署:
# cloud/oracle/manifests/rss-system/karakeep.yaml
containers:
- name: karakeep # 主应用 (Next.js)
image: ghcr.io/karakeep-app/karakeep:release
ports:
- containerPort: 3000
env:
- name: NEXTAUTH_URL
value: "https://keep.meirong.dev"
- name: BROWSER_WEB_URL
value: "http://localhost:9222"
- name: MEILI_ADDR
value: "http://localhost:7700"
# ... 密钥通过 ExternalSecret 从 Vault 注入
- name: chrome # Headless Chrome (网页快照)
image: gcr.io/zenika-hub/alpine-chrome:124
command: ["chromium-browser"]
args: ["--headless", "--no-sandbox", "--remote-debugging-port=9222"]
- name: meilisearch # 全文搜索引擎
image: getmeili/meilisearch:v1.13.3
env:
- name: MEILI_MAX_INDEXING_THREADS
value: "1" # Oracle ARM 免费实例资源有限
踩坑:KaraKeep 的 Docker Hub 镜像
karakeep/karakeep不存在,必须用 GitHub Container Registry 的ghcr.io/karakeep-app/karakeep:release。
踩坑:Meilisearch 在 Oracle ARM 免费实例上频繁 OOM CrashLoopBackOff,报错
Resource temporarily unavailable (os error 11)。解决方法:将内存限制提高到 768Mi 并设置MEILI_MAX_INDEXING_THREADS=1限制索引线程数。
Redpanda Connect 双管道配置#
Redpanda Connect 的核心是两条独立管道,运行在同一个 Pod 的两个容器中:
管道 1:Webhook → KaraKeep(文章存档)
# connect.yaml — 接收 Miniflux Webhook,保存到 KaraKeep
input:
http_server:
path: /save_entry
allowed_verbs: ["POST"]
pipeline:
processors:
- mapping: |
root.type = "link"
root.url = this.entry.url
root.title = this.entry.title
output:
http_client:
url: "http://karakeep.rss-system.svc:3000/api/v1/bookmarks"
verb: POST
headers:
Authorization: "Bearer ${KARAKEEP_API_KEY}"
Content-Type: application/json
管道 2:KaraKeep → Gotify(精选推送)
# poll-karakeep.yaml — 轮询 KaraKeep tag=telegram 书签,推送到 Gotify
input:
generate:
interval: "5m"
mapping: 'root = {}'
pipeline:
processors:
# 1. 拉取所有未归档书签
- http:
url: "http://karakeep.rss-system.svc:3000/api/v1/bookmarks?favourited=false&archived=false"
verb: GET
headers:
Authorization: "Bearer ${KARAKEEP_API_KEY}"
# 2. 筛选带 telegram 标签的
- mapping: |
root = this.bookmarks.filter(b -> b.tags.any(t -> t.name == "telegram"))
# 3. 展开数组,逐条处理
- unarchive:
format: json_array
# 4. 内存缓存去重 (24h TTL)
- cache:
operator: set
resource: dedup_cache
key: ${! this.id }
value: "seen"
- dedupe:
cache: dedup_cache
key: ${! this.id }
# 5. 构造 Gotify 消息
- mapping: |
root.title = "📌 " + this.title.or(this.content.title).or("New Bookmark")
root.message = this.url.or(this.content.url).or("")
root.priority = 5
output:
http_client:
url: "${GOTIFY_URL}/message"
verb: POST
headers:
X-Gotify-Key: "${GOTIFY_TOKEN}"
Content-Type: application/json
resources:
caches:
dedup_cache:
memory:
default_ttl: "24h"
为什么用两个容器而非两个 stream 模式管道? Redpanda Connect 的 stream 模式适合同构管道,而这两条管道的输入类型完全不同(HTTP server vs 定时生成),分容器更清晰且互不干扰。
Gotify 部署 (Homelab K3s)#
Gotify 部署相对简单,通过 ArgoCD GitOps 管理:
# k8s/helm/manifests/gotify.yaml
containers:
- name: gotify
image: gotify/server:latest
ports:
- containerPort: 80
env:
- name: GOTIFY_DEFAULTUSER_PASS
valueFrom:
secretKeyRef:
name: gotify-secret
key: default-user-password
volumeMounts:
- name: data
mountPath: /app/data
密钥通过 External Secrets Operator 从 HashiCorp Vault 同步到 K8s Secret。
跨集群通信#
Oracle K3s 上的 Redpanda Connect 需要调用 Homelab K3s 上的 Gotify API。两个集群通过 Tailscale 互联:
Redpanda Connect (oracle-k3s)
→ http://gotify.personal-services.svc.cluster.local:80 ✗ 跨集群不可达
→ https://notify.meirong.dev ✗ 经过 Cloudflare Tunnel 增加延迟
→ http://100.x.x.x:80 ✓ 通过 Tailscale 直连
实际实现中,Gotify URL 存储在 Vault 的 homelab/redpanda-connect 密钥路径下,通过 ExternalSecret 注入为环境变量 GOTIFY_URL。
SSO 保护#
KaraKeep 和 Gotify 都通过 oauth2-proxy + Traefik ForwardAuth 实现 SSO 保护:
# HTTPRoute 中添加 SSO 过滤器
rules:
- matches:
- path:
type: PathPrefix
value: /
filters:
- type: ExtensionRef
extensionRef:
group: traefik.io
kind: Middleware
name: sso-forwardauth
backendRefs:
- name: karakeep
port: 3000
由于 Traefik Gateway API 要求 Middleware 与 HTTPRoute 在同一命名空间,需要在 rss-system 命名空间额外创建 sso-forwardauth Middleware:
# cloud/oracle/manifests/base/traefik-config.yaml
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: sso-forwardauth
namespace: rss-system # 必须与 KaraKeep HTTPRoute 同命名空间
spec:
forwardAuth:
address: http://oauth2-proxy.auth-system.svc.cluster.local:4180/
trustForwardHeader: true
踩坑:最初忘了在
rss-system命名空间创建 Middleware,导致keep.meirong.dev返回 404,而非预期的 302 SSO 重定向。Traefik 找不到 Middleware 时直接返回 404 而不是报错,排查时容易误以为是路由配置问题。
网关与 DNS 配置#
| 服务 | 集群 | 域名 | Cloudflare Tunnel |
|---|---|---|---|
| KaraKeep | oracle-k3s | keep.meirong.dev |
Oracle Tunnel |
| Gotify | homelab | notify.meirong.dev |
Homelab Tunnel |
两个域名分别通过各自集群的 Cloudflare Tunnel 暴露,Terraform 管理 DNS 记录和 Tunnel 入站规则。
部署流程#
完整的部署步骤:
# 1. 在 Vault 中创建密钥
vault kv put secret/homelab/karakeep \
nextauth_secret=$(openssl rand -hex 32) \
meili_master_key=$(openssl rand -hex 16)
vault kv put secret/homelab/gotify \
default_user_password="<password>"
vault kv put secret/homelab/redpanda-connect \
karakeep_api_key="" \
gotify_url="" \
gotify_token=""
# 2. 部署 Oracle K3s 服务
kubectl --context oracle-k3s apply -k cloud/oracle/manifests/
# 3. 部署 Homelab Gotify (ArgoCD 自动同步)
git add k8s/helm/manifests/gotify.yaml
git commit -m "feat: add gotify push notification service"
git push # ArgoCD 3 分钟内自动部署
# 4. 配置 Cloudflare DNS
cd cloudflare/terraform && just apply # notify.meirong.dev
cd cloud/oracle/cloudflare && just apply # keep.meirong.dev
# 5. 部署后手动配置
# - 登录 KaraKeep UI → 生成 API Key → 更新 Vault
# - 登录 Gotify UI → 创建 App → 获取 Token → 更新 Vault
# - 登录 Miniflux → Settings → Webhook URL: http://redpanda-connect.rss-system.svc:4195/save_entry
# - 安装 Gotify Telegram 插件
最终效果#
| 服务 | URL | 状态 |
|---|---|---|
| KaraKeep | keep.meirong.dev |
SSO 保护 ✓ |
| Gotify | notify.meirong.dev |
SSO 保护 ✓ |
| IT-Tools | tool.meirong.dev |
公开访问 ✓ |
| Squoosh | squoosh.meirong.dev |
公开访问 ✓ |
信息流验证:
- 在 Miniflux 中保存文章 → KaraKeep 自动创建书签 ✓
- 在 KaraKeep 中给书签添加
telegram标签 → 5 分钟内 Gotify 收到通知 ✓ - Gotify 通过 Telegram 插件转发到频道 ✓
- 重复书签不会重复推送(内存去重 24h TTL) ✓
踩坑总结#
| 问题 | 现象 | 原因 | 解决 |
|---|---|---|---|
| KaraKeep 镜像拉取失败 | ImagePullBackOff |
Docker Hub 上没有 karakeep/karakeep |
改用 ghcr.io/karakeep-app/karakeep:release |
| Meilisearch 崩溃 | CrashLoopBackOff,os error 11 |
Oracle ARM 免费实例内存不足 | 内存限制 768Mi + MEILI_MAX_INDEXING_THREADS=1 |
| keep.meirong.dev 404 | 浏览器返回 404 | rss-system 命名空间缺少 sso-forwardauth Middleware |
在 traefik-config.yaml 中添加 |
| ArgoCD 覆盖手动变更 | kubectl apply 后 filter 消失 | selfHeal: true 从旧 Git 版本恢复 |
必须先 git push 再等 ArgoCD 同步 |
下一步#
- 配置 Miniflux Webhook 触发保存
- 安装 Gotify Telegram 插件并关联频道
- 在 KaraKeep 中创建 API Key 更新到 Vault
- 考虑接入 Ollama 实现 AI 自动摘要和标签
本文是 Homelab 系列的一部分。上一篇:在K3s上部署ZITADEL实现SSO单点登录