Files
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

10 KiB
Raw Permalink Blame History

技术栈构建约定Agent 改 Dockerfile 时遵循)

通用

  1. 基础镜像Docker Hubpython:* / 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 兜底,非适配层)

若镜像里仍带着 PlatformModuleplatform.enabled: true进程启动可能需要:

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 默认 developmentSELF_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 流程走,交付时写明实验性


持久化与 /dataMCP 默认)

控制面默认为每个应用挂载 容器内 /data(可配置,见 inspect_projectpolicy.storage)。

约定 说明
SQLite / 本地文件 路径放在 /data/...,例如 /data/app.db
上传目录 额外 persistence{ "name": "uploads", "mountPath": "/var/uploads" }
日志 ephemeral 可写容器内;需保留则挂卷
多卷 spec.persistence[];若未包含 /data,控制面仍自动补默认卷

示例环境变量(应用代码侧):

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

const url = process.env.DATABASE_URL;
if (!url) throw new Error("DATABASE_URL is required");

示例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

推荐结构:

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 + 状态进 /dataexposeWeb: false 若无需公网页。

Nodepnpm / npm

检测:package.json,优先 pnpm-lock.yamlpackage-lock.jsonyarn.lock

Dockerfile 硬规则踩过的坑Agent 必守)

错误 现象 正确做法
build 阶段 npm ci 之前 ENV NODE_ENV=production nest: not found / vite: not found;日志里 packages 只有几百个(缺 devDependencies npm ci(装全量含 dev ENV NODE_ENV=productionnpm 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_repositorypushUrl,见 SKILL

推荐Nest + Vite 全栈模板

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

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-alpinedistspec.port 多为 80
纯静态站构建同样需要 devDependenciesvite在 build 阶段装全。

Registry 加速示例 .npmrc

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 注入;不代表业务接口已可用