- 后端
- 云原生
【免费下载链接】deis
Deis v1, the CoreOS and Docker PaaS: Your PaaS. Your Rules.
Slug Runner 是 Deis v1(基于 CoreOS 与 Docker 的 PaaS)构建链路中的核心执行组件:它以 Docker 容器形态接收由 Slug Builder 编译出的 gzipped 应用 tarball(slug),并在应用环境中启动任意命令、交互式 Shell 或 Procfile 中定义的进程类型。读完本文你将掌握 slug 的三种装载方式(stdin、SLUG_URL、绑定挂载)、start命令与默认进程类型的解析机制、profile.d 环境注入、基于 sdutil 的服务发现配置,以及它在 Deis 整体 git push 部署流程中的真实调用位置。
Slug 与 Slug Runner 的定位
在 Heroku 风格的部署模型中,应用源码并不直接在服务器上运行,而是先被"编译"为一个称为slug的产物——一个包含已安装依赖、预编译产物与运行配置的 gzipped tarball。Deis 的 Builder 组件中同时存放着两个配套工具(见 builder/rootfs/usr/local/src/ 目录):
- slugbuilder:把应用源码 tarball 通过官方及第三方 Heroku buildpack 编译成 slug.tgz;
- slugrunner:把编译好的 slug 解压到容器内应用目录,并代为执行用户指定的命令或进程。
Slug Runner 官方定位是"运行由 slugbuilder 产生的 Heroku 风格 slug 的 Docker 容器"。它做的事情非常聚焦:通过 stdin 或 URL 接收一个 gzipped tarball,让你能够在应用环境中运行一条命令,或启动应用 Procfile 中定义的进程。
获取 Slug Runner 镜像
使用前需要先安装 Docker。有两种获取方式:
方式一:直接拉取公共镜像
$ docker pull flynn/slugrunner方式二:从源码构建
$ cd slugrunner $ make仓库中的 Makefile 揭示了构建过程的细节:它先通过git clone拉取sdutil工具源码并用godep go build编译出build/sdutil二进制(这是服务发现功能所依赖的可执行文件),随后执行docker build -t flynn/slugrunner .构建镜像。也就是说,make产出的镜像自带 sdutil,开箱即可支持下文的服务发现特性。
在 Deis 的 Builder 容器内部,slugrunner 的源码目录会被注册为可构建标签,相关路由定义见 builder/routes.go,其中/usr/local/src/slugrunner/目录对应deis/slugrunner镜像标签。
运行容器:三种装载 Slug 的方式
Slug Runner 的入口脚本是 runner/init(由 Dockerfile 的ENTRYPOINT ["/runner/init"]指定)。脚本启动后会按以下优先级加载应用 slug:
- 绑定挂载优先:如果
/app(应用主目录)下已有内容(例如通过docker run -v挂载或 ONBUILD 注入),则直接使用; - SLUG_URL 次之:通过
curl -s "$SLUG_URL" | tar -xzC $HOME下载并解压,随后unset SLUG_URL防止变量泄漏到应用环境; - stdin 兜底:
cat | tar -xzC $HOME,从标准输入流式解压。
方式一:stdin 管道传入 slug
把本地myslug.tgz通过管道传给容器,并执行应用内的 Rake 任务(输出附加到 stdout):
$ cat myslug.tgz | docker run -i -a stdin -a stdout flynn/slugrunner rake mytask-i -a stdin保证 stdin 保持开启且被附加,slug 二进制流才能被runner/init的cat正确读取。
方式二:SLUG_URL 环境变量加载
通过 URL 加载 slug 是目前唯一能交互式运行的方式,例如启动 Bash:
$ docker run -e SLUG_URL=http://example.com/slug.tgz -i -t flynn/slugrunner /bin/bash-i -t分配伪终端。脚本内部unset SLUG_URL之后,应用环境里不会再残留该变量。
方式三:结合 ONBUILD 的镜像化 slug
Dockerfile 末尾定义了三条 ONBUILD 指令:
ONBUILD RUN mkdir -p /app ONBUILD WORKDIR /app ONBUILD ADD slug.tgz /app这意味着你可以构建一个"把 slug.tgz 直接打进镜像"的派生镜像——这正是 Deis 在 builder/rootfs/etc/confd/templates/builder 中的做法:当应用仓库没有 Dockerfile 时,Builder 先用 slugbuilder 编译出 slug,再生成一个仅含FROM deis/slugrunner的 Dockerfile,最终docker build时 slug.tgz 通过 ONBUILD 被注入/app。此时 slugrunner 走的是第一种"绑定挂载/预置内容"分支,无需 stdin 或 URL。
命令执行环境:不只是解压那么简单
README 明确说明:命令在应用环境中执行、工作目录是应用根目录、带有默认环境变量,并且会 source 应用.profile.d目录下的脚本。runner/init脚本具体实现了这套环境准备逻辑:
export HOME=/app mkdir -p $HOME ... ## Load profile.d and release config shopt -s nullglob mkdir -p .profile.d # If a file is created in slugbuilder with the wrong UID, change it. find . -user 1000 -exec chown slug:slug {} \; if [[ -s .release ]]; then ruby -e "require 'yaml';(YAML.load_file('.release')['config_vars'] || {}).each{|k,v| puts \"#{k}='#{v}'\"}" > .profile.d/config_vars fi for file in .profile.d/*; do source $file done hash -r关键点如下:
- 用户与权限修复:slug 是由 slugbuilder 以 UID 1000 的用户编译的,而容器内应用用户是
slug(UID 2000,见 Dockerfile)。脚本会把所有属主为 1000 的文件改归slug:slug,但不动 UID 0(root)拥有的文件,避免破坏系统文件权限。 - .release 配置变量注入:如果 slug 根目录存在
.release文件(slugbuilder 编译时由 buildpack 的/bin/release脚本生成),其中的config_vars会被 Ruby 解析后写入.profile.d/config_vars,随后与其他 profile 脚本一起被source,从而成为进程的默认环境变量。 - profile.d 脚本顺序执行:所有
.profile.d/*脚本按字典序逐个 source,这使 buildpack 可以为应用注入 PATH、语言运行时版本等默认环境。 - hash -r:清空 shell 命令哈希表,确保新加入 PATH 的二进制(如 ruby、rake)能被正确找到。
start 命令:按 Procfile 启动进程
Slug Runner 内置一个start子命令,用于启动应用 Procfile 中定义的进程类型,或 buildpack 提供的默认进程类型:
$ cat myslug.tgz | docker run -i -a stdin -a stdout -a stderr flynn/slugrunner start webrunner/init中start的解析逻辑是:
case "$1" in start) if [[ -f Procfile ]]; then command="$(ruby -e "require 'yaml';puts YAML.load_file('Procfile')['$2']")" else command="$(ruby -e "require 'yaml';puts (YAML.load_file('.release')['default_process_types'] || {})['$2']")" fi ;; *) command="$@" ;; esac- 若应用根目录存在Procfile(YAML 格式),则直接读取其中对应进程类型的命令;
- 若没有 Procfile,则回退到
.release文件中的default_process_types(由 buildpack 声明,是 Heroku 官方一度非正式弃用、但 Deis 仍兼容的默认进程类型来源); - 其他任何参数则原样作为 shell 命令执行。
Deis 的 Builder 在构建完成后也会做同样的判断:优先读取应用 Procfile,否则从slug.tgz内提取./Procfile或./.release中的进程类型(见 builder/rootfs/etc/confd/templates/builder),并将结果作为 build 钩子的PROCFILE数据上报给 controller,以便调度器按进程类型启动容器。默认的web进程类型会由调度器(如 controller/scheduler/fleet.py)实际拉起。
服务发现:通过 sdutil 注册
Slug Runner 支持基于go-discover的服务发现,实际执行时通过sdutil exec包装用户命令。runner/init中的相关逻辑如下:
if [[ $SD_NAME && $PORT ]]; then if [[ $SD_HOST ]]; then runner="sdutil exec -h $SD_HOST -s $SD_NAME:$PORT bash -c" unset SD_HOST else runner="sdutil exec -s $SD_NAME:$PORT bash -c" fi unset SD_NAME elif [[ $SD_ARGS ]]; then runner="sdutil $SD_ARGS bash -c" unset SD_ARGS else runner="bash -c" fi exec $runner "$command"三种模式的优先级与行为:
| 环境变量 | 行为 |
|---|---|
SD_NAME+PORT | 命令通过sdutil exec -s $SD_NAME:$PORT bash -c ...运行,向服务发现注册$SD_NAME:$PORT;SD_NAME在执行前被 unset(避免递归注册),PORT保留,因为应用常在不使用服务发现时也读取它 |
上述两者再加SD_HOST | 追加-h $SD_HOST指定服务发现的后端主机地址,随后SD_HOST被 unset |
SD_ARGS | 完全自定义 sdutil 命令行:sdutil $SD_ARGS bash -c ...,随后SD_ARGS被 unset |
| 均未设置 | 退化为普通bash -c,不涉及服务发现 |
注意这里最终通过exec替换当前 shell 进程,保证应用进程成为容器的 PID 1 进程,信号与日志处理语义与直接运行一致。
基础镜像环境
镜像基于 Heroku 官方Cedar镜像(FROM heroku/cedar:14),这是 Heroku 经典运行环境,内置了 ruby、python、node 等语言运行时,与 buildpack 编译出的 slug 天然兼容。Dockerfile 中的其他关键设置:
- 创建
slug系统用户(UID/GID 2000),主目录为/app;USER slug使容器内所有进程以非 root 身份运行; - 默认暴露端口:
ENV PORT 5000+EXPOSE 5000,应用可通过$PORT环境变量感知监听端口(可在运行容器时覆盖); WORKDIR /app、ENV HOME /app与runner/init中的export HOME=/app保持一致;ENV DEIS_RELEASE 1.13.4标记了当前 Deis 版本。
在 Deis v1 中的完整调用链
把以上内容串起来,一次git push deis master触发部署时,Slug Runner 的完整生命周期是:
- Builder 的 git receive-pack 钩子(builder/rootfs/etc/confd/templates/builder)接收到代码后,若无 Dockerfile,则以
deis/slugbuilder运行编译容器,把$TMP_DIR:/tmp/app与$CACHE_DIR:/tmp/cache:rw挂载进去; - slugbuilder 依据 buildpacks 把源码编译成
/tmp/slug.tgz,Builder 通过docker cp把它取回应用目录; - Builder 写入
FROM deis/slugrunner的 Dockerfile 并追加ENV GIT_SHA ...,docker build时触发 ONBUILD 将slug.tgz注入/app; - 镜像被打上
HOST:PORT/app:git-xxxx标签推送到私有 registry(见 push-images 模板 中deis/slugrunner:latest的推送逻辑); - 之后调度器(fleet)在集群节点上用该镜像启动应用容器,容器入口
/runner/init检测到/app已预置内容,直接跳过 stdin/URL 分支,source 完 profile.d 后执行start <process-type>或调度器注入的命令。
小结
Slug Runner 的职责虽小,却是 Deis v1"buildpack 式部署"模式的最后一环:它把 buildpack 生态(Procfile、.release、.profile.d、slug 格式)与 Docker 运行时无缝衔接,并以环境变量方式原生支持服务发现,是整个 git push 部署链路从"编译"到"运行"的收尾组件。其核心实现集中在 runner/init(约 76 行 Bash),配合 Dockerfile 阅读即可完整掌握其行为。项目采用 BSD 许可,源码目录同时附带 LICENSE 文件可供查阅。
- 后端
- 云原生
【免费下载链接】deis
Deis v1, the CoreOS and Docker PaaS: Your PaaS. Your Rules.
相关推荐
Deis Slug Builder 深度解析:用 Docker + Buildpacks 将应用源码编译为 Heroku 风格 Slug
Deis Slug Builder 深度解析:用 Docker + Buildpacks 将应用源码编译为 Heroku 风格 Slug 本篇文章以 Deis
后端云原生FriendlyId作用域slug:在同一模型下创建唯一slug的完整指南
FriendlyId作为ActiveRecord的多功能工具,提供了强大的slug生成功能,其中作用域slug是最实用的高级特性之一。本文将详细介绍如何在同一模
后端Draft: <slug>
Draft: <slug Components topology ledger <! Lock the SHAPE before depth. One row
人工智能AI Agent代码智能体多智能体MCP ClientsAgent 编排
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考