news 2026/9/11 12:13:14

Dokku Deployment Tasks 指南:使用 app.json 与 Procfile release 在部署生命周期中执行任务

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Dokku Deployment Tasks 指南:使用 app.json 与 Procfile release 在部署生命周期中执行任务

Dokku Deployment Tasks 指南:使用 app.json 与 Procfile release 在部署生命周期中执行任务

【免费下载链接】dokkuA docker-powered PaaS that helps you build and manage the lifecycle of applications项目地址: https://gitcode.com/GitHub_Trending/do/dokku

Dokku 的 Deployment Tasks 机制允许你在应用完全部署之前或之后的关键节点执行命令,典型场景包括数据库初始化检查、数据库迁移、静态资源收集(如 Django 的collectstatic)、CDN 同步与缓存预热。本文以官方文档 docs/advanced-usage/deployment-tasks.md 为主线,结合 app-json 插件 源码与 单元测试,完整讲解app.jsonscripts.dokku.predeployscripts.dokku.postdeployscripts.postdeployProcfilerelease命令的使用时机、执行顺序、镜像提交语义,以及appjson-path属性的配置与排查方法。

什么是 Deployment Tasks

在 Dokku 中,应用的部署由 "release"(镜像构建完成后的发布阶段)与 "deploy"(调度容器)两大部分组成。Deployment Tasks 是在这一流程中插入的"钩子",用于在应用尚未完全就绪时执行一次性或每次部署都会运行的操作。

官方文档给出的典型用例包括:

  • 检查数据库是否已初始化;
  • 运行数据库迁移;
  • 执行任何需要为服务器准备环境的命令(例如 Django 的collectstatic)。

所有任务都在构建好的 Docker 镜像上下文中执行——除非应用挂载了 volume,否则这些命令不会影响宿主机。

需要在 release 阶段于宿主机上执行命令的场景,请参考 插件创建文档 构建自定义插件。

四种任务阶段的选择与限制

Dokku 提供四类 Deployment Task,各自有不同的适用场景与镜像变更提交语义:

任务阶段来源执行时机镜像变更是否提交典型用例
scripts.dokku.predeployapp.json镜像构建完成之后、容器调度之前以不同方式打包资源;从源码安装自定义包或将二进制文件复制到镜像中
scripts.dokku.postdeployapp.json容器调度之后通知 Slack 部署完成;与中心负载均衡器协调流量路由
scripts.postdeployapp.json容器调度之后,且仅应用首次创建部署时执行一次设置 OAuth 客户端与 DNS;向应用的测试数据库加载种子/测试数据
releaseProcfile镜像构建完成之后、容器调度之前,且在scripts.dokku.predeploy之后将 CSS、JS 等资源从 slug 发送到 CDN/S3;预热或失效缓存;运行数据库迁移

选择建议:

  • 需要修改镜像内容(如安装包、复制二进制)时,使用scripts.dokku.predeploy,因为只有该阶段会把变更提交回镜像;
  • 只通知外部系统、不改变镜像时,使用scripts.dokku.postdeployProcfilerelease
  • 只在应用首次创建后执行一次(如初始化 OAuth、加载种子数据)时,使用scripts.postdeploy

各阶段失败时的行为

WARNING:任何失败的app.jsonDeployment Task 都会导致部署失败。但无论哪个阶段失败,都不会影响已运行的容器(deploy 会回滚到上一版本)。

与 Dockerfile ENTRYPOINT 的交互

如果应用使用 Dockerfile 构建且声明了ENTRYPOINT,Deployment Task 的命令会原样传递给该 entrypoint。唯一的例外是以下四种 tini 包装型 entrypoint,它们会被跳过(即任务命令直接以 shell 执行,不再经过 entrypoint):

  • ["/tini", "--"]
  • ["/bin/tini", "--"]
  • ["/usr/bin/tini", "--"]
  • ["/usr/local/bin/tini", "--"]

这一逻辑在 plugins/app-json/functions.go 的constructScript中有源码级实现:nonSkippableEntrypoints映射中列出的 tini entrypoint 会被"跳过",其余 entrypoint 存在时,命令会通过shellquote.Split拆分为单词后原样作为容器命令执行。

配置 app.json 的位置(appjson-path 属性)

Dokku 会按部署方式在不同的基准目录中查找app.json

  • 通过git:from-imagegit:load-image部署时:基准目录为 Docker 镜像的WORKDIR
  • 其他部署方式(git pushgit:from-archivegit:sync):基准目录为源码树根目录。

在 monorepo 等场景下,你可能希望指定其他路径,可通过app-json:set命令设置appjson-path属性:

dokku app-json:set node-js-app appjson-path .dokku/app.json

关键语义:

  • 该值始终是相对于基准搜索目录的路径,在任何上下文中都不会被当作绝对路径处理;
  • 若该文件在仓库中不存在,Dokku 会继续构建流程,如同仓库没有app.json一样(参见 tests/unit/app-json.bats 中的app-nonexistent.json测试);
  • 传入空值可恢复默认:
dokku app-json:set node-js-app appjson-path

appjson-path也支持全局设置,全局默认值为app.json,仅当应用未设置专属值时才会使用全局值:

dokku app-json:set --global appjson-path global-app.json

同样,传入空值即可恢复默认:

dokku app-json:set --global appjson-path

从源码看,appjson-path是 app-json 插件唯一支持的属性:在 plugins/app-json/appjson.go 中,DefaultPropertiesGlobalProperties均只声明了appjson-path一个键。

查看 app-json 报告(app-json:report)

IMPORTANTapp-json:report自 0.25.0 起可用。

运行app-json:report(不带应用名则列出所有应用):

dokku app-json:report

输出示例:

=====> node-js-app app-json information App-json computed appjson path: app2.json App-json global appjson path: App-json appjson path: app2.json =====> python-sample app-json information App-json computed appjson path: app.json App-json global appjson path: App-json appjson path: =====> ruby-sample app-json information App-json computed appjson path: app.json App-json global appjson path: App-json appjson path:

三个字段的含义:

  • appjson-path:应用级原始值,未设置时为空;
  • global-appjson-path:全局原始值,未设置时为空;
  • computed-appjson-path:部署时实际生效的值,优先级为:应用级值 → 全局值 → 内置默认值app.json

指定应用查询:

dokku app-json:report node-js-app

只输出单项值(便于脚本消费),使用--app-json-appjson-path等 flag:

dokku app-json:report node-js-app --app-json-appjson-path

输出:

app2.json

computed-appjson-path的计算逻辑在 plugins/app-json/report.go 中实现:依次取应用级值、全局值,均为空时回落到"app.json"。支持的其他 flag 还包括--app-json-computed-appjson-path--app-json-global-appjson-path。该行为有完整的单元测试覆盖,见 tests/unit/app-json.bats(round-trip、raw vs computed vs global 等用例)。

编写 Deployment Tasks

app.json 部署任务

Dokku 对 Heroku 的app.json清单提供有限支持,与 Deployment Tasks 相关的键有三个:

  • scripts.dokku.predeploy:在应用 Docker 镜像构建之后、任何容器调度之前运行;此阶段的镜像变更会被提交;
  • scripts.dokku.postdeploy:在应用容器调度之后运行;此阶段的镜像变更不会被提交;
  • scripts.postdeploy:在应用容器调度之后运行;此阶段的镜像变更不会被提交,且仅在应用首次创建部署时执行一次。

目前 Dokku 仅支持上述scripts.dokku.predeployscripts.dokku.postdeployscripts.postdeploy同样可用),app.json中的其他字段会被忽略,可以省略。示例:

{ "scripts": { "dokku": { "predeploy": "touch /app/predeploy.test", "postdeploy": "curl https://some.external.api.service.com/deployment?state=success" }, "postdeploy": "curl https://some.external.api.service.com/created?state=success" } }

从源码看,这三个键在 plugins/app-json/appjson.go 的Scripts结构体中均有对应的 JSON 字段映射(scripts.dokku.predeployscripts.dokku.postdeployscripts.postdeploy),而解析函数ReadAppJSON使用 hujson 解析,意味着文件内容允许 JSONC(带注释的 JSON)语法。取值逻辑在getPhaseScript(plugins/app-json/functions.go)中:heroku.postdeploy阶段读取Scripts.Postdeploypredeploy阶段读取Scripts.Dokku.Predeploy,其余阶段读取Scripts.Dokku.Postdeploy

Procfile release 命令

IMPORTANTrelease命令自 0.14.0 起可用。

Procfile支持特殊的release命令,行为类似 Heroku 的 Release Phase:在应用 Docker 镜像构建之后、任何容器调度之前执行,且运行于scripts.dokku.predeploy之后。使用方法是在 Procfile 中添加release段:

release: curl https://some.external.api.service.com/deployment?state=built

scripts.dokku.predeploy不同,release阶段对磁盘的变更不会持久化到镜像。

WARNING:对release命令进行扩容(scaling)很可能会引发部署中的未知问题,强烈不建议这样做。

release命令的读取通过procfile-get-command触发器完成(plugins/app-json/functions.go),并在executeScript中以phaseSource = "Procfile"标记来源。

底层执行机制:任务是如何运行的

从源码层面看,Deployment Tasks 的执行由 app-json 插件的两个核心触发器驱动:

  • pre-release-builder(plugins/app-json/triggers.go):在处理完app.json中的环境变量之后执行predeploy任务;
  • post-release-builder(plugins/app-json/triggers.go):依次执行release任务、依据app.jsonformation设置进程伸缩,并在首次部署时(通过heroku.postdeploy属性做幂等标记)执行scripts.postdeploy
  • post-deploy(plugins/app-json/triggers.go):执行scripts.dokku.postdeploy,且会提前判断任务是否存在,避免对无 postdeploy 任务的应用做多余的镜像解析。

这些触发器由部署主流程调用:release_and_deploy(plugins/common/functions)先调用dokku_release触发builder-release,成功后通过cmd-deploy触发scheduler-deploy,从而形成"构建 → predeploy → release → 调度容器 → postdeploy"的完整链条。

任务的容器执行细节体现在executeScript(plugins/app-json/functions.go)中:

  • 通过docker-args-deploydocker-args-process-deploy触发器收集部署参数,并过滤掉--cpus--memory--publish--restart-p-P等资源限制与端口发布类参数;
  • 为任务容器打上dokku_phase_script=<phase>标签,herokuish 镜像额外挂载cache-<app>:/tmp/cache,CNB(pack)镜像则使用--entrypoint=/cnb/lifecycle/launcher覆盖默认入口;
  • 以应用的 shell(DOKKU_APP_SHELL)执行set -e包装的脚本,herokuish 镜像还会先 source/app/.profile.d/*并设置HOME=/app;以/开头的命令会先校验二进制可执行性(plugins/app-json/functions.go);
  • 任务失败时输出容器日志并令整个部署失败;
  • 只有predeploy阶段会把执行容器的变更通过docker container commit提交回镜像(同时保留 Dockerfile 的ENTRYPOINT/CMD变更,并写入com.dokku.app-namecom.dokku.<phase>-phase标签),其他阶段执行完毕后容器被清理、变更丢弃。

这一"提交与否"的区别在 tests/unit/app-json.bats 中得到验证:predeploy写入的/app/predeploy.test在后续dokku run中仍存在,而postdeploy写入的/app/postdeploy.test不存在。

完整示例:一次带迁移与通知的部署

综合以上内容,一个完整的实践配置如下。

项目根目录app.json

{ "scripts": { "dokku": { "predeploy": "python manage.py collectstatic --noinput", "postdeploy": "curl -X POST https://hooks.example.com/deploy?state=success" }, "postdeploy": "curl -X POST https://hooks.example.com/app-created" } }

项目根目录Procfile

web: gunicorn myapp.wsgi --bind 0.0.0.0:$PORT release: python manage.py migrate

monorepo 场景下指定清单文件位置:

dokku app-json:set my-app appjson-path services/web/app.json

随后正常推送代码即可触发上述全部任务,部署日志中会出现类似下面的提示(对应 tests/unit/app-json.bats 中的断言文本):

Executing predeploy task from app.json: python manage.py collectstatic --noinput Executing release task from Procfile in ephemeral container: python manage.py migrate Executing postdeploy task from app.json in ephemeral container: ...

如需排查某个应用实际使用的 app.json 路径,直接查询计算值:

dokku app-json:report my-app --app-json-computed-appjson-path

小结

Deployment Tasks 将"部署前的镜像准备"与"部署后的外部协调"以声明式清单(app.json)和进程文件(Procfile)的形式固化下来。使用时把握三点即可:需要变更镜像内容就用scripts.dokku.predeploy;仅通知外部系统就用scripts.dokku.postdeployProcfile release;只执行一次的应用初始化用scripts.postdeploy。配合appjson-pathapp-json:report,monorepo 与多应用环境下也能精确控制任务清单的位置与生效路径。

【免费下载链接】dokkuA docker-powered PaaS that helps you build and manage the lifecycle of applications项目地址: https://gitcode.com/GitHub_Trending/do/dokku

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 12:11:25

项目管理深度解析(三十三)——控制质量评估绩效

摘要&#xff1a;本文围绕项目管理中控制质量评估绩效这一主题&#xff0c;系统解析其核心概念、关键输入、常用工具与实施步骤&#xff0c;并梳理实践中的常见误区。文章重点对比了控制质量与质量保证的区别&#xff0c;介绍了因果图、控制图、帕累托图等数据分析工具&#xf…

作者头像 李华
网站建设 2026/9/11 12:07:53

可靠性三综合试验全流程解析:从原理到实操要点

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 12:06:24

Rollbar.js Tracing 指南:如何快速打通前后端分布式追踪链路

Rollbar.js Tracing 指南&#xff1a;如何快速打通前后端分布式追踪链路 【免费下载链接】midscene GUI Agent for E2E Testing 项目地址: https://gitcode.com/GitHub_Trending/mid/midscene 后端突然 500&#xff0c;日志里只剩报错堆栈&#xff1a;用户当时点了哪个按…

作者头像 李华
网站建设 2026/9/11 12:05:53

DBeaver 如何从源码构建 DBeaver CE 完整产品

DBeaver 如何从源码构建 DBeaver CE 完整产品 【免费下载链接】dbeaver Free universal database tool and SQL client 项目地址: https://gitcode.com/GitHub_Trending/db/dbeaver DBeaver CE 是基于 Eclipse RCP 和 OSGi 插件架构的 Java 桌面数据库工具&#xff0c;源…

作者头像 李华
网站建设 2026/9/11 12:05:44

MySQL 8.0报错1251:认证协议不匹配的排查与解决方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华