在某些技术领域,Kubernetes被认为是一种不必要的复杂浪费时间,初创公司应该避免这种浪费。使用 Kubernetes 和一个小团队被视为过度工程的标志。
翻译自 Paul Butler 的 The Hater's Guide to Kubernetes。我自己犯了蔑视的错误。 我有时可能会抱怨 kubernetes,但它确实是一项很棒的技术。 我强烈推荐给我所有的竞争对手。 — 保罗·巴特勒 (@paulgb) September 9, 2022 尽管我说了一些讽刺的话,但是"惊人的技术产品"这确实是发自内心的赞美。 在那篇博文中,我写了一篇关于 kubernetes 的复杂性对于它所做的事情是多么必要。 我们已经在 Jamsocket 上运行 kubernetes 生产几年了,我发现它工作得很好。 Kubernetes 的宁静已经在内部实现。 其中一个关键是剥离 Kubernetes 功能的一小部分,并假装其余部分不存在。 这篇文章最初是关于我们如何使用 Kubernetes 的内部指南,所以它并不意味着对每个初创公司都有指导意义; 不过,我认为这是一个很好的起点,可以避免在浩瀚的 kubernetes 中出现许多沙洲。
在我看来,如果你想要以下三件事,Kubernetes是最好的方法:
在服务器上运行多个进程以计划作业。
以冗余方式运行它们,并在它们之间实现负载平衡。
将它们及其关系配置为 **。
从根本上说,Kubernetes 只是一个抽象层,它允许您将一组机器视为单个(无头)机器。 如果这是你的用例,并且你可以避免它的其他部分,你可以走得很远。
有些人告诉我,第 2 点是矫枉过正,初创公司不应该专注于零停机部署或高可用性。 但是我们经常每天进行多次部署,当我们的产品出现故障时,我们客户的产品也会为他们的用户带来失败。 即使是一分钟的停机时间也会被某人注意到。 滚动部署使我们有信心随意且频繁地进行部署。
对于后台,JamSocket 是一种服务,用于动态启动 Web 应用程序可以与之通信的进程。 有点像 AWS Lambda,但流程生命周期绑定到 websocket 连接,而不是单个请求响应。 我们使用 Kubernetes 来运行支持此功能所需的长时间运行的进程。 API 服务器、容器注册表、控制器、日志收集器、一些 DNS 服务、指标收集等。
有些事情我们不使用 kubernetes:
短暂的过程本身。 我们很早就尝试过,但我们很快发现它有局限性(稍后会详细介绍)。
静态营销**。 为此,我们使用 Vercel。 它更昂贵,但对于一家小型初创公司来说,一个小时的工程时间的机会成本也更高,而 Vercel 为我们节省的时间比它花费的时间还要多。 直接存储我们不想丢失的任何数据。 我们确实使用一些持久卷来缓存或派生数据,但除此之外,我们还在群集外部使用托管的 postgres db 和 blob 存储。
值得一提的是,我们自己并不管理 kubernetes——使用 kubernetes 的主要优势是我们可以将其基础设施级别的操作外包! 我们对 Google Kubernetes Engine 很满意,虽然 Google Domains 的惨败动摇了我对 Google Cloud 的信心,但至少我可以高枕无忧,因为我知道迁移到 Amazon EKS 相对简单。 我们毫不犹豫地使用某些类型的 k8s 资源。 我在这里只列出我们显式创建的资源; 这些资源中的大多数都隐式地创建了其他资源(如 pod),我不会提及,但我们肯定会(间接)使用它们。
部署:我们的大多数 Pod 都是通过部署创建的。 每个对我们服务的功能至关重要的部署都有多个副本和滚动更新。
服务业:具体来说,clusterip 用于内部服务,loadbalancer 用于外部服务。 我们避免使用 nodeport 和 externalname 服务,而是更愿意将 DNS 配置保留在 kubernetes 之外。
cronjob:用于清理脚本和类似内容。
configmap跟secrets:用于将数据传递给上述资源。
statefulset跟persistentvolumeclaim:我们使用了一些有状态函数集。 配置比部署稍微复杂一些,但它们可以在重新启动后保留卷。 我们更愿意将重要数据保存到 k8s 之外的托管服务中。 我们对卷没有严格的规则,因为有时在服务重启后保留缓存之类的东西是件好事,但如果可能的话,我会避免使用它们,因为它们可能与滚动部署(死锁)交互不佳。
rbac:我们在一些地方使用它,例如授予服务刷新机密的权限。 它为我们的小集群增加了足够的复杂性,我基本上避免了它。
手动编写 yaml。yaml 有足够的陷阱,所以我尽可能避免它。 相反,我们的 Kubernetes 资源定义是使用 TypeScript 和 Pulumi 创建的。 非内置资源和算子。我之前写过关于控制循环模式是一把双刃剑的文章:这是使 K8s 强大的核心思想,但它也是间接性和复杂性。 Operator 模式和自定义资源允许第三方软件使用 Kubernetes 强大的基础设施来执行自己的控制循环,这个想法在理论上很棒,但在实践中我发现很笨拙。 我们不使用 cert-manager,我们使用 caddy 的证书自动化。 helm。由于运算符和缺乏 yaml 规则,Helm 无法工作,但我也认为使用非结构化字符串模板来生成机器可解析的内容意味着引入漏洞而没有好处。 对我来说,这就像在黑板上钉钉子一样,对不起。 名称中带有“网格”的任何内容。我想他们为某些人工作,但他们不为我工作,他们也不为这个人工作。 入口资源。我没有留下任何伤疤,我知道有些人会有效地使用它们,但我们成功使用 kubernetes 的主题之一是避免添加不必要的间接层。 配置球童对我们有用,这就是我们所做的一切。
尝试在本地复制整个 k8s 堆栈。我们不会使用 k3s 或 kind 之类的工具来精确地复制生产,而只是使用 docker compose 或我们自己的脚本来启动我们目前真正关心的系统子集。
我在上面提到了一个事实,即我们在 kubernetes 上运行短暂的、交互式的、会话活跃的进程很短的时间。 我们很快意识到,Kubernetes 是为容器启动时间而设计的,而不是为健壮性和模块化而设计的。 作为一般规则,我的观点是 kubernetes 适合冗余运行一些长时间运行的进程,但如果一个人正在等待 pod 启动,kubernetes 是错误的选择。 我承认我在这里谈论的是我的书,但至少它是一本开源的书:我们使用麻省理工学院许可的 Rust 编排器,称为 plane,我们专门设计它来快速调度和运行交互式工作负载的进程(即有人在等待他们)。 为了完整起见,我还应该提到,已经出现的一些 kubernetes 替代品非常好。 特别是如果你不希望或不需要我的初始列表中的要求 3(将基础结构指定为 ** 的能力)。 对于我们的一个产品,我们选择使用 Railway 而不是我们的 K8S 集群,主要是为了预览环境。 一些真正尊重 Render 的朋友赞不绝口(我涉足过,但个人认为 Railway 的环境模型更简洁)。 我也更喜欢 Flight Control 的自带云方法。 对于许多 SaaS 类型的应用程序,您可能已经使用这些工具(参考前面提到的工具)走得很远。 但是,如果你满足本文开头列出的三个要求,并采取严格的方法,那么不要让任何人告诉你现在使用 Kubernetes 还为时过早。