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:不重建镜像,用自有同步协议把改动文件送进运行中的容器,再触发进程重启。3 秒内完成一次”代码 → 服务”循环。
Tilt 的工作原理可以拆成 三件事:
-
Tiltfile:用 Starlark(受限的 Python 子集,Bazel 也用)写配置,声明服务、镜像、K8s YAML。类比:像一份”开工清单”,写清要起哪些店、货从哪来。
-
dev loop(开发循环):监听文件变化 → 决定 full rebuild 还是 live_update → 执行 → 更新 Web UI。类比:像自动流水线,你改图纸它就重做那一环。
-
live_update:在 Tiltfile 里写”改了
src/就 sync 到容器/app/src/,再重启进程”——绕开镜像重建。类比:不重盖整栋楼,只换坏掉的那块砖。
案例 1:单服务最小 Tiltfile
Section titled “案例 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 跳过镜像重建
Section titled “案例 2:live_update 跳过镜像重建”docker_build( 'my-api', '.', live_update=[ sync('./src', '/app/src'), run('pip install -r requirements.txt', trigger='./requirements.txt'), restart_container(), ])k8s_yaml('k8s/api.yaml')逐步解释:
sync('./src', '/app/src'):本机src/一改,同步进容器对应目录(不必重建镜像)run(..., trigger=...):只有requirements.txt变了才重装依赖restart_container():文件到位后重启进程,让新代码生效
对比:没 live_update 约 2 分钟;有则约 3 秒,差约 40 倍。
案例 3:多服务依赖编排
Section titled “案例 3:多服务依赖编排”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'])逐行说明:循环给四个服务各自 build + apply;resource_deps 规定 web 等 api、auth 就绪后再起,避免 web 一启动就连不上后端。
-
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 不适用场景
Section titled “适用 vs 不适用场景”适用:
- K8s 多服务项目(≥ 3 个服务)的本地开发
- 团队需要统一 dev 环境(一份 Tiltfile,所有人
tilt up同样的栈) - 服务之间有复杂依赖(数据库、消息队列、缓存)需要一起拉起来
不适用:
- 单服务、纯前端、纯静态网站 → 用 vite / docker-compose 就够
- 生产部署 → Tilt 只管 dev,不要混淆
- 不用 K8s 的栈(纯 ECS / Lambda / Heroku)→ Tilt 强依赖 K8s API
历史小故事(可跳过)
Section titled “历史小故事(可跳过)”- 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 分钟跑通第一个项目)
- 官方对比:From Skaffold to Tilt(配置语言与 UI 差异)
- 三方综述:Skaffold vs Tilt 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 默认就会识别
- skaffold —— Skaffold — K8s 本地开发的 build-deploy 自动循环