Community生产力与协作github.com

Cem-Tas96/claude-skills

个人 Claude Code 技能集,在 macOS 和 Windows 机器间同步。位于 ~/.claude/skills/。

claude-skills 是什么?

该技能为开发者提供了一套全面的自动化部署前检查流程(称为“Gameboy Gate”),专为搭载 Coolify 的 4GB ARM 服务器(绰号 Gameboy)设计。它强制所有新项目在发布前必须通过一系列严格的资源和性能检查,从而防止部署后出现内存不足、响应缓慢或停机问题。该技能支持在 Claude Code、Cursor 和类似 AI 编码代理中运行,通过 SSH 在目标服务器上执行命令,并利用本地(gitignored)配置文件进行安全连接。 工作流程包括八个阶段:从获取实时服务器快照和 AI 驱动的资源限制计算(基于项目类型,如 Node.js、Python 或静态站点),到在 Coolify 数据库中永久设置这些限制,配置环境变量(如 NODE_OPTIONS)、Traefik 速率限制、文件描述符限制,最后使用 Apache Bench 运行 50 个并发用户的负载测试。如果任何检查(如 p99 延迟超过 3000ms、内存峰值超过限制、失败请求超过 0.1%)失败,门禁会阻止发布,并标记具体失败原因和修复步骤。

兼容平台✓Claude Code~Codex CLI~Cursor
npx skills add Cem-Tas96/claude-skills

Installed? Explore more 生产力与协作 skills: steipete/gemini, steipete/gh-issues, steipete/skill-creator · View all 6 →

在你喜欢的 AI 中提问

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

文档

🎮 Gameboy-Gate — 每个项目的入学考试

必须: 部署到 Gameboy(<GAMEBOY_IP>)上的每个项目都必须通过此门禁——无论谁在使用 Claude,无论什么项目。没有通过则禁止发布。

🔒 连接数据本地化: <GAMEBOY_IP>、<GAMEBOY_SSH>、<COOLIFY_URL>、<WATCHDOG_*> 的实际值存储在 gitignore 的 gameboy-gate/gameboy.local.md 文件中(不在公开仓库中)。每次运行门禁前,读取此文件并替换占位符。

上下文:Gameboy

服务器:Hetzner CAX11 (ARM)
IP:     <GAMEBOY_IP>          # 实际值:见 gameboy.local.md
SSH:    <GAMEBOY_SSH>         # 实际值:见 gameboy.local.md
RAM:    4GB 总计
CPU:    2x vCPU (ARM Neoverse-N1 @ 2.0GHz)
磁盘:   40GB SSD
操作系统:Ubuntu (Docker + Coolify)
Coolify UI:<COOLIFY_URL>     # 实际值:见 gameboy.local.md

固定开销预留(永不低于):

操作系统 + 内核:              400MB
Coolify(含数据库、Redis、代理、哨兵):900MB
安全缓冲区:                   200MB
──────────────────────────────
项目可用:                   ~2500MB(来自 4096MB 总计)

正在运行的项目(截至 2026年6月26日——实际名称见 gameboy.local.md):

  • app-workflow (Activepieces):1536MB 限制 / 768MB 预留
  • app-billing (Next.js):512MB 限制 / 256MB 预留
  • app-landing (静态):256MB 限制 / 128MB 预留
  • 已占用: 2304MB 限制 / 1152MB 预留

门禁流程(按此顺序,不可跳过)

阶段 G0:获取服务器快照

始终先执行——这是当前服务器状态的真相:

<GAMEBOY_SSH> "
free -h &&
docker stats --no-stream --format 'table {{.Name}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.CPUPerc}}' &&
df -h / &&
docker inspect \$(docker ps -q) --format '{{.Name}}: mem={{.HostConfig.Memory}}' 2>/dev/null | sort
"

从中推导:

  • TOTAL_RAM_MB(几乎总是 3840MB = 3.7GB 字节)
  • COOLIFY_OVERHEAD_MB = 所有 Coolify 自身容器当前的总和(coolify、coolify-db、coolify-redis、coolify-sentinel、coolify-proxy)
  • RUNNING_PROJECTS_MB = 所有运行中项目容器的 mem_limit 总和
  • AVAILABLE_MB = TOTAL_RAM_MB - 400(操作系统)- COOLIFY_OVERHEAD_MB - RUNNING_PROJECTS_MB - 200(安全缓冲区)

阶段 G1:限制计算(AI 驱动,无硬编码)

根据项目类型,AI 计算正确的限制:

识别项目类型:
- Node.js / Next.js 应用     → BASE_MB = 350,每个 Worker 的 WORKER_OVERHEAD = 50
- Python / Django / FastAPI   → BASE_MB = 200,每个 Worker 的 WORKER_OVERHEAD = 30
- Go / Rust 服务             → BASE_MB = 80,每个 Worker 的 WORKER_OVERHEAD = 20
- 静态站点 (nginx/caddy)     → BASE_MB = 64,WORKER_OVERHEAD = 0
- Postgres                    → BASE_MB = 128,每个连接的 WORKER_OVERHEAD = 10
- Redis                       → BASE_MB = 64,WORKER_OVERHEAD = 0
- 后台 Worker (队列)         → BASE_MB = 200,每个 Worker 的 WORKER_OVERHEAD = 50
- AI/LLM 服务 (本地)         → BASE_MB = 800+(如果模型本地,至少 2GB)

限制公式:

MEM_LIMIT_MB    = min(AVAILABLE_MB * 0.8, BASE_MB * 3)   -- 可用资源的 80%,最多 3 倍 Base
MEM_RESERVATION = MEM_LIMIT_MB * 0.5                      -- 50% 作为保证最小值
MEM_SWAP_MB     = MEM_LIMIT_MB * 1.4                      -- 允许 40% 交换开销
CPU_LIMIT       = round(2.0 * (MEM_LIMIT_MB / AVAILABLE_MB), 1)  -- 与 RAM 占比成正比

NODE_OPTIONS_MB = floor(MEM_LIMIT_MB * 0.70)              -- V8 堆 = 容器限制的 70%
UV_THREADPOOL_SIZE = min(8, cpu_count * 2)                -- libuv 线程数

设置前的验证检查:

  • MEM_LIMIT_MB >= 128(启动所需最小值)
  • AVAILABLE_MB - MEM_LIMIT_MB >= 200(安全缓冲区保留)
  • 所有 mem_limits 总和 ≤ 3500MB(不超过物理内存的 85% 过度分配)
  • 如果验证失败 → 阻止,通知 Cem

阶段 G2:在 Coolify 数据库中永久设置限制

这是防止部署回归的关键修复。 Coolify 在每次部署时从其自身数据库重新生成 docker-compose.yaml。只有在数据库中设置了限制,它们才能持久保存。

如果已知应用 UUID:

<GAMEBOY_SSH> "
docker exec coolify-db psql -U coolify -d coolify -c \"
UPDATE applications SET
  limits_memory = '<MEM_LIMIT_MB>m',
  limits_memory_swap = '<MEM_SWAP_MB>m',
  limits_memory_swappiness = 10,
  limits_memory_reservation = '<MEM_RESERVATION>m',
  limits_cpus = '<CPU_LIMIT>',
  limits_cpu_shares = 1024
WHERE uuid = '<APP_UUID>';
SELECT uuid, name, limits_memory, limits_cpus FROM applications WHERE uuid='<APP_UUID>';
\"
"

从 Coolify URL 获取 UUID:<COOLIFY_URL>/project/.../application/<UUID>/...

如果 UUID 未知 → 通过数据库搜索:

docker exec coolify-db psql -U coolify -d coolify -c "SELECT uuid, name FROM applications ORDER BY created_at DESC LIMIT 10;"

阶段 G3:设置环境变量

在项目的 .env 中(通过 echo >> .env 或 Coolify UI):

# 对于 Node.js/Next.js:
NODE_OPTIONS=--max-old-space-size=<NODE_OPTIONS_MB>
UV_THREADPOOL_SIZE=<UV_THREADPOOL_SIZE>

# 对于工作流应用(Activepieces 等):
AP_MAX_CONCURRENT_JOBS=5     # 防止沙盒爆炸
AP_TRIGGER_DEFAULT_POLL_INTERVAL=15  # 减少数据库基线负载

# 对于所有应用:
# (生产环境中不要启用 DEBUG 日志!)
NODE_ENV=production

重要:避免换行错误! 检查每个变量是否在单独一行:

grep -c '^[A-Z_].*=' .env  # 应等于变量数量
# 没有一行应包含两个 '=',除非在值中
awk -F= 'NF>2 && $1 ~ /^[A-Z_]/' .env && echo "错误:行中存在重复 = !"

阶段 G4:启用 Traefik 速率限制

对于每个新的公共服务:

<GAMEBOY_SSH> "
cat > /data/coolify/proxy/dynamic/rate-limit-global.yaml << 'EOF'
http:
  middlewares:
    rate-limit-strict:
      rateLimit:
        average: 60
        burst: 120
        period: 1s
        sourceCriterion:
          ipStrategy:
            depth: 1
    rate-limit-api:
      rateLimit:
        average: 30
        burst: 60
        period: 1s
        sourceCriterion:
          ipStrategy:
            depth: 1
EOF
echo '速率限制配置已写入'
"

Traefik 会自动拾取(watch=true)——无需重启。

然后将中间件添加到应用路由器(在应用特定的 Traefik 配置中):

middlewares:
  - rate-limit-strict

阶段 G5:设置文件描述符限制

Docker 守护进程必须知道限制。每个 Gameboy 设置只需一次,会持久保留:

<GAMEBOY_SSH> "
# 检查是否已设置
grep -q 'nofile' /etc/docker/daemon.json && echo 'already set' || {
  # 备份并更新
  cp /etc/docker/daemon.json /etc/docker/daemon.json.bak.\$(date +%Y%m%d)
  python3 -c \"
import json
with open('/etc/docker/daemon.json') as f: d = json.load(f)
d['default-ulimits'] = {'nofile': {'Name': 'nofile', 'Hard': 65536, 'Soft': 65536}}
with open('/etc/docker/daemon.json', 'w') as f: json.dump(d, f, indent=2)
print('daemon.json 已更新')
  \"
  # 注意:systemctl restart docker 会重启所有容器!
  # 仅在无活动流量时执行 → 与 Cem 协调
  echo '需要:systemctl restart docker(短暂停机!)'
}
"

阶段 G6:负载测试(真正的入学考试)

50 个并发用户,60 秒:

# 测试 1:基本可访问性
curl -o /dev/null -s -w "HTTP=%{http_code} %{time_total}s\n" https://<APP_URL>/

# 测试 2:50 个并发 HTTP 请求(30 秒)
/usr/sbin/ab -n 3000 -c 50 -t 30 -r -k https://<APP_URL>/ 2>&1 | grep -E "Requests|Failed|Time per|50%|90%|99%"

# 测试 3:负载期间监控内存
<GAMEBOY_SSH> "for i in \$(seq 1 6); do sleep 10; docker stats --no-stream --format '{{.Name}}: {{.MemUsage}} {{.MemPerc}} CPU={{.CPUPerc}}' | grep -v NAMES; echo '---'; done" &
MONITOR_PID=\$!
/usr/sbin/ab -n 5000 -c 50 -t 55 -r https://<APP_URL>/ 2>/dev/null
wait \$MONITOR_PID

阶段 G7:通过/失败标准

通过(必须全部满足):

标准阈值
HTTP 状态2xx 或 3xx(无 502/503/504)
p99 延迟< 3000ms
内存增长稳定(t=0 到 t=60s 之间 < +20%)
内存低于限制< 容器限制的 90%
无失败请求< 0.1%
测试后服务器可达所有 URL 返回 HTTP 200/302
测试后 CPU< 80% 持续

失败(任一条件即阻止):

  • 测试后任何容器关闭/重启
  • 内存达到限制(> 95%)
  • 失败请求 > 1%
  • p99 > 5000ms
  • 看门狗在测试期间介入(检查:<WATCHDOG_LOG>)

阶段 G8:门禁结果

═══════════════════════════════════════════════════════
🎮 GAMEBOY-GATE 结果
═══════════════════════════════════════════════════════
项目:     [名称]
应用 UUID:[Coolify UUID]
测试时间:[日期/时间]

限制计算:
  服务器 RAM:              3840MB
  Coolify 开销:            [n]MB(实测)
  其他项目:                [n]MB(限制总和)
  可用:                    [n]MB
  已分配:                  内存=[n]m,CPU=[n],NODE_OPTIONS=[n]MB

检查:
  ✅/❌ Coolify 数据库限制已持久设置
  ✅/❌ NODE_OPTIONS 正确(无换行错误)
  ✅/❌ 速率限制已启用
  ✅/❌ 文件描述符限制 65536
  ✅/❌ Redis maxmemory 策略已设置(如果是 Redis)
  ✅/❌ 负载测试 50c/30s 通过

负载测试:
  请求/秒:[n]     (通过要求 > 10)
  p50:      [n]ms
  p99:      [n]ms   (通过要求 < 3000ms)
  失败:     [n]%    (通过要求 < 0.1%)
  内存峰值:[n]MB   (通过要求 < 限制的 90%)

═══════════════════════════════════════════════════════
判决:✅ 通过 — 允许发布
       ❌ 失败 — 发布被阻止([原因])
═══════════════════════════════════════════════════════

常见失败原因及修复

失败:NODE_OPTIONS 未设置

# 检查:
docker exec <container> printenv NODE_OPTIONS
# 修复:
grep -n 'NODE_OPTIONS' .env  # 检查换行错误
# 错误:HOST=0.0.0.0NODE_OPTIONS=... → 修复:
sed -i 's/HOST=0.0.0.0NODE_OPTIONS=/HOST=0.0.0.0\nNODE_OPTIONS=/' .env

失败:测试后容器 OOM

根本原因几乎总是以下之一:

  1. NODE_OPTIONS 未设置 → V8 占用全部容器内存
  2. 沙盒进程(AP、代码执行)没有并发限制
  3. 应用代码中的内存泄漏(较少见)
# 检查是否发生重启:
docker inspect <container> --format '{{.RestartCount}}'
# 检查是否发生 OOM 杀死:
dmesg | grep -i 'oom\|killed' | tail -10

失败:p99 > 3000ms

  1. 没有缓存的重量级 API 端点 → 实现 Redis 缓存
  2. PG 连接池耗尽 → 增加 MAX_CONCURRENT_REQUESTS 或池大小
  3. 每个请求的前向认证开销 → 实现 JWT 缓存

失败:失败请求 > 0.1%

  1. Traefik 超时短于应用响应 → 增加 Traefik 超时
  2. 文件描述符限制耗尽 → 步骤 G5(ulimits)
  3. PG max_connections 耗尽 → 在 PG 中设置 max_connections=200

部署韧性:部署生存协议

每次 Coolify 部署必须保证以下事项以防止服务器崩溃:

每次部署前检查:

<GAMEBOY_SSH> "
# 可用 RAM 必须 > 800MB
FREE=\$(free -m | awk '/^Mem:/{print \$7}')
echo "可用:\${FREE}MB"
[ \$FREE -lt 800 ] && echo '警告:部署内存不足!' || echo 'RAM 适合部署'
# 磁盘必须 > 5GB 空闲(拉取镜像需要空间)
DISK=\$(df / | awk 'NR==2{print \$4}')
echo "磁盘空闲:\$((DISK/1024))MB"
[ \$DISK -lt 5242880 ] && echo '警告:磁盘空间不足!' || echo '磁盘 OK'
"

Coolify 在部署时做什么(以及我们必须确保什么):

  1. Coolify 读取其数据库 → 生成 docker-compose.yaml,包含数据库中设置的限制(G2 → 持久 ✅)
  2. 容器停止 → 短暂停机
  3. 新容器启动 → Healthcheck 等待应用就绪
  4. Traefik 获取新的后端端点

Healthcheck 配置(在 Coolify UI 或直接在数据库中):

docker exec coolify-db psql -U coolify -d coolify -c "
UPDATE applications SET
  health_check_enabled = true,
  health_check_path = '/api/v1/flags',  -- 或 '/', '/health', '/ping'
  health_check_port = '80',
  health_check_interval = 15,
  health_check_timeout = 5,
  health_check_retries = 3,
  health_check_start_period = 90  -- 大型应用 90 秒启动时间
WHERE uuid = '<APP_UUID>';
"

内存预算表(截至 2026年6月26日)

GAMEBOY 4GB RAM — 预算概览
════════════════════════════════════════════════════════════
系统/操作系统:              400MB(固定)
Coolify 堆栈:              ~950MB(coolify+db+redis+sentinel+proxy)
────────────────────────────────────────────────────────────
项目可用:                 ~2650MB
════════════════════════════════════════════════════════════
app-workflow:          1536MB 限制 / 768MB 预留
app-billing:            512MB 限制 / 256MB 预留
app-landing:            256MB 限制 / 128MB 预留
────────────────────────────────────────────────────────────
仍然空闲:                ~346MB 限制空间
════════════════════════════════════════════════════════════

警告:Gameboy 的限制容量已占用约 91%。
大于 300MB 的新项目需要进行重新平衡(缩小现有
项目或优化 Coolify 堆栈)。

如果新项目需要超过 200MB → 首先检查是否可以将 landing(256MB)减少到 128MB(静态站点,所需内存少)。


每次部署的检查清单(快速参考)

部署前:
  [ ] 可用 RAM > 800MB
  [ ] 磁盘 > 5GB 空闲
  [ ] Coolify 数据库限制已设置(不为 0!)
  [ ] .env 中的 NODE_OPTIONS 正确(无换行错误)
  [ ] 已为公共端点配置速率限制

部署后:
  [ ] 容器正在运行(docker ps → Up X seconds (healthy))
  [ ] URL 可达(HTTP 2xx/3xx)
  [ ] NODE_OPTIONS 生效(docker exec <c> printenv NODE_OPTIONS)
  [ ] 内存稳定(docker stats → 未达到限制)
  [ ] 无 OOM 杀死(dmesg | grep -i …

相关技能