Kubernetes 学习笔记(二十):Helm、CRD 和 Operator 怎么扩展 Kubernetes
Han Lv5

前言

前面十九篇基本围绕 Kubernetes 自带对象展开:Pod、Deployment、Service、ConfigMap、Secret、PVC、Ingress、NetworkPolicy 等。

如果只是部署一个简单服务,写几份 YAML 还能接受。但真实应用经常不是一个 YAML:

1
2
3
4
5
6
7
8
9
Deployment
Service
ConfigMap
Secret
Ingress
ServiceAccount
Role
RoleBinding
PVC

每个环境还要有不同参数:

1
2
3
dev:副本 1,资源小,域名 dev.example.com
test:副本 2,资源中等,域名 test.example.com
prod:副本 5,资源更高,域名 api.example.com

这时就会遇到两个问题:

1
2
复杂 YAML 怎么组织?
Kubernetes 能不能认识新的业务对象?

Helm、CRD、Operator 分别在这条线上出现。

我目前先这样理解:

1
2
3
4
5
6
7
8
Helm
帮我们打包和参数化一组 Kubernetes YAML

CRD
让 Kubernetes 认识新的资源类型

Operator
用控制器逻辑管理复杂应用的生命周期

Helm:给一组 YAML 做模板和发布

Helm 可以理解成 Kubernetes 的包管理工具。它把一组 Kubernetes 资源打包成 Chart。

一个 Chart 大概像这样:

1
2
3
4
5
6
7
8
demo-api-chart
├─ Chart.yaml
├─ values.yaml
└─ templates
├─ deployment.yaml
├─ service.yaml
├─ ingress.yaml
└─ configmap.yaml

templates/ 里放模板,values.yaml 里放参数。

例如模板里写:

1
replicas: {{ .Values.replicaCount }}

values.yaml 里写:

1
2
3
4
5
6
replicaCount: 3
image:
repository: example/demo-api
tag: 1.0.0
service:
port: 80

渲染后得到真正提交给 Kubernetes 的 YAML。

1
2
3
4
5
6
7
8
9
Chart templates
+
values.yaml
|
v
渲染后的 Kubernetes YAML
|
v
提交给 API Server

Helm Release

用 Helm 安装一个 Chart,会得到一个 Release。

1
helm install demo-api ./demo-api-chart

可以理解成:

1
2
3
4
5
Chart
软件包

Release
某次安装出来的实例

同一个 Chart 可以安装多次,得到不同 Release:

1
2
3
demo-api-dev
demo-api-test
demo-api-prod

每个 Release 可以使用不同 values:

1
2
helm install demo-api-dev ./demo-api-chart -f values-dev.yaml
helm install demo-api-prod ./demo-api-chart -f values-prod.yaml

这样就不用维护多份几乎一样的 YAML。

1
2
3
4
5
同一套模板
|
├─ dev values
├─ test values
└─ prod values

Helm 解决了什么,不解决什么

Helm 主要解决的是 YAML 组织、模板渲染、发布记录等问题。

它适合:

  • 一组资源打包发布;
  • 多环境参数化;
  • 复用第三方组件安装模板;
  • 管理应用版本。

它不直接解决:

  • 应用自身是否健康;
  • 数据库迁移是否安全;
  • 复杂状态机怎么自动处理;
  • 某个业务系统的运维知识。

可以这样看:

1
2
3
4
5
Helm
帮你把资源“生成并提交”

Controller / Operator
持续观察并处理运行过程

Helm 更像安装清单,Operator 更像长期值班的运维同学。

CRD:让 Kubernetes 认识新资源

Kubernetes 自带很多资源:

1
2
3
4
5
Pod
Deployment
Service
ConfigMap
Secret

CRD,全称 CustomResourceDefinition,可以让集群认识新的资源类型。

比如定义一个 Backup 资源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: backups.example.com
spec:
group: example.com
scope: Namespaced
names:
plural: backups
singular: backup
kind: Backup
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
database:
type: string
schedule:
type: string

创建 CRD 后,就可以创建自定义资源:

1
2
3
4
5
6
7
apiVersion: example.com/v1
kind: Backup
metadata:
name: mysql-backup
spec:
database: mysql
schedule: "0 2 * * *"

这时 Kubernetes API Server 能保存和校验这个新对象。

1
2
3
4
5
CRD
定义一种新资源

Custom Resource
这种资源的具体实例

只有 CRD 还不够

CRD 让 Kubernetes 认识新对象,但它不会自动知道怎么处理这个对象。

1
2
3
4
5
6
7
8
创建 Backup 对象
|
v
API Server 保存它
|
v
如果没有控制器
就没人真正执行备份

这和 Deployment 不一样。Deployment 有 Kubernetes 内置控制器负责管理 ReplicaSet 和 Pod。

自定义资源通常需要配套控制器:

1
2
3
4
5
6
7
Custom Resource
|
v
Custom Controller
|
v
创建 Job / 调用 API / 更新状态

所以 CRD 更像“新增表单类型”,Controller 才是“处理表单的人”。

Operator:把运维知识写成控制器

Operator 通常由 CRD 和自定义控制器组成,用来自动化管理复杂应用。

例如数据库 Operator 可能支持:

  • 创建数据库集群;
  • 配置主从或副本;
  • 处理备份;
  • 扩容缩容;
  • 故障切换;
  • 升级版本;
  • 更新状态。

用户提交一个自定义资源:

1
2
3
4
5
6
7
8
9
apiVersion: database.example.com/v1
kind: DatabaseCluster
metadata:
name: demo-mysql
spec:
replicas: 3
version: "8.4"
storage:
size: 100Gi

Operator 看到后执行:

1
2
3
4
5
6
创建 StatefulSet
创建 Service
创建 PVC
配置备份任务
监听状态变化
故障时尝试修复

这和前面控制循环是一脉相承的:

1
2
3
4
5
6
7
8
9
读取期望状态
|
v
观察实际状态
|
v
发现差异后修正
|
└── 继续循环

只不过这次控制器不是 Kubernetes 内置的,而是我们或第三方提供的。

Helm 和 Operator 的区别

我一开始容易把 Helm 和 Operator 混在一起,因为它们都能“安装应用”。

现在我这样区分:

1
2
3
4
5
6
7
Helm
主要负责模板化、安装、升级一组资源
更偏交付打包

Operator
主要负责持续观察和自动运维
更偏运行期管理

例如安装 MySQL:

1
2
3
4
5
Helm Chart
帮我生成 StatefulSet、Service、PVC

MySQL Operator
不只创建资源,还可能处理备份、故障切换、升级顺序

简单服务用 Helm 组织 YAML 已经很好。复杂有状态系统,如果运维流程也要自动化,才更适合看 Operator。

CRD 使用时要注意什么

CRD 很强,但也不能滥用。

我会先问几个问题:

  • 是否真的需要一种新资源类型?
  • 普通 Deployment、Job、ConfigMap 是否已经够用?
  • 有没有控制器持续处理这个资源?
  • 版本升级和字段兼容怎么做?
  • 删除自定义资源时,关联资源怎么清理?
  • 谁维护这个 Operator?

因为 CRD 一旦进入集群,就扩展了 API 面。

1
2
3
4
5
6
7
新增 CRD
|
v
新增 API 类型
|
v
需要版本、权限、升级、备份和清理策略

如果只是为了少写几行 YAML,就不一定值得创建 CRD。

一个更现实的使用顺序

对普通应用来说,我觉得可以按这个顺序理解:

1
2
3
4
5
6
7
8
9
10
11
第一步:直接写 YAML
先理解 Kubernetes 原生对象

第二步:用 Helm 组织重复 YAML
解决多环境、多资源打包

第三步:使用成熟 Operator
管理数据库、消息队列等复杂系统

第四步:自己写 CRD / Operator
只有当通用对象真的表达不了业务运维模型时再做

不要一开始就把所有东西都 Operator 化。

很多普通 Web 服务只需要:

1
2
3
4
5
6
Deployment
Service
ConfigMap
Secret
Ingress
HPA

再用 Helm 管理参数就够了。

把整个系列串起来

到这一篇,Kubernetes 的基本地图就比较完整了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
运行单元
Pod / Container / Image

副本和发布
Deployment / ReplicaSet / Rollout

访问入口
Service / Ingress / Gateway

配置和存储
ConfigMap / Secret / Volume / PVC / PV

资源和健康
requests / limits / probes

组织和关系
Namespace / Label / Selector / Annotation

安全和网络
ServiceAccount / RBAC / SecurityContext / NetworkPolicy

特殊任务
DaemonSet / Job / CronJob / StatefulSet

扩展能力
Helm / CRD / Operator

这些概念看起来很多,但核心思路一直没变:

1
2
3
4
5
6
7
声明期望状态
|
v
控制器持续观察
|
v
让实际状态尽量靠近期望状态

小结

这一篇整理下来,我对 Helm、CRD 和 Operator 的理解可以概括成几句话:

  • Helm 用来打包、模板化和发布一组 Kubernetes 资源;
  • Chart 是包,Release 是安装出来的实例;
  • values 用来承载不同环境的参数;
  • CRD 可以让 Kubernetes 认识新的资源类型;
  • 只有 CRD 没有控制器,通常只是能保存对象,不能自动执行业务逻辑;
  • Operator 把特定应用的运维知识写成控制器;
  • Helm 更偏交付打包,Operator 更偏运行期自动管理;
  • CRD 和 Operator 很强,但要考虑版本、权限、升级和维护成本;
  • 普通应用可以先用原生对象和 Helm,复杂有状态系统再考虑 Operator。

这个 Kubernetes 入门系列到这里,可以算先完成了一张基础地图。后面如果继续写,我更想按专题深入,比如 HPA 自动扩缩容、调度策略、服务网格、CI/CD 发布流程、生产集群故障案例等。

参考资料

 评论