大纲
原文地址 https://blog.csdn.net/liuyij3430448/article/details/129534732
k8s中 “Pod Service Deployment”等都是一种资源,k8s的作用就是管理这些资源。(当资源创建,更新,删除后会有对应的操作)。
单纯的资源是没有任何意义的,需要配合对应的Controller[控制器] 来管理资源
例如
Pod 的扩容 就是使用ReplicationController等实现的,单纯的Pod只是一个存储在Etcd中的数据
当k8s 内置的资源无法满足我们的需求时,可以使用k8s提供的CustomResourceDefinition + 自定义controller实现自己的资源与资源控制
有了自定义的资源(CRD),但是没有相应的Controller对其进行操作,那这些资源也就只能简单的存储到k8s集群中了(etcd)。
因此还需要开发相应的Controller来对相应的CRD进行监听、处理业务逻辑
Controller就是一遵循k8s相关规范,然后连接到k8s master API 并监听CRD事件的进程(CRD的事件一般就是指:Add, Update, Delete)
Operator是一个更加复杂的Controller,还可以实现一些运维操作
Operator 官方资料 https://kubernetes.io/zh-cn/docs/concepts/extend-kubernetes/operator/
Operator 就是一种 Controller
Kubernetes 的 Operator 模式概念允许你在不修改 Kubernetes 自身代码的情况下,
通过为一个或多个自定义资源关联控制器来扩展集群的能力。
简单讲 Operator = 资源(可以是k8s内置资源或者自定义的CRD) + controller + 一些运维操作
例如本文中最后的实例
会创建一个数据库表监控CRD资源,在创建一个自己的controller,监控表的创建,保持指定数量行,删除表。 这一系列操作就是一个 Operator
要使用自定义资源,就需要先在k8s集群中定义这个资源,定义资源就需要使用CustomResourceDefinition
注意:要使用CustomResourceDefinition, Kubernetes 服务器版本必须不低于版本 1.16.
官方文档 https://kubernetes.io/zh-cn/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/
以一个自定义的MyCrdTest 资源为例 CustomResourceDefinition.yaml文件中需要定 资源的名称,资源的组,资源的版本 以及spec等内容
# 定义自定义的 MyCrdTest 资源
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:# 名字必需与下面的 spec 字段匹配,并且格式为 '<名称的复数形式>.<组名>'name: mycrdtests.liuyjiang.mycrdtest.com
spec: # 组名称,用于 REST API: /apis/<组>/<版本>group: liuyjiang.mycrdtest.comnames:# 名称的复数形式,用于 URL:/apis/<组>/<版本>/<名称的复数形式>plural: mycrdtests# 名称的单数形式,作为命令行使用时和显示时的别名singular: mycrdtest# kind 通常是单数形式的驼峰命名(CamelCased)形式。你的资源清单会使用这一形式。kind: MyCrdTest# shortNames 允许你在命令行使用较短的字符串来匹配资源shortNames:- mct# 可以是 Namespaced 或 Cluster scope: Namespaced versions:- name: v1# 每个版本都可以通过服务标志启用/禁用。served: true# 必须将一个且只有一个版本标记为存储版本。storage: trueschema:openAPIV3Schema:type: objectproperties: #自定义CRD中的specspec:type: objectproperties:# 自定义的资源spec 中的属性 mymsgmymsg: type: string# 自定义的资源spec 中的属性 myarray myarray: type: arrayitems:type: string # 自定义的资源spec 中的属性 mynumber mynumber: type: integer#自定义CRD中的status status:type: object properties: mystatus: type: stringmyip: type: string
使用命令kubectl apply -f crd-simple.yaml 创建CRD 相关文件见 《/yaml/crd-simple.yaml》
这样自定义资源的定义就有了,接下来就可以创建自定义资源MyCrdTest
上一步中创建了自定义资源定义CustomResourceDefinition 这样就可以基于定义的内容创建自定义资源MyCrdTest
my-crd-test.yaml 文件内容如下
# 这个就是对应crd-simple.yaml 中定义的
# api使用 crd中定义的组名称(group)+ 版本号(versions name)
apiVersion: "liuyjiang.mycrdtest.com/v1"
# kind使用 crd中定义的kind名称
kind: MyCrdTest
metadata:name: test-001
# 对应 crd-simple.yaml schema中的spec 配置
spec:mymsg: "hello world"mynumber: 3myarray: - aaabbbccc
# 对应 crd-simple.yaml schema中的status 配置
status:mystatus: "running"myip: "192.168.0.211"
使用kubectl apply -f my-crd-test.yaml 创建自定义资源MyCrdTest
使用kubectl get 【资源】查看创建的资源
可以自定义 kubectl get 显示字段
自定义kubectl get 显示字段各式如下
kubectl get [资源类型] [资源名称] -o custom-columns=【显示的的字段】:【.资源配置】
例如yaml中配置
metadata:name: test-001
spec:mymsg: "hello world"
status:mystatus: "running"myip: "192.168.0.211"
使用以下命令查看内容 注意.号
kubectl get mct -o custom-columns=NAME:.metadata.name,MSG:.spec.mymsg,IP:.status.myip,STATUS:.status.mystatus
也可以使用模板文件
kubectl get mct -o custom-columns-file=tpl.txt模板文件tpl.txt内容为
NAME MSG IP STATUS
metadata.name spec.mymsg status.myip status.mystatus
这样一个自定义的资源就创建完成了,注意此时资源只存在Etcd中并没有对应的控制器来管理资源
k8s 提供了大量的 client端库来操作集群,官方资料: https://kubernetes.io/zh-cn/docs/reference/using-api/client-libraries/
这里使用fabric8io/kubernetes-client来操作k8s集群,quarkus中就是使用fabric8io/kubernetes-client这个库
fabric8io/kubernetes-client 官方地址 https://github.com/fabric8io/kubernetes-client
关于quarkus可以参考
《quarkus 搭建与基础开发环境配置总结》
《quarkus 生产环境与k8s集成总结》
使用fabric8io/kubernetes-client 需要在项目中添加依赖
io.fabric8 kubernetes-client 6.4.1
在maven pom.xml中加入依赖,就可以使用kubernetes-client 注意:可能还需要引入 jackson相关的依赖
以下方式是在集群外(程序未部署到k8s集群中)创建KubernetesClient
这样就可以拿到KubernetesClient,然后对Pod Service Deployment进行操作private String caCertData = "LS0tLS1C***=";
private String clientCertData = "LS0tLS****LQo=";
private String clientKeyData = "LS0tLS****=";
private String host = "https://192.168.0.160:6443";Config config = new ConfigBuilder().withMasterUrl(host).withCaCertData(caCertData).withClientCertData(clientCertData).withClientKeyData(clientKeyData).withDefaultNamespace().build();
//集群外创建client 需要指定 host 证书 私钥等
KubernetesClient client = new KubernetesClientBuilder().withConfig(config).build();
以上代码中的caCertData clientCertData clientKeyData host
可以使用k8s集群master节点 /root/.kube/config中的配置
//以下方式是在集群内(程序部署在k8s集群中)创建KubernetesClient
KubernetesClient client = new KubernetesClientBuilder().build();
不需要配置 host 证书 私钥等
然后就可以使用client
client.pods()..
client.services()..
来操作k8s集群中的资源了
此时我们只需要在自己的java springboot项目中,使用kubernetes-client 去监听自定义的资源的状态并对状态做对应的响应操作即可
对应的就是watch操作
后续实例中会详解讲解 watch操作 监控资源状态变更
把java项目部署到k8s集群中,是一个很好的选择。但是对于我们自定义的资源默认的ServiceAccount中是没有对应的定义的,所以需要创建ServiceAccount,并让ServiceAccount与ClusterRole 绑定。这样java程序能够在集群内部访问k8s api接口
参考资料《快速上手k8s权限管理 立即掌握User Role RoleBinding kubeconfig 实战教程》
相关的权限配置如下
# 创建ServiceAccountapiVersion: v1kind: ServiceAccountmetadata:name: crdtest-serviceaccountnamespace: crd-tm-testlabels:myk8s.crd-test.com: crd-tm-test---# 需要操作自定义的 CRD TableMonitor 需要配置对TableMonitor资源的操作权限apiVersion: rbac.authorization.k8s.io/v1beta1kind: ClusterRolemetadata:name: crdtest-clusterrolerules:- apiGroups:- "myk8s.crd-test.com"resources: - tablemonitorsverbs:- list- watch---# 让ServiceAccount 与 ClusterRole绑定
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:name: crdtest-cluster-role-binding
roleRef:apiGroup: rbac.authorization.k8s.iokind: ClusterRolename: crdtest-clusterrole
subjects:- kind: ServiceAccountname: crdtest-serviceaccountnamespace: crd-tm-test
本例子为了简洁并利于理解,使用数据库中的表作为一个需要管理的资源。
类似Pod管理的是容器,我们创建一个TableMonitor资源来管理表
架构设计入下图
数据库表监控Operator的业务逻辑是
创建一个TableMonitor资源的定义 CRD yaml文件 crd-table-monitor.yaml 内容如下 (见my-docker-demo-k8s-operator/yaml/crd-table-monitor.yaml)
# 定义自定义的TableMonitor 资源
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:# 名字必需与下面的 spec 字段匹配,并且格式为 '<名称的复数形式>.<组名>'name: tablemonitors.liuyijiang.crd-tm.comspec: # 组名称,用于 REST API: /apis/<组>/<版本>group: liuyijiang.crd-tm.comnames:# 名称的复数形式,用于 URL:/apis/<组>/<版本>/<名称的复数形式>plural: tablemonitors# 名称的单数形式,作为命令行使用时和显示时的别名singular: tablemonitor# kind 通常是单数形式的驼峰命名(CamelCased)形式。你的资源清单会使用这一形式。kind: TableMonitor# shortNames 允许你在命令行使用较短的字符串来匹配资源shortNames:- tmscope: Namespaced versions:- name: v1# 每个版本都可以通过服务标志启用/禁用。served: true# 必须将一个且只有一个版本标记为存储版本。storage: trueschema:openAPIV3Schema:type: objectproperties: spec:type: objectproperties:# 自定义的资源spec 中的属性 dbUserName 需要连接的数据库的账户名dbUserName: type: string# 自定义的资源spec 中的属性 dbUserPassword 需要连接的数据库的密码dbUserPassword: type: string# 自定义的资源spec 中的属性 dbUrl 需要连接的数据库的urldbUrl: type: string# 自定义的资源spec 中的属性 tableName 需要操作的表名称tableName: type: string # 自定义的资源spec 中的属性 tableColumns 注意类型是 array 表中的字段tableColumns: type: arrayitems:type: string # 自定义的资源spec 中的属性 dataNum 表中保存的数据行数 dataNum: type: integerstatus:type: object properties: # 恢复次数 每调整一次表行数就加1recoverNum: type: integer# 运行数据库ip hostIp: type: string# 运行状态 runingStatus: type: string
kubectl apply -f crd-table-monitor.yaml 创建CRD
程序就是一个简单的springboot 其中fabric8/kubernetes-client版本号6.4.1 项目主要结构如下
TableMonitorSpec.java TableMonitorStatus.java 都是一个实现了KubernetesResource接口的POJO类
对应crd-table-monitor.yaml中配置的spec 和 status 相关字段
TableMonitorSpec.java
TableMonitorStatus.java
TableMonitor.java 则是对应的TableMonitor资源 注意组和版本号
TableMonitorList.java 是一个继承了DefaultKubernetesResourceList的类,用于保存多个TableMonitor
TableService.java的作用就是一个数据库操作的类,用于创建表,删表,加数据,删数据等
使用spring提供的jdbctemplate来操作数据库,例如以下是一个建表操作
数据库操作的类很简单 就不多赘述
TableMonitorListener.java 是本Operator 程序的核心类
代码如下:
package com.k8s.operator.listener;import java.util.List;
import java.util.concurrent.ConcurrentHashMap;import javax.annotation.PostConstruct;import org.springframework.stereotype.Component;import com.k8s.operator.crd.TableMonitor;
import com.k8s.operator.crd.TableMonitorList;
import com.k8s.operator.crd.TableMonitorStatus;
import com.k8s.operator.database.DataTable;
import com.k8s.operator.database.TableService;import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import io.fabric8.kubernetes.client.Watch;
import io.fabric8.kubernetes.client.Watcher;
import io.fabric8.kubernetes.client.WatcherException;
import io.fabric8.kubernetes.client.dsl.MixedOperation;
import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
import io.fabric8.kubernetes.client.dsl.Resource;/*** 核心就是这个TableMonitorListener* * TableMonitorListener watch 自定义的TableMonitor资源 处理 1 新建 2 删除 3 数据是否满足指定* * @author liuyijiang**/
@Component
public class TableMonitorListener {/*** TableMonitor 控制器*/private MixedOperation> tableMonitor;/*** 数据库操作类*/private TableService tableService = new TableService();/*** 保存所有的k8s集中部署的TableMonitor*/private ConcurrentHashMap CRDS = new ConcurrentHashMap<>();/*** 初始化*/@PostConstructpublic void initListerner() {System.out.println("start TableMonitorListener");/*** 初始化客户端*/init();/*** 监听表的状态变化*/watchTable();/*** 监听资源的变化*/watchCRD();}/*** 初始化KubernetesClient 客户端*/private void init() {System.out.println("init KubernetesClientBuilder !!!!!!!!!!!!!!!!");KubernetesClient client = new KubernetesClientBuilder().build();/*** 获取自定义资源的客户端*/NonNamespaceOperation> tableMonitorNonNamespace = client.resources(TableMonitor.class, TableMonitorList.class);//使用default 命名空间tableMonitorNonNamespace = ((MixedOperation>) tableMonitorNonNamespace).inNamespace("default");tableMonitor = (MixedOperation> ) tableMonitorNonNamespace;}/*** 监控表 保证表中的数据始终保持在 dataNum配置的值*/private void watchTable() {new Thread(() -> {System.out.println("start watchTable ");while (true) {for (String id : CRDS.keySet()) {TableMonitor tm = CRDS.get(id);/*** 检查是否存在表*/System.out.println("check if table is not EXISTS");tableService.createTable(tm);/*** 查询出TableMonitor 表中存在的数据*/int num = tableService.getTableDataCount(tm);if (num != tm.getSpec().getDataNum()) {// 小于定义的值if (num < tm.getSpec().getDataNum()) {System.out.println("### data too little !!!!!!!!!!!!!!!!");int flag = tm.getSpec().getDataNum() - num; // 获得少了的量for (int i = 0; i < flag; i++) {tableService.insertTableData(tm); // 创建少的数据}} else { // 大于定义的值System.out.println("### data too more !!!!!!!!!!!!!!!!");int flag = num - tm.getSpec().getDataNum(); // 获得多了的量List list = tableService.listTableData(tm);for (int i = 0; i < flag; i++) {tableService.deleteTableData(tm, list.get(i)); // 删除多的数据}}//更新资源状态 添加一次恢复次数tm.getStatus().setRecoverNum(tm.getStatus().getRecoverNum() + 1);tableMonitor.resource(tm).createOrReplace();}}// 10秒钟监控一次try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}}}).start();}public void watchCRD() {/*** 监控资源*/Watch watch = tableMonitor.watch(new Watcher() {@Overridepublic void eventReceived(Action action, TableMonitor resource) {// 新增if (action.equals(Action.ADDED)) { // 新增资源System.out.println("===============create kind=================" + resource);// 建表tableService.createTable(resource);CRDS.put(resource.getMetadata().getName(), resource); // 资源放入到map中//添加资源状态resource.setStatus(new TableMonitorStatus());resource.getStatus().setRuningStatus("running");resource.getStatus().setRecoverNum(0);String ip = resource.getSpec().getDbUrl().substring(13,26);resource.getStatus().setHostIp(ip);//更新资源状态tableMonitor.resource(resource).createOrReplace();} else if (action.equals(Action.DELETED)) { // 删除资源System.out.println("===============delete kind=================" + resource);tableService.dropTable(resource);CRDS.remove(resource.getMetadata().getName());}}@Overridepublic void onClose(WatcherException cause) {System.out.println(cause);}});}}
以上便是 java实现Operator 的基本代码
主要事项
关于k8s权限 角色 可以参考《快速上手k8s权限管理 立即掌握User Role RoleBinding kubeconfig 实战教程》
注意:所有相关配置文件保存在 /my-docker-demo-k8s-operator/yaml/operrator相关 Docker相关
Dockerfile内容如下
FROM ascdc/jdk8
VOLUME ["/data/service/logs","/data/service/tmp"]
WORKDIR "/data/service"
EXPOSE 5533
COPY my-docker-demo-k8s-operator.jar my-docker-demo-k8s-operator.jar
ENTRYPOINT ["nohup","java","-jar","my-docker-demo-k8s-operator.jar","&"]
Dockerfile参考 《Dockerfile文件总结》
docker build -t tm-controller .
docker tag tm-controller registry.cn-hangzhou.aliyuncs.com/jimliu/tm-controller
docker push registry.cn-hangzhou.aliyuncs.com/jimliu/tm-controller
此时镜像创建完毕
springboot k8s Operator部署文件deploy.yml如下 (也可以不部署在k8s中 注意kubernetes-clinet的创建方式)
# 创建命名空间
apiVersion: v1
kind: Namespace
metadata:name: crd-tm-testlabels:liuyijiang.crd-tm.com: crd-tm-test---# 创建阿里云私库秘钥
apiVersion: v1
kind: Secret
metadata:name: myaliyunsecret-crdtmtestnamespace: crd-tm-test labels:liuyijiang.crd-tm.com: crd-tm-test
data:.dockerconfigjson: eyJhdXRocyI6eyJyZWdpc3RyeS5.....省略换成自己的
type: kubernetes.io/dockerconfigjson---# 创建ServiceAccount 用于 程序中访问自定义资源
apiVersion: v1
kind: ServiceAccount
metadata:name: crdtest-serviceaccountnamespace: crd-tm-testlabels:liuyijiang.crd-tm.com: crd-tm-test
imagePullSecrets:- name: myaliyunsecret-crdtmtest---# 需要操作自定义的 CRD TableMonitor 需要配置对TableMonitor资源的操作权限
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:name: crdtest-clusterrolelabels:liuyijiang.crd-tm.com: crd-tm-test
rules:- apiGroups:- "liuyijiang.crd-tm.com" #apiGroups crd-table-monitor.yaml中定义的 groupresources: - tablemonitors #只操作TableMonitor资源 注意为crd-table-monitor.yaml中配置的复数名称verbs: #可以操作的类型- list- watch- get- create- delete - update - edit - exec---# 让ServiceAccount 与 ClusterRole绑定
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:name: crdtest-cluster-role-bindinglabels:liuyijiang.crd-tm.com: crd-tm-test
roleRef:apiGroup: rbac.authorization.k8s.iokind: ClusterRolename: crdtest-clusterrole
subjects:- kind: ServiceAccountname: crdtest-serviceaccount namespace: crd-tm-test ---# 创建项目容器pod
apiVersion: v1
kind: Pod
metadata: name: tm-controller-podnamespace: crd-tm-test labels: liuyijiang.crd-tm.com: crd-tm-testspec: # 注意指定serviceAccountserviceAccountName: crdtest-serviceaccountrestartPolicy: Alwayscontainers: - image: registry.cn-hangzhou.aliyuncs.com/jimliu/tm-controller:latestname: tm-controller-runtime
执行 kubectl apply -f deploy.yml 部署operator
执行 kubectl logs -f tm-controller-pod -n crd-tm-test 查看日志 注意-n 命名空间
到此 springboot k8s Operator部署完成
现在创建一个资源来测试 springboot k8s Operator
资源创建文件 tm1.yaml 内容如下
# api使用 crd中定义的组名称
apiVersion: "liuyijiang.crd-tm.com/v1"
# kind使用 crd中定义的kind名称
kind: TableMonitor
metadata:name: test-001
spec:dbUserName: "root"dbUserPassword: "123456"dbUrl: "jdbc:mysql://192.168.0.206:3306/t0003"tableName: "tb_my_test"dataNum: 3tableColumns: - aaabbbccc
首先可以看到t0003数据库中目前还没有tb_my_test 这张表
kubectl apply -f tm1.yaml 创建资源
kubectl apply -f tm1.yaml
kubectl get tm -o custom-columns-file=tpl.txt
springboot k8s Operator 日志出现建表操作
删除表中的一条数据
10秒后 表数据恢复到dataNum指定的值3
同时恢复次数增加1次
执行 kubectl delete -f tm1.yaml 删除TableMonitor资源
原文地址 https://blog.csdn.net/liuyij3430448/article/details/129534732
上一篇:动态规划之子序列问题总结