Communitygithub.com

Bohdan-Meshcheriakov/deploy-data-clerk

描述如何在 Data Clerk 托管平台(data-clerk.com Kubernetes 集群)上构建、部署、配置和维护此应用。当用户需要部署、发布、上线应用,添加或更改环境变量、API 密钥或秘密,编辑 secrets.env 或使用 sops,安装相关工具,添加数据库表或迁移,询问数据库、备份或数据恢复,域名或 HTTPS,网站宕机或推送后未更新,Dockerfile 或 Git 相关问题时应使用本技能。

deploy-data-clerk 是什么?

此技能旨在简化在 Data Clerk 托管平台上的应用部署和管理,Data Clerk 是一个由运营商(人类)管理的小型 Kubernetes 集群。其核心理念是让开发者只需推送代码到 `main` 分支,集群自动完成构建、部署和配置,无需直接接触 Kubernetes 或云控制台。该技能详细记录了从项目初始化、Dockerfile 编写、环境变量管理(使用 sops 加密的 `secrets.env`)到数据库迁移和备份恢复的完整工作流程。它强调了“契约”(contract)的重要性,例如应用必须监听 `$PORT`、镜像标签格式、健康检查路径等约束,以确保部署可靠。此外,技能还清晰划分了开发者和运营商的职责范围,指导用户何时应向运营商请求协助(如添加域名、增加资源、数据库恢复等),从而在不暴露集群权限的前提下保障应用的稳定运行。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Bohdan-Meshcheriakov/data-clerk-skills/tree/HEAD/skills/deploy-data-clerk

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

在 Data Clerk 上部署

此应用运行在 Data Clerk 上,这是一个由运营商(人,联系方式见 PROJECT.md)管理的小型 Kubernetes 集群。此仓库的拥有者通常不是基础设施人员。他们永远不需要接触服务器、Kubernetes 或云控制台:他们推送到 main,集群完成其余工作。 你的工作是保持这种状态:在仓库内工作,遵守以下契约,当确实需要集群侧操作时,给运营商写一个清晰的请求,而不是临时凑合。

用通俗的语言向用户解释(例如“你的网站将在大约五分钟内更新”),而不是使用 Kubernetes 术语。

从这里开始

  1. 阅读 deploy-data-clerk/PROJECT.md。它包含此项目的值:公共 URL、部署文件夹、镜像名称、端口、迁移命令、限制、运营商是谁。下面所有提到“来自 PROJECT.md”的地方都指该文件。如果 PROJECT.md 中的文件夹名称与 deploy-data-clerk/ 不同(旧项目使用 deploy/),则使用 PROJECT.md 中指定的名称。
  2. 没有 PROJECT.md,或者它仍然包含 __PLACEHOLDER__ 值?项目尚未完成接入:请遵循 references/new-project.md。
  3. 编写或更改 Dockerfile、选择如何组织应用结构、或者技术栈不是 Node?请阅读 references/stacks.md(任何语言,全栈 vs 分离前端 + API,FastAPI,Go)。
  4. 在机器上执行第一个机密相关任务之前,确保工具正常工作:sops --version 和测试解密(sops decrypt <部署文件夹>/secrets.env > /dev/null)。如果任一失败,请遵循 references/setup-tools.md。

变更如何到达线上站点

变更内容发生什么上线时间
代码(推送到 main)GitHub Actions 构建 Docker 镜像并推送;集群注意到新的镜像标签并滚动更新~3–7 分钟(构建时间 + ~1 分钟)
secrets.env(推送到 main)集群每 5 分钟重新读取部署文件夹,解密,更新应用的环境变量并重启应用~5 分钟
migrations/ 中的新文件在新版本启动前自动运行随代码部署一起
main 之外的任何内容不执行任何操作。分支和 PR 不会被部署—

滚动更新是谨慎的:新版本在旧版本旁边启动,先运行迁移,只有在新版本通过健康检查后才会切换流量。如果构建、迁移或健康检查失败,旧版本将继续服务,站点保持在线。部署失败意味着“变更未上线”,而不是“站点宕机”。

要检查部署:gh run list --workflow "Deploy to cluster" --limit 3(或 GitHub 上的 Actions 选项卡),然后打开公共 URL。要在没有代码变更的情况下重新部署:gh workflow run "Deploy to cluster",它将构建一个全新的标签,因此应用会以全新的环境重启。

契约(保证部署正常工作)

每条规则的存在是因为违反它会导致静默失败或数据丢失。遵守它们,并告诉用户某个请求是否会违反规则。

  • 监听 $PORT(PROJECT.md 中的端口,由集群设置),在 0.0.0.0 上,而不是 localhost。
  • 镜像从 <部署文件夹>/Dockerfile 构建,目标为 linux/amd64,并以非 root 用户运行。不能是 root,不能使用 1024 以下的端口,不能有额外的 Linux 能力。
  • 镜像标签格式为 YYYYMMDDHHMMSS-<短sha>。集群部署编号最高的标签,并静默忽略任何不符合此格式的标签。永远不要更改 .github/workflows/deploy-cluster.yml 中的标签步骤。
  • 容器的磁盘在每次部署和重启时被清空。 永远不要将用户上传、SQLite 文件或任何有价值的内容存放在本地磁盘上。将数据放在数据库中;对于文件(照片、视频),向运营商请求对象存储。
  • 健康检查: 集群探测 PROJECT.md 中的健康路径(推荐 /healthz)。使其快速返回 200,不要进行数据库调用或页面渲染,否则慢速的数据库会使集群认为应用已死。
  • DATABASE_URL 来自集群。 永远不要将其放在 secrets.env 中(会覆盖真实值),也永远不要在镜像构建时设置它。
  • 运行时 vs 构建时。 secrets.env 仅在运行时到达应用。镜像在 GitHub Actions 中构建,不包含它。任何在构建时内联到前端的变量(VITE_*、NEXT_PUBLIC_*、REACT_APP_*)不能来自 secrets.env:在服务器端运行时读取这些值并传递给页面,或者(如果不保密)在代码中提交它们。
  • 应用在缺少时自行生成的机密(身份验证/会话密钥、加密盐、推送密钥):在 secrets.env 中设置它们。否则每次重启都会生成新的,导致用户登出、推送订阅中断等。
  • 迁移必须对旧版本安全。 大约一分钟内,两个版本都针对新 schema 运行。可以自由添加列和表。重命名或删除需要两次部署:首先停止使用,然后移除。详细信息在 references/database.md 中。

环境变量和机密

所有运行时配置都位于 <部署文件夹>/secrets.env 中,使用 sops 加密提交。仅加密值;键名保持可读。使用 sops 本身进行更改。永远不要将其解密到可能提交的文件中,也永远不要将秘密值打印到聊天或日志中。

sops set deploy-data-clerk/secrets.env '["STRIPE_SECRET_KEY"]' '"sk_live_..."'
sops unset deploy-data-clerk/secrets.env '["OLD_KEY"]'
sops decrypt deploy-data-clerk/secrets.env | cut -d= -f1

然后提交并推送到 main;大约 5 分钟后上线。当用户需要提供值时(来自某个控制面板的 API 密钥),让他们自己运行 sops set 行,或者仅将值粘贴到他们自己的终端中。完整指南(密钥、存储位置、轮换、注意事项)在 references/secrets.md 中。

数据库

Postgres 18,由运营商管理。应用通过 DATABASE_URL 获得自己的数据库和用户。Schema 更改是通过迁移文件实现的,这些文件在部署时自动应用(references/database.md)。

备份: 每天 UTC 时间 03:00 进行完整备份,加上每次更改的连续日志,存储在 Google Cloud Storage 的服务器之外。运营商可以将数据库恢复到过去 7 天内的任意时刻。因此,如果数据被删除或迁移出错,不要惊慌,也不要试图手动修复:记录发生的时间,并向运营商请求恢复(模板在 references/troubleshooting.md 中)。恢复会回滚整个数据库,因此该时刻之后所做的更改将丢失。

你的代码可能需要的网络事实

  • 流量路径:访问者 → Cloudflare → Traefik(集群入口)→ 应用。HTTPS 证书是自动的。应用本身在 $PORT 上使用明文 HTTP;X-Forwarded-Proto: https 表示原始请求是 HTTPS。信任代理头部,不要在应用中重定向到 HTTPS(会导致循环)。
  • 真实访问者 IP: 读取 CF-Connecting-IP 头部。X-Forwarded-For 包含 Cloudflare 地址,因此基于它的速率限制会将所有访问者放入一个桶中。
  • Traefik 添加以下头部:HSTS(2 年,preload)、X-Content-Type-Options: nosniff、X-Frame-Options: SAMEORIGIN(站点无法在其他域名上以 iframe 嵌入)。额外头部(CSP、用于 SharedArrayBuffer 的 COOP/COEP、允许 iframe)由运营商根据需要设置。
  • 入口不压缩响应。Cloudflare 压缩常见的文本类型;要完全控制,在应用中压缩或提供预压缩资源。
  • 长连接(SSE、WebSockets)没问题,但 Cloudflare 会断开空闲约 100 秒的连接。每约 30 秒发送一次心跳,并在客户端重新连接。
  • 出站: 应用可以调用任何公共互联网端点上的任何端口。这包括 LLM API、Stripe、电子邮件、S3,以及在其公共端点上的托管数据库或 Redis(Neon、Supabase、Atlas、Upstash)。它无法访问私有网络(VPC 对等连接、PrivateLink、私有 IP)或集群上的其他项目。入站流量仅通过公共 URL 进入。
  • 第三方服务: 将它们的密钥和 URL 放在 secrets.env 中。如果服务需要 IP 白名单,集群有一个静态出站 IP:向运营商询问(未公开),并告诉用户该 IP 与集群上的其他项目共享。服务器位于美国中部,因此外部数据库优先选择美国中部/美国东部区域(欧洲数据库每个查询增加约 100 毫秒)。外部数据库是用户自己的:Data Clerk 的备份不覆盖它。对于慢速 LLM 调用,请流式传输响应或将其作为后台任务运行;等待时间超过约 100 秒的浏览器请求将被切断。
  • 资源有上限(PROJECT.md 中的内存/CPU)。超过内存限制会杀死并重启应用。如果发生这种情况,请向运营商请求更多空间,而不是在代码中对抗它。
  • 应用运行一个副本(一个副本)。不要依赖于在部署后仍然存在的内存中状态,将会话保留在数据库中或签名 cookie 中。

只有运营商才能做的事情

用户没有集群访问权限,这是设计使然。给运营商写一个简短的请求(模板见 references/troubleshooting.md),用于:

新域名或子域名 · 更多内存/CPU · 文件/对象存储 · 数据库恢复 · 应用日志或崩溃原因 · 更改安全头部 · 更改迁移或启动命令、端口或健康路径 · 计划任务(cron) · 额外服务(Redis、队列) · 同一项目中的第二个应用 · 接入新仓库。

更多详细信息

  • references/stacks.md:适用于任何语言的通用契约;全栈 vs SPA + API vs 静态;Node、Python FastAPI、Go 和其他技术栈。
  • references/new-project.md:接入仓库(要添加的文件、需要向运营商请求的内容、每个技术栈的 Dockerfile)。
  • references/secrets.md:sops 深入、密钥文件、轮换、构建时变量。
  • references/database.md:迁移、连接字符串、备份、数据修复。
  • references/setup-tools.md:在 macOS、Linux 和 Windows 上安全安装 sops/git/gh。运行 scripts/install-tools.sh 或 scripts/install-tools.ps1。
  • references/troubleshooting.md:“我的变更未上线”、“站点宕机”、常见错误、运营商请求模板。
  • assets/:Dockerfile 模板(Node、Python、Go)以及部署工作流、kustomization.yaml、.sops.yaml、.gitattributes 和 PROJECT.md 的模板。

相关技能