Tilt:本地构建、Live Update 与多服务开发
一次容器应用修改通常经过保存文件、构建镜像、导入或推送、部署、等待 Pod、查看响应。Tilt 把这些动作连起来,持续观察文件变化,并把构建日志、工作负载状态和访问入口集中到本机 Web 界面。
文件变化怎样变成运行结果
Tiltfile 使用 Starlark 描述项目。docker_build 关联镜像名称和构建目录,k8s_yaml 提供 Kubernetes 对象,k8s_resource 调整资源名称、依赖和端口转发。Tilt 根据清单中的镜像引用,把一次构建与使用它的 Deployment 对应起来。
首次启动
源码 → Docker 构建 → 镜像进入节点或仓库 → 部署 → Pod 就绪
后续文件变化
├── 命中同步路径 → 更新运行中容器文件 → 按需执行容器命令
├── 命中 fall_back_on → 完整构建并重新部署
├── 位于构建上下文,但没有同步规则 → 完整构建
└── 未被观察或已被忽略 → 不触发更新Live Update 缩短了第二次以后的反馈时间。容器中被同步的文件并没有形成新的镜像层,因此要区分源码、镜像和当前容器内容。退出 Tilt 后,其他发布系统仍应能够从源码完整构建出同样的应用。
Tilt 也能运行本地测试进程、开发服务器或 Docker Compose 项目。它适合开发环境的反复修改;生产发布需要稳定的制品、审批和回退流程,不应依赖个人电脑持续运行的文件同步。
安装并运行完整项目
固定版本与实验环境
以下操作使用 Linux amd64、Bash、Docker、kubectl 和 kind 测试集群。Docker 操作者需要访问 daemon;容器构建由 daemon 执行,Dockerfile 的目录准备阶段使用 root,应用运行用户为 10001:10001。不要在生产 context 中运行。
安装 Tilt v0.36.0 到当前普通用户的临时目录,文件摘要来自官方发行资产。
set -euo pipefail
umask 077
tilt_lab_dir=$(mktemp -d /tmp/tilt-lab.XXXXXX)
cd "$tilt_lab_dir"
mkdir -p bin web
curl -q --fail --show-error --location --max-time 120 \
https://github.com/tilt-dev/tilt/releases/download/v0.36.0/tilt.0.36.0.linux.x86_64.tar.gz \
-o tilt.tar.gz
printf '%s tilt.tar.gz\n' \
'9ce610083efc76ffa518ec9b001ddb1711b652adce3333f57ffff6be50ad9719' \
| sha256sum -c -
tar -xzf tilt.tar.gz tilt
install -m 0755 tilt bin/tilt
./bin/tilt version
docker version
kubectl --context kind-tooling-dev cluster-info
kubectl --context kind-tooling-dev create namespace tilt-lab摘要应为 OK,版本应为 v0.36.0。namespace 已存在时停止并确认归属。后续保持同一工作目录;bin、下载文件和源码不会写入其他项目。
应用文件与 Kubernetes 清单
目录中的五份文件如下。可以逐份保存,也可使用对应的下载入口;正文保留完整内容,便于调整到自己的项目。
实验目录
├── bin/tilt
├── Dockerfile
├── .dockerignore
├── Tiltfile
├── k8s.yaml
└── web/index.htmlweb/index.html 提供可辨认的响应。静态页面不需要编译,适合直接观察文件同步;应用源码需要编译或重启时,后面再添加对应动作。
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><title>Tilt lab</title></head>
<body><h1>tilt-live-v1</h1></body></html>Dockerfile 使用 BusyBox httpd,应用监听非特权端口 8080。目录归属允许运行用户接收后续同步文件。
FROM busybox:1.37.0
RUN mkdir -p /www && chown 10001:10001 /www
COPY --chown=10001:10001 web/ /www/
USER 10001:10001
EXPOSE 8080
CMD ["httpd", "-f", "-p", "8080", "-h", "/www"].dockerignore 排除安装文件与 Git 元数据,避免复制无关内容或因工具文件变化触发构建。
.git
bin
tilt
clean.yaml
*.tar.gzk8s.yaml 定义一个 Deployment 和 Service。readinessProbe 使用实际 HTTP 路径;资源预算只是这个小实验的设置,业务服务需要按负载测量。
apiVersion: apps/v1
kind: Deployment
metadata:
name: tilt-web
namespace: tilt-lab
spec:
replicas: 1
selector:
matchLabels: {app: tilt-web}
template:
metadata:
labels: {app: tilt-web}
spec:
securityContext:
runAsUser: 10001
runAsGroup: 10001
runAsNonRoot: true
containers:
- name: web
image: local/tilt-web
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
readinessProbe:
httpGet: {path: /, port: 8080}
periodSeconds: 2
resources:
requests: {cpu: 10m, memory: 16Mi}
limits: {cpu: 100m, memory: 64Mi}
---
apiVersion: v1
kind: Service
metadata:
name: tilt-web
namespace: tilt-lab
spec:
selector: {app: tilt-web}
ports:
- port: 8080
targetPort: 8080Tiltfile 连接以上文件:
if k8s_context() != 'kind-tooling-dev':
fail('Use the dedicated kind-tooling-dev context')
allow_k8s_contexts('kind-tooling-dev')
analytics_settings(False)
docker_prune_settings(disable=True)
docker_build('local/tilt-web', '.', live_update=[
fall_back_on('Dockerfile'),
sync('web', '/www'),
])
k8s_yaml('k8s.yaml')
k8s_resource('tilt-web', port_forwards=[
port_forward(18082, 8080, host='127.0.0.1'),
])开头显式比较 context;allow_k8s_contexts 会追加允许项,默认允许的本地集群仍在,因此单独使用它无法表达“只允许这一处”。配置关闭遥测与自动 Docker prune,避免实验清理其他项目的构建缓存。参数定义见 Tiltfile API。
Tilt 识别 kind 后可以把构建镜像加载到节点;远程集群通常需要可访问的镜像仓库。不同集群的镜像发现与仓库配置见开发集群选择说明。
启动会话并核对响应
在当前目录启动,终端保持运行:
./bin/tilt up --context kind-tooling-dev --namespace tilt-lab \
--host=127.0.0.1 --port=10350 --stream打开本机 10350 端口上的 Tilt 页面,找到 tilt-web。首次会话需要完成镜像构建、加载、部署和探针检查。10350 是 Tilt 界面,18082 才是应用转发端口;两者不要混用。启动与退出行为见 tilt up 命令。
另开终端执行:
kubectl --context kind-tooling-dev -n tilt-lab \
rollout status deployment/tilt-web --timeout=180s
curl -q --noproxy '*' --fail-with-body --max-time 10 \
--retry 5 --retry-all-errors --retry-delay 1 http://127.0.0.1:18082/
kubectl --context kind-tooling-dev -n tilt-lab \
get pods -l app=tilt-web -o custom-columns=NAME:.metadata.name,UID:.metadata.uid,IMAGE:.spec.containers[0].image响应包含 tilt-live-v1。记录 Pod UID 和镜像字段;后面用它们区分原地同步与重新部署。只读 GET 的有限重试用于等待转发建立,不应直接套用于有业务副作用的请求。
比较同步、重建与多服务依赖
改页面,观察原地更新
保持 tilt up 运行,在编辑器中把 web/index.html 的 tilt-live-v1 改为 tilt-live-v2 并保存。Tilt 应记录文件变化和 Live Update,随后再次请求 18082。
curl -q --noproxy '*' --fail-with-body --max-time 10 http://127.0.0.1:18082/
kubectl --context kind-tooling-dev -n tilt-lab \
get pods -l app=tilt-web -o custom-columns=NAME:.metadata.name,UID:.metadata.uid,IMAGE:.spec.containers[0].image响应应出现 tilt-live-v2,在没有其他重启或发布的条件下,Pod UID 和镜像字段保持不变。httpd 每次请求读取页面文件,因此同步后即可生效;这里不需要重启进程。
一般应用的行为还取决于加载方式。Node.js 需要重新加载模块,Java 需要编译后的类或新的进程,单纯复制源码不会改变已运行的程序。run 可以在同步后执行容器命令,但工作目录、工具、权限和进程重启都要由配置明确指定。配置顺序、匹配规则和进程重启方式见 Live Update 规范,Java 的增量编译示例见官方 Java 指南。
改构建文件,观察完整重建
在 Dockerfile 的 FROM 后添加一行 LABEL lab.rebuild="v2" 并保存。该文件命中 fall_back_on,Tilt 应进入完整镜像构建与部署,而不是继续同步。
观察构建日志、新镜像字段和 Pod UID。新 Pod 就绪后,响应仍应包含 tilt-live-v2:完整构建已将最新页面复制进镜像。如果变回 v1,检查修改是否保存在实际构建目录、COPY 路径是否正确,以及应用是否被旧卷覆盖。
这个对照同时检查了两条路径:快速更新能改变响应,干净构建也能从源码恢复同样内容。Live Update 运行时修改不应成为唯一可用的应用状态。
文件、命令与进程之间的区别
sync 的本地路径必须处于被观察的构建上下文内。文件被 .dockerignore 或构建过滤规则排除时,保存它可能不会触发更新。run 的 trigger 决定何时执行命令,但不会额外建立文件观察或同步规则。
例如需要在同步配置后运行校验,可以在 sync 之后增加一个 run 步骤,命令使用容器内绝对路径。校验命令非零退出会让更新失败;这时文件可能已同步,不能假定运行容器仍是完整旧状态。恢复文件后重新触发更新,必要时用完整构建和新 Pod 消除半更新状态。
同一进程不会因任意文件变化自动重新启动。不要把“文件内容已改变”“进程加载新内容”和“业务请求使用新行为”合成一个成功条件,应以实际应用响应确认。
给本地测试和服务建立依赖
多服务项目中,local_resource 可运行生成代码或测试命令,k8s_resource 的 resource_deps 指向 Tilt 资源名。以当前页面检查为例,可在 Tiltfile 中加入:
local_resource(
'check-page',
cmd=['sh', '-c', 'grep -q "tilt-live-" web/index.html'],
deps=['web/index.html'],
)
k8s_resource('tilt-web', resource_deps=['check-page'])该片段使用开发机上的 sh 与 grep,适用于当前 Linux 环境。它让首次启动先完成页面检查。resource_deps 主要约束初始化次序;依赖资源后续更新不会自动让所有下游重新执行,也不能把它当作每次发布的事务性审批。具体调度行为见资源依赖文档。
昂贵的测试或少用的服务可单独设为手动触发:
local_resource(
'manual-check',
cmd=['sh', '-c', 'grep -q "tilt-live-" web/index.html'],
deps=['web/index.html'],
trigger_mode=TRIGGER_MODE_MANUAL,
auto_init=False,
)auto_init=False 让它首次启动也等待手动操作。保存文件后,Tilt 记录待更新,点击对应资源的触发按钮才执行;自动与手动触发区别见更新控制文档。这些本地命令继承开发者权限,不应在未审查的项目中直接运行 Tiltfile。
定位失败、验证干净构建并清理
从失败阶段选择检查对象
| 失败阶段 | 观察位置 | 处理方法 |
|---|---|---|
| Tiltfile 加载 | 文件名、行号、API 参数错误 | 修正语法、路径或版本,尚未开始构建时不查 Pod 日志 |
| Docker 构建 | build log 与具体指令退出码 | 修复 COPY、依赖源、构建权限 |
| 镜像加载或拉取 | Tilt 镜像日志、Pod Events | 区分本地节点导入与远程 registry 访问 |
| Pod 未 Ready | conditions、Events、readinessProbe | 检查端口、进程和资源约束 |
| 同步失败 | sync 源目标路径和容器写权限 | 核对 ignore、目录归属、容器工具 |
| 页面未更新 | 文件内容、进程重载与实际响应 | 判断停在同步、加载还是访问到旧实例 |
可以在 Dockerfile 中加入 RUN false,验证构建错误怎样显示;恢复该行后重新保存。旧 Pod 可能继续提供旧响应,因此错误时页面仍可打开不能代表新构建成功。修复后应看到一次成功构建,并按新镜像与 HTTP 响应核对发布结果。
反馈慢时,先区分 Docker 上下文传输、依赖下载、镜像加载、调度和探针等待。只在相应阶段优化:缩小构建上下文、改善依赖缓存、选择合适仓库或修复慢启动。单纯缩短探针周期不能让应用更早具备处理请求的能力。
停止会话,再删除资源
在运行 tilt up 的终端按 Ctrl-C,等待进程退出。文件观察、Tilt 管理的本地长运行进程和该会话的转发随会话停止;Kubernetes 工作负载继续运行。另开终端运行 tilt down 不等于向旧的 tilt up 进程发送停止信号。
回到原项目目录执行:
./bin/tilt down --context kind-tooling-dev --namespace tilt-lab
kubectl --context kind-tooling-dev -n tilt-lab \
wait --for=delete pod -l app=tilt-web --timeout=120s
kubectl --context kind-tooling-dev -n tilt-lab get deployment,pod,service等待结束后,实验的 Deployment、Pod 和 Service 应不存在;删除请求返回时,Pod 可能还在 Terminating,等待超时应检查 finalizer 和节点状态。namespace 由前文手动创建,因此仍保留。tilt down 默认不删除 Namespace、Docker volume,带 tilt.dev/down-policy: keep 注解的 Kubernetes 对象也保留,见清理命令。共享数据库与清单之外的资源应按各自归属处理。
不依赖 Live Update 的构建与访问
保持 Tilt 已退出,使用独立镜像标签重新构建。下面在实验目录执行,不清空 Docker 全局缓存:
docker build --no-cache -t local/tilt-web:clean .
kind load docker-image local/tilt-web:clean --name tooling-dev
sed 's|image: local/tilt-web$|image: local/tilt-web:clean|' k8s.yaml > clean.yaml
kubectl --context kind-tooling-dev -n tilt-lab apply -f clean.yaml
kubectl --context kind-tooling-dev -n tilt-lab \
rollout status deployment/tilt-web --timeout=180s
kubectl --context kind-tooling-dev -n tilt-lab \
port-forward --address=127.0.0.1 deployment/tilt-web 18082:8080clean.yaml 只替换本实验清单中唯一的镜像字段,保留原清单供 Tilt 使用。另一个终端请求 18082,确认最新页面内容。由此可以在 Tilt 故障时继续标准镜像构建与部署。
关闭端口转发,再清理专用 namespace:
kubectl --context kind-tooling-dev delete namespace tilt-lab \
--wait=true --timeout=120s源码留在实验目录供再次使用。镜像与构建缓存不自动清空;删除前确认没有其他容器或项目使用,不执行全局 docker system prune。
升级 Tilt 时保留旧二进制和配置副本,在隔离集群重跑首次启动、同步、强制重建及构建失败恢复。远程扩展还需固定来源和版本,避免每次启动运行变化中的远端代码。回退开发工具只能恢复本机执行方式,不能替代应用制品和数据库的回退。
