带你读《云原生应用开发:Operator原理与实践》——2.1.1 CRD 介绍

简介: 带你读《云原生应用开发:Operator原理与实践》——2.1.1 CRD 介绍

第2章 Operator原理


Operator 的概念是由 CoreOS 公司的工程师于 2016 年提出的,它可以让工程师根据应用独有的领域逻辑编写自定义的控制器。我们通过一个简单的例子理解 Operator。假设有一个连接数据库的 Go Web 程序,开发者想将其部署到 k8s 集群。在理想情况下,你会希望用 Deployment 部署应用,然后暴露给 Service,对于应用服务的后端则是使用 StatuflSet 部署数据库,所以需要完成两部分的部署才能完成整个应用服务部署,前提条件:

(1)无状态部分,部署 Go Web 应用;(2)有状态部分,部署数据库。

在上面的例子中,我们可以应用自身对应用程序与数据库之间关系的了解创建一个控制器,该控制器将以某种特定方式运行时执行某些操作,比如备份、更新、数据还原这些任务该如何完成取决于应用程序本身和业务限制(领域知识)。这些与应用强相关的操作就是 Kubernetes Operator 要实现的:代替原本需要由网站可靠性工程师(SRE,Site Reliability Enginner) 和 运 维 工 程 师 来 完 成 的 操 作。 在 Kubernetes 中 Operator 就 是Kubernetes API 的客户端,扮演 Controller 的角色管理 CRD。


2.1 Operator 简介


在介绍 Operator 之前,我们先通过 Kubeclt 命令行创建 Kubernetes 的 Namespace,那么,从发送一条创建命令到被 Kubernetes 执行完成这一过程发生了什么。

首先通过执行以下命令创建一个 Namespace。

kubeclt create -f namespace-nginx.yaml

Yaml 文件见代码清单 2-1。

代码清单 2-1

apiVersion: v1
kind: Namespace
metadata:
 name: nginx

这时 Kubectl 会向 API 服务器发送一个 POST 请求,API 请求格式见代码清单 2-2。

代码清单 2-2

curl --request POST \
 --url http://${k8s.host}:${k8s.port}/api/v1/namespaces \
 --header 'content-type: application/json' \
  --data '{
 "apiVersion":"v1",
 "kind":"Namespace",
 "metadata":{
 "name":"nginx"
 }

从中可以看出 Kubernetes 集群的组件交互都是通过 RESTful API 的形式完成的。流程如图 2-1 所示。

image.png

图 2-1 Namespace 资源创建流程

从上面的 cURL 指令可以看出,尽管我们编写的是 Yaml 文件格式,但是 Kubernetes APIServer 接收的是 JSON 数据类型,而并非用户编写的 Yaml,然后创建的所有资源的请求都通过 Kube-APIServer 预处理、检测处理后持久化到 ETCD 组件中。其中APIServer 也是 Kubernetes 集群中与 ETCD 交互的唯一一个组件。如果 Namespace资源被创建在 ETCD 之后,Kubernetes 事件监听机制就会将 Namespace 资源的变化情况发送给监听Namespaces 资源的 Namespace 控制器,最后由 Namespace 控制器执行创建 Nginx 命名空间的具体操作。同理,Kubernetes 中的其他资源操作类似,都会存在对应的资源控制器来处理响应的资源请求。这些控制器共同组成了 Kubernetes API 控制器集合。


我们不难发现,实现 Kubernetes 中某一种资源类型,如 Namespace、ReplicaSet、Pod 等,Kubernetes 中的资源类型需要满足以下要求。

(1)对该领域类型的模型抽象,如上面 namespace-nginx.yaml 文件描述的 Yaml 数据结构,这个抽象决定了 Kubernetes Client 发送到 Kubernetes APIServer 的 RESTful API 请求数据内容,也描述了这个领域类型本身。

(2)实际去处理这个领域类型抽象的控制器,例如 Namespace 控制器、ReplicaSet控制器、Pod 控制器,这些控制器实现了这个抽象描述的具体业务逻辑,并通过 RESTful API 提供这些服务。


我们将这种资源设计方式称为“声明式 API”。而当 Kubernetes 开发者需要扩展Kubernetes 能力时,也可以遵循这种模式,即提供一份对想要扩展的能力的抽象以及实现了这个抽象具体逻辑的控制器。前者称作 CRD,后者称作 Controller。


Operator 就是通过这种方式实现 Kubernetes 扩展性的一种模式,Operator 模式可以将一个领域问题的解决办法想像成一个操作者,这个操作者在用户和集群之间,通过一份份订单去操作集群的 API,来达到满足这个领域各种需求的目的。这里的订单就是 CR(即 CRD 的一个实例),而操作者就是控制器,是具体逻辑的实现者。之所以强调是 Operator,而不是计算机领域里传统的 Server 角色,是因为 Operator 本质上不创造和提供新的服务,它只是已有 Kubernetes APIServer 的组合。下面将着重介绍这些基础概念,什么是 CRD,什么是 Controller。


2.1.1 CRD 介绍


1. 声明式 API

什 么 是 声 明 式 API 呢? 首 先 我 们 需 要 了 解 在 Kubernetes 中, 使 Deployment、DamenSet,StatefulSet 等资源来管理应用 Workload,使用 Service、Ingress 等来管理应用的访问方式,使用 ConfigMap 和 Secret 来管理应用配置。在集群中对这些资源的创建、更新、删除的动作都会被转换为事件(Event),Kubernetes 的 Controller Manager 负责监听这些事件并触发相应的任务来满足用户的期望。这种方式称为声明式,用户只需要关心应用程序的最终状态,其他的过程都通过 Kubernetes 来完成,通过这种方式可以大大简化应用配置管理的复杂度。

声明式 API 指的是用户提交一个定义好的 API 对象来描述所期望的状态。例如上面创建的 Namespace 资源,这个资源类型表明用户期望的结果是创建一个名字为 Nginx 的命名空间。那么用户无须关心如何创建 Namespaces 资源,只需要关注结果即可。

什么是过程式 API 呢?过程式 API 一次只能处理一个写请求,否则可能会产生冲突,已不具备合并操作的能力 。

声明式 API 的特点是允许有多个 API 写端以 PATCH 的方式对 API 对象进行修改,而无须关心本地原始Yaml文件的内容。声明式API才是 Kubernetes 项目编排能力的核心。对于 API 对象的增、删、改、查,可以在完全无须外界干预的情况下,完成对“实际状态”和“期望状态”的调谐(Reconcile)过程。这种调谐过程的实现者则是 Controller 的处理逻辑。

当开发者对 Kubernetes 的使用逐渐增多之后,会发现这些默认的资源不足以支撑我们的系统,以 Nginx Ingress 为例,如果用户想要实现负载均衡限流器功能,目前 NiginxIngress 的配置不支持这样的特性,对于这种非通用的特性,Kubernetes 提供了一种扩展性的支撑方式,即自定义资源。典型地,声明式 API 特点如下。

(1)你的 API 包含相对而言为数不多、尺寸较小的对象(资源)。

(2)对象定义了应用或者基础设施的配置信息。

(3)对象更新操作频率较低。

(4)通常需要人来读取或写入对象。

(5)对象的主要操作是 CRUD 风格的(创建、读取、更新和删除)。

(6)不需要跨对象的事务支持:API 对象代表的是期望状态而非确切实际状态。

命令式 API 与声明式有所不同。 以下迹象表明你的 API 可能不是声明式 API。

(1)客户端发出“做这个操作”的指令,之后在该操作结束时获得同步响应。

(2)客户端发出“做这个操作”的指令,并获得一个操作 ID,之后需要检查一Operation 对象来判断请求是否成功完成。

(3)将你的 API 类比为 RPC。

(4)需要较高的访问带宽(长期保持每秒数十个请求)。

(5)在对象上执行的常规操作并非是 CRUD 。

(6) API 不太容易用对象来建模。

2. CRD 场景

什么是 CRD 呢?为什么要有 CRD 资源呢?通过对下面内容的学习,大家会对 CRD有概念性的认识。首先 Kubernetes 为用户提供了丰富的资源,如资源对象、配置对象、存储对象和策略对象,如表 2-1 所示。

image.png

虽然 Kubernetes 为我们提供了丰富的资源类型,但是在不同应用场景下,某些传统资源类型仍不能满足用户需求,他们对平台可能存在一些特殊的需求,为了满足这些需求,Kubernetes 社区为我们提供了一种抽象 Kubernetes 的扩展资源。这种抽象的资源类型叫作自定义资源定义(CRD,Custom Resource Definition)。CRD 为我们提供资源的快速注册和使用的接口。其实在很早的 k8s 版本中自定义资源就已经被提出,当时叫作TPR(Third Party Resource),这是与 CRD 类似的概念,但是在 1.9 以上的版本中被弃用,而 CRD 则进入 beta 状态。

(1)什么时候需要添加定制资源?

① 你希望使用 Kubernetes 客户端库和 CLI 来创建和更改新的资源。

② 你希望 Kubectl 能够直接支持你的资源。

③ 你希望构造新的自动化机制,监测新对象的更新事件,并对其他对象执行 CRUD (增加、检索、更新、删除)操作,或者监测后者更新前者。

④ 你希望编写自动化组件来处理对对象的更新。

⑤ 你希望使用 Kubernetes API 对诸如 .spec、.status 和 .metadata 等字段进行约定。

⑥ 你希望对象是对一组受控资源的抽象,或者对其他资源的归纳提炼。

(2)如何定义一个 CRD ?

我们通过代码清单 2-3 中的 Yaml 文件来创建一个 Appconfig CRD 资源。

代码清单 2-3

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
 # 名称必须符合下面的格式 :<plural>.<group>
 name: crontabs.stable.example.com
spec:
 # REST API 使用的组名称 :/apis/<group>/<version>
  group: stable.example.com
 # REST API 使用的版本号 :/apis/<group>/<version>
 versions:
 - name: v1
 # 可以通过 served 来开关每个版本
 served: true
 # 有且仅有一个版本开启存储
 storage: true
 schema:
 openAPIV3Schema:
 type: object
 properties:
 spec:
 type: object
 properties:
 cronSpec:
 type: string
 image:
 type: string
 replicas:
 type: integer
 # Namespaced 或 Cluster
 scope: Namespaced
 names:
 # URL 中使用的复数名称 : /apis/<group>/<version>/<plural>
 plural: crontabs
 # CLI 中使用的单数名称
 singular: crontab
 # CamelCased 格式的单数类型。在清单文件中使用
 kind: CronTab
 # CLI 中使用的资源简称
 shortNames:
 - ct

首先我们解释一下创建的 CRD 的参数。

① 第一行和第二行我们定义了 CRD 的版本。

② metadata 定义了访问的 CRD 资源的名称,名称必须与下面的 spec 字段匹配。

③ Spec.group 定义了资源属于什么组,这里我们定义成 stable.example.com。

④ Spec.versions 定义了资源存储的版本,这里定义为 v1。

⑤ scope 定义了我们创建的资源的作用范围?这里定义成 Namespace 范围。

⑥ plural singular 定义了资源的单复数形式,这个根据实际情况命名即可。

我们执行 kubectl apply -f crd.yaml 就可以创建名称为 appconfig 的 CRD 资源了。同 时 我 们 定 义 的 CRD 资 源 RESTful API 将 会 定 义 成 /apis/stable.example.com/v1/namespaces/*/crontabs/。

(3)如何创建一个 CRD 实例对象?

首先定义一个符合上面 CRD 资源的 apiVersion 和资源类型(kind)(见代码清单 2-4)。

代码清单 2-4

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
 name: my-new-cron-object
spec:
 cronSpec: "* * * * */5"
 image: my-awesome-cron-image

创建 CRD 对象后,可以创建自定义对象,自定义对象可包含自定义字段。这些字段可以包含任意 JSON。如代码清单 2-4 所示,cronSpec 和 image 自定义字段在自定义对象中设置 CronTab。CronTab 类型来自上面创建的 CRD 对象的规范,然后执行kubectle apply -f my-crontab.yaml 即可。


3. k8s API 设计规范

Kubernetes API 是集群系统中的重要组成部分,Kubernetes 中各种资源(对象)的数据通过该 API 被传送到后端的持久化存储(ETCD)中。Kubernetes 集群中的各部件之间通过该 API 实现解耦合,Kubernetes API 中的资源对象都拥有通用的元数据,资源对象也可能存在嵌套现象,比如在一个 Pod 中嵌套多个 Container。创建一个 API 对象是指通过 API 调用创建一条有意义的记录,该记录一旦被创建,Kubernetes 将确保对应的资源对象会被自动创建并托管维护。在 Kubernetes 系统中,大多数情况下,API 定义和实现都符合标准的 HTTP REST 格式,如通过标准的 HTTP 动作(POST、PUT、GET、DELETE)来完成对相关资源对象的查询、修改、删除等操作。但同时 Kubernetes 也为某些非标准的 REST 行为提供了附加的 API,例如,监听某个资源变化的 Watch 接口等。另外,某些 API 可能违背严格的 REST 模式,因为接口不是返回单一的 JSON 对象,而是返回其他类型的数据,如 JSON 对象流(Stream)或非结构化的文本日志数据等。 从上面的 CRD 定义中可以发现,在 Kubernetes 中要想完成一个 CRD,需要指定 Group、Version 和 Kind,这在 Kubernetes 的 APIServer 中简称为 GVK。当我们在 Kubernetes 中谈论 API 时,经常会提起 4 个术语:Groups 、Versions、Kinds和Resources。

(1) Groups /Versions

Kubernetes 中的 API 组简单来说就是相关功能的集合。每个组都有一个或多个版本,它允许我们随着时间的推移改变 API 的职责。

(2) Kinds/Resources

每个 API 组 - 版本包含一个或多个 API 类型,我们称之为 Kinds。虽然一个 Kind 可以在不同版本之间改变表单内容,但每个表单必须能够以某种方式存储其他表单的所有数据(我们可以将数据存储在字段或者注释中)。 这意味着,使用旧的 API 版本不会导致新的数据丢失或损坏。

Resources 只是 API 中的一个 Kind 的使用方式。通常情况下,Kind 和 Resources 之间是一对一的映射。 例如,Pods 资源对应于 Pod 种类。但是有时同一类型可能由多个资源返回。例如,Scale Kind 是由所有 Scale 子资源返回的,它由 Deployments/Scale 或 Replicasets/Scale 组成。这就是允许 Kubernetes HPA(Horizontal Pod Autoscaler) 与不同资源交互的原因。然而,使用 CRD 每个 Kind 都将对应一个 Resource。

GVK 是定义一种类型的方式。例如,Daemonsets 就是 Kubernetes 中的一种资源,当我们要求 Kubernetes 创建一个 Daemonsets 的时候,Kubectl 是如何知道该怎么向 APIServer 发送这个信息呢?是所有的不同资源都发向同一个 URL,还是每种资源都是不同的?例如 Daemonsets 资源内容(见代码清单 2-5)。

代码清单 2-5

apiVersion: apps/v1
kind: DaemonSet
metadata:
 name: node-exporter

这里声明了 apiVersion 是 apps/v1,其实就是隐含了 Group 是 apps,Version 是 v1,Kind 就是定义的 DaemonSet,而 Kubectl 接收到这个声明之后,就可以根据这个声明去调用 APIServer 对应的 URL 来获取信息。例如,/api/apps/v1/daemonset 这样的 API 就是由上面的设计规则实现的。Kubernetes 以符合 REST 规范的 URI 来组织资源,组织的路径如图 2-2 所示。

image.png

图 2-2 API 资源格式路径图

前面介绍了 GVK(Group、Version、Kind), 接下来介绍 APIServer 的第二个概念 GVR (Group、Version、Resource)。其实理解了 GVK 之后再理解 GVR 就很容易了,这就是面向对象编程中的类和对象的概念是一样的。Kind 相当于一个类,Resource是具体的 Kind,可以理解为一个类的对象资源。那么 GVR 资源如何对应到 GVK ?这就是 REST Mapping 的功能:REST Mapping 可以将指定的一个 GVR(如 Daemonset 资源)通过转换映射返回对应的 GVK 以及支持的操作等。

(3) API 版本

为了在兼容旧版本的同时不断升级 API,Kubernetes 提供了多版本 API 的支持能力,每个版本的 API 通过一个版本号路径前缀加以区分,例如 /api/v1beta3。通常情况下,新旧几个不同的 API 版本都能涵盖所有的 Kubernetes 资源对象,在不同的版本之间这些 API 存在一些细微差别。Kubernetes 开发团队基于 API 级别选择版本而不是基于资源和域级别来选择版本,是为了确保 API 能够描述一个清晰、连续的系统资源和行为的视图,能够控制访问的整个过程和实验性 API 的访问 。

API 详细说明如下。

① GET /< 资源名的复数格式 >:获得某一类型的资源列表,例如 GET /Pods 返回一个 Pod 资源列表。

② POST /< 资源名的复数格式 >:创建一个资源,该资源来自用户提供的 JSON 对象。

③ GET /< 资源名复数格式 >/< 名字 >:通过给出的名称(Name)获得单个资源,例如 GET /pods/podname 返回一个名称为“podname”的 Pod。

④ DELETE /< 资源名复数格式 >/< 名字 >:通过给出的名字删除单个资源。删除选项(DeleteOptions)中可以指定的优雅删除(Grace Deletion)的时间(Grace Period Seconds),该可选项表明了从服务端接收删除请求到资源被删除的时间间隔(单位为 s)。不同的类别(Kind)可能为优雅删除时间(Grace Period)申明默认值。用户提交的优雅删除时间将覆盖该默认值,包括值为 0 的优雅删除时间。

⑤ PUT /< 资源名复数格式 >/< 名字 >:通过给出的资源名和客户端提供的 JSON 对象来更新或创建资源。

⑥ PATCH /< 资源名复数格式 >/< 名字 >:选择修改资源详细指定的域。

此外,Kubernetes API 添加了资源变动的“观察者”模式的 API 。

① GET /watch/< 资源名复数格式 >:随时间变化,不断接收一连串的 JSON 对象,这些 JSON 对象记录了给定资源类别内所有资源对象的变化情况。

② GET /watch/< 资源名复数格式 >/:随时间变化,不断接收一连串的 JSON 对象,这些 JSON 对象记录了某个给定资源对象的变化情况。

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
2月前
|
Kubernetes 监控 Cloud Native
云原生时代下的应用开发与部署实践
【10月更文挑战第4天】在云原生的浪潮中,开发者和运维人员面临着新的挑战和机遇。本文将通过实际案例,展示如何在云平台上高效地开发、部署和管理应用,同时确保系统的可扩展性和高可用性。我们将深入探讨容器化技术、微服务架构以及持续集成/持续部署(CI/CD)流程的实施策略,旨在为读者提供一套完整的云原生解决方案框架。
|
3月前
|
Kubernetes Cloud Native 持续交付
云原生技术在现代应用开发中的角色与实践
【9月更文挑战第9天】 随着云计算技术的飞速发展,云原生(Cloud Native)已经成为推动企业数字化转型的核心力量。本文将深入探讨云原生的基本概念、关键技术及其在实际开发中的应用案例,旨在为读者提供一条清晰的云原生技术学习路径和应用指南。通过实例分析,我们将揭示云原生如何优化资源管理、提升应用性能及加快部署速度,进而帮助企业构建更加灵活、可靠和高效的软件系统。
|
1月前
|
Kubernetes Cloud Native 持续交付
云原生技术在现代应用开发中的实践与思考
【10月更文挑战第35天】云原生技术,作为云计算的进阶形态,正引领着软件开发和运维的新潮流。本文将深入探讨云原生技术的核心理念、关键技术组件以及在实际项目中的应用案例,帮助读者理解如何利用云原生技术优化应用架构,提高开发效率和系统稳定性。我们将从容器化、微服务、持续集成/持续部署(CI/CD)等角度出发,结合实际代码示例,展现云原生技术的强大能力。
|
1月前
|
监控 Cloud Native 持续交付
云原生技术深度解析:重塑现代应用开发与部署范式####
本文深入探讨了云原生技术的核心概念、关键技术组件及其在现代软件开发中的重要性。通过剖析容器化、微服务架构、持续集成/持续部署(CI/CD)等关键技术,本文旨在揭示云原生技术如何促进应用的敏捷性、可扩展性和高可用性,进而推动企业数字化转型进程。不同于传统摘要仅概述内容要点,本部分将融入具体案例分析,直观展示云原生技术在实际应用中的显著成效与挑战应对策略,为读者提供更加丰富、立体的理解视角。 ####
|
2月前
|
Kubernetes Cloud Native 持续交付
云原生技术:重塑现代应用开发与部署模式####
本文深入探讨了云原生技术的核心概念、发展历程及其在现代软件开发和部署中的关键作用。通过分析云原生架构的特点,如容器化、微服务、持续集成与持续部署(CI/CD),以及它如何促进应用的可伸缩性、灵活性和效率,本文旨在为读者提供一个关于云原生技术全面而深入的理解。此外,还将探讨实施云原生策略时面临的挑战及应对策略,帮助组织更好地把握数字化转型的机遇。 ####
|
2月前
|
人工智能 Serverless API
云原生应用开发平台CAP:一站式应用开发及生命周期管理解决方案
阿里云的云应用开发平台CAP(Cloud Application Platform)是一款一站式应用开发及应用生命周期管理平台。它提供丰富的Serverless与AI应用模板、高效的开发者工具链及企业级应用管理功能,帮助开发者快速构建、部署和管理云上应用,大幅提升研发、部署和运维效能。
193 1
|
2月前
|
Kubernetes Cloud Native 持续交付
云原生技术在现代应用开发中的实践与展望
【10月更文挑战第7天】随着技术的不断演进,云计算已从简单的资源租用模式转变为支持复杂、高效、灵活的云原生应用架构。本文将深入探讨云原生技术的核心概念及其在现代应用开发中的应用,通过分析Kubernetes容器编排和微服务架构的实践案例,揭示云原生技术如何推动软件开发的现代化进程。文章旨在为开发者和架构师提供一套实用的云原生应用开发指南,同时展望未来云原生技术的发展方向。
37 8
|
2月前
|
Cloud Native 测试技术 云计算
云原生技术在现代应用开发中的角色与实践
【9月更文挑战第31天】本文深入探讨了云原生技术如何革新现代应用开发流程,通过实际案例分析,揭示了其对提高开发效率、确保系统可扩展性和可靠性的显著影响。文章不仅介绍了云原生的核心概念,还提供了实施策略和最佳实践,旨在为开发者提供一条清晰的云原生转型之路。
|
3月前
|
Kubernetes Cloud Native 持续交付
云原生技术在现代应用开发中的实践与思考
【9月更文挑战第23天】本文将深入探讨云原生技术如何革新现代应用的开发流程。通过分析云原生的核心概念、优势以及实际应用案例,我们旨在揭示这一新兴技术范式如何助力开发者和企业更高效、灵活地构建和部署应用程序。文章还将提供具体代码示例,展示云原生技术在实际项目中的应用,帮助读者更好地理解和掌握该技术。
|
3月前
|
Cloud Native 持续交付 开发者
云原生技术在现代应用开发中的应用与实践
【9月更文挑战第22天】本文将深入探讨云原生技术如何革新现代应用开发,通过实际案例分析其对提高开发效率、促进持续集成与交付的显著影响。我们将从云原生的基本概念出发,逐步展开到容器化、微服务架构、自动化管理的实践操作,以及这些技术如何协同工作以支持复杂应用的快速迭代和扩展。文章旨在为开发者提供一套云原生技术的实践框架,帮助他们构建更加灵活、可维护的应用系统。