Tilt — K8s 微服务本地开发的"文件保存即上线"
是什么
Tilt 是一个让你在本机开发”运行在 Kubernetes 上的多服务项目”的工具。日常类比:像写前端时 vite 的 hot reload——你保存文件,浏览器自动刷新;Tilt 让你保存文件,K8s pod 里的代码自动更新。
你写一份 Tiltfile(配置文件),跑 tilt up,它就帮你做这一连串事:
- 监听代码文件改动
- 重新构建 Docker 镜像(或者跳过重建,直接同步文件进容器)
- 把新镜像 apply 到本地 K8s 集群
- 在 Web UI 里展示每个服务的 build 日志、运行状态、报错
没有 Tilt 时你要手动跑:docker build . && docker push && kubectl rollout restart deployment/foo,每次改一行代码都重来一遍,慢到怀疑人生。
为什么重要
不理解”K8s 本地开发为什么慢”,就感受不到 Tilt 的价值。背景:
- 现代后端常拆成 10+ 个微服务,本地用 Docker Desktop / kind / minikube 跑一个 K8s 集群
- 每个服务一个 Docker 镜像,改一行代码要 build → push → rollout,单服务 30 秒到几分钟
- 10 个服务并行开发时,光等构建就要等到下班
docker-compose不够用,因为生产环境是 K8s,本地不一致就会出 bug
Tilt 的核心创新是 live_update:不重建镜像,直接把改动的文件 kubectl cp 进运行中的容器,再触发进程重启。3 秒内完成一次”代码 → 服务”循环。
核心要点
Tilt 的工作原理可以拆成 三件事:
-
Tiltfile:用 Starlark(一种受限的 Python 子集,Bazel 也用)写配置。声明你有哪些服务、镜像怎么 build、K8s YAML 在哪里。
-
dev loop(开发循环):Tilt 监听文件变化 → 决定是 full rebuild 还是 live_update → 执行 → 更新 Web UI。
-
live_update 协议:在 Tiltfile 里告诉 Tilt”改了
src/就 sync 进容器的/app/src/,然后restart_container()”——绕开镜像重建。
# Tiltfile 示例docker_build( 'my-app', '.', live_update=[ sync('./src', '/app/src'), run('pip install -r requirements.txt', trigger='./requirements.txt'), restart_container(), ])k8s_yaml('deploy.yaml')k8s_resource('my-app', port_forwards=8080)实践案例
案例 1:单服务最小 Tiltfile
docker_build('my-api', '.')k8s_yaml('k8s/api.yaml')k8s_resource('my-api', port_forwards='8080:80')跑 tilt up:
- Tilt 自动 build
my-api镜像 - apply
k8s/api.yaml到当前 kubectl context - 把容器 80 端口 forward 到本机 8080
- 浏览器开
localhost:10350看 Tilt 的 Web UI
案例 2:live_update 跳过镜像重建
Python Flask 项目,改一行 view 函数:
- 没 live_update:build 镜像 90 秒 → push → rollout → 总耗时 2 分钟
- 有 live_update:
sync文件进容器 →restart_container→ 总耗时 3 秒
差距 40 倍。这是 Tilt 在大型项目里被采用的核心理由。
案例 3:多服务依赖编排
10 个服务的项目:
for svc in ['api', 'worker', 'web', 'auth']: docker_build('myorg/' + svc, './services/' + svc) k8s_yaml('k8s/' + svc + '.yaml')
k8s_resource('web', resource_deps=['api', 'auth'])resource_deps 告诉 Tilt”web 启动前必须先启动 api 和 auth”,避免启动顺序乱掉导致的连接错误。
踩过的坑
-
Tiltfile 不是 Python:是 Starlark,不能
import os、不能用 list comprehension 的某些写法、没有 class。新人常以为是 Python 一通乱写然后报错。 -
live_update 镜像必须有源码路径:
sync('./src', '/app/src')要求容器里/app/src真的存在。如果 Dockerfile 只COPY了编译产物没拷源码,sync会写到不被服务读取的地方,看着没报错但代码没生效。 -
Web UI 端口冲突:默认
localhost:10350,被占用就启动失败。tilt up --port 12345改端口。 -
不适合生产:Tilt 是 dev tool,只在本地或 dev 集群跑。生产用 ArgoCD / Flux / Helm,不要把 Tiltfile 当 deployment manifest。
-
kubectl context 一定要切对:Tilt 用当前 kubectl context apply YAML。忘记切到 minikube 直接 apply 到生产 是最经典的事故,建议在 Tiltfile 里写
allow_k8s_contexts(['minikube'])守门。
适用 vs 不适用场景
适用:
- K8s 多服务项目(≥ 3 个服务)的本地开发
- 团队需要统一 dev 环境(一份 Tiltfile,所有人
tilt up同样的栈) - 服务之间有复杂依赖(数据库、消息队列、缓存)需要一起拉起来
不适用:
- 单服务、纯前端、纯静态网站 → 用 vite / docker-compose 就够
- 生产部署 → Tilt 只管 dev,不要混淆
- 不用 K8s 的栈(纯 ECS / Lambda / Heroku)→ Tilt 强依赖 K8s API
历史小故事(可跳过)
- 2018 年:Tilt 由 Windmill Engineering 创立(创始人 Daniel Bentley 来自 Medium、Google),最初目标是”让 K8s 本地开发不那么痛苦”
- 2019 年:开源到 GitHub,迅速被 K8s 社区采纳
- 2022 年:Docker 公司收购 Windmill,Tilt 成为 Docker 旗下产品,Apache 2.0 协议保持开源
- 现在:9.7k stars,主要竞品是 Google 的 Skaffold(更早,YAML 配置)和 DevSpace
Tilt 与 Skaffold 的关键差异:Tilt 用 Starlark 写配置可以加逻辑(循环、条件),Skaffold 是 YAML 静态声明,复杂项目里 Tilt 表达力更强。
学到什么
- dev loop 速度是生产力的乘数:30 秒 vs 3 秒,一天差几百次循环,体感差距巨大
- live_update 是关键创新:跳过镜像构建只同步文件,是把 K8s 本地开发从”分钟级”压到”秒级”的核心技巧
- 声明式 + 编排:Tiltfile 不仅是配置,还编排了 build / deploy / 端口转发 / 依赖顺序,一份文件管所有
- dev tool 和 prod tool 要分开:Tilt 解决 dev,ArgoCD 解决 prod,混用会出大事
延伸阅读
- 官网教程:tilt.dev/getting-started(30 分钟跑通第一个项目)
- 对比文章:Tilt vs Skaffold vs DevSpace(官方写的对比,相对客观)
- 源码入口:tilt-dev/tilt 的
internal/engine/是 dev loop 核心 - skaffold —— Google 的同类工具,YAML 配置
- k3d —— 本地跑轻量 K8s 集群,常和 Tilt 搭配
关联
- skaffold —— K8s 本地 dev 工具,Tilt 最大竞品
- helm —— Tilt 可以调 Helm chart 作为 build 输入
- kustomize —— Tilt 也支持 kustomize 渲染 YAML
- k3d —— 轻量 K8s,常作为 Tilt 的本地集群后端
- minikube —— 经典本地 K8s,Tilt 默认就会识别