【K8s】Kubernetes CRD 介绍(控制器)
文章目录
- CRD 概述
- 1. 操作CRD
- 1.1 创建 CRD
- 1.2 操作 CRD
- 2. 其他笔记
- 2.1 Kubectl 发现机制
- 2.2 校验 CR
- 2.3 简称和属性
- 3. 架构设计
- 3.1 控制器概览
- 参考
CRD 概述
CR(Custom Resource)其实就是在 Kubernetes 中定义一个自己的资源类型,是一个具体的 “自定义 API 资源” 实例,为了能够让 Kubernetes 认识这个 CR,就需要让 Kubernetes 明白这个 CR 的宏观定义是什么,也就是需要创建所谓的 CRD(Custom Resource Definition)来表述。

可能这么说并不是太直观,换个方式表达一下,我们想要创建一个 Pod 的时候,那么会编写一个 YAML 配置,然后前两行一般会这么写:
[root@liqiang.io]# head -2 pod.yamlapiVersion: v1kind: Pod
这里的 Resource 就是 Pod,我们可以它通过在 Kubernetes 中创建一个真实的 Pod,但是这个 Pod 要运行哪些 Container?这些 Container 我们要怎么在 pod.yaml 中定义,这个要怎么知道呢?这就需要有个描述文件用来定义 Pod 这个 Resource 需要具有哪些属性,这些属性的类型是什么,结构是怎样的,这个描述文件就是所谓的 CRD。
当然,Pod 作为一个内置资源,是没有这种所谓的 CRD 的,这里是作为一个介绍,当我们要创建一个非内置资源的时候,就需要有这么一个叫做 CRD 的描述文件了。
1. 操作CRD
1.1 创建 CRD
在 Kubernetes 中,CRD 本身也是一种资源,它是 Kubernetes 内部 apiextensions-apiserver 提供的 API Group:apiextensions.k8s.io/v1,注意,这里是 v1 版本,在 Kubernetes 1.16 版本中,CRD 转正了,成为了正式版本 CRD GA,但是,在 v1.16 之前以及 v1.7 以及之后,都是以 v1beta1 的版本存在,所以在编写的时候注意一下你的 Kubernetes 版本。
自己定义一个 CRD 也需要编写 YAML 文件,这里是我的一个示例,我通过这个 CRD 来定义我的网站 liqiang.io 的管理员的 CR,当我想添加一个管理员的时候,只需要直接添加一个 CR 就可以了:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:name: admins.admin.liqiang.io
spec:group: admin.liqiang.ionames:kind: Adminplural: adminsscope: Namespacedversions:- name: v1served: truestorage: trueschema:openAPIV3Schema:description: Admin define the admin user for liqiang.io website.type: objectproperties:spec:type: objectproperties:username:type: stringpassword:type: stringemail:type: string
-
apiVersion 就是指 CRD 的一个 apiVersion 声明,声明它是一个 CRD 的需求或者说定义的 Schema。
-
kind 就是
CustomResourcesDefinition,指 CRD。 -
metadata.name是一个用户自定义资源中自己自定义的一个名字。一般我们建议使用“顶级域名.xxx.APIGroup”这样的格式,比如这里就是 admins.admin.liqiang.io,APIGroup对应spec.group标签,值为 “admin.liqiang.io” 。 -
spec 用于指定该 CRD 的 group、version。比如在创建 Pod 或者 Deployment 时,它的 group 可能为 apps/v1 或者 apps/v1beta1 之类,这里我们也同样需要去定义 CRD 的 group。
- 图中的 group 为 admin.liqiang.io;
- verison 为 v1alpha1;
- names 指的是它的 kind 是什么,比如 Deployment 的 kind 就是 Deployment,Pod 的 kind 就是 Pod,这里的 kind 被定义为了 Foo;
- plural 字段就是一个昵称,比如当一些字段或者一些资源的名字比较长时,可以用该字段自定义一些昵称来简化它的长度;
- scope 字段表明该 CRD 是否被命名空间管理。比如 ClusterRoleBinding 就是 Cluster 级别的。再比如 Pod、Deployment 可以被创建到不同的命名空间里,那么它们的 scope 就是 Namespaced 的。这里的 CRD 就是 Namespaced 的。
当写完这个 CRD 的描述文件之后,可以直接通过 kubectl apply 00-admin-crd.yaml 在 Kubernetes 集群中注册 CRD 对象。然后 Kubernetes 中的 kube-apiserver 组件中运行的 apiextensions-apiserver 就会检查对应 CRD 的名字是否合法之类的,当一切检查都 OK 之后,那么就会返回给你创建成功的状态了:
[root@liqiang.io]# kubectl apply -f 00-admin-crd.yaml
customresourcedefinition.apiextensions.k8s.io/admins.admin.liqiang.io created
k get crds
NAME CREATED AT
admins.admin.liqiang.io 2019-11-23T06:52:23Z
内部的流程是这样的:

1.2 操作 CRD
现在在 Kubernetes 中已经多了一种新的资源叫做 admins.admin.liqiang.io,可以使用它来定义系统的管理员了,这里我就添加一个:

记得,需要 apply 一下:
[root@liqiang.io]# kubectl apply -f 01-admin-cr.yaml
admin.admin.liqiang.io/liuliqiang created
[root@liqiang.io]# kubectl get admins.admin.liqiang.io
NAME AGE
liuliqiang 117s
现在就创建成功了。然后,不要了,那就删除掉,删除也是很简单的:
[root@liqiang.io]# kubectl delete admins.admin.liqiang.io liuliqiang
admin.admin.liqiang.io "liuliqiang" deleted
[root@liqiang.io]# kubectl get admins.admin.liqiang.io
No resources found in default namespace.
这个新建和删除 Admin 的过程虽然用得挺简单,但是,事实上,他们除了把 Kubernetes 当作一个 DB 之外,没有太多的实际价值。他们更多的价值体现在我后续介绍的 Controller 和 Operator 之上。所以,管理 CRD 的定义以及使用的基本内容就是这么多了,非常简单。
2. 其他笔记
2.1 Kubectl 发现机制
在我创建完 CRD 之后,kubectl 会通过 API 服务器的 discovery information 来查找关于新资源的信息,下面展示当使用 kubectl 获取一个 CR 时,内部的流程:

这个过程是通过 discovery RESTMapper 实现的,关于 RESTMapper 我在后面会继续介绍。
2.2 校验 CR
创建资源的时候,肯定不是任由我们随意得编写 YAML 声明文件,当提交给 Kubernetes 之后,Kubernetes 会对我们提交得声明文件进行校验,从前边 CRD 的定义中可以看到,CRD 是基于 OpenAPI v3 schema 进行的。

同时也可以看到,这种校验是比较初级的,比较简略的。如果想要实现更加复杂的校验,那么可以通过 Kubernetes 的 admission webhook 来实现,这个在我介绍 Istio 的时候有介绍过,如果没印象,不妨找来看看。
Adminssion webhook 的内部校验流程是这样的:

2.3 简称和属性
当我输入 kubectl api-resources 的时候,可以看到我定义的 CRD 和一些内置的 Resource 还是有一些区别的,例如内置的 Service 有简写: svc,那么 CRD 是否可以有简写,答案是肯定的:

如果通过kubectl get all来列举所有的资源,然后会发现并不会看到自己的 CRD,如果想要让自己的 CRD 对应的 CR 出现在 get all 中,那么可以在 CRD 的声明中加入 categories 属性:

3. 架构设计
3.1 控制器概览
只定义一个 CRD 其实没有什么作用,它只会被 API Server 简单地计入到 etcd 中。如何依据这个 CRD 定义的资源和 Schema 来做一些复杂的操作,则是由 Controller,也就是控制器来实现的。
Controller 其实是 Kubernetes 提供的一种可插拔式的方法来扩展或者控制声明式的 Kubernetes 资源。它是 Kubernetes 的大脑,负责大部分资源的控制操作。以 Deployment 为例,它就是通过 kube-controller-manager 来部署的。
比如说声明一个 Deployment 有 replicas、有 2 个 Pod,那么 kube-controller-manager 在观察 etcd 时接收到了该请求之后,就会去创建两个对应的 Pod 的副本,并且它会去实时地观察着这些 Pod 的状态,如果这些 Pod 发生变化了、回滚了、失败了、重启了等等,它都会去做一些对应的操作。
所以 Controller 才是控制整个 Kubernetes 资源最终表现出来的状态的大脑。
用户声明完成 CRD 之后,也需要创建一个控制器来完成对应的目标。比如之前的 Foo,它希望去创建一个 Deployment,replicas 为 1,这就需要我们创建一个控制器用于创建对应的 Deployment 才能真正实现 CRD 的功能。
控制器工作流程概览

这里以 kube-controller-manager 为例。
如上图所示,左侧是一个 Informer,它的机制就是通过去 watch kube-apiserver,而 kube-apiserver 会去监督所有 etcd 中资源的创建、更新与删除。Informer 主要有两个方法:一个是 ListFunc;一个是 WatchFunc。
- ListFunc 就是像 “kuberctl get pods” 这类操作,把当前所有的资源都列出来;
- WatchFunc 会和 apiserver 建立一个长链接,一旦有一个新的对象提交上去之后,apiserver 就会反向推送回来,告诉 Informer 有一个新的对象创建或者更新等操作。
Informer 接收到了对象的需求之后,就会调用对应的函数(比如图中的三个函数 AddFunc, UpdateFunc 以及 DeleteFunc),并将其按照 key 值的格式放到一个队列中去,key 值的命名规则就是 “namespace/name”,name 就是对应的资源的名字。比如我们刚才所说的在 default 的 namespace 中创建一个 foo 类型的资源,那么它的 key 值就是 “default/example-foo”。Controller 从队列中拿到一个对象之后,就会去做相应的操作。
下图就是控制器的工作流程。

首先,通过 kube-apiserver 来推送事件,比如 Added, Updated, Deleted;然后进入到 Controller 的 ListAndWatch() 循环中;ListAndWatch 中有一个先入先出的队列,在操作的时候就将其 Pop() 出来;然后去找对应的 Handler。Handler 会将其交给对应的函数(比如 Add(), Update(), Delete())。
一个函数一般会有多个 Worker。多个 Worker 的意思是说比如同时有好几个对象进来,那么这个 Controller 可能会同时启动五个、十个这样的 Worker 来并行地执行,每个 Worker 可以处理不同的对象实例。
工作完成之后,即把对应的对象创建出来之后,就把这个 key 丢掉,代表已经处理完成。如果处理过程中有什么问题,就直接报错,打出一个事件来,再把这个 key 重新放回到队列中,下一个 Worker 就可以接收过来继续进行相同的处理。
参考
Kubernetes CRD 介绍
从零开始入门 K8s | Kubernetes API 编程范式
相关文章:
【K8s】Kubernetes CRD 介绍(控制器)
文章目录 CRD 概述1. 操作CRD1.1 创建 CRD1.2 操作 CRD 2. 其他笔记2.1 Kubectl 发现机制2.2 校验 CR2.3 简称和属性 3. 架构设计3.1 控制器概览 参考 CRD 概述 CR(Custom Resource)其实就是在 Kubernetes 中定义一个自己的资源类型,是一个具…...
Python 小程序之PDF文档加解密
PDF文档的加密和解密 文章目录 PDF文档的加密和解密前言一、总体构思二、使用到的库三、PDF文档的加密1.用户输入模块2.打开并读取文档数据3.遍历保存数据到新文档4.新文档进行加密5.新文档命名生成路径6.保存新加密的文档 四、PDF文档的解密1.用户输入模块2.前提准备2.文件解密…...
使用python脚本一个简单的搭建ansible集群
1.环境说明: 角色主机名ip地址控制主机server192.168.174.150受控主机/被管节点client1192.168.174.151受控主机/被管节点client2192.168.174.152 2.安装python和pip包 yum install -y epel-release yum install -y python python-pip 3.pip安装依赖库 pip in…...
【价值几十万的仿抖音直播电商系统源码共享】
当下,传统的图文电商模式已经走向没落,以抖音为首的直播电商模式备受用户追捧,它具有实时直播和强互动的特点,是传统电商所不具备的优势。而且,当前正是直播电商的红利期,很多主播和品牌商都通过直播电商业…...
对于vue3项目中使用shareReward还是shareReward.value的问题
问: // 设置当前有没有分享过 默认是false const shareReward ref(false) // 是否是第一次分享 来判断是否发放抽奖次数 function haveShare() { console.log(进入haveshare) if (userInfo.uid && shareReward.value) { rewardTimes(2).then((res) &…...
利用websockify将websocket通信转换成tcp
文章目录 前言websockifywebsockify 介绍websockify 使用 探索的过程提供基础TCP服务测试可用 实现Websocket客户端开始测试websockify功能再次启动websockify单独实现一个js版本websocket客户端 什么是VNC总结 前言 目前遇到一个问题,原本的服务都是利用tcp通信的…...
【LeetCode刷题】-- 163.缺失的区间
163.缺失的区间 class Solution {public List<List<Integer>> findMissingRanges(int[] nums, int lower, int upper) {List<List<Integer>> res new ArrayList<>();for(int num : nums){if(lower < num){res.add(Arrays.asList(lower,num -…...
ClickHouse为何如此之快
针对ClickHouse为什么很快的问题,基于对ClickHouse的基础概念之上,一般会回答是因为是列式存储数据库,同时也会说是使用了向量化引擎,所以快。上面两方面的解释也都能够站得住脚,但是依然不能够解释真正核心的原因。因…...
Avalonia中如何将View事件映射到ViewModel层
前言 前面的文章里面我们有介绍在Wpf中如何在View层将事件映射到ViewModel层的文章,传送门,既然WPF和Avalonia是两套不同的前端框架,那么WPF里面实现模式肯定在这边就用不了,本篇我们将分享一下如何在Avalonia前端框架下面将事件映射到ViewModel层。本章内容还是在上一节的…...
(第42天)DataGuard 搭建之使用 Duplicate 复制
环境准备 本文讲解 Oracle 19C 环境通过 Duplicate 在线复制搭建单机 Active DataGuard 的完整步骤,以下为测试环境信息: 角色主机名IP地址数据库版本实例名DB名DB_UNIQUE名services名TNS名sys密码主lucifer10.211.55.20019CoradboradboradboradbORADB_PRIoracle备luciferdg…...
LeetCode 0070. 爬楼梯:动态规划(递推)
【LetMeFly】70.爬楼梯:动态规划(递推) 力扣题目链接:https://leetcode.cn/problems/climbing-stairs/ 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#x…...
XMemcached network layout exception java.nio.channels.ClosedChannelException
java.nio.channels.ClosedChannelException 表示尝试在已关闭的通道上进行 I/O 操作,通常发生在网络连接意外关闭后尝试在关闭的通道上执行读取或写入操作。 XMemcached network layout exception 可能是由于 XMemcached 客户端在尝试与 Memcached 服务器通信时发生…...
记录 | vscode pyhton c++调试launch.json配置
下面提供 vscode 中 python 和 c 调试配置的 launch.json (好用,已用好几年,建议收藏) {// 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid830387&qu…...
Java入门基础:浅显易懂 死循环
文章目录 一、什么是死循环二、以fo循环示例三、如何避免死循环 一、什么是死循环 死循环就是循环语句的 循环布尔表达式 一直为true,没有终止循环的条件或者终止循环的条件根本不可能达成 二、以fo循环示例 /** 终止循环的条件根本不可能达成* 循环布尔表达式&a…...
LeetCode刷题--- 验证二叉搜索树
个人主页:元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客 http://t.csdnimg.cn/ZxuNL个人专栏:力扣递归算法题 http://t.csdnimg.cn/ZxuNL 【C】 http://t.csdnimg.cn/c9twt 前言:这个专栏主要讲述递归递归、搜索与回溯算法&#x…...
go-zero 开发入门-加法客服端示例
定义 RPC 接口文件 接口文件 add.proto 的内容如下: syntax "proto3"; package add;// 当 protoc-gen-go 版本大于 1.4.0 时需加上 go_package,否则编译报错“unable to determine Go import path for” option go_package "./add&qu…...
Python 快速入门——基础语法
python 的语法逻辑完全靠缩进,建议缩进 4 个空格。 如果是顶级代码,那么必须顶格书写,哪怕只有一个空格也会有语法错误。 下面示例中,满足 if 条件要输出两行内容,这两行内容必须都缩进,而且具有相同的缩进…...
EasyRecovery2024苹果电脑mac破解版安装包下载
EasyRecovery是一款操作安全、价格便宜、用户自主操作的非破坏性的只读应用程序,它不会往源驱上写任何东西,也不会对源驱做任何改变。它支持从各种各样的存储介质恢复删除或者丢失的文件,其支持的媒体介质包括:硬盘驱动器、光驱、…...
Git常用命令大全
1.强制推送(慎用,除非你认为其他冲突等可以丢弃 或者不是很重要) git push -- force2.创建文件等小命令 touch a // 创建一个a文件 echo 1234 >> a // 把1234这个内容放入a文件 cat a // 打开a文件 读取出a文件中的内容 mkdir test /…...
vue项目本地正常运行,打包到线上时无法访问js等资源
nginx配置错误,如: location /aaa/ {gzip on;gzip_static on;try_files $uri $uri/ /aaa/index.html;alias /home/ec2-user/data/aaa/;#这里必须以斜杆结束,否则就会报错}前端配置文件错误,如: config/index.js文件的b…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...
【Go语言基础【12】】指针:声明、取地址、解引用
文章目录 零、概述:指针 vs. 引用(类比其他语言)一、指针基础概念二、指针声明与初始化三、指针操作符1. &:取地址(拿到内存地址)2. *:解引用(拿到值) 四、空指针&am…...
AI语音助手的Python实现
引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...
JDK 17 序列化是怎么回事
如何序列化?其实很简单,就是根据每个类型,用工厂类调用。逐个完成。 没什么漂亮的代码,只有有效、稳定的代码。 代码中调用toJson toJson 代码 mapper.writeValueAsString ObjectMapper DefaultSerializerProvider 一堆实…...
