Java操作Istio(kubernetes-client)
istio由于他对服务代码的完全不侵入性,以及kubernetes类云平台的普及 实用性越来越强。但istio至今也没有合适的dashboard,并且不同公司不同的业务场景导致使用istio做的事情也不相同.有偏向灰度发布的场景也有偏向对流量分布的场景。免不得需要使用业务代码操作istio的流量规则等配置。这里提供一个快速可行的操作方案实现方式在使用中我们更多是需要通过代码对istio进行控制,而
istio由于他对服务代码的完全不侵入性,以及kubernetes类云平台的普及 实用性越来越强。但istio至今也没有合适的dashboard,并且不同公司不同的业务场景导致使用istio做的事情也不相同.有偏向灰度发布的场景也有偏向对流量分布的场景。免不得需要使用业务代码操作istio的流量规则等配置。这里提供一个快速可行的操作方案
文章目录
实现方式
在使用中我们更多是需要通过代码对istio进行控制,而istio在kubernetes中的规则资源是以CRD的形式存在的。所以 使用原本的kubernetes客户端
, 扩展其资源对象
和 请求拼装逻辑
是可以实现对istio资源进操控的。
java代码的sdk中操作kubernetes集群时较多使用的是fabric的kubernetes-client
与kubernetes提供的client-java
,这里由于之前对kubernetes资源进操作时使用的客户端是fabric的kubernetes-client所以这里进行扩展时采用的也是kubernetes-client
而关于这个方案的实现方式,在github上也有人已经进行了完整的代码实现(
https://github.com/snowdrop/istio-java-api)
这里我们可以使用相对简单的方案去实现这些功能,扩展kubernetes-client的资源CRD,保留原生资源操作方法的前提下扩展对Istio资源进行支持
以下功能Demo已上传github,地址
https://github.com/llarao/istio-java.git
kubernetes-client扩展
- 创建项目,拉取项目包
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-client</artifactId>
<version>4.9.1</version>
</dependency>
可以看到kubernetes-client包含了client包以及model相关包。这里我们可以将对istio的操作需求分为两步
- 提供对istio的操作支持
- 提供对istio资源对象的链式拼装能力
其中的步骤1,实现相对简单。完成配置后我们可以通过kubernetes-client原生支持的操作方式对istio的资源进操作增删改查的操作。实现代码后效果如下
步骤2完成后可以通过Builder类快速对资源对象完成拼装。实现代码后效果如下
接下来分别进行说明
提供对istio资源的操作支持
- 增加资源对象模型类
- 增加资源对象Operations类
- 创建客户端连接类
增加资源对象模型类
创建资源对象模型实际就是根据资源对象的完整结构创建资源类,建议根据官网的对象结构进行创建(原因是istio版本更新相对频繁 ,对象结构虽然没有大变化但还是有新的功能字段的)。这里以VirtualService举例,根据官方的结构表格以及字段类型进行对象的创建
https://istio.io/docs/reference/config/networking/virtual-service/
模型创建,模型创建时需要注意几个点,首先VirtualServic最外层需要实现HashMetadata接口。
内层需要实现KubernetesResource接口(如VirtualServiceSpec)
这里需要额外注意一下,资源的kind是VirtualService。 apiVersion是networking.istio.io/v1alpha3
资源对象创建完成后还需要额外创建两个对象
分别是List对象与Doneable对象
List对象是批量获取时返回的对象类型(如client.apps().deployments().innamespace().list()),注意kind和apiversion的调整,编写时可以参考kubernetes原生对象的类来编写(如DeploymentList)。
Doneable对象中提供的done方法会在触发replace时使用(最开始这里忘记写了,后来需要对vs规则做修改时报错,无法修改导致原因就是这里),编写时同理可以参考原生类对应的类来编写
增加资源对象Operations类
完成了基础数据模型编写后,需要开始调整client相关的类让集群客户端能够对我们新资源进行支持。这一支持首先就要提供Operations类,这个类主要用来提供请求调用时地址拼装的相关参数。
为了拼装这些参数首先我们要了解istio生成规则时真实的请求地址是什么。
进入kubernetes master节点
执行apply命令,补充-v=10|grephttp
这样我们就了解了istio规则真实请求的api地址,那么我们将这个真实地址通过Operations来拼装出来
apiGoupName、piGroupVersion、pural三个字段的对应关系如下所示,这样我们就可以定义Operations里的参数了。(Operations类如上所示,编写时依然可以参考原生资源)
这里字段的对应关系可以看下kubernetes-client中请求拼装的方法 —》OperationSupport.getRootUrl()方法。这里拼装请求地址时使用的方法
创建客户端连接类
前置工作到这里就告一段落,开始对接客户端。下来几个类都是与客户端直接关联的适配器或者链接类
- 基础客户端接口,接口用于对接上一步我们完成的Operations类,这里注意需要继承Client接口
- Istio客户端类,实现了基础客户端接口,可以看到这里通过Operations的构造方法反悔了Operations控制器
- 集群客户端适配器,创建客户端后需要将这个适配器注入到扩展适配器列表中
- 集群操作客户端接口,这里我们继承kubernetesClient,保持对原生资源的支持
- 集群操作客户端实现类,这里一定要注意进行Adapters.register操作,将第三部定义的适配器注入到扩展适配列表中,不然客户端反射操作时会抛出异常。让集群客户端初始化时就进行这个注入过程就好。
完成这些我们第一步的操作就完成了。
提供对istio资源对象的链式拼装能力
这一步进行的内容并不难,只是工作相对繁琐、重复、容易遗漏。需要格外注意,不然链路调用时会出现莫名其妙的问题定位较难。
进行这一步时可以使用原生对象进行参考来编写。
首先我们先明确一下,fabric中对资源对象定义了四种附属对象,分别用于不同的场景
- Builder
- Fluent
- FluentImpl
- Nested
如下
但不是每个资源对象只有这四个类,是每一层对象有这些类,以VS举例
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: bookinfo
spec:
hosts:
- "*"
gateways:
- bookinfo-gateway
http:
- match:
- uri:
exact: /productpage
- uri:
prefix: /static
- uri:
exact: /login
- uri:
exact: /logout
- uri:
prefix: /api/v1/products
route:
- destination:
host: productpage
port:
number: 9080
我们创建对象模型时需要根据规则的层级去包装对象比如Virtualservice中包含Spec对象,Spec对象中包含Hosts对象包含Http对象,每一层我们需要为其定义这些附属对象。这些才是工作量,这里初步算了一下单VS的对象差不多就有40多个(如果打算编写的话,建议从Gateway资源开始做,结构简单)
接下来我们对每一种对象进行说明
Builder
Builder类是将链路调用对象与资源对象转化的对象,需要继承其对应的FluentImpl类
箭头指向的地方都是通过FluentImpl类中的方法对资源对象或Builder对象进行赋值的过程,Builder实际上最大的作用就是通过FluentImpl属性值向资源对象进行赋予。
Fluent接口
Fluent接口需要为对应资源模块里所有的字段提供对齐操作的方法,根据每一个字段是数组是集合,是基础对象还是自定义对象进行不同的处理,这里做举例说明但不一定说全,可以参考原生对象的Fluent做参考。
- 关于跳转,我们进行对象的链式拼装时需要向下级跳转,完成下级拼装后再会跳的过程如下,这一过程是需要通过自定义Nested内部类来实现的
- 关于资源类型
- 数组: 建议提供以下方法,如果是自定义资源对象需要包含中间部分引入Nested内部类向下级跳转的方法
List<SpecHttpRoute> getRoutes();
SpecHttpRoute buildRoutes(int index);
List<SpecHttpRoute> buildRoutes();
A withRoutes(List<SpecHttpRoute> items);
A withRoutes(SpecHttpRoute... items);
Boolean hasRoutes();
SpecHttpRouteNested<A> addNewRoutes();
SpecHttpRouteNested<A> setNewRouteLike(SpecHttpRoute item);
SpecHttpRouteNested<A> setNewRouteLike(int i , SpecHttpRoute item);
SpecHttpRouteNested<A> editFirstRoutes();
SpecHttpRouteNested<A> editLastRoutes();
SpecHttpRouteNested<A> editRoutes(int i);
A addNewRoutes(SpecHttpRoute specHttp);
A setToRoutes(int index, SpecHttpRoute item);
A addToRoutes(int index, SpecHttpRoute item);
A addToRoutes(SpecHttpRoute... items);
A addAllToRoutes(Collection<SpecHttpRoute> items);
A removeFromRoutes(SpecHttpRoute... items);
A removeAllFromRoutes(Collection<SpecHttpRoute> items);
- 自定义资源类型,自定义资源对象需要包含中间部分引入Nested内部类向下级跳转的方法
SpecHttpRewrite buildRewrite();
A withRewrite(SpecHttpRewrite item);
Boolean hasRewrite();
SpecHttpFluent.SpecHttpRewriteNested<A> withNewRewrite();
SpecHttpFluent.SpecHttpRewriteNested<A> withNewRewriteLike(SpecHttpRewrite item);
SpecHttpFluent.SpecHttpRewriteNested<A> editRewrite();
SpecHttpFluent.SpecHttpRewriteNested<A> editOrNewRewrite();
SpecHttpFluent.SpecHttpRewriteNested<A> editOrNewRewriteLike(SpecHttpRewrite item);
- 普通资源类型
String getExact();
A withExact(String item);
(ps:这个类有点长就分两个图来截了。)
需要注意的是关于Nested内部接口的定义,继承Nested,下级Fluent对象泛型为当前Nested。
public interface SpecHttpRouteNested<N> extends io.fabric8.kubernetes.api.builder.Nested<N>, SpecHttpRouteFluent<SpecHttpRouteNested<N>> {
public N and(); public N endRoute();
}
FluentImpl
实现Fluent接口定义实现类,需要注意红线部分,以及箭头部分,Fluent中包含当前资源对象里的所有属性,但自定义类型的属性在FluentImpl中必须改成对应的Builder,每个方法的实现方式都相对简单,可以自己编写或者参考Deployment的即可
- 关于NestedImpl:在FluentImpl中需要对Fluent中定义的Nested进行实现。需要注意的点已经标注了红线与箭头
NestedImpl需要继承对应下级的FluentImpl,需要实现同级Fluent的Nested
包含了and和end方法,实际end方法就是在调用and方法。实现的能力是根据下级中赋予的属性生成对应的Builder,赋予本级对应的Builder属性中。
具体每一个类都可以参考原有的资源对象来编写,这里只做简述。这样完成了代码后能够实现如下的资源拼装能力。
END
这样可以完成对istio的操作客户端。并且可以完全复用kubernetes-client中的客户端以及其他原生资源对象的操作方法。开发量不大而且也不复杂,只是刚开始看的时候会被多个相似的类弄乱,理清类间的调用逻辑其实并不复杂。
这样的操作并不只针对istio的资源,如果公司自研了一些CRD,也可以使用这种方式进行封装提供操作客户端。
ps:再多说一句,istio赋予规则时需要开启自动注入,这里并不建议对命名空间开启自动注入。毕竟sidecar对资源占用不小是不争的事实。建议通过annotation注解对指定的deployment开启,尽可能节省资源(但这毕竟是业务层相关的逻辑了,按需取舍吧)
注入间的覆盖逻辑如下
更多推荐
所有评论(0)