Files
company-deploy-skills/dokploy-gitea-deploy/references/stack-profiles.md
Lon 321d2e6165 docs: scope A — 妙搭 experimental, ordinary Dockerfile main path
Align skill with company-deploy-mcp: support matrix, no gateway/login
parity promise, boot-only self-host env note.
2026-08-01 02:59:37 +08:00

281 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 技术栈构建约定Agent 改 Dockerfile 时遵循)
## 通用
1. 基础镜像Docker Hub`python:*` / `node:*` / `nginx:*`),避免默认 `ghcr.io`
2. 多阶段或「依赖层 → 代码层」拆分。
3. BuildKit`--mount=type=cache` 缓存包管理器目录。
4. 生产 `CMD`/`ENTRYPOINT` 明确;进程监听 **`spec.port`**(与 plan 一致)。
5. 健康检查:实现 `/healthz` 或约定路径,写入 `spec.healthcheckPath`
6. Dokploy **不要每次 cleanCache**
7. **有状态数据写挂载路径**(见下),不要只写容器可写层。
8. **本控制面 = 普通容器自部署**Gitea + Dokploy**不是**飞书妙搭 / TCE 托管平台。
---
## 支持矩阵Agent 选型)
| 栈 | 状态 | Agent 默认行为 |
|----|------|----------------|
| 普通 Python / Node / 静态站 + Dockerfile | **支持** | 正常 create → apply → 交付 URL |
| 自管 Nest/FastAPI/Flask自己的登录与 DB | **支持** | 同上 |
| 飞书妙搭 / `@lark-apaas/*` 全栈模板 | **实验性 · 非主路径** | **不要承诺「可用」**;先告知用户边界;仅用户坚持且理解风险时才部署 |
| 依赖妙搭网关登录 / `/spark` runtime / 平台对象存储 / 插件能力 | **不支持** | 引导 `lark-apps` 在妙搭发布,或业务侧去平台化后再上本控制面 |
**主路径产品承诺:** 有 Dockerfile、监听约定端口、状态写 `/data`、可选 PG/MySQL → HTTPS 链接。
**不承诺:** 与妙搭线上一致的登录、权限、平台 API、观测、能力插件。
### 妙搭 / Lark APaaS仅 boot 兜底,非适配层)
若镜像里仍带着 `PlatformModule``platform.enabled: true`**进程启动**可能需要:
```text
FORCE_AUTHN_INNERAPI_DOMAIN=<应用公网 URL>
```
控制面 `apply` **仅注入最小 boot env**(可关:`SELF_HOST_INJECT_ENV=false`**不做**登录伪造、SPA 模板渲染、runtime API 补齐:
| 变量 | 作用 |
|------|------|
| `FORCE_AUTHN_INNERAPI_DOMAIN` | 公网 URL避免 HttpClient 构造失败 |
| `FORCE_FRAMEWORK_ENVIRONMENT` | 默认 `development``SELF_HOST_FRAMEWORK_ENVIRONMENT` |
| `FORCE_FRAMEWORK_DISABLE_DATAPASS` | 默认 **`false`**(保留 DataPaas → `DRIZZLE_DATABASE`;勿默认关掉) |
| `COMPANY_DEPLOY_SELF_HOST` | 标记自部署,供业务仓可选分支 |
| `SERVER_HOST` / `SERVER_PORT` / `PORT` | `0.0.0.0` + `spec.port` |
| `SUDA_DATABASE_URL` / `DATABASE_URL` | 仅当 plan 带了 `databases[]` 时写入 |
**明确不在控制面 / skill 范围:**
- 注入 `window.appId` / `__BASENAME__` / CSRF 与 hbs 模板(须业务仓自己处理或去平台化)
- 伪造 `x-larkgw-suda-webuser`、业务 `users` 种子、角色权限
- `/spark``/__runtime__`、观测 collect、平台账号/对象存储
- 保证页面「能打开且接口正常」
Agent 话术:检测到 `@lark-apaas` 时,**先说明「仅可能进程可启动,接口与登录不保证」**;推荐改为普通栈或妙搭云端(`lark-apps`)。用户仍要求部署时,按普通 Dockerfile 流程走,**交付时写明实验性**。
---
## 持久化与 `/data`MCP 默认)
控制面默认为每个应用挂载 **容器内 `/data`**(可配置,见 `inspect_project``policy.storage`)。
| 约定 | 说明 |
|------|------|
| SQLite / 本地文件 | 路径放在 `/data/...`,例如 `/data/app.db` |
| 上传目录 | 额外 `persistence``{ "name": "uploads", "mountPath": "/var/uploads" }` |
| 日志 | ephemeral 可写容器内;需保留则挂卷 |
| 多卷 | `spec.persistence[]`;若未包含 `/data`,控制面仍自动补默认卷 |
示例环境变量(应用代码侧):
```text
DATA_DIR=/data
DATABASE_PATH=/data/app.db
```
**禁止**假设宿主机路径;只认容器内 `mountPath`
---
## 托管数据库Postgres / MySQL
plan 中声明 `spec.databases[]` 后,控制面注入连接串环境变量(默认名 `DATABASE_URL`)。
应用应:
1. 启动时读取 `process.env.DATABASE_URL`(或自定义 `envVar`
2. **不要**在镜像里写死密码
3. 迁移在启动脚本或独立 job 中执行(注意并发与锁)
示例Node
```js
const url = process.env.DATABASE_URL;
if (!url) throw new Error("DATABASE_URL is required");
```
示例Python
```python
import os
url = os.environ.get("DATABASE_URL")
if not url:
raise RuntimeError("DATABASE_URL is required")
```
Docker 内网 host 由 Dokploy 管理;应用只消费完整 URL。
---
## Pythonuv 优先)
检测:`pyproject.toml` + `uv.lock`,或 `requirements.txt`
推荐结构:
```dockerfile
FROM python:3.12-slim-bookworm
WORKDIR /app
ENV UV_LINK_MODE=copy \
UV_CONCURRENT_DOWNLOADS=16 \
UV_HTTP_TIMEOUT=120 \
PYTHONUNBUFFERED=1
ARG PIP_INDEX_URL=https://mirrors.aliyun.com/pypi/simple/
ENV UV_DEFAULT_INDEX=${PIP_INDEX_URL} \
UV_INDEX_URL=${PIP_INDEX_URL} \
PIP_INDEX_URL=${PIP_INDEX_URL}
RUN --mount=type=cache,target=/root/.cache/pip \
pip install --no-cache-dir "uv>=0.6.0"
COPY pyproject.toml uv.lock README.md ./
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-dev --no-install-project
COPY src ./src
# …其它源码…
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-dev
# 监听 0.0.0.0,端口与 plan.spec.port 一致
ENV DATA_DIR=/data
EXPOSE 8000
CMD ["uv", "run", "your-entrypoint"]
```
说明:
- 慢点通常是 **PyPI 下包**,不是 `pip install uv` 本身。
- 主机 `~/.pip/pip.conf` **不会**自动进容器;必须 Dockerfile/build-arg。
- 飞书长连接类:`replicas=1` + 状态进 `/data``exposeWeb: false` 若无需公网页。
---
## Nodepnpm / npm
检测:`package.json`,优先 `pnpm-lock.yaml``package-lock.json``yarn.lock`
### Dockerfile 硬规则踩过的坑Agent 必守)
| 错误 | 现象 | 正确做法 |
|------|------|----------|
| 在 **build 阶段 `npm ci` 之前** `ENV NODE_ENV=production` | `nest: not found` / `vite: not found`;日志里 packages 只有几百个(缺 devDependencies | **先** `npm ci`(装全量含 dev**再** `ENV NODE_ENV=production``npm run build` |
| 不设 Node 堆、大前端 Vite 打包 | `FATAL ERROR: JavaScript heap out of memory` | build 阶段:`ENV NODE_OPTIONS=--max-old-space-size=3072`(小机 34G 可试 2560 |
| multi-stage 里 **build 与 runtime 无依赖** | Docker **并行** 两个 `npm ci`,小机内存翻倍易 OOM | runtime 至少 `COPY --from=build …` 一件产物,让 build 先完成 |
| 用用户 SSH 推 Gitea | Permission denied | 用 `ensure_repository``pushUrl`,见 SKILL |
### 推荐Nest + Vite 全栈模板
```dockerfile
FROM node:22-bookworm-slim AS build
WORKDIR /app
# ① 提高堆;② 此时不要 NODE_ENV=production
ENV NODE_OPTIONS=--max-old-space-size=3072
COPY package.json package-lock.json ./
RUN npm ci --ignore-scripts
COPY . ./
# ③ 仅编译时 production
ENV NODE_ENV=production
RUN npm run build:prod && npm cache clean --force
FROM node:22-bookworm-slim AS runtime
WORKDIR /app
ENV NODE_ENV=production \
SERVER_HOST=0.0.0.0 \
SERVER_PORT=3000 \
DATA_DIR=/data
COPY package.json package-lock.json ./
# ④ 依赖 build避免与 vite 并行 npm ci
COPY --from=build /app/package.json /tmp/.build-done
RUN npm ci --omit=dev --ignore-scripts && npm cache clean --force
COPY --from=build /app/dist ./
EXPOSE 3000
CMD ["node", "server/main.js"]
```
### 推荐:较轻的 Node APIpnpm
```dockerfile
FROM node:22-bookworm-slim AS build
WORKDIR /app
ENV NODE_OPTIONS=--max-old-space-size=2048
RUN corepack enable
COPY package.json pnpm-lock.yaml ./
RUN --mount=type=cache,target=/root/.local/share/pnpm/store \
pnpm install --frozen-lockfile
COPY . .
ENV NODE_ENV=production
RUN pnpm run build
FROM node:22-bookworm-slim AS runner
WORKDIR /app
ENV NODE_ENV=production DATA_DIR=/data
COPY --from=build /app/dist ./dist
COPY --from=build /app/package.json ./
COPY --from=build /app/node_modules ./node_modules
# 若 node_modules 含 dev可改为 runner 单独 pnpm install --prod
EXPOSE 3000
CMD ["node", "dist/index.js"]
```
静态前端Vite 等)可用 `nginx:stable-alpine``dist``spec.port` 多为 `80`
纯静态站构建同样需要 **devDependencies**vite在 build 阶段装全。
Registry 加速示例 `.npmrc`
```ini
registry=https://registry.npmmirror.com
```
### 部署前 Agent 自检Node
- [ ] Dockerfile **没有**在安装依赖前设置 `NODE_ENV=production`
- [ ] `nest` / `vite` / `tsc` 等 CLI 在 **devDependencies** 且 build 阶段能装到
- [ ] 大前端:`NODE_OPTIONS=--max-old-space-size=3072`(或更高,视机器)
- [ ] multi-stageruntime 依赖 build避免并行双 `npm ci`
- [ ] 运行 `CMD` 只跑编译产物,**不**在 runtime 再 `nest build`
---
## Docker Compose
- 当前 MCP MVP **仅** `buildType: dockerfile` 单应用。
- 多服务 Composelegacy / 运维在 Dokploy 选 Compose 类型,或拆成多个 MCP 应用 + 托管 DB。
- 生产注意DB 密码走 `databases[]` 注入;卷走 `persistence[]`
---
## 构建耗时预期(经验)
| 场景 | 预期 |
|------|------|
| 首次全量(弱网 + 下包) | 1040 分钟可能 |
| 依赖层已缓存,只改代码 | 数分钟内 |
| 仅拉预构建业务镜像 | 通常更短 |
若日志出现 `Prepared N packages in 30m`:换源 + 分层缓存 + 禁止 cleanCache长期用私有镜像仓库。
---
## 与 plan.spec 对齐检查
部署前 Agent 自检:
- [ ] 容器监听 `0.0.0.0:$PORT`,且 `spec.port` 一致
- [ ] 有状态路径落在 `/data` 或其它 `persistence.mountPath`
- [ ] 需要 PG/MySQL 时 plan 带 `databases`,代码读对应 env
- [ ] Web 服务 `exposeWeb: true`默认Worker 显式 `false`
- [ ] `.gitignore` 排除 `.env`、密钥、本地 db
### DataPaas / DRIZZLE仅当镜像仍用 @lark-apaas DataPaas
- 保持 `FORCE_FRAMEWORK_DISABLE_DATAPASS=false`(默认)
- plan 带 Postgres 时控制面会写 `SUDA_DATABASE_URL` + `DATABASE_URL`
- 这只服务 **Nest 注入**;不代表业务接口已可用