首页 抖音推荐文章正文

Kubernetes 2.0会是什么样子

抖音推荐 2025年07月24日 06:10 1 admin
作者 | matdevdug译者 | 平川策划 | Tina

本文最初发布于 matduggan.com。

大约在 2012 到 2013 年,在系统管理员社区,我开始经常听到一种名为 “Borg” 的技术。显然,它是谷歌内部的一种 Linux 容器系统,运行着他们所有的服务。这个术语有点令人困惑,具有“单元格”的集群中有名为 “Borglet” 的东西,但一些基本要素开始传播开来。一个是“服务”的概念,应用程序可以使用服务来响应用户请求,一个是“作业”的概念,应用程序可以使用作业来完成运行时间更长的批处理作业。

然后在 2014 年 6 月 7 日,Kubernetes 第一次提交。这个希腊词语是“舵手”的意思,在最初的三年里,几乎没人能读对。

很快,微软、RedHat、IBM、Docker 加入了 Kubernetes 社区,似乎使 Kubernetes 从一个有趣的谷歌项目变成了“一个真正的产品”。2015 年 7 月 21 日,我们迎来了 Kubernetes v1.0 版本以及云原生计算基金会(CNCF)。

在初次提交之后的十年里,Kubernetes 成为我职业生涯的一个重要组成部分。在家里,在工作中,在业余项目里,只要有必要,我就会使用它。这是一个学习曲线陡峭的工具,但也是一个巨大的力量倍增器。我们不再管理服务器级别的基础设施;一切都是声明性、可扩展、可恢复的,如果你幸运的话,还是自我修复的。

但这个过程并非没有问题。一些常见的趋势出现了,在一些 Kubernetes 没有严格要求的地方出现了错误或配置错误。即使在十年后,我们看到,生态系统内部仍然有很多变动,人们还会踩到已经记录在案的地雷。那么,根据我们现在已经了解的情况,我们能做些什么改变,使这个伟大的工具更适用于更多的人和问题?

k8s 做对了什么?

让我们从积极的方面开始。为什么到现在我们还在谈论这个平台?

大规模容器

对于软件开发,容器是一个非常有意义的工具。它帮助开发者摆脱了个人笔记本电脑配置的混乱,提供了一个适用于整个技术栈的用完即弃的标准概念。虽然像 Docker Compose 这样的工具可以实现一些容器的部署,但它们很笨重,很多步骤仍然需要管理员来处理。我设置了一个 Compose 栈和一个部署脚本,它会从负载均衡器中移除实例,拉取新容器,确保它们已经启动,然后重新添加到 LB,很多人都是这样做的。

K8s 允许横向扩展,也就是可以将笔记本电脑上的容器部署到成千上万的服务器上。这种灵活性使组织能够重新审视他们的整体设计策略,放弃单体应用,采用更灵活(通常也更复杂)的微服务设计。

低维护成本

如果将运维的历史看作是一种“从宠物到牲畜的命名时间线”,那么让我们从我亲切地称之为“辛普森”的时代开始。服务器是由团队配置好的裸机盒子,它们通常都有一个独一无二的名字,在团队内部成了一个俚语。服务器运行的时间越长,积累的垃圾就越多,甚至连重启都成了可怕的操作,更不用说尝试重建了。我称之为“辛普森”时代,因为在我当时工作的职位中,用辛普森一家的角色命名服务器的情况出奇地常见。没有什么可以自我修复,一切都是手动操作。

然后我们进入了“01 时代”。像 Puppet 和 Ansible 这样的工具变得司空见惯,服务器更是用完即弃,你开始看到,堡垒机和其他访问控制系统成为常态。服务器并不都是面向互联网的,它们在负载均衡器后面,我们已经放弃了像 “app01” 或 “vpn02” 这样的可爱的名字。组织在设计时就允许它可以偶尔丢失一些服务器。然而,故障仍然不是自我修复的,仍然需要有人 SSH 进去看看哪里坏了,在工具中编写一个修复方案,然后在整个机群中部署它。操作系统升级仍然是件复杂的事。

现在,我们处于“UUID 时代”。服务器的存在是为了运行容器,它们完全是用完即弃的概念。没有人关心特定版本的操作系统支持多久,你只需要准备一个新的 AMI 并替换整个机器。K8s 不是唯一能使这一点成为可能的技术,但它加速了这一点的实现。现在,通过一个带有 SSH 密钥的堡垒服务器去修复底层服务器的问题,这种想法更多地被视为“破窗”解决方案。几乎所有的解决方案都是“销毁那个节点,让 k8s 按需重新组织,新建一个节点”。

很多过去至关重要的 Linux 技能现在大多是锦上添花,而不是必需的。你可以为此感到高兴或悲伤,无疑,我经常在这两种情绪之间切换,但事实就是这样。

运行作业

K8s 的作业系统并不完美,但与过去几年中非常常见的 “snowflake cron01 box”,它还是要好得多。无论是按照 cron 计划运行还是从消息队列运行,现在它都可以可靠地将作业放入队列,运行它们,并在它们出现问题时重启,你的生活并不会受到影响。

这不仅将人类从耗时且无聊的任务中解放出来,而且还能更有效地利用资源。你仍然需要为队列中的每个项目启动一个 pod,但团队在 “pod” 概念内部有很多灵活性,他们可以确定需要运行什么以及如何运行。这确实提高了很多人的生活质量,包括我自己,我们只需要轻松地将任务置于后台,就不用再考虑它们了。

服务可发现性和负载均衡

多年来,IP 地址硬编码一直困扰着我,它们存在于应用程序内部,作为请求路由目标的模板。如果你够幸运的话,这些依赖关系是基于 DNS 条目而不是 IP 地址的,那么你就可以更改 DNS 条目背后的内容,而不必协调上百万应用程序的部署。

K8s 允许使用简单的 DNS 名称调用其他服务。它消除了一大类错误和麻烦,简化了整个过程。有了服务 API,你就有了一个稳定的、长期存在的 IP 和主机名,你只需指向它们,而不需要考虑任何底层概念。你甚至还有像 ExternalName 这样的概念,允许你将外部服务和集群的内部服务一样看待。

我会在 Kubernetes 2.0 中加入什么?放弃 YAML,使用 HCL

YAML 之所以吸引人,是因为它既不是 JSON 也不是 XML,这就像说你的新车很棒,因为它既不是马也不是独轮车。它在 k8s 中的演示效果更好,在 repo 中看起来也更美观,而且还能给人一种文件格式简单的错觉。实际上,对于我们尝试用 k8s 来做的事情来说,YAML 太过复杂,而且还不是一个足够安全的格式。缩进容易出错,文件不易扩展(你真的不想要一个超长的 YAML 文件),调试可能很烦人。YAML 规范中有很多不易察觉的行为。

我仍然记得,我第一次看到挪威问题(Norway Problem)时都不敢相信自己的眼睛。你可能比较幸运,没有处理过这个问题,所谓 YAML 的挪威问题是指 'NO' 被解释为 false。想象一下,向你的挪威同事解释,他们的整个国家在你的配置文件中被评估为 false。再加上由于缺少引号而意外出现的数字,问题不胜枚举。有关 YAML 的愚蠢,这里 有篇文章比我写得好。

为什么选择 HCL?

HCL 已经是 Terraform 的格式,所以至少,我们只需要讨厌一种配置语言,而不是两种。HCL 是强类型的,有显式类型。它有良好的验证机制。其设计初衷就是用来做我们要求 YAML 做的事,而且比较容易阅读。它有内置函数,人们已经在使用,这有助于我们从 YAML 工作流程中移除一些第三方工具。

我敢打赌,今天已经有 30% 的 Kubernetes 集群通过 Terraform 使用 HCL 进行管理。我们不需要 Terraform 部分就能从这门更优越的配置语言中获得很多好处。

唯一的缺点是 HCL 比 YAML 更冗长一点,在集成到像 Kubernetes 这样的 Apache 2.0 项目中时,它的 Mozilla 公共许可证 2.0(MPL-2.0)需要仔细的法律审查。然而,对于它能提供的生活质量改善,这些是值得克服的障碍。

为什么 HCL 更好?

让我们来看一个简单的 YAML 文件。

# YAML 不强制类型replicas: "3" # 字符串而不是整数resources: limits: memory: 512 # 缺少单位后缀 requests: cpu: 0.5m # CPU 单位拼写错误(应该是 500m)

即使在最基本的示例中,也到处都是陷阱。HCL 和类型系统会捕捉到所有这些问题。

replicas = 3 # 明确为整数resources { limits { memory = "512Mi" # 内存值为字符串 } requests { cpu = 0.5 # CPU 值为数字 }}

像这样的 YAML 文件,你的 k8s 软件仓库中可能有 6000 个。

# 需要外部工具或模板来处理动态值apiVersion: v1kind: ConfigMapmetadata: name: app-configdata: # 值的生成或转换并不简单 DATABASE_URL: "postgres://user:password@db:5432/mydb" API_KEY: "static-key-value" TIMESTAMP: "2023-06-18T00:00:00Z" # 硬编码的时间戳

现在看看 HCL,它不需要外部工具。

resource "kubernetes_config_map" "app_config" { metadata { name = "app-config" } data = { DATABASE_URL = "postgres://${var.db_user}:${var.db_password}@${var.db_host}:${var.db_port}/${var.db_name}" API_KEY = var.api_key != "" ? var.api_key : random_string.api_key.result TIMESTAMP = timestamp() }}resource "random_string" "api_key" { length = 32 special = false}

HCL 可提供以下好处:

  • 类型安全:在部署前防止类型相关的错误

  • 变量和引用:减少重复并提高可维护性

  • 函数和表达式:实现配置动态生成

  • 条件逻辑:支持特定于环境的配置

  • 循环和迭代:简化重复配置

  • 更好的注释:改进文档和可读性

  • 错误处理:错误更容易识别和修复

  • 模块化:实现配置组件的重用

  • 验证:防止无效配置

  • 数据转换:支持复杂数据操作

允许 etcd 替换

我知道,我是第 1 万个写文章讨论它的人。Etcd 做得很好,但要说它是唯一一个适合这项工作的工具,多少有点疯狂。对于比较小的集群或硬件配置,它占用了大量的资源,而你的节点数量永远不足以发挥这种集群类型的优势。现在,k8s 和 etcd 之间的关系也很奇怪,基本上,k8s 是 etcd 仅剩的客户了。

我建议将 kine 的工作正式化。对于项目的长期健康来说,能够接入更多的后端是有好处的,增加这种抽象意味着未来更容易替换成新的 / 不同的后端,并且还允许根据部署的硬件进行有针对性的优化。

我觉得,这个最终看起来会像这个样子:https://github.com/canonical/k8s-dqlite。内存模式的分布式 SQlite ,支持 Raft 共识,几乎不需要升级工作,使集群操作员在 k8s 安装的持久层有更多的灵活性。如果你在数据中心有一个传统的服务器设置,并且 etcd 资源使用不是问题,那就太好了!但这能使低端 k8s 有更好的体验,并(有望)减少对 etcd 项目的依赖。

超越 Helm:原生包管理器

Helm 是一个临时解决方案成长为永久依赖的完美示例。这原本一个黑客马拉松项目,感谢 Helm 维护者们的辛勤工作,将其变成了将软件安装到 k8s 集群的惯常做法。在没有比较深入地集成 k8s 的情况下,它已经做得足够好了。

话虽如此,Helm 很难用。Go 模板很难调试,其中通常包含复杂的逻辑,会导致非常令人困惑的错误场景。通常,这些场景提供的错误消息很不明确。Helm 并不是一个非常好的包系统,因为你需要它做的一些基本任务(传递依赖和解决依赖冲突),它做得并不好。

我是什么意思?

告诉我这个条件逻辑在做什么:

# Helm 复杂条件逻辑的真实示例{{- if or (and .Values.rbac.create .Values.serviceAccount.create) (and .Values.rbac.create (not .Values.serviceAccount.create) .Values.serviceAccount.name) }}apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: {{ template "myapp.fullname" . }} labels: {{- include "myapp.labels" . | nindent 4 }}{{- end }}

或者如果我向 chart 提供多个值文件,哪一个会生效:

helm install myapp ./mychart -f values-dev.yaml -f values-override.yaml --set service.type=NodePort

好的,如果我想用一个 Helm chart 管理我的应用程序和所有应用程序依赖项,这就有意义了。我有一个应用程序,它本身依赖于其他东西,所以我想把它们全部放在一起。所以我在 Chart.yaml 中定义了 sub-chart 或 umbrella chart 。

dependencies:- name: nginx version: "1.2.3" repository: "<https://example.com/charts>"- name: memcached version: "1.2.3" repository: "<https://another.example.com/charts>"

但假设我有多个应用程序,有 2 个服务都依赖于 nginx 或类似的东西,这完全有可能:

Kubernetes 2.0会是什么样子

Helm 未能优雅地处理这种情况,因为模板名称是全局的,模板是按字母顺序加载的。一般来说,你需要:

  • 对同一个 chart 的依赖声明不要超过一次(对于许多微服务来说都很难做到)

  • 如果你确实有同一个 chart 声明多次的情况,则必须使用完全相同的版本

问题不止这些。

  • 跨命名空间安装很难

  • Chart 验证过程非常令人痛苦,没有人用

我们看下 artifacthub 的首页:

Kubernetes 2.0会是什么样子

我想获取 elasticsearch,因为那似乎很重要。

Kubernetes 2.0会是什么样子Kubernetes 2.0会是什么样子

对于官方提供的 Elastic Helm chart 来说,这看起来很糟糕。当然,ingress-nginx 没问题,因为对于整个行业,它都是一个绝对关键的依赖项。

Kubernetes 2.0会是什么样子

此外,“Kubernetes” chart 的维护者怎么还没有被标记为经过验证的发布者?天哪,这得经过多少验证才行。

  • 在 chart 搜索中没有元数据。你只能通过名称和描述进行搜索,不能通过特性、能力或其他元数据进行搜索。
Kubernetes 2.0会是什么样子
  • Helm 没有执行严格的语义版本控制
# 非语义版本的 Chart.yamlapiVersion: v2name: myappversion: "v1.2-alpha" 
  • 如果你用 CRD 卸载并重新安装 chart,它可能会删除这些 CRD 创建的资源。这一点已经多次让我陷入困境,非常不安全。

我可以再写 5000 字,但仍然无法概述所有的问题。没有办法让 Helm 变得足够好,以至于可以胜任“地球上所有关键基础设施的包管理器”这个任务。

k8s 包系统会是什么样子?

我们假设 k8s 的包系统名为 KubePkg,因为如果有一个东西是 Kubernetes 生态系统所需要的,那么它的名字应该是另一个以 K 开头的缩写。我们会尽量复制 Linux 生态系统中现有的工作,同时利用 k8s 的 CRD 能力。我的想法大致是这样的:

Kubernetes 2.0会是什么样子

包是和 Linux 包类似的捆绑包(bundle):

Kubernetes 2.0会是什么样子

其中有一个定义文件,涵盖了你在安装东西时实际会遇到的许多真实场景。

apiVersion: kubepkg.io/v1kind: Packagemetadata: name: postgresql version: 14.5.2spec: maintainer: name: "PostgreSQL Team" email: "maintainers@postgresql.example.com" description: "PostgreSQL 数据库服务器" website: "https://postgresql.org" license: "PostgreSQL" # 带有语义版本控制的依赖项 dependencies: - name: storage-provisioner versionConstraint: ">=1.0.0" - name: metrics-collector versionConstraint: "^2.0.0" optional: true # 安全上下文和要求 security: requiredCapabilities: ["CHOWN", "SETGID", "SETUID"] securityContextConstraints: runAsUser: 999 fsGroup: 999 networkPolicies: - ports: - port: 5432 protocol: TCP # 要创建的资源(嵌入式或引用) resources: - apiVersion: v1 kind: Service metadata: name: postgresql spec: ports: - port: 5432 - apiVersion: apps/v1 kind: StatefulSet metadata: name: postgresql spec: # StatefulSet 定义 # 使用 JSON Schema 的配置模式 configurationSchema: type: object properties: replicas: type: integer minimum: 1 default: 1 persistence: type: object properties: size: type: string pattern: "^[0-9]+[GMK]i$" default: "10Gi" # 有恰当排序的生命周期钩子 hooks: preInstall: - name: database-prerequisites job: spec: template: spec: containers: - name: init image: postgres:14.5 postInstall: - name: database-init job: spec: # 作业定义 preUpgrade: - name: backup job: spec: # 备份作业定义 postUpgrade: - name: verify job: spec: # 验证作业定义 preRemove: - name: final-backup job: spec: # 最终备份作业定义 # 有状态应用程序的状态管理 stateManagement: backupStrategy: type: "snapshot" # 或 "dump" schedule: "0 2 * * *" # 每天凌晨 2 点 retention: count: 7 recoveryStrategy: type: "pointInTime" verificationJob: spec: # 验证恢复成功的作业 dataLocations: - path: "/var/lib/postgresql/data" volumeMount: "data" upgradeStrategies: - fromVersion: "*" toVersion: "*" strategy: "backup-restore" - fromVersion: "14.*.*" toVersion: "14.*.*" strategy: "in-place"

需要有一个真正的签名过程,让你对这个过程有更多的控制。

apiVersion: kubepkg.io/v1kind: Repositorymetadata: name: official-repospec: url: "https://repo.kubepkg.io/official" type: "OCI" # or "HTTP" # 验证设置 verification: publicKeys: - name: "KubePkg Official" keyData: | -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvF4+... -----END PUBLIC KEY----- trustPolicy: type: "AllowList" # 或 "KeyRing" allowedSigners: - "KubePkg Official" - "Trusted Partner" verificationLevel: "Strict" # 或 "Warn", "None"

如果软件包可以自动更新,而我不需要自己做任何事情,那该有多好。

apiVersion: kubepkg.io/v1kind: Repositorymetadata: name: official-repospec: url: "https://repo.kubepkg.io/official"apiVersion: kubepkg.io/v1kind: Installationmetadata: name: postgresql-main namespace: databasespec: packageRef: name: postgresql version: "14.5.2" # 配置值(根据模式验证) configuration: replicas: 3 persistence: size: "100Gi" resources: limits: memory: "4Gi" cpu: "2" # 更新策略 updatePolicy: automatic: false allowedVersions: "14.x.x" schedule: "0 2 * * 0" # 每周日早上 2 点 approvalRequired: true # 状态管理引用 stateRef: name: postgresql-main-state # 使用的服务账户 serviceAccountName: postgresql-installer

为什么 k8s 需要一个满足以下要求的系统?

  1. 真正的 Kubernetes 原生:一切都是 Kubernetes 资源,有适当的状态和事件

  2. 为状态管理提供一等支持:内置支持有状态应用程序

  3. 增强的安全性:强大的签名、验证和安全扫描

  4. 声明式配置:没有模板,只利用模式实现结构化的配置

  5. 生命周期管理:全面的生命周期钩子和升级策略

  6. 依赖解析:类似 Linux 的依赖管理,具有语义版本控制

  7. 审计追踪:完整的变更历史记录,包括谁、干了什么、何时,而不是 Helm 目前提供的东西

  8. 策略执行:支持组织策略和合规性

  9. 简化用户体验:为大家所熟悉,类似 Linux 的包管理命令。我们试图与几十年来行之有效的包系统走不同的方向,这似乎很疯狂。

默认使用 IPv6

想象一下,在全球范围内,为了解决以下问题中的任何一个,已经投入了多少时间和精力。

  1. 我需要这个集群中的这个 Pod 与那个集群中的那个 Pod 通信。

  2. NAT 穿透过程中出现了问题,我需要解决它

  3. 因为没有考虑会使用多少个 IP,我集群的 IP 地址用完了。记住:一个公司开始时只有一个 /20 子网(4096 个地址),当他们部署了 40 个节点,每个节点 30 个 Pod 时,他们突然就会意识到自己已经接近 IP 限制。

我并不是建议整个互联网切换到 IPv6,现在,k8s 既支持 IPv6-only,也支持双栈方法。但我要说的是,现在是时候更改默认设置了,直接使用 IPv6。这样可以一次性消除一大堆问题。

  • 集群内的网络拓扑更平坦、更简单。

  • 如果组织想获得公共 IP,就可以选择忽略多个集群之间的差异。

  • 更容易准确理解栈内流量。

  • 内置 IPSec。

这与在全球推动采用 IPv6 无关,但得承认,我们不再生活在一个必须接受 IPv4 限制的世界,你可能突然就需要 10000 个 IP 。

组织使用公共 IPv6 地址的好处显而易见,而且,它的价值也足以获得云服务提供商、用户甚至是企业霸主的支持。亚马逊云科技从不需要在 VPC 中寻找更多的私有 IPv4 空间。这肯定是有价值的。

结 论

对于这些想法,人们经常会反驳说,“ Kubernetes 是一个开放平台,所以社区可以构建这些解决方案。”虽然这是真的,但这个论点忽略了一个关键点:默认值是技术最强大的力量。由核心项目定义的“快乐路径”决定了 90% 的用户将如何与之互动。如果系统默认需要签名包,并提供一种强大的原生方式来管理它们,那么生态系统就会采用它。

我知道,这是一个雄心勃勃的清单。既然要做梦,就大胆地做。毕竟,我们这个行业曾经认为将技术命名为 Kubernetes 就会流行起来,而且不知何故,它确实流行起来了!

在移动开发和 Web 开发等其他领域,我们经常看到这种情况,平台评估他们的情况并做出激进的改变。这些项目不一定是维护者或公司必须要承担的,但我认为至少应该有人重新审视和思考下,“既然我们在全球所有数据中心的占比已经如此之大,这是否值得做呢?”

有任何问题或反馈,欢迎与我联系:https://c.im/@matdevdug

原文链接:

https://matduggan.com/what-would-a-kubernetes-2-0-look-like/

声明:本文为 InfoQ 翻译,未经许可禁止转载。

今日好文推荐跳槽实现财富自由!小扎千万年薪快要“掏空”OpenAI核心人才,还高调“晒”挖人成绩单:各栈大牛,近70%是华人十周前才写下第一行代码,如今颠覆 9 个行业?员工人均 10 万粉,00 后创业者狂言:我们将超越 OpenAI我押注12亿美元买下当年移动界的“OpenAI”,却眼睁睁看它49天夭折离开百川去创业!8 个人用 2 个多月肝出一款热门 Agent 产品,创始人:Agent 技术有些玄学

发表评论

泰日号Copyright Your WebSite.Some Rights Reserved. 网站地图 备案号:川ICP备66666666号 Z-BlogPHP强力驱动