文档/部署应用

部署应用#

所有部署都经过 Luma Control:CLI 发送 manifest,Control 渲染 Nomad job、更新 DNS 和路由、提交 job,并把进度实时返回。

luma deploy app.yaml -> Luma Control -> Nomad job -> Nomad client -> Docker container

选择部署方式#

你手上有 命令 过程
已发布的容器镜像 luma deploy app.yaml 部署 manifest 中写明的镜像
本地代码目录 luma build local . 用本机 Docker Buildx 构建,推送到集群镜像仓库,然后部署
GitHub 或 Gitea 仓库 luma import owner/repo 由构建节点克隆、构建、推送并部署
docker-compose.yml luma compose deploy luma.compose.yml 把所有服务一起部署到同一个节点

luma deploy 从不构建源码。构建需要构建节点和镜像仓库,见构建。

部署镜像#

用 luma init 生成 manifest,或手写一份:

name: api
image: ghcr.io/acme/api:1.4.2
region: cn
exposure: cn-edge
domain: api.example.com
port: 3000
replicas: 2
resources:
  limits:
    memory: 512M
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:3000/health"]

然后:

luma validate api.yaml            # schema, region/exposure rules; no network
luma deploy api.yaml --dry-run    # print the Nomad job Control will submit
luma deploy api.yaml

部署是 upsert:用同一个 name 重新部署会更新已有的 Nomad job,Nomad 会保留上一个版本用于回滚。生产环境请使用不可变的镜像 tag 或 digest;latest 这类可变 tag 会让回滚结果不可预测。

Control 按以下步骤执行并逐步输出:

  1. 保存 manifest 和作用域密钥;
  2. 渲染 Nomad job;tailscale-relay 和 tcp-relay 还会渲染 Traefik 路由文件;
  3. 创建或更新 Cloudflare DNS(--skip-dns 跳过);
  4. 提交 job 并等待滚动发布完成;
  5. 探测 cn-edge 和 external-edge 服务的公网路由;如果 Traefik 尚未识别,会重建一次 allocation。

默认等待 3000 秒(--timeout)。如果某一步在前面步骤已经修改集群后失败,部署会记录为 failed_partial,之后仍可用 luma app remove 清理。

所有字段见 部署 YAML 参考;调度与入口的含义见核心概念和暴露模型。

密钥#

在 manifest 中引用变量,不要把值写进 Git:

env:
  DATABASE_URL: ${DATABASE_URL}

部署时从 .env 提供,或提前保存:

luma deploy api.yaml --env .env                         # only referenced variables are sent
luma secret set DATABASE_URL --scope api                # prompts for the value
luma secret import .env.production --scope api          # every variable in the file

密钥按应用名隔离,api/DATABASE_URL 和 worker/DATABASE_URL 互不影响。私有镜像的仓库凭据用 luma registry login 设置。详见密钥与凭据。

构建#

源码构建需要一个装有 Docker Buildx 的构建节点(Linux,或使用 OrbStack / Docker Desktop 的 macOS)和一个集群能拉取的镜像仓库:

luma registry serve --node builder          # deploy the managed registry on a node
luma build config --node builder --default-node builder

Luma 会根据目标区域的节点决定镜像架构:ARM 节点得到 linux/arm64,混合架构的区域得到多平台镜像。只有覆盖全部目标架构时才接受 --platform。

原生架构构建节点#

先升级 Control 和构建节点 agent,再同时声明 AMD64 与 ARM64 构建节点:

luma build config --node builder --node air3 --default-node builder \
  --registry-host 100.64.0.70:5000 --push-host 100.64.0.70:5000

所有仓库构建节点必须上报 native-arm-build-v1 能力,自动选择还需要 repository-build-plan-v1。先升级 Control 和全部构建 agent。macOS 的 OrbStack 和 Docker Desktop 均受支持;luma status 会显示原生容器平台,macOS ARM 节点对应 linux/arm64。

当声明的构建节点具有不同架构时,Control 会先解析仓库 manifest(包括指定的 Compose sidecar 和服务级部署位置),锁定 Git commit,再选择原生构建节点。只要包含 ARM64,即使同时构建 AMD64,也必须使用原生 ARM64 builder。原生节点忙碌或离线时继续排队;没有对应 ARM 原生节点则报错,不能回退到模拟构建。运行时密钥只在部署阶段使用。

显式 --build-node 只能固定兼容节点,不能绕过 ARM 要求。每次 ARM 构建前都会核实所有 Buildx worker 的实际 Docker 引擎架构,不能仅依据包含 QEMU 模拟架构的支持列表。该校验覆盖仓库导入、Compose、重试和 luma build local,也包括自定义 Buildx builder。本地旧 CLI 需要先升级;无法验证原生架构的 remote/Kubernetes Buildx driver 不允许执行 ARM 构建。

混合 AMD64/ARM64 请求整体交给 ARM builder,其中 AMD64 部分可以使用模拟执行,不拆分到多台机器。不同 ARM 架构(例如 ARMv7 与 ARM64)需要分开提交。不含 ARM 的构建保留原有回退行为。pushHost 必须能被所有 builder 访问,不能使用指向各自机器的 localhost。

构建本地代码#

cd app
luma build local . --env .env

项目身份取自 origin 远程地址(没有时用 --repo-url)。Luma 用本机 Docker Buildx 构建,按与 luma import 相同的仓库路径推送到集群镜像仓库,然后部署。本机必须能访问该镜像仓库。可选参数:--platform、--context、--dockerfile、--compose-sidecar、--builder(复用已有 Buildx builder)和 --proxy(基础镜像和 RUN 步骤使用的 HTTP 代理)。

导入仓库#

luma import acme/app --ref main --env .env                      # GitHub owner/repo
luma import https://gitea.example.com/acme/app.git --ref v1.4.0

私有仓库先保存一次令牌,之后引用它:

printf '%s' "$GITHUB_TOKEN" | luma git-provider set github personal --username octo --token-stdin
luma git-provider repos github:personal
luma import --provider-id github:personal --repository acme/app --ref main

导入会查找单服务 manifest(.luma.yml、luma.yml、*.luma.yml)或 Compose sidecar(luma.compose.yml、.luma.compose.yml、*.luma.compose.yml、docker-compose.luma.yml)。可以用 --compose-sidecar 仓库内路径 明确指定;仓库里没有部署文件时用 --manifest 本地文件。对 Compose 仓库,带 build: 的服务会被构建并改写为推送后的 image:。本地用 luma compose validate --import-mode luma.compose.yml 检查这一路径。

Dockerfile 阶段与固定 Git 输入#

build.target 选择一个字面量 Dockerfile 阶段名。远程仓库构建还可以把其他仓库作为具名源码上下文:

# docker-compose.yml; the same build fields work in a single-service manifest
services:
  worker:
    build:
      context: .
      dockerfile: Dockerfile
      target: release
      x-luma-git-contexts:
        library:
          lock: deploy/library.lock.json

lock 是相对主仓库根目录的文件路径,不是内联对象,也不相对 build.context。该 JSON 文件必须且只能包含 repository、commit、archive_sha256:

{
  "repository": "https://gitea.example.com/acme/library.git",
  "commit": "0123456789abcdef0123456789abcdef01234567",
  "archive_sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}

将示例摘要替换为完整小写 commit 和 git archive --format=tar.gz <commit> 的 SHA256。Builder 在校验两者后才把导出的文件作为 BuildKit 具名上下文 library,Dockerfile 可用 COPY --from=library 引用。Git 托管平台的 archive 接口可能产生不同字节,不能代替该归档。

额外仓库必须与主仓库具有相同 HTTPS origin(主机和端口),URL 不得包含凭据、query 或 fragment。读取使用主仓库选定的 Git provider 凭据,该凭据必须能访问每个锁定仓库。拉取和校验都发生在 Docker 之外;进入 BuildKit 的只有已验证源码,不包含 Git 凭据、凭据辅助文件或 .git 元数据。

导入时显式要求协议能力:

luma import --provider-id gitea:team --repository acme/app --ref "$APP_COMMIT" \
  --compose-sidecar deploy/luma.compose.yml \
  --require-build-capability repository-build-inputs-v1

CLI 必须支持该参数,Control 必须声明 required-build-capability-v1,选中的就绪 Builder 必须声明 repository-build-inputs-v1。Control 在执行时再次检查实时 Builder,并在部署前要求构建结果确认能力;重试保留要求。缺能力会明确停止,不会静默忽略额外输入。源码实现不代表已发布或线上已加载:使用前需要发布相应 Luma 版本并升级 Control 与 Builder。

外部 --manifest 会自动要求同一能力,无需重复传该参数。仅当仓库没有清单时,Builder 才使用外部清单中通过校验的构建和调度字段;仓库自带清单在构建与部署阶段都优先。外部清单的运行环境和密钥不会作为构建输入传递。使用新版 CLI 的外部清单入口前,必须升级 Control 与 Builder。

luma build local 不支持 x-luma-git-contexts,包括 Compose 构建。额外 Git 输入必须走远程 luma import,不能把 provider 凭据塞进 Dockerfile 绕过。仅使用 build.target 不代表必须有额外 Git 输入。

构建记录与队列#

导入和重试会进入按仓库、ref 和部署目标划分的持久队列:同一目标的新提交会取消更早的排队请求,而 main 和 dev 互不阻塞。忙碌的构建节点只会让自己的请求排队,不会影响其他构建节点。

luma build list --app api --status failed
luma build logs <build-id>
luma build retry <build-id>
luma build cancel <build-id>

构建被接受后关闭 CLI 不会取消构建。排队中的请求在 Control 重启后保留;被中断的进行中构建会标记为失败,而不是自动重放。

Compose 应用#

保持标准的 docker-compose.yml,再加一个说明部署位置和方式的 Luma sidecar:

luma compose init --compose docker-compose.yml --output luma.compose.yml
luma compose validate luma.compose.yml
luma compose deploy luma.compose.yml --dry-run
luma compose deploy luma.compose.yml --env .env

同一个应用的所有服务运行在同一个节点上,共享网络命名空间。持久卷默认使用该节点的本地存储。Compose 不支持 cloudflare-tunnel。详见 Compose 与存储。

部署方式记录#

Control 会记住每个应用上一次成功的构建部署方式(本地构建、导入、镜像部署或 Compose,以及 ref、构建节点、平台、区域等参数)。之后的 deploy、import、build local、build retry 或 compose deploy 会与记录对比:

luma workflow show api
luma workflow record api --note "Built on the builder node" -- import acme/api --ref main
luma workflow run api --path ~/src/api

记录中不会包含令牌或 .env 内容。备注是自由文本,不要写入密钥。

CI#

CI 可以在不保存登录的情况下使用 CLI:

python -m pip install "luma-infra==0.2.14"
export LUMA_CONTROL_URL=https://luma.example.com
export LUMA_DEPLOY_TOKEN="$CI_LUMA_MANAGEMENT_TOKEN"

luma validate deploy/app.yaml --format json
luma deploy deploy/app.yaml --dry-run --format json
luma deploy deploy/app.yaml --format ndjson --timeout 3000

连接参数的优先级为:命令行参数 > 环境变量(LUMA_CONTROL_URL、LUMA_DEPLOY_TOKEN、LUMA_CONTROL_CONTEXT、LUMA_INSECURE、LUMA_RESOLVE_IP)> 已保存的登录。与已保存登录不同的 --control-url 不会借用其令牌。支持 --format 的命令可输出 text、json 和 ndjson,请同时检查退出状态。

在工作站上,可以把多个集群保存为登录,并按命令选择:

luma login https://staging.example.com --token-stdin < staging.token
luma context list
luma deploy app.yaml --control-context staging