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)
这里我们可以使用相对简单的方案去实现这些功能
io.fabric8
kubernetes-client
4.9.1
可以看到kubernetes-client包含了client包以及model相关包。这里我们可以将对istio的操作需求分为两步
其中的步骤1,实现相对简单。完成配置后我们可以通过kubernetes-client原生支持的操作方式对istio的资源进操作增删改查的操作。实现代码后效果如下
步骤2完成后可以通过Builder类快速对资源对象完成拼装。实现代码后效果如下
接下来分别进行说明
创建资源对象模型实际就是根据资源对象的完整结构创建资源类,建议根据官网的对象结构进行创建(原因是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规则做修改时报错,无法修改导致原因就是这里),编写时同理可以参考原生类对应的类来编写
完成了基础数据模型编写后,需要开始调整client相关的类让集群客户端能够对我们新资源进行支持。这一支持首先就要提供Operations类,这个类主要用来提供请求调用时地址拼装的相关参数。
为了拼装这些参数首先我们要了解istio生成规则时真实的请求地址是什么。
进入kubernetes master节点
执行apply命令,补充-v=10|grephttp
这样我们就了解了istio规则真实请求的api地址,那么我们将这个真实地址通过Operations来拼装出来
apiGoupName、piGroupVersion、pural三个字段的对应关系如下所示,这样我们就可以定义Operations里的参数了。(Operations类如上所示,编写时依然可以参考原生资源)
这里字段的对应关系可以看下kubernetes-client中请求拼装的方法 —》OperationSupport.getRootUrl()方法。这里拼装请求地址时使用的方法
前置工作到这里就告一段落,开始对接客户端。下来几个类都是与客户端直接关联的适配器或者链接类
完成这些我们第一步的操作就完成了。
这一步进行的内容并不难,只是工作相对繁琐、重复、容易遗漏。需要格外注意,不然链路调用时会出现莫名其妙的问题定位较难。
进行这一步时可以使用原生对象进行参考来编写。
首先我们先明确一下,fabric中对资源对象定义了四种附属对象,分别用于不同的场景
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类是将链路调用对象与资源对象转化的对象,需要继承其对应的FluentImpl类
箭头指向的地方都是通过FluentImpl类中的方法对资源对象或Builder对象进行赋值的过程,Builder实际上最大的作用就是通过FluentImpl属性值向资源对象进行赋予。
Fluent接口需要为对应资源模块里所有的字段提供对齐操作的方法,根据每一个字段是数组是集合,是基础对象还是自定义对象进行不同的处理,这里做举例说明但不一定说全,可以参考原生对象的Fluent做参考。
List getRoutes();
SpecHttpRoute buildRoutes(int index);
List buildRoutes();
A withRoutes(List items);
A withRoutes(SpecHttpRoute... items);
Boolean hasRoutes();
SpecHttpRouteNested addNewRoutes();
SpecHttpRouteNested setNewRouteLike(SpecHttpRoute item);
SpecHttpRouteNested setNewRouteLike(int i , SpecHttpRoute item);
SpecHttpRouteNested editFirstRoutes();
SpecHttpRouteNested editLastRoutes();
SpecHttpRouteNested 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 items);
A removeFromRoutes(SpecHttpRoute... items);
A removeAllFromRoutes(Collection items);
SpecHttpRewrite buildRewrite();
A withRewrite(SpecHttpRewrite item);
Boolean hasRewrite();
SpecHttpFluent.SpecHttpRewriteNested withNewRewrite();
SpecHttpFluent.SpecHttpRewriteNested withNewRewriteLike(SpecHttpRewrite item);
SpecHttpFluent.SpecHttpRewriteNested editRewrite();
SpecHttpFluent.SpecHttpRewriteNested editOrNewRewrite();
SpecHttpFluent.SpecHttpRewriteNested editOrNewRewriteLike(SpecHttpRewrite item);
String getExact();
A withExact(String item);
(ps:这个类有点长就分两个图来截了。)
需要注意的是关于Nested内部接口的定义,继承Nested,下级Fluent对象泛型为当前Nested。
public interface SpecHttpRouteNested extends io.fabric8.kubernetes.api.builder.Nested, SpecHttpRouteFluent> {
public N and(); public N endRoute();
}
实现Fluent接口定义实现类,需要注意红线部分,以及箭头部分,Fluent中包含当前资源对象里的所有属性,但自定义类型的属性在FluentImpl中必须改成对应的Builder,每个方法的实现方式都相对简单,可以自己编写或者参考Deployment的即可
具体每一个类都可以参考原有的资源对象来编写,这里只做简述。这样完成了代码后能够实现如下的资源拼装能力。
这样可以完成对istio的操作客户端。并且可以完全复用kubernetes-client中的客户端以及其他原生资源对象的操作方法。开发量不大而且也不复杂,只是刚开始看的时候会被多个相似的类弄乱,理清类间的调用逻辑其实并不复杂。
这样的操作并不只针对istio的资源,如果公司自研了一些CRD,也可以使用这种方式进行封装提供操作客户端。
ps:再多说一句,istio赋予规则时需要开启自动注入,这里并不建议对命名空间开启自动注入。毕竟sidecar对资源占用不小是不争的事实。建议通过annotation注解对指定的deployment开启,尽可能节省资源(但这毕竟是业务层相关的逻辑了,按需取舍吧)
注入间的覆盖逻辑如下