Kubernetes 官方 java 客户端之七:patch 操作
欢迎访问我的 GitHub
这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
概览
本文是《Kubernetes 官方 java 客户端》系列的第七篇,以下提到的 java 客户端都是指 client-jar.jar;
本文主要内容是通过 java 客户端发起 patch 请求,用来修改已有资源;
接下来会对 kubernetes 的 patch 做一些介绍,由于咱们这里的重点还是 java 客户端的 patch 操作,因此不会对 patch 的原理和概念展开太多,仅做最基本的说明能即可;
本文内容
这是篇万字长文,所以一开始就要明确本文的核心内容:开发一个 SpringBoot 应用并部署在 kubernetes 环境,这个应用通过 kubernetes 的 java 客户端向 API Server 发请求,请求内容包括:创建名为 test123 的 deployment、对这个 deployment 进行 patch 操作,如下图:
接下来先了解一些 kubernetes 的 patch 相关的基本知识;
关于 patch
是对各种资源的增删改查是 kubernetes 的基本操作;
对于修改操作,分为 Replace 和 Patch 两种;
Replace 好理解,就是用指定资源替换现有资源,replace 有个特点,就是 optimistic lock 约束(类似与转账操作,先读再计算再写入);
Patch 用来对资源做局部更新,没有 optimistic lock 约束,总是最后的请求会生效,因此如果您只打算修改局部信息,例如某个属性,只要指定属性去做 patch 即可(如果用 Replace,就只能先取得整个资源,在本地修改指定属性,再用 Replace 整体替换);
更详细的信息请参考下图,来自官方文档,地址:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/
patch 的四种类型
kubernetes 的 patch 一共有四种:
json patch:在请求中指定操作类型,例如:add、replace,再指定 json 内容进行操作 z,请参考:https://tools.ietf.org/html/rfc6902
merge patch:合并操作,可以提交整个资源的信息,与现有信息进行合并后生效,也可以提交部分信息用于替换,请参考:https://tools.ietf.org/html/rfc7386
strategic merge patch:json patch 和 merge patch 都遵守 rfc 标准,但是 strategic merge patch 却是 kubernetes 独有的,官方中文文档中称为策略性合并,也是 merge 的一种,但是真正执行时 kubernetes 会做合并还是替换是和具体的资源定义相关的(具体策略由 Kubernetes 源代码中字段标记中的 patchStrategy 键的值指定),以 Pod 的 Container 为例,下面是其源码,红框中显示其 Container 节点的 patchStrategy 属性是 merge,也就是说如果您提交了一份 strategic merge patch,里面的内容是关于 Pod 的 Container 的,那么原有的 Container 不会被替换,而是合并(例如以前只有 nginx,提交的 strategic merge patch 是 redis,那么最终 pod 下会有两个 container:nginx 和 redis):
通过源码查看资源的 patchStrategy 属性是很麻烦的事情,因此也可以通过 Kubernetes API 文档来查看,如下图,地址是:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#podspec-v1-core
第四种是 apply patch:主要是指 kubernetes 1.14 版本开始的 server-side apply,由 APIServer 做 diff 和 merge 操作,很多原本易碎的现象都得到了解决(例如 controller 和 kubectl 都在更新),另外要格外注意的是:1.14 版本默认是不开启 server-side apply 特性的,具体的开启操作在下面会详细讲解;
以上是对 kubernetes 四种 patch 的简介,讲得很浅,如果您想深入了解每种 patch,建议参阅官方资料,接下来咱们聚焦 java 客户端对这些 patch 能力的实现;
源码下载
如果您不想编码,可以在 GitHub 下载所有源码,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos):
这个 git 项目中有多个文件夹,本章的应用在 kubernetesclient 文件夹下,如下图红框所示:
实战步骤概述
接下来会创建一个 springboot 工程(该工程是 kubernetesclient 的子工程),针对四种 patch 咱们都有对应的操作;
每种 patch 都会准备对应的 json 文件,提前将这些文件的内容保存在字符串变量中,在程序里用 kubernetes 客户端的 patch 专用 API,将此 json 字符串发送出去,流程简图如下:
编码完成后,就来动手验证功能,具体操作如下:
部署名为 patch 的 deployment,这里面是咱们编码的 SpringBoot 工程,提供多个 web 接口;
浏览器访问/patch/deploy 接口,就会创建名为 test123 的 deployment,这个 deployment 里面是个 nginx,接下来的 patch 操作都是针对这个名为 test123 的 deployment;
浏览器访问 test123 的 nginx 服务,确保部署成功了;
浏览器访问/patch/json 接口,该接口会修改 test123 的一个属性:terminationGracePeriodSeconds
浏览器访问/patch/fullmerge 接口,该接口会提交全量 merge 请求,修改内容很少,仅增加了一个 label 属性;
接下来是对比 merge patch 和 strategic merge patch 区别,分别访问/patch/partmerge 和/patch/strategic 这两个接口,其实它们操作的是同一段 patch 内容(一个新的 container),结果 merge patch 会替换原有的 continer,而 strategic merge patch 不会动原有的 container,而是新增 container,导致 test123 这个 deployment 下面的 pod 从一个变为两个;
最后是 apply yaml patch,访问接口/patch/apply,会将 nginx 容器的标签从 1.18.0 改为 1.19.1,咱们只要在浏览器访问 test123 里面的 nginx 服务就能确定是否修改生效了;
准备工作
准备工作包括创建工程、编写辅助功能代码、初始化代码等:
打开《Kubernetes官方java客户端之一:准备 》一文创建的项目 kubernetesclient,新增名为 patch 的子工程,pom.xml 内容如下:
编写一个辅助类 ClassPathResourceReader.java,作用是读取 json 文件的内容作为字符串返回:
接下来新建本篇文章的核心类 PatchExample.java,首先这个类中有 main 方法,整个应用从这里启动:
接下来有两个常量定义,分别是 kubernetes 环境里用来测试的 deployment 名称,以及 namespace 名称:
然后定义几个字符串变量,执行 patch 操作时用到的 json 内容都保存到这些字符串变量中:
在 resources 文件夹中放入 json 文件,稍后的初始化代码会将这些文件读取到字符串变量中,如下图,这些 json 文件的内容稍后会详细说明:
编写初始化代码(通过 PostConstruct 注解实现),主要是客户端配置,还有将 json 文件的内容读出来,保存到刚刚准备的字符串变量中:
以上就是准备工作
创建服务
1. 首先要开发一个部署的接口,通过调用此接口可以在 kubernetes 环境部署一个 deployment:
2. 部署服务的 path 是/patch/deploy,代码如下,可见部署 deployment 的代码分为三步:创建 api 实例、用字符串创建 body 实例、把 body 传给 api 即可:
body 实例用到的 json 字符串来自 deploy.json 文件,内容如下,很简单,只有 nginx 的 1.18.0 版本的 pod:
如此一来,web 浏览器只要访问/patch/deploy 就能创建 deployment 了;
发起 patch 请求的通用方法
通过 kubernetes 的客户端发起不同的 patch 请求,其大致步骤都是相同的,只是参数有所不同,我这里做了个私有方法,发起几种 patch 请求的操作都调用此方法实现(只是入参不同而已),可见都是先建好 ApiClient 实例,将 patch 类型传入,再创建 V1Patch 实例,将 patch 字符串传入,最后执行 ExtensionsV1beta1Api 实例的 patchNamespacedDeployment 方法即可发送 patch 请求:
上述代码中,有一行代码要格外重视,就是 patchClient.setDebugging(true)这段,执行了这一行,在 log 日志中就会将 http 的请求和响应全部打印出来,是我们调试的利器,但是日志内容过多,生产环境请慎用;
上述 patch 方法有六个入参,其实除了 patch 类型和 patch 内容,其他参数都可以固定下来,于是再做个简化版的 patch 方法:
入参 patchFormat 的值是四种 patch 类型的定义,在 V1Patch.java 中,其值如下所示:
接下来可以轻松的开发各种类型 patch 的代码了;
执行 json patch
首先来看 json patch 要提交的内容,即 json.json 文件的内容,这些内容在应用启动时被保存到变量 jsonStr,如下所示,非常简单,修改了 terminationGracePeriodSeconds 属性的值,原来是 30,这个属性在停止 pod 的时候用到,是等待 pod 的主进程的最长时间:
接下来就是 web 接口的代码,可见非常简单,仅需调用前面准备好的 patch 方法:
merge patch(全量)
先尝试全量的 merge patch,也就是准备好完整的 deployment 内容,修改其中一部分后进行提交,下图是 json 文件 merge.json 的内容,其内容前面的 deploy.json 相比,仅增加了红框处的内容,即增加了一个 label:
代码依然很简单:
merge patch(增量)
前面曾提到 merge patch 和 strategic merge patch 的区别:merge patch 提交一个 container 时做的是替换,而 strategic merge patch 提交一个 container 时做的是合并,为了展示这两种 patch 的不同,这里我们就用同一个 json 内容,分别执行 merge patch 和 strategic merge patch,看看结果有什么区别,这是最直观的学习方法;
这个 json 对应的文件是 strategic.json,内容如下:
增量 merge 的代码如下:
strategic merge patch
strategic merge patch 用的 json 内容和前面的增量 merge patch 是同一个,代码如下:
apply yaml patch
apply yaml patch 与其他 patch 略有不同,调用 ExtensionsV1beta1Api 的 patchNamespacedDeployment 方法发请求时,fieldManager 和 force 字段不能像之前那样为空了:
上面的代码中,如果 force 字段不等于 true,可能会导致 patch 失败,在官方文档也有说明,如下图红框:
apply yaml patch 的 json 字符串来自文件 applyYaml.json,其内容是从 deploy.json 直接复制的,然后改了下图两个红框中的内容,红框 1 修改了 nginx 的版本号,用来验证 patch 是否生效(原有版本是 1.18),红框 2 是 kubernetes1.16 之前的一个问题,protocol 字段必填,否则会报错,问题详情请参考:https://github.com/kubernetes-sigs/structured-merge-diff/issues/130
以上就是所有代码和 patch 的内容了,接下来部署到 kubernetes 环境实战吧
制作镜像并且部署
在 patch 工程目录下执行以下命令编译构建:
在 patch 工程目录下创建 Dockerfile 文件,内容如下:
在 patch 工程目录下执行以下命令创建 docker 镜像:
如果您已经配置了 docker 镜像仓库私服,建议将此镜像推送到私服上去,以便 kubernetes 上可以使用该镜像,我这边的推送命令如下,仅供参考(涉及到身份验证的话还请执行 docker login 登录):
SSH 登录 kubernetes 环境,新建 patch.yaml 文件,内容如下,image 那里请按您的镜像情况自行调整:
执行以下命令即可完成部署:
用于验证 patch 的 deployment 名为 test123(浏览器访问/patch/deploy 就会创建),这个 deployment 里面是个 nginx 容器,咱们要给它准备一个 NodePort 类型的 service,以便验证的是否可以通过浏览器访问,该 service 对应的 yaml 文件是 nginx-service.yaml,内容如下:
执行以下命令即可完成部署:
终于,部署工作全部完成,可以验证 patch 了!
验证的步骤
先说一下验证的步骤:
调用创建 deployment 的接口,创建名为 test123 的 deployment,里面是个 nginx-1.18 版本的 pod,可以通过浏览器访问到(前面的 nginx-service.yaml 已经部署了 service);
通过 web 请求执行各种 patch 操作;
创建 deployment
假设 kubernetes 宿主机 IP 地址是 192.168.50.135,所以通过浏览器访问:http://192.168.50.135:30102/patch/deploy,即可创建名为 test123 的 deployment,如下图,创建成功后 deployment 的详细信息会展示在浏览器上:
用 kubectl 命令验证 deployment 和 pod 都正常:
3. 浏览器访问 test123 这个 deployment 里面的 nginx 容器,地址是http://192.168.50.135:30101/,如下图红框所示,返回的 header 中显示,nginx 的版本是 1.18.0:
4. 接下来开始提交 patch;
验证 json patch
json patch 修改的是原 deployment 的 terminationGracePeriodSeconds 属性,所以咱们先来看看修改前是啥样的,执行命令 kubectl get deployment test123 -o yaml,如下图红框,terminationGracePeriodSeconds 等于 30:
浏览器访问 http://192.168.50.135:30102/patch/json,即可发起 json patch 请求,并将 deployment 的结果返回,如下图所示,terminationGracePeriodSeconds 属性值已经改变:
再次用命令 kubectl get deployment test123 -o yaml 查看,如下图红框,json patch 已经生效:
4. 执行 kubectl logs -f patch-56cd7f7f87-bpl5r -n kubernetesclient 可以看到咱们应用通过 java 客户端与 kubernetes 的 API Server 之前的详细日志(patch-56cd7f7f87-bpl5r 是 patch 工程对应的 pod),如下图:
5. json patch 验证已经完成,接着验证下一个;
验证 merge patch(全量)
1. merge patch(全量)给原有的 deployment 增加了一个 label,先看看执行 patch 之前是啥样,如下图红框:
2. 浏览器访问http://192.168.50.135:30102/patch/fullmerge ,就发起了 merge 请求,操作成功后再次查看,如下图红框,新增了一个 label:
验证 merge patch(增量)
1. 浏览器访问http://192.168.50.135:30102/patch/partmerge
2. 在 kubernetes 环境查看 test123 这个 deployment 的 pod,发现原有 pod 被删除,新增了一个:
3. 执行命令 kubectl describe pod test123-5ff477967-tv729 查看新 pod 的详情,发现已经部署 nginx 了,而是 patch 中提交的 tomcat,如下图所示,看来增量 merge patch 实际上做是替换操作:
验证 strategic merge patch
此时的 test123 这个 deployment,其 pod 已经被刚才的 merge patch 操作改成了 tomcat,不适合接下来的验证,因此要执行以下操作进行清理和重新部署:
删除 test123 这个 deployment:kubectl delete deployment test123
浏览器访问:http://192.168.50.135:30102/patch/deploy ,就会重新部署 test123
上述操作完成后,test123 就恢复到最初状态了,在浏览器执行 http://192.168.50.135:30102/patch/strategic ,即可提交 strategic merge patch
再去执行 kubectl get pod 命令查看,会发现 pod 会被删除,然后创建新 pod,这个新 pod 的容器数量是 2,如下图红框:
执行命令 kubectl describe pod test123-59db4854f5-bstz5 查看新 pod 的详情,下图两个红框,可见原有的 strategic merge patch 并没有替换 container,而是新增了一个:
此时您应该对 merge patch 和 strategic merge patch 的区别应该有了清晰的认识;
开启 kubernetes 的 Server-side Apply(低于 1.16 版本才需要执行)
最后一个实战是 apply yaml patch,此类型 patch 主要是用于 Server-side Apply 特性的,
该特性自 kubernetes 的 1.14 版本就有了,但是默认并未开启,直到 1.16 版本才默认开启,因此,如果您的 kubernetes 低于 1.16 版本,需要开启这个特性;
java 客户端的官方 demo 代码中,有一些简单描述,如下图红框:
kubernetes 的官方文档中,提到此特性在低版本可以通过开关来开启,文档地址:https://kubernetes.cn/docs/reference/command-line-tools-reference/feature-gates/
我这里 kubernetes 版本是 1.14,因此需要手动开启 Server-side Apply,首先 SSH 登录 kubernetes 环境;
打开文件/etc/kubernetes/manifests/kube-apiserver.yaml ,给 spec.containers.command 增加一个参数--feature-gates=ServerSideApply=true,如下图红框所示:
修改完毕后请保存,由于 kubernetes 一直在监听此文件,因此会立即自动生效;
验证 apply yaml patch
此时的 test123,由于刚刚验证过 strategic merge patch,现在的 pod 里面有 nginx 和 tomcat 两个 container;
浏览器访问 http://192.168.50.135:30102/patch/apply
此时 pod 会重建,如下图,可见最终 container 还是两个,也就是说,尽管 applyYaml.json 中的 container 只有一个 nginx,但由于是在服务端 merge 的,服务端只会升级 nginx 的版本,对于已有的 tomcat 这个 container 依旧会保留:
用浏览器访问 test123 提供的 nginx 服务,如下图红框所示,nginx 版本已经升级,证明本次 patch 已经生效:
至此,通过 kubernetes 的 java 客户端执行 patch 操作的实战就全部完成了,从理论到环境准备,再到实际操作,涉及到太多内容,感谢您的耐心,希望本文能助您用好 java 客户端这个利器,更高效的操作 kubernetes 环境;
欢迎关注 InfoQ:程序员欣宸
版权声明: 本文为 InfoQ 作者【程序员欣宸】的原创文章。
原文链接:【http://xie.infoq.cn/article/271125fd265e05576c7d1f7bd】。文章转载请联系作者。
评论