Skip to main content

Command Palette

Search for a command to run...

自己的 Kubernetes 控制器(1)——工作准备

Updated
3 min read

原文:Your own Kubernetes controller - Laying out the work

作者:Nicolas Fränkel

时至今日,Kubernetes 已经成为容器化应用部署的首选平台,是个难以忽视的存在。

Kubernetes是一个开源系统,用于自动化部署、扩展和管理容器化应用程序。

短短几年里,Kubernetes 在 CNCF 的大旗下高歌猛进,在 DevOps 领域已经深入人心。这其中的原因众说纷纭,其中一个非常有说服力的理由是,用户能够避免被锁定在单一云提供商的 API 上。如果你对 2000 年左右微软的桌面垄断有所了解,你可能会明白我的意思。

Kubernetes 的扩展相对来说比较容易,这是它获得广泛认同的一个重要原因。很多软件供应商在 Docker 镜像之外,还会提供一或多个 Operator。

我假设读者仅对 Kubernetes 有所了解,对控制器一无所知,在这个假设的基础上,我将用三篇连载来讲述如何使用 Go 以外的语言实现自己的控制器。

控制器是什么

配置管理工具可以分为两种:

分类描述工具
指令式指定做事方法,例如启动两个节点Ansible、SaltStack 等
声明式指定目标状态,例如总计五个节点Puppet、Chef 等

声明式的工具通常会周期性的执行以下任务:

  1. 查询当前状态

  2. 评估要从当前状态达到目标状态所需完成的步骤

  3. 执行这些步骤

这个算法描述的是一个控制回路。

Kubernetes 里,已经有了这些控制回路的实现。例如 ReplicaSetDeployment。这两个对象都可以针对特定镜像设置目标 Pod 数量。Kubernetes 会持续生成副本,直到达到预设的实例数量。如果副本数量发生变化,那么就会新建或删除副本,以达到目标副本数量。

现在你可能已经猜到了,控制器就是一个控制循环的实现:检查当前状态,用现有状态计算差异,弥补差异。除了 DeploymentReplicaSet 的控制器之外,Kubernetes 还提供了很多开箱即用的控制器。

  • Service

  • DeamonSet

  • PersistentVolume

  • Job

  • ...

其实大多数的 Kubernetes 资源都是由控制器管理的。

初识 Operator

对控制器感兴趣的读者,可能已经在搜索过程中偶然发现了 Operator 这个名词。如果你的时间非常有限,我建议你跳过这一部分,将这两个术语视为近义词即可。

前面说到 Kubernetes 的扩展性。其中一个扩展方法就是创建控制器,这也是本文的的重点内容。另一个方式就是对 Kubernetes 模型本身进行扩展:在开箱即用的 Pod、Job 等内置资源以外,还可以使用 CRD 来提供额外的资源类型。

例如下面的代码定义了一个叫做 Hazelcast 的资源:

hazelcast-crd.yml

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: hazelcasts.hazelcast.com
spec:
  group: hazelcast.com
  names:
    kind: Hazelcast
    listKind: HazelcastList
    plural: hazelcasts
    singular: hazelcast
  scope: Namespaced
  subresources:
    status: {}
versions:
    - name: v1alpha1
      served: true
      storage: true

把文件提交给 API Server,让 Kubernetes 注册这个新的 Hazelcast CRD。

kubectl apply -f hazelcast-crd.yml

这个动作完成之后,就可以像其他内置资源一样进行常用操作了:

kubectl get hazelcasts

Operator 就是一个用于某种 CRD 的控制器。如果知道怎么实现控制器,也就能够创建 Operator 了。

控制器的需求

现在我们看看 Kubernetes 控制器的需求。

控制器的部署位置

下图是一个简化的 Kubernetes 架构图:

Kubernetes 的内置控制器是其控制平面的组成部分。然而自定义控制器是不会出现在这里(Controller Manager)的。控制器没什么限制,它可以在集群内部以 Pod 的形式运行,也可以作为独立的外部进程。

当然 Pod 形式会享受各种 Kubernetes 上运行容器化应用的福利,例如自愈等。

和 Kubernetes 的通信

在 Kubernetes 中,API Server 是一个通信组件。客户端发送 HTTP 请求,API Server 处理请求后发回响应。给 kubectl 加上参数就能观察到这一过程:

$ kubectl get pods --v=8
I0209 12:36:31.330067   13717 round_trippers.go:420] GET https://192.168.99.103:8443/api/v1/namespaces/default/pods?limit=500
I0209 12:36:31.330078   13717 round_trippers.go:427] Request Headers:
I0209 12:36:31.330081   13717 round_trippers.go:431]     Accept: application/json;as=Table;v=v1beta1;g=meta.k8s.io, application/json
I0209 12:36:31.330085   13717 round_trippers.go:431]     User-Agent: kubectl/v1.17.2 (darwin/amd64) kubernetes/59603c6
I0209 12:36:31.339770   13717 round_trippers.go:446] Response Status: 200 OK in 9 milliseconds
I0209 12:36:31.339780   13717 round_trippers.go:449] Response Headers:
I0209 12:36:31.339798   13717 round_trippers.go:452]     Content-Length: 2933
I0209 12:36:31.339804   13717 round_trippers.go:452]     Date: Sun, 09 Feb 2020 11:36:31 GMT
I0209 12:36:31.339822   13717 round_trippers.go:452]     Content-Type: application/json
I0209 12:36:31.340084   13717 request.go:1017] Response Body:
{ "kind":"Table",
  "apiVersion":"meta.k8s.io/v1beta1",
  "metadata":{
    "selfLink":"/api/v1/namespaces/default/pods",
    "resourceVersion":"2387836" },
  "columnDefinitions":[
    { "name":"Name",
      "type":"string",
      "format":"name",
      "description":"Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names",
      "priority":0 },
    { "name":"Ready",
      "type":"string",
      "format":"",
      "description":"The aggregate readiness state of this pod for accepting traffic.",
      "priority":0 },
    { "name":"Status",
      "type":"string",
      "format":"",
      "description":"The aggregate status of the containers in this pod.",
      "priority":0 },
    { "name":"Restarts",
      "type":"integer",
      "format":"",
      "description":"The number of times the containers in this pod have been restarted.",
      "priority":0 },
    { "name":"Age",
      "type":"stri
[truncated 1909 chars]

这个通信过程的需求很简单:

  1. 能够处理 HTTP 的请求和响应

  2. JSON 解析(或者说序列化和反序列化)

是的,有 JSON 和 HTTP 的处理能力就够了,所以要编写一个控制器,并不一定必须使用特定语言(例如 Go),理论上用单纯的 Shell 也是可以实现的。

Go 的定位

在进入实现细节之前,首先要看看 Kubernetes 的生态。

历史上好像 Kubernetes 的祖先是用 Java 开发的,后来被移植到了 Go 上。这可能是部分代码不符合 Go 语言风格的原因。尽管 Go 具有垃圾收集功能,但它还是被称为一种低级语言,很适合运行接近于裸机的软件。这种说法是否成立,远远超出了本文的范围,也超出了我的能力。

然而 Kubernetes 生态中大量软件是使用 Go 语言编写的,我想是有其原因的。

如果你已经对 Go 相当了解,那么继续使用是个很好的选择——改弦易辙需要勇气。这并不只是一个语言的问题,除了语法之外,还有很多其他内容:

要多久才能用新语言写出地道的代码

我记得我在学习 Java 的时候,读过 C 语言开发者写的代码。虽然语法是 Java,但是却写出了 C 语言的风格,例如在方法结束之前释放本地变量的引用。

多久才能搞清楚在什么条件下使用什么库

我不了解 Go,但是我知道 Java。Java 生态的丰富是人所皆知的。例如测试的场景,就有 JUnit 4、JUnit 5 以及 TestNG 可以选择,另外需要加入断言库么?这还只是测试呢。

选择正确的工具链要多久

如果已经在使用 JetBrains 的产品,那么从 JetBrains IDE 之间跳转是比较容易的,例如 IDEA 和 GoLand。但是 IDE 市场非常混乱,例如微软正在推广的包含丰富插件的 VS Code。而 Java 世界中,Eclipse 仍然占据客观的市场份额。各种产品都有自己的优劣,自己的拥趸。工具的选择可能在组织内部引发圣战。

新工具形成生产力要多久

各种 IDE 都有各自的玩法。例如我从 Eclipse 切换到 IntelliJ 的过程中,几个星期后才停掉了频繁保存文件的习惯。除了 IDE 之外,还有除错工具等。新的语言能怎么除错?有什么先决条件么?

另外前面说的几个点只是开发,如果考虑到相关的构建、集成和投产环境,其投入可能又会有数倍的增长。

我希望上面几点能够让读者意识到,语言的切换事关重大。在很多情况下,沿用原有的语言可能是个更好的选择。

结论

本文的第一部分,大概了解了一下 Kubernetes 控制器的基础内容。我们详细介绍了什么是控制器,以及开发控制器的需要:即能够与 HTTP/JSON 通信。在下一篇帖子中,我们将详细介绍并实际开发自己的自定义控制器。

More from this blog

龙虾恐慌:AIOps 又要改名了?

ChatGPT 开始,把 AI 拉近到普罗大众的面前,让无数人感受到 AI 的亲民魅力。而龙虾,则把大模型驱动的自动化能力,突然间变得水灵灵、活泼泼地走进千家万户。它不只是“风口上的猪”,而是风口本身。热度高到让 Mac mini 一度断货,不知道这在不在库克的预料之内。 每代人都有每代人的鸡蛋,春节期间,我就领了我的鸡蛋。翻出古老的 MacBook Air M1,充值各种大模型。当然了,这个工具

Mar 9, 20261 min read

再见 2025

我猜不少人以为这个号废了吧?并没有,只是今年变化有点大,一直有种抄起键盘,无从说起的感觉,所以一直偷懒到今天,2025 的最后一天。 今年是我的第四个本命年,去年末一期播客里,大内说本命年不是灾年,是变化年,有危也有机。可是讲真啊,只看到危,没看到机。 各种因缘际会,从鹅厂跳槽到前东家,已经接近四年,第一个合同期已经进入尾声。除了前两年还在云原生领域嗷嗷叫,后两年基本都是些鸡零狗碎的东西了,用老东家的术语说是——偏离主航道,可谓是前景暗淡了。 一旦确定要滚蛋,反倒心思轻松起来,每天骑着我的小红车...

Jan 5, 20261 min read

辅助编程?dora 说:我知道你很急可是请你别急

从 OpenGPT 把大模型的火烧旺了之后,这三年来,相信很多组织或摩拳擦掌、或躬身入局,希望借助聪明能干的大模型,或想偿还技术宅,或想降本增效,或想弯道超车。一时间,沉寂许久的 AIxx 又活过来了,LLM Ops、Vibe Coding、中医大模型、GPT 算命等等,全都老树发新芽,焕发了勃勃生机。那么视角拉回从业者最关注的饭碗相关的领域之一——AI 辅助开发,产生了什么触动,应该如何拥抱呢? DORA 的年度报告中给出了很有意思的结论——强者恒强。 执行摘要部分总结了几个有趣的点: 问题...

Oct 6, 20251 min read

[译]dora:ai 辅助软件开发状态报告

执行摘要 在 2025 年,科技领导者面临的核心问题已不再是“是否要采用 AI”,而是“如何实现其价值”。 DORA 的研究基于超过 100 小时的定性访谈和来自全球近 5,000 名技术专业人士的问卷调查。研究揭示了一个关键事实:AI 在软件开发中的主要角色是“放大器”。它会放大高效能组织的优势,也会凸显组织的缺陷。 关键结论:AI 是放大器 AI 投资的最大回报并非来自工具本身,而是来自组织底层系统的战略性建设: 高质量的内部平台 清晰的工作流 团队的协同能力 缺少这些基础,AI ...

Oct 2, 202514 min read

僭越了,有人在用 Rust 写 Kubernetes

一个新语言问世,最爱做的事情之一,就是重写存量软件了。 云原生喝酒 SIG 重点扶持项目——rk8s(https://github.com/rk8s-dev/rk8s) 也可以归在这个范畴里,只不过这个项目重写的东西比较大,是 Kubernetes。 从 2025 年 1 月第一个 Commit 开始,到现在有了 200 多次 Commit,十几万行代码。当然距离 Kubernetes 的几百万行代码还差得远——老马就是喜欢整这种大无畏项目。 另外该项目也是国内第一个脱离 Cargo 转向使用 ...

Sep 27, 20253 min read

【伪】架构师

342 posts