Luma Deployment YAML#
Luma 的部署文件不是 Docker Compose。它是一个更小的 service manifest,用户只描述服务入口、镜像、区域和少量运行参数。luma deploy 会把它提交给控制面,控制面再渲染成 Nomad jobspec、同步 DNS、配置 Traefik 路由,并通过 Nomad HTTP API 部署。
最小公开服务#
国内公开服务通常写成这样:
name: status
image: traefik/whoami:latest
region: cn
exposure: cn-edge
domain: status.example.com
port: 80
replicas: 1
部署:
luma deploy status.yaml
结果:
- Cloudflare 写入
status.example.com记录; - Traefik 为
Host(status.example.com)创建 HTTPS 路由; - Nomad 部署
statusjob; - 请求转发到容器内
port: 80。
字段参考#
| 字段 | 必填 | 类型 | 说明 |
|---|---|---|---|
name |
是 | string | 服务名。Luma 会转成 slug,用作 Nomad job/group/task 名称。 |
image |
是* | string | 容器镜像,例如 ghcr.io/acme/api:1.0.0。latest 或未带 tag 会在部署时解析成 name@sha256:... 再部署。*提供 build 块时可省略,镜像由构建产出。 |
build |
否 | map | 从源码构建镜像(luma import)。子字段:context(默认 .)、dockerfile(默认 Dockerfile)、platform(默认 linux/amd64)。提供 build 时 image 可省略。见下方「从 Git 仓库构建部署」。 |
region |
是 | cn / global / home 或已创建的自定义 Region |
服务运行区域。自定义 Region 需先 luma region create。 |
engine |
否 | nomad |
该服务的编排后端。当前集群默认是 Nomad,通常不需要填。 |
node |
否 | string | 指定 Luma 节点名,也就是 luma node join --name 的值。用于把服务钉到某台机器;控制面会渲染成 Nomad 的 ${node.unique.name}(或 meta.luma_node_name)约束,仍会同时加 region 约束。 |
exposure |
否 | 见下方 | 访问方式。新文件必须显式表达公开、隧道或内部访问语义。 |
domain |
公开服务必填 | string | 用户访问的域名。 |
port |
公开服务必填 | integer | 容器内部监听端口,不是云服务器安全组端口。 |
replicas |
否 | integer | 副本数,渲染成 Nomad group count,默认 1,必须大于等于 1。 |
env / environment |
否 | map | 环境变量,会写进 Nomad task 的 env。 |
command |
否 | string/list | 覆盖容器启动命令。 |
constraints |
否 | string[] | 追加 Nomad placement 约束。Luma 会自动加 region 约束。 |
labels |
否 | string[] | 追加服务标签。公开 Traefik 路由所需的 service tags 会自动生成。 |
networks |
否 | string[] | 追加网络声明。公开服务的入口由 Traefik Nomad provider 自动发现。 |
proxy |
否 | boolean | 服务运行时是否需要走 egress proxy。为 true 时会自动挂上 egress 代理和代理环境变量。调度仍按 region。不是镜像拉取代理。 |
resources |
否 | map | 渲染到 Nomad task 的 resources 块,CPU 使用 reservations.cpus 弹性共享,不执行 limits.cpus。内存 reservation 映射到 memory、limit 映射到 memory_max;首次部署这类任务时会自动启用 Nomad 内存超卖,确保 limit 真正成为容器硬上限。 |
healthcheck |
否 | map | 单服务 cn-edge / external-edge 渲染成 Nomad HTTP 或 TCP 检查;可提取的 HTTP URL 提供路径,否则回退到 TCP,不会在容器中执行任意 shell 命令。其他暴露模式应检查实际生成的 Checks。 |
requirements |
否 | map | 组件依赖契约。支持 capabilities(cloudflare / tailscale / egress / registry)、secrets、init 和 notes。init 支持 cloudflare-dns、cloudflare-tunnel、tailscale-node、egress、registry、secrets。部署预览会合并显式声明与运行时推导,并在必需项缺失时阻止提交;正式部署会先执行并记录初始化计划。 |
publishPort |
公开服务可用 | integer | 显式启用 Nomad bridge 端口映射,把宿主机 publishPort 转到容器 port。原生 Mac/OrbStack 单服务仍需省略此项;Mac Compose 支持 Docker 管理的端口映射,见自定义 Region 公网入口。 |
relay |
tailscale-relay 可选 | map | 覆盖 Tailscale relay 上游。默认由 node 指定的节点及其 Tailscale 地址推导;Compose 可继承同组服务或本地卷的节点约束。 |
tcp |
tcp-relay 可选 | map | TCP relay 高级上游覆盖。正常情况不需要填写;入口由 publishPort / port 自动派生。 |
tunnel |
cloudflare-tunnel 可用 | map | Cloudflare Tunnel token env 等设置。 |
dns.servers |
否 | IP 或 IP 数组 | 容器使用的 DNS 解析服务器;省略时继承原有默认设置。 |
stackPath |
否 | string | 覆盖生成 jobspec 路径。通常不用。 |
routePath |
否 | string | 覆盖 tailscale route 文件路径。通常不用。 |
容器 DNS#
单服务 manifest 使用 dns.servers:
dns:
servers: [100.100.2.136, 100.100.2.138]
Compose 使用标准 docker-compose.yml 中的服务字段,不能放在 Luma sidecar:
services:
worker:
image: example/worker:1
dns: [100.100.2.136, 100.100.2.138]
支持 IPv4、IPv6 和单个 IP 字符串。仅设置容器的运行时解析器,不修改宿主机、Control、镜像拉取或出口代理的 DNS。阿里云 SLS 私网端点需使用云上内网 DNS;其他云或本地节点应配置其实际可达的解析服务器。
独立 worker 和 host 网络下的服务使用 Docker dns_servers;Linux CNI bridge 使用 Nomad 网络层的 DNS.Servers;Mac Compose 将配置传给共享网络的容器。共享网络内所有服务必须显式声明相同的 dns,否则拒绝部署,避免一个服务的设置改变其他服务的解析行为。
CLI 和 Control 都需升级到支持 container-dns-v1 的版本。修改 Git 中的配置后必须先部署一次,升级 Control 或直接重启旧任务不会读取新的 Git 配置。部署成功后,后续容器重建沿用已提交的 DNS 设置。
更新策略#
Luma 渲染 Nomad update 策略时默认启用 auto_revert、max_parallel = 1 和健康窗口,失败发布会自动回滚到上一版。多副本服务按 Nomad rolling update 逐个替换。
单副本的 cn-edge / external-edge 服务如果使用动态端口(未设置 publishPort),Luma 会渲染 Nomad canary + auto-promote:先启动一个新 allocation,等它健康后再提升为正式版本并停掉旧 allocation。建议公开 HTTP 服务都配置 healthcheck,这样 Nomad 会等 service check 稳定;没有 healthcheck 时只能按 task running 判断健康。
显式 publishPort、tailscale-relay 默认 host network、tcp-relay、内部服务和有本地状态的服务不会默认启用 canary,因为新旧 allocation 同时存在可能撞宿主机端口或本地数据。它们仍然使用 auto_revert 和 max_parallel = 1,但无法保证单副本完全无中断。
Compose 部署也渲染同样的 Nomad update 基线策略。多副本单服务使用逐个滚动更新,不额外启动 canary;副本配置与限制见 Compose 副本数。单副本时,对没有 publishPort 的 cn-edge / external-edge compose 服务,Luma 使用 Nomad dynamic port,并在整组没有固定宿主机端口、tailscale-relay / tcp-relay、持久卷时启用 canary + auto-promote:先起新 compose allocation,健康后再切换。只要 compose 里任一服务需要固定宿主机端口或声明持久卷,就跳过 canary,避免新旧整组同时存在导致端口冲突或状态数据风险。
exposure 选择#
| exposure | region | 是否需要 domain/port | 适合场景 |
|---|---|---|---|
cn-edge |
cn |
是 | 国内公开 Web/API,走国内 Traefik 和备案域名。 |
external-edge |
global |
是 | 海外公开服务,例如外网 API 网关、低频海外工具。 |
tailscale-relay |
home 或已开启此能力的自定义 Region |
是 | 通过现有 Traefik + Tailscale 暴露私网节点服务。 |
tcp-relay |
已允许此入口的 Region | 是 | 数据库等原生 TCP 服务,公网端口独占,走 Traefik TCP -> task host port。 |
cloudflare-tunnel |
通常 home |
是 | 家里/私有服务通过 Cloudflare Tunnel 暴露。 |
none |
任意已注册 Region | 否 | 内部任务、worker、队列消费者,不直接公开。 |
规则:
exposure: cn-edge必须配region: cn。exposure: external-edge必须配region: global。cn-edge/external-edge默认使用 Nomad 动态端口;如果要保留固定宿主端口,可显式设置publishPort。exposure: tailscale-relay可用于home,也可用于执行过luma region update render --exposure tailscale-relay的自定义 Region。未提供relay.host/relay.url时需要节点约束;Compose 可继承同组节点。离线校验只检查语法,Control 按实时 Region 能力校验。exposure: tcp-relay的 Traefik TCP entrypoint 由publishPort或port自动生成。普通 MySQL 不能可靠使用 SNI 分流,因此当前实现按端口独占转发。- 公开服务必须提供
domain和整数port。 public已移除;请使用exposure。
常用模板#
国内公开 API#
name: api
image: ghcr.io/acme/api:1.0.0
region: cn
exposure: cn-edge
domain: api.example.com
port: 3000
replicas: 2
env:
NODE_ENV: production
DATABASE_URL: ${DATABASE_URL}
环境变量和 Secret#
普通非敏感配置可以直接写在 manifest 里:
env:
NODE_ENV: production
LOG_LEVEL: info
敏感值不要写明文。可以手动把 secret 存到控制面:
luma secret set DATABASE_URL --scope api
luma secret set OPENAI_API_KEY --scope api
luma secret list
如果项目已经有 .env 文件,推荐直接在部署时提供它:
luma deploy service.yaml --env .env
Compose 部署同理:
luma compose deploy luma.compose.yml --env .env
--env 会按当前应用名隔离保存 secret。比如 name: api 的服务会把 .env 中实际被 manifest 引用的 DATABASE_URL 保存为 api/DATABASE_URL 这个作用域内的值;另一个 name: worker 的服务即使也有 DATABASE_URL,也不会互相覆盖。.env 里没有被 YAML 引用的变量不会上传。
然后在 YAML 里引用:
env:
DATABASE_URL: ${DATABASE_URL}
OPENAI_API_KEY: ${OPENAI_API_KEY}
部署时,客户端只提交 manifest。Luma Control 会从控制面 secret store 读取这些变量,并写入 Nomad task 的 env。luma secret list 只显示 key,不显示 value。
如果缺少引用的变量,部署会失败并提示:
missing deployment secrets: DATABASE_URL. Run: luma secret set <NAME>
私有镜像仓库#
镜像拉取凭证不要写进 manifest,也不要作为容器环境变量传给业务服务。先在控制面保存 registry credential:
luma registry login ghcr.io --username <user> --password-stdin
luma registry list
然后 manifest 仍然只写镜像:
image: ghcr.io/acme/private-api:1.0.0
已配置 Builder Registry 时,Luma 将匹配外部 image 的源仓库凭据租给 Builder,由 Builder 复制镜像并返回已验证的内部镜像 digest。最终 Nomad job 使用内部镜像引用;只有内部仓库需要认证时,才把对应凭据写入 docker config.auth,不会把外部源仓库凭据交给运行节点。luma registry list 只显示 registry host 和 username,不显示 password/token。
Compose 组件依赖#
Compose sidecar 的 services.<name> 可以声明与单服务相同的 requirements 契约:
services:
web:
exposure: cn-edge
domain: web.example.com
port: 3000
requirements:
capabilities: [cloudflare]
init: [cloudflare-dns]
Control 会在 Compose 预览和部署前逐服务检查这些依赖。未知 capability 或初始化动作会在解析阶段直接报错,不会被静默忽略。
常见 GitHub 场景:GitHub Actions 把应用镜像推到私有 GHCR,同一个仓库还可以用 GitHub Pages 发布文档或营销页。在上述 Builder Registry 链路中,GHCR 凭据供 Builder 复制源镜像,运行节点拉取内部镜像,不需要把 GitHub token 写进 manifest,也不影响 GitHub Pages 的静态站点发布。
镜像获取和服务运行时 proxy: true 是两条路径。proxy: true 只给容器里的出站 HTTP/HTTPS 请求注入代理。配置 Builder Registry 后,外部源镜像访问、进程级代理及 Docker Hub 风格镜像的 defaults.imageMirrors 回退由 Builder 负责;不会因为源仓库拉取失败就改运行节点的 Docker egress proxy。设置 defaults.imageMirrors: [] 可以禁用镜像源回退。
最终镜像使用托管 HTTP Registry 时,正式部署会在提交 Nomad job 前检查全部符合落点约束的 Docker 目标节点,并在修改任何配置前要求所有 agent 声明 managed-registry-access-v2。新 agent 保留 v1/v2 并共用同一修正实现,兼容仍要求 v1 的旧 Control。Linux 使用 Docker 实际代理字段;OrbStack 使用其原生代理设置及 auto 模式下的系统有效代理,不能仅凭内置 broker 地址判断外部代理。Manager 只做只读验证;配置修改要求空闲引擎。具体 HTTP 地址范围、历史兼容例外和维护边界见运维说明。
从 Git provider 构建部署#
默认的 luma deploy 只部署已构建好的镜像。luma import 多走一步:在集群里的构建节点上 git clone 一个 GitHub/Gitea 仓库、自动发现 .luma.yml 或 luma.compose.yml、按仓库里的 Dockerfile 或 Compose build: 构建镜像、推送到集群内自托管 registry,再走正常部署链路。适合「源码到上线」的一条龙,不依赖外部 CI。
前提(一次性):
- 至少一个节点装好
docker buildx(节点 agent 会自动 advertisedocker-build能力)。 - 用
luma registry serve --node <build-node>起一个集群内 registry(详见运维文档的接入 SOP)。 - 私有仓库需先保存 Git provider 凭据;公开仓库可直接用 repo URL。
printf '%s' "$GITEA_TOKEN" | luma git-provider set gitea lin \
--base-url https://gcode.example.com \
--username lin \
--token-stdin
luma git-provider repos gitea:lin
仓库根目录放一个 .luma.yml,就是普通的 service manifest,只是用 build 块代替 image:
name: myapp
region: cn
exposure: cn-edge
domain: myapp.example.com
port: 8080
build:
context: .
dockerfile: Dockerfile
platform: linux/amd64 # 默认值;构建节点是 arm64 而目标节点是 amd64 时尤其重要
env:
NODE_ENV: production
部署:
luma import https://github.com/acme/myapp --build-node build-1
GitHub 仓库可以短写:
luma import acme/myapp --build-node build-1
短写只表示 GitHub owner/repo,会展开为 https://github.com/acme/myapp.git。Gitea/self-hosted Git 推荐用保存的 provider:
也可以使用保存的 provider 账户:
luma build config --node builder --registry-host 100.64.0.70:5000 --push-host 100.64.0.70:5000
luma import --provider-id gitea:lin --repository acme/myapp --env .env
Compose 仓库同样支持 import:luma import 会发现 luma.compose.yml / .luma.compose.yml / *.luma.compose.yml / *.compose.luma.yml / docker-compose.luma.yml,构建 docker-compose.yml 中带 build: 的服务,推送到 builder registry,并在最终部署 payload 中注入 image:。本地校验 build-only Compose 时用:
luma compose validate --import-mode luma.compose.yml
If a repository has separate staging/production sidecars, pass the exact repository-relative Compose sidecar to import:
luma import https://github.com/acme/app.git \
--ref v1.2.3 \
--compose-sidecar deploy/staging.luma.compose.yml
The path is resolved only after the Builder clones the repository. Absolute or
non-normalized paths, .., missing files, invalid Luma Compose sidecars, and
symlink escapes are rejected. Explicit selection is fail-closed and never
falls back to another discovered manifest.
普通 luma compose validate / luma compose deploy 不构建镜像,因此仍要求运行时 Compose 的每个 service 都已经有 image:。
CLI 会流式回传 clone → build → push → deploy 每一步。构建节点来自控制面声明的 builder 节点;通常无需传 --build-node,只有临时覆盖时才传,且 Control 会拒绝未声明的构建节点。--env .env 会把运行时环境变量作为 scoped secrets 交给控制面,由最终 .luma.yml / Compose 内容过滤并保存。单服务 import 可用 --region / --exposure / --domain / --port / --platform 覆盖 .luma.yml 里的对应字段;Compose import 只接受 --region 覆盖 sidecar,服务级入口要写在 luma.compose.yml 的 services: 里。构建出的镜像 tag 形如 <build-node-tailscale-host>:5000/acme/myapp:<git-sha>,其它区域的节点经 Tailscale 内网拉取;HTTP 访问由目标节点在 join 或最终部署预检时按需准备,luma registry serve 不再为全体节点配置 insecure-registries。构建历史用 luma build list 查看,失败详情用 luma build logs <id>,修好凭据或配置后可 luma build retry <id>。
dashboard 的「创建应用」页顶部也有「仓库导入」入口,可选择 Git provider/账户/仓库/ref 或手填 URL,进度实时显示。
build 块字段:
| 字段 | 默认 | 说明 |
|---|---|---|
context |
. |
Docker 构建上下文(仓库内相对路径)。 |
dockerfile |
Dockerfile |
Dockerfile 路径(仓库内相对路径)。 |
platform |
linux/amd64 |
docker buildx build --platform 的目标平台。 |
海外 worker#
name: fetch-worker
image: ghcr.io/acme/fetch-worker:1.0.0
region: global
exposure: none
replicas: 1
env:
QUEUE_URL: redis://redis:6379/0
OPENAI_BASE_URL: https://api.openai.com/v1
渲染后会自动带上 region 约束:
constraint {
attribute = "${meta.region}"
value = "global"
}
指定部署到某个节点#
如果服务必须固定在某台机器上,例如有本地磁盘状态、只想跑在家里的 Mac mini、或临时调试某个 worker,可以使用 node:
name: home-db
image: postgres:16
region: home
node: mac-mini-home
exposure: none
volumes:
- home_db_data:/var/lib/postgresql/data
控制面部署时会同时保留 region 约束,并把 Luma 节点名渲染成 Nomad 的节点约束:
constraint {
attribute = "${meta.region}"
value = "home"
}
constraint {
attribute = "${meta.luma_node_name}"
value = "mac-mini-home"
}
node 使用的是 Luma 节点名,不是 Docker hostname。这个区别对 OrbStack 很重要:多台 Mac 的 Docker hostname 可能都叫 orbstack,但 Luma 用 meta.luma_node_name 指向唯一节点,避免服务跑到错误机器。
Nomad 节点身份是稳定的 UUID。节点离开集群后用同一个 Luma 节点名重新 join,meta.luma_node_name 不变,固定节点服务约束仍然有效;不用手工把 Docker hostname 写进 manifest。
普通服务使用本地卷#
新的持久化原生服务用 named volume 或 bind mount。Control 在首次部署前记录归属节点,后续更新钉在该节点;显式改成其他 node 时部署会继续,但会给出 Storage placement 警告,旧节点上的数据不会被复制;可写持久化挂载要求 replicas: 1。不要为新应用注册 storage class 或 NFS。
name: home-db
image: postgres:16
region: home
exposure: none
replicas: 1
volumes:
- home-db-data:/var/lib/postgresql/data
Compose sidecar 把宿主机路径写在 volumes.<name>.local.path,见 compose-storage.md。
旧 storageClass(仅迁移)#
旧部署可能保留顶层 storage 元数据,但原生 Nomad 服务的实际挂载来自 volumes:命名源渲染为 Docker volume,路径源渲染为 bind mount。storageClass 元数据不会自动把它们改成 NFS 挂载;迁移前应检查运行中的 Nomad mount、Docker volume driver options 和宿主机挂载。新文件不要再写这块。
name: home-db
image: postgres:16
region: home
exposure: none
volumes:
- pg-data:/var/lib/postgresql/data
storage:
pg-data:
storageClass: db-storage
path: home-db/pg-data
accessMode: ReadWriteOnce
luma storage list / check / apply / remove 继续管理这些遗留 class。切换后端会给出 Storage placement 警告但不再阻止部署;数据不会自动迁移,验证后可设置 adopted: true,新空卷可声明 initialize: empty。
需要代理的 worker#
如果服务运行时需要通过 Luma egress proxy 访问外网,声明 proxy: true。不要为了使用默认代理手写 networks: [egress] 或 HTTP_PROXY / HTTPS_PROXY;Luma 会自动渲染这些字段。如果你显式写了同名 env,Luma 会保留你的值。
proxy: true 只管容器自己的出站 HTTP/HTTPS 请求,和服务如何被访问是两件事。比如 region: home + exposure: tailscale-relay + proxy: true 是有效组合:用户入站流量走公网 Traefik -> Tailscale -> home task,容器访问外网时走 egress_mihomo。
name: ai-worker
image: ghcr.io/acme/ai-worker:1.0.0
region: cn
exposure: none
proxy: true
env:
OPENAI_BASE_URL: https://api.openai.com/v1
渲染后会自动带上:
env {
HTTP_PROXY = "http://egress_mihomo:7890"
HTTPS_PROXY = "http://egress_mihomo:7890"
}
constraint {
attribute = "${meta.region}"
value = "cn"
}
小机器资源限制#
如果 manager 只有 2c2g,并且业务服务也部署在 manager 上,建议给每个非核心服务显式设置资源边界。CPU 只通过 reservations.cpus 声明调度份额,运行时可使用空闲 CPU,不设独立硬上限。limits.memory 是内存硬上限,reservations.memory 是调度预留量。未填 CPU 预留时使用 100 MHz;只填内存上限时预留 min(256 MiB, 上限)。旧 limits.cpus 会明确告警并忽略,不再预占调度额度;它不是可执行的 CPU 硬上限:
name: api
image: ghcr.io/acme/api:1.0.0
region: cn
exposure: cn-edge
domain: api.example.com
port: 3000
resources:
limits:
memory: 512M
reservations:
cpus: "0.10"
memory: 128M
这些规则在普通服务和 Compose 的 deploy.resources 中一致。内存预留不能超过上限;不填内存上限时,保留 Nomad 以内存预留量为容器上限的行为。内存超额分配不等于增加物理内存,多个服务同时冲高仍可能 OOM。
控制面升级只改变后续生成的 Job;已有服务需要重新部署才生效,不需要批量升级 worker。
家里内部服务#
name: backup-job
image: ghcr.io/acme/backup-job:1.0.0
region: home
exposure: none
replicas: 1
家里服务通过 Tailscale Relay 暴露#
name: home-panel
image: ghcr.io/acme/home-panel:1.0.0
region: home
exposure: tailscale-relay
domain: panel.example.com
port: 8080
publishPort: 8080
replicas: 1
默认情况下,Luma Control 会在服务部署后查看 Nomad allocation 实际运行在哪些 home 节点,并把 route 上游指向这些节点的 host port。若服务必须固定到某台机器,再显式指定 node:
node: home-mac-mini
也可以手动覆盖完整上游 URL:
relay:
url: http://home-1.your-tailnet.ts.net:8080
公开 TCP 服务#
服务 manifest:
name: ledger-db
image: mysql:8.4.9
region: home
node: lab
exposure: tcp-relay
domain: ledger-db.example.net
port: 3306
publishPort: 3306
replicas: 1
Luma 会把 DNS 指到公网 edge,自动确保 Traefik 监听 tcp-3306 entrypoint,并写入 Traefik TCP route。domain 用于 DNS;普通 MySQL 连接无法提供 HTTP Host 或可靠起始 SNI,所以同一个发布端口一次只应给一个 TCP 服务使用。
publishPort 是目标 task 节点上的宿主机端口,并会在 Nomad bridge 模式下映射到容器 port。如果同一台机器已有本机容器或非 Luma 服务占用 3306,请选择其它端口并同步调整客户端连接端口或入口配置。原生 Mac/OrbStack 单服务需省略 publishPort 并使用真实监听端口;Mac Compose 则支持独立的 publishPort。
Cloudflare Tunnel 服务#
name: home-tool
image: ghcr.io/acme/home-tool:1.0.0
region: home
exposure: cloudflare-tunnel
domain: tool.example.com
port: 8080
replicas: 1
tunnel:
tokenEnv: CLOUDFLARE_TUNNEL_TOKEN
生成前检查清单#
- 域名是否是用户真正要访问的入口。
port是否是容器内部监听端口,而不是公网端口。region和exposure是否匹配。- 公开服务是否填写了
domain和port。 - 镜像是否带 tag;
latest/未带 tag 会在部署时解析成 digest,但生产回滚仍建议使用固定版本 tag 或 digest,避免旧 Nomad job 版本重新拉到新镜像内容。 - secret 不要直接写明文,优先写
${ENV_NAME},部署时用--env .env或 scopedluma secret set --scope <app>提供值。 - worker 默认使用
exposure: none,不要给它配公网域名。 - home 节点不要承载核心高频公网服务。
验证命令#
luma validate service.yaml
luma deploy service.yaml --dry-run
单服务 luma validate 在本地校验并渲染,默认文本输出为摘要;--format json 包含渲染产物。luma deploy --dry-run 打印本地生成的 Nomad job 和适用的路由,不向 Control 提交部署,也不读取实时集群放置、存储或镜像状态。正式部署时 Control 仍会解析实际节点、存储归属、密钥和镜像,因此本地预览不保证与最终提交的 job 完全一致,也不证明运行时就绪。