跳转到内容

Tilt — K8s 微服务本地开发的"文件保存即上线"

是什么

Tilt 是一个让你在本机开发”运行在 Kubernetes 上的多服务项目”的工具。日常类比:像写前端时 vite 的 hot reload——你保存文件,浏览器自动刷新;Tilt 让你保存文件,K8s pod 里的代码自动更新。

你写一份 Tiltfile(配置文件),跑 tilt up,它就帮你做这一连串事:

  1. 监听代码文件改动
  2. 重新构建 Docker 镜像(或者跳过重建,直接同步文件进容器)
  3. 把新镜像 apply 到本地 K8s 集群
  4. 在 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 的工作原理可以拆成 三件事

  1. Tiltfile:用 Starlark(一种受限的 Python 子集,Bazel 也用)写配置。声明你有哪些服务、镜像怎么 build、K8s YAML 在哪里。

  2. dev loop(开发循环):Tilt 监听文件变化 → 决定是 full rebuild 还是 live_update → 执行 → 更新 Web UI。

  3. 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

  1. Tilt 自动 build my-api 镜像
  2. apply k8s/api.yaml 到当前 kubectl context
  3. 把容器 80 端口 forward 到本机 8080
  4. 浏览器开 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”,避免启动顺序乱掉导致的连接错误。

踩过的坑

  1. Tiltfile 不是 Python:是 Starlark,不能 import os、不能用 list comprehension 的某些写法、没有 class。新人常以为是 Python 一通乱写然后报错。

  2. live_update 镜像必须有源码路径sync('./src', '/app/src') 要求容器里 /app/src 真的存在。如果 Dockerfile 只 COPY 了编译产物没拷源码,sync 会写到不被服务读取的地方,看着没报错但代码没生效。

  3. Web UI 端口冲突:默认 localhost:10350,被占用就启动失败。tilt up --port 12345 改端口。

  4. 不适合生产:Tilt 是 dev tool,只在本地或 dev 集群跑。生产用 ArgoCD / Flux / Helm,不要把 Tiltfile 当 deployment manifest

  5. 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 表达力更强。

学到什么

  1. dev loop 速度是生产力的乘数:30 秒 vs 3 秒,一天差几百次循环,体感差距巨大
  2. live_update 是关键创新:跳过镜像构建只同步文件,是把 K8s 本地开发从”分钟级”压到”秒级”的核心技巧
  3. 声明式 + 编排:Tiltfile 不仅是配置,还编排了 build / deploy / 端口转发 / 依赖顺序,一份文件管所有
  4. dev tool 和 prod tool 要分开:Tilt 解决 dev,ArgoCD 解决 prod,混用会出大事

延伸阅读

关联

  • skaffold —— K8s 本地 dev 工具,Tilt 最大竞品
  • helm —— Tilt 可以调 Helm chart 作为 build 输入
  • kustomize —— Tilt 也支持 kustomize 渲染 YAML
  • k3d —— 轻量 K8s,常作为 Tilt 的本地集群后端
  • minikube —— 经典本地 K8s,Tilt 默认就会识别

反向链接

  • helm —— Helm — Kubernetes 包管理器
  • kustomize —— Kustomize — 不动原 YAML 的 K8s 配置叠加器
  • skaffold —— Skaffold — K8s 本地开发的 build-deploy 自动循环