推送与部署(普通应用)

普通应用 = 你把镜像推到平台分配的私有仓库,平台轮询到新镜像后自动部署。 本页讲从 nae createrunning 的完整闭环、镜像必须满足的运行时约束、以及怎么排查部署失败。 (模板应用走另一条路:一次性部署、镜像 pin 在模板版本,见模板。)

一条龙

# 1) 建应用:拿到推送地址,此时状态 awaiting_image(等首次推镜像)
nae create --app-id myweb --tag latest --port 8080 --cpu 500m --mem 512Mi
nae app myweb            # 看 PushCN / PushGlobal(推送地址)与 Route(访问地址)

# 2) 构建并推送镜像到上一步拿到的地址(无需 docker login,见下)
docker build --platform linux/amd64 -t <PushCN 或 PushGlobal>:latest .
docker push <PushCN 或 PushGlobal>:latest

# 3) 平台轮询检测到 latest 标签的新 digest → 自动部署 → running。无需手动「deploy」
nae app myweb            # 等状态变 running
nae events myweb         # 看 image_detected → deploy_started → running 时间线

推送镜像

镜像运行时约束(重要)

平台以非 root 用户运行容器,且容器无新增特权能力。镜像必须满足:

  1. 能以非 root 用户启动——启动时不要 chown、不要写系统目录(/etc/var/cache 等)。 缓存/临时文件请写 /tmp 或挂载的可写卷。
  2. 监听端口 ≥ 1024——非 root 不能绑定 1024 以下端口(如 80/443)。让程序监听如 8080, 并把它填进 --port

不满足会在部署阶段崩溃。典型坑:官方 nginx:alpine 启动即 chown("/var/cache/nginx/...") failed (Operation not permitted),且默认监听 80。 换 rootless 基底并改监听端口即可。

已验证可用的 nginx 静态站点最小示例:

# 用 unprivileged 基底,不 chown、不抢低端口
FROM nginxinc/nginx-unprivileged:alpine
COPY dist/ /usr/share/nginx/html/
# 该镜像默认就监听 8080、以非 root 跑,无需再改
nae create --app-id mysite --tag latest --port 8080

部署状态怎么看

应用整体状态 nae app <id>Status 只有这几种:

Status 含义
awaiting_image 普通应用已建、等首次推镜像
running 在跑
stopped 已停止(缩到 0 副本)
deleting 删除中(模板级联清理)

「正在部署 / 部署失败」不是应用状态,而是某个镜像版本的状态——见 nae versions <id> 里每条版本的 Statusdeploying / succeeded / failed。最新版本 failed 就是这次部署没起来。

排查部署失败:

nae versions myweb       # 哪个版本 failed
nae events myweb         # 时间线:image_detected / deploy_started / deploy_failed
nae logs myweb           # 容器日志(崩溃原因,如上面的 chown 报错)

前端 SPA 注意事项(路径前缀)

普通应用默认剥离 /apps/<appid> 前缀:外部请求 …/apps/myweb/api/health 到容器里是 /api/health。 这对「在根路径服务」的程序刚好。但前端 SPA 用绝对路径引用静态资源会 404

若你的框架自带 basePath(如 Next.js Node server 已配 /apps/myweb),则需要保留前缀: 创建时加 --keep-path-prefix,让平台不剥离。

# 自带 basePath 的框架:保留前缀
nae create --app-id mynext --tag latest --port 3000 --keep-path-prefix
# 建好后想切换前缀策略,不必删库重建:
nae update mynext --strip-path-prefix     # 改回剥离
nae update mynext --keep-path-prefix      # 改成保留

让普通应用连上模板应用

模板应用(PostgreSQL/Redis 等)create 返回的 Route 就是集群内 DNS 主机名,形如 app-<id>-<appid>.<namespace>.svc.cluster.local:<port>。把它填进业务应用的 env 即可连上:

# 1) 建模板,记下各自的 Route 主机名
nae create --kind template --app-id mypg --template postgres --storage 5Gi \
  --config '{"database":"appdb","username":"appuser","password":"<pg密码>"}'
nae create --kind template --app-id myredis --template redis --storage 2Gi \
  --config '{"password":"<redis密码>"}'

# 2) 业务应用把模板的 Route 主机名 + 端口填进 env(用 --env-file 避免 shell 引号问题)
cat > env.json <<'EOF'
{
  "PGHOST": "app-<id>-mypg.<ns>.svc.cluster.local",
  "PGPORT": "5432",
  "PGUSER": "appuser",
  "PGPASSWORD": "<pg密码>",
  "PGDATABASE": "appdb",
  "REDIS_HOST": "app-<id>-myredis.<ns>.svc.cluster.local",
  "REDIS_PORT": "6379"
}
EOF
nae create --app-id myapi --tag latest --port 8080 --env-file env.json

<id>/<ns>nae app mypg 返回的 Route 为准,直接抄那个主机名即可。 环境变量事后要改用 nae update myapi --env-file env.json(见应用管理),不必删库重建。