Dokku 基于 Docker 镜像的应用部署指南:git:from-image 与 git:load-image 实战详解
【免费下载链接】dokkuA docker-powered PaaS that helps you build and manage the lifecycle of applications项目地址: https://gitcode.com/GitHub_Trending/do/dokku
导读
在 Dokku(一个基于 Docker 的 PaaS 平台)中,应用仓库通常由git push推送的源码构成,但在"镜像已经由 CI/CD 流水线构建完成、只想直接部署"的场景下,源码反而不是最优载体。本文讲解 Dokku 提供的两条专为镜像部署设计的能力:git:from-image(从镜像仓库中的镜像初始化/更新应用,0.24.0 引入)与git:load-image(从标准输入流加载镜像 tar 归档,0.30.0 引入)。读完本文,你将掌握如何把任意 Docker 镜像作为应用部署源、如何解决镜像拉取与幂等重建问题、如何配置私有镜像登录与自定义构建上下文,以及这两条命令在源码层的完整执行链路。
一、为什么需要"从镜像初始化应用仓库"
Dokku 的应用模型以 git 仓库为核心:部署时 Dokku 接收代码、识别构建方式(buildpack / Dockerfile / CNB 等)并产出可运行镜像。但有一种常见场景无法套用这套模型——代码根本不在这台 Dokku 主机上:
- 应用镜像在远程 CI/CD 流水线中构建完成,只希望 Dokku 负责运行;
- 团队不维护 Dokku 主机上的源码副本,镜像就是唯一交付物;
- 需要持续跟踪某个第三方基础镜像的变更。
Dokku 的解法是:让一个"只含一行 FROM 的 Dockerfile"成为应用仓库的全部内容。git:from-image与git:load-image负责把这条FROM写入应用仓库,其余构建、部署流程与普通 Dockerfile 部署完全一致。
关联文档:docs/deployment/methods/image.md
二、git:from-image:从镜像仓库初始化应用
2.1 基本用法
git:from-image会初始化一个应用仓库,或将其更新为包含指定镜像的FROM指令:
dokku git:from-image node-js-app my-registry/node-js-getting-started:latest执行后,Dokku 会像仓库中只包含下面这个 Dockerfile 一样构建应用:
FROM my-registry/node-js-getting-started:latest这是跟踪"只部署某个镜像"变更的极佳方式,尤其是镜像由远端 CI/CD 流水线推送的场景——仓库里没有代码,只有对镜像的引用。
2.2 本地已存在镜像时的拉取行为与 --force
如果指定镜像已经存在于 Dokku 主机,Dokku 不会再次拉取(这是源码中明确的分支判断:先通过docker image ls -q $DOCKER_IMAGE检查本地是否已有该镜像,有则跳过docker image pull,参见 plugins/git/git-from-image)。
当远端镜像已更新、但 tag 保持不变时,需要强制重新拉取:
dokku git:from-image --force node-js-app my-registry/node-js-getting-started:latest--force在实现上通过设置DOKKU_APPS_FORCE_DELETE=1环境变量来启用强制拉取分支(见 plugins/git/internal-functions)。
2.3 幂等性与镜像摘要(digest):避免"无变更提前退出"
对相同参数重复触发构建时,Dokku 检测不到任何变更,会提前以 0 退出,不会产生新的部署。源码对应分支在 plugins/git/git-from-directory:当git diff-index --quiet HEAD --判定无差异时,会输出No changes detected, skipping git commit警告并跳过提交。
因此,如果 tag 被复用但底层镜像内容已变化,强烈建议改用镜像 digest 而不是 tag。可通过以下命令获取 digest:
# 适用于已推送到远端 registry 的镜像 docker inspect --format='{{index .RepoDigests 0}}' $IMAGE_NAME # 适用于本地构建、未推送的镜像 # (当上面命令输出为空时使用) docker images --no-trunc --quiet $IMAGE_NAME将 digest 用于git:from-image:
# 其中镜像 sha 为:sha256:9d187c3025d03c033dcc71e3a284fee53be88cc4c0356a19242758bc80cab673 dokku git:from-image node-js-app my-registry/node-js-getting-started@sha256:9d187c3025d03c033dcc71e3a284fee53be88cc4c0356a19242758bc80cab6732.4 自定义提交作者
git:from-image可选地接受 gituser.name与user.email(按此顺序)来定制提交作者;留空时分别回退为Dokku与automated@dokku.sh:
dokku git:from-image node-js-app my-registry/node-js-getting-started:latest "Camila" "camila@example.com"2.5 私有镜像:先登录 registry
如果镜像是需要 docker login 才能访问的私有镜像,应先用registry:login登录 registry,完整流程参见 registry 管理文档。源码层面,trigger-git-git-from-image会读取应用对应的DOCKER_CONFIG目录并导出(fn-registry-docker-config-dir "$APP"),从而让后续docker image pull使用已登录的凭证(见 plugins/git/git-from-image)。
2.6 --build-dir:自定义构建上下文
部分镜像依赖ONBUILD ADD/ONBUILD COPY指令,此时需要一个自定义构建上下文。通过--build-dir指定:
dokku git:from-image --build-dir path/to/build node-js-app domy-registrykku/node-js-getting-started:latest "Camila" "camila@example.com"--build-dir下所有文件会被复制进仓库供docker build过程使用。两个关键限制:
- 构建上下文必须每次部署时都指定,不会在构建之间持久化;
- 源码中会先校验目录存在性(
[[ ! -d "$BUILD_DIR" ]]时直接dokku_log_fail "Invalid BUILD_DIR specified for docker build context"),随后用rsync -a将目录内容同步进临时工作目录(见 plugins/git/git-from-image)。
关于 Dockerfile 部署的更多配置方式,参见 Dockerfile 构建文档。
三、git:load-image:无 Registry 场景的镜像部署
3.1 适用场景与基本用法
当环境里没有 Docker Registry 充当镜像存储中介时(例如在 CI 中构建镜像后直接部署,镜像不出 CI 机器),git:load-image可以从镜像归档 tar 文件初始化/更新应用仓库。
镜像通过标准输入流传输,典型的远端调用方式:
docker image save my-registry/node-js-getting-started:latest | ssh dokku@dokku.me git:load-image node-js-app my-registry/node-js-getting-started:latest上述示例中:docker image save把镜像打成 tar 流 → 经 ssh 管道传送到 Dokku 主机 →git:load-image在输入流上执行。与git:from-image一致,Dokku 会像仓库只包含下面 Dockerfile 一样构建应用:
FROM my-registry/node-js-getting-started:latest3.2 唯一镜像 tag 建议
通过git:load-image部署时,强烈建议在构建镜像时使用唯一 tag。否则与git:from-image相同的"无变更提前退出"问题会出现:复用 tag 会触发变更检测,若检测不到变化,Dokku 会以 0 提前退出。如果 tag 复用但底层镜像已变化,同样建议改用 digest(获取命令与上节完全相同):
# 适用于已推送到远端 registry 的镜像 docker inspect --format='{{index .RepoDigests 0}}' $IMAGE_NAME # 适用于本地构建、未推送的镜像 # (当上面命令输出为空时使用) docker images --no-trunc --quiet $IMAGE_NAME使用 digest 的调用示例:
# 其中镜像 sha 为:sha256:9d187c3025d03c033dcc71e3a284fee53be88cc4c0356a19242758bc80cab673 docker image save my-registry/node-js-getting-started:latest | ssh dokku@dokku.me git:load-image node-js-app my-registry/node-js-getting-started@sha256:9d187c3025d03c033dcc71e3a284fee53be88cc4c0356a19242758bc80cab6733.3 自定义提交作者
与git:from-image相同,可选传入 gituser.name与user.email(按此顺序),留空时回退为Dokku与automated@dokku.sh:
docker image save my-registry/node-js-getting-started:latest | ssh dokku@dokku.me git:load-image node-js-app my-registry/node-js-getting-started:latest "Camila" "camila@example.com"3.4 --build-dir 自定义构建上下文
同样支持--build-dir以兼容依赖ONBUILD ADD/ONBUILD COPY的镜像:
docker image save my-registry/node-js-getting-started:latest | ssh dokku@dokku.me git:load-image --build-dir path/to/build node-js-app my-registry/node-js-getting-started:latest "Camila" "camila@example.com"构建上下文同样每次部署都必须指定,不跨构建持久化。Dockerfile 部署的其他配置方式参见 Dockerfile 构建文档。
四、源码级执行链路解析
了解底层调用链有助于排查问题与理解行为差异。两条命令的核心实现位于 git 插件的 internal-functions(cmd-git-load-image见第 148 行、cmd-git-from-image见第 194 行),外层入口分别为 plugins/git/subcommands/from-image 与 plugins/git/subcommands/load-image,帮助信息定义在 plugins/git/help-functions。
4.1 参数解析与前置校验
两条命令共享相似的模式:
- 遍历参数,识别
--build-dir(以及git:from-image独有的--force)旗标,剩余位置参数依次对应APP、DOCKER_IMAGE、USER_NAME、USER_EMAIL; - 通过
verify_app_name校验应用名合法性; git:from-image在镜像参数为空时输出Please specify a docker image并失败退出;git:load-image额外校验标准输入必须是管道流而非终端([[ ! -t 0 ]],否则报Expecting tar archive containing docker image on STDIN),随后执行cat | docker load加载镜像,再用fn-verify-app-image确认 tar 中确实包含指定镜像,否则报Loaded image tarball but the specified docker image was not found。
4.2 git-from-image 触发器:生成构建上下文
核心触发器 plugins/git/git-from-image 完成以下工作:
- 创建临时工作目录(
mktemp); - 若指定了
--build-dir,用rsync -a同步构建上下文; - 生成仅含两行的 Dockerfile:
FROM $DOCKER_IMAGE与一条LABEL com.dokku.docker-image-labeler/alternate-tags=[\"$DOCKER_IMAGE\"](后者用于镜像标签标注); - 按需导出应用级
DOCKER_CONFIG(私有 registry 登录凭证); - 镜像不存在时
docker image pull;存在但开启--force(即DOKKU_APPS_FORCE_DELETE=1)时强制重新拉取;否则跳过拉取; - 通过
fn-plugin-property-write "git" "$APP" "source-image" "$DOCKER_IMAGE"持久化镜像来源; - 触发
git-from-directory进入仓库写入阶段。
4.3 git-from-directory 触发器:初始化或更新仓库
plugins/git/git-from-directory 负责真正的 git 操作:
- 首次初始化:
git init、切到计算出的部署分支(fn-git-computed-deploy-branch)、配置user.name/user.email、add --all后提交Initial commit; - 更新已有仓库:克隆现有应用仓库、复制新的构建上下文、
add --all后通过diff-index --quiet HEAD --做变更检测——无变更时输出No changes detected, skipping git commit并提示可用ps:rebuild从既有源码重建;有变更则以Automated commit @ <timestamp>提交并 fetch; - 最后调用
git_receive_app进入常规部署流程(构建、发布、调度)。
这也解释了文档中"相同参数重复触发会提前退出"的根因:镜像引用未变 → Dockerfile 未变 → git 无差异 → 跳过提交,自然也就不会触发新的构建。
4.4 部署源记录
两条命令最终都会触发deploy-source-set "$APP" "docker-image" "$DOCKER_IMAGE"(见 plugins/git/internal-functions 与第 191 行),把应用的部署源标记为docker-image类型并记录镜像标识,供报告展示与后续重建使用。这是与git:from-archive(归档文件部署,见 plugins/git/subcommands/from-archive)并列的第三种"非 push 式"部署源。
五、实践建议与适用边界
| 场景 | 推荐命令 | 说明 |
|---|---|---|
| 镜像在远端 registry,主机可联网拉取 | git:from-image | 最直接,主机自行管理拉取;--force应对 tag 复用 |
| CI 构建产物直接部署,无 registry | git:load-image | 镜像 tar 走 ssh 管道,不落中间存储 |
| 镜像 tag 复用且内容变化 | 改用镜像 digest | 规避"无变更提前退出" |
| 私有镜像 | 先registry:login | 应用级DOCKER_CONFIG自动生效 |
依赖ONBUILD ADD/COPY的镜像 | 配合--build-dir | 构建上下文每次部署都要指定,不持久化 |
几点边界提醒:
- 两条命令面向的是"镜像即部署物"的交付模式,与常规源码 push 部署是互补关系,不互相排斥;
- 部署源(
docker-image)与提交作者均可通过命令参数定制,未指定作者时统一回退为Dokku <automated@dokku.sh>; - 若需要从 git 仓库而非镜像初始化/更新应用仓库,可参考同目录下的 git 部署文档 与 归档部署文档;构建阶段的行为细节可进一步阅读 Dockerfile 构建文档。
【免费下载链接】dokkuA docker-powered PaaS that helps you build and manage the lifecycle of applications项目地址: https://gitcode.com/GitHub_Trending/do/dokku
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考