写点什么

Kubernetes 官方 java 客户端之七:patch 操作

作者:程序员欣宸
  • 2022 年 4 月 04 日
  • 本文字数:12403 字

    阅读完需:约 41 分钟

Kubernetes官方java客户端之七:patch操作

欢迎访问我的 GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos


概览

  1. 本文是《Kubernetes 官方 java 客户端》系列的第七篇,以下提到的 java 客户端都是指 client-jar.jar

  2. 本文主要内容是通过 java 客户端发起 patch 请求,用来修改已有资源;

  3. 接下来会对 kubernetes 的 patch 做一些介绍,由于咱们这里的重点还是 java 客户端的 patch 操作,因此不会对 patch 的原理和概念展开太多,仅做最基本的说明能即可;


本文内容

  • 这是篇万字长文,所以一开始就要明确本文的核心内容:开发一个 SpringBoot 应用并部署在 kubernetes 环境,这个应用通过 kubernetes 的 java 客户端向 API Server 发请求,请求内容包括:创建名为 test123 的 deployment、对这个 deployment 进行 patch 操作,如下图:



  • 接下来先了解一些 kubernetes 的 patch 相关的基本知识;

关于 patch

  1. 是对各种资源的增删改查是 kubernetes 的基本操作;

  2. 对于修改操作,分为 Replace Patch 两种;

  3. Replace 好理解,就是用指定资源替换现有资源,replace 有个特点,就是 optimistic lock 约束(类似与转账操作,先读再计算再写入);

  4. Patch 用来对资源做局部更新,没有 optimistic lock 约束,总是最后的请求会生效,因此如果您只打算修改局部信息,例如某个属性,只要指定属性去做 patch 即可(如果用 Replace,就只能先取得整个资源,在本地修改指定属性,再用 Replace 整体替换);

  5. 更详细的信息请参考下图,来自官方文档,地址: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 能力的实现;


源码下载

  1. 如果您不想编码,可以在 GitHub 下载所有源码,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos):



  1. 这个 git 项目中有多个文件夹,本章的应用在 kubernetesclient 文件夹下,如下图红框所示:


实战步骤概述

  • 接下来会创建一个 springboot 工程(该工程是 kubernetesclient 的子工程),针对四种 patch 咱们都有对应的操作;

  • 每种 patch 都会准备对应的 json 文件,提前将这些文件的内容保存在字符串变量中,在程序里用 kubernetes 客户端的 patch 专用 API,将此 json 字符串发送出去,流程简图如下:


  • 编码完成后,就来动手验证功能,具体操作如下:


  1. 部署名为 patch 的 deployment,这里面是咱们编码的 SpringBoot 工程,提供多个 web 接口;

  2. 浏览器访问/patch/deploy 接口就会创建名为 test123 的 deployment,这个 deployment 里面是个 nginx,接下来的 patch 操作都是针对这个名为 test123 的 deployment;

  3. 浏览器访问 test123 的 nginx 服务,确保部署成功了;

  4. 浏览器访问/patch/json 接口,该接口会修改 test123 的一个属性:terminationGracePeriodSeconds

  5. 浏览器访问/patch/fullmerge 接口,该接口会提交全量 merge 请求,修改内容很少,仅增加了一个 label 属性;

  6. 接下来是对比 merge patch 和 strategic merge patch 区别,分别访问/patch/partmerge /patch/strategic 这两个接口,其实它们操作的是同一段 patch 内容(一个新的 container),结果 merge patch 会替换原有的 continer,而 strategic merge patch 不会动原有的 container,而是新增 container,导致 test123 这个 deployment 下面的 pod 从一个变为两个;

  7. 最后是 apply yaml patch,访问接口/patch/apply,会将 nginx 容器的标签从 1.18.0 改为 1.19.1,咱们只要在浏览器访问 test123 里面的 nginx 服务就能确定是否修改生效了;

准备工作

准备工作包括创建工程、编写辅助功能代码、初始化代码等:


  1. 打开《Kubernetes官方java客户端之一:准备 》一文创建的项目 kubernetesclient,新增名为 patch 的子工程,pom.xml 内容如下:

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>
<parent> <groupId>com.bolingcavalry</groupId> <artifactId>kubernetesclient</artifactId> <version>1.0-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent>
<groupId>com.bolingcavalry</groupId> <artifactId>patch</artifactId> <version>0.0.1-SNAPSHOT</version> <name>patch</name> <description>patch demo</description> <packaging>jar</packaging>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId> </exclusion> </exclusions> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>
<dependency> <groupId>io.kubernetes</groupId> <artifactId>client-java</artifactId> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.3.0.RELEASE</version> <!--该配置会在jar中增加layer描述文件,以及提取layer的工具--> <configuration> <layers> <enabled>true</enabled> </layers> </configuration> </plugin> </plugins> </build>
</project>
复制代码
  1. 编写一个辅助类 ClassPathResourceReader.java,作用是读取 json 文件的内容作为字符串返回:

package com.bolingcavalry.patch;
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.stream.Collectors;import org.springframework.core.io.ClassPathResource;
public class ClassPathResourceReader {
private final String path;
private String content;
public ClassPathResourceReader(String path) { this.path = path; }
public String getContent() { if (content == null) { try { ClassPathResource resource = new ClassPathResource(path); BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream())); content = reader.lines().collect(Collectors.joining("\n")); reader.close(); } catch (IOException ex) { throw new RuntimeException(ex); } } return content; }}
复制代码
  1. 接下来新建本篇文章的核心类 PatchExample.java,首先这个类中有 main 方法,整个应用从这里启动:

public static void main(String[] args) {        SpringApplication.run(PatchExample.class, args);}
复制代码


  1. 接下来有两个常量定义,分别是 kubernetes 环境里用来测试的 deployment 名称,以及 namespace 名称:

static String DEPLOYMENT_NAME = "test123";
static String NAMESPACE = "default";
复制代码


  1. 然后定义几个字符串变量,执行 patch 操作时用到的 json 内容都保存到这些字符串变量中:

static String deployStr, jsonStr, mergeStr, strategicStr, applyYamlStr;
复制代码
  1. resources 文件夹中放入 json 文件,稍后的初始化代码会将这些文件读取到字符串变量中,如下图,这些 json 文件的内容稍后会详细说明:

  2. 编写初始化代码(通过 PostConstruct 注解实现),主要是客户端配置,还有将 json 文件的内容读出来,保存到刚刚准备的字符串变量中:

@PostConstruct    private void init() throws IOException {        // 设置api配置        ApiClient client = Config.defaultClient();        Configuration.setDefaultApiClient(client);        // 设置超时时间        Configuration.getDefaultApiClient().setConnectTimeout(30000);
// 部署用的JSON字符串 deployStr = new ClassPathResourceReader("deploy.json").getContent();
// json patch用的JSON字符串 jsonStr = new ClassPathResourceReader("json.json").getContent();
// merge patch用的JSON字符串,和部署的JSON相比:replicas从1变成2,增加一个名为from的label,值为merge mergeStr = new ClassPathResourceReader("merge.json").getContent();
// strategic merge patch用的JSON字符串 strategicStr = new ClassPathResourceReader("strategic.json").getContent();
// server side apply用的JSON字符串 applyYamlStr = new ClassPathResourceReader("applyYaml.json").getContent(); }
复制代码
  • 以上就是准备工作

创建服务

1. 首先要开发一个部署的接口,通过调用此接口可以在 kubernetes 环境部署一个 deployment:

2. 部署服务的 path 是/patch/deploy,代码如下,可见部署 deployment 的代码分为三步:创建 api 实例、用字符串创建 body 实例、把 body 传给 api 即可:

/**     * 通用patch方法     * @param patchFormat patch类型,一共有四种     * @param deploymentName deployment的名称     * @param namespace namespace名称     * @param jsonStr patch的json内容     * @param fieldManager server side apply用到     * @param force server side apply要设置为true     * @return patch结果对象转成的字符串     * @throws Exception     */    private String patch(String patchFormat, String deploymentName, String namespace, String jsonStr, String fieldManager, Boolean force) throws Exception {        // 创建api对象,指定格式是patchFormat        ApiClient patchClient = ClientBuilder                .standard()                .setOverridePatchFormat(patchFormat)                .build();
log.info("start deploy : " + patchFormat);
// 开启debug便于调试,生产环境慎用!!! patchClient.setDebugging(true);
// 创建deployment ExtensionsV1beta1Deployment deployment = new ExtensionsV1beta1Api(patchClient) .patchNamespacedDeployment( deploymentName, namespace, new V1Patch(jsonStr), null, null, fieldManager, force );
log.info("end deploy : " + patchFormat);
return new GsonBuilder().setPrettyPrinting().create().toJson(deployment); }
复制代码
  1. body 实例用到的 json 字符串来自 deploy.json 文件,内容如下,很简单,只有 nginx 的 1.18.0 版本的 pod:


{  "kind":"Deployment",  "apiVersion":"extensions/v1beta1",  "metadata":{    "name":"test123",    "labels":{      "run":"test123"    }  },  "spec":{    "replicas":1,    "selector":{      "matchLabels":{        "run":"test123"      }    },    "template":{      "metadata":{        "creationTimestamp":null,        "labels":{          "run":"test123"        }      },      "spec":{        "terminationGracePeriodSeconds":30,        "containers":[          {            "name":"test123",            "image":"nginx:1.18.0",            "ports":[              {                "containerPort":80              }            ],            "resources":{
} } ] } }, "strategy":{
} }, "status":{
}}
复制代码
  1. 如此一来,web 浏览器只要访问/patch/deploy 就能创建 deployment 了;

发起 patch 请求的通用方法

  • 通过 kubernetes 的客户端发起不同的 patch 请求,其大致步骤都是相同的,只是参数有所不同,我这里做了个私有方法,发起几种 patch 请求的操作都调用此方法实现(只是入参不同而已),可见都是先建好 ApiClient 实例,将 patch 类型传入,再创建 V1Patch 实例,将 patch 字符串传入,最后执行 ExtensionsV1beta1Api 实例的 patchNamespacedDeployment 方法即可发送 patch 请求:

/**     * 通用patch方法     * @param patchFormat patch类型,一共有四种     * @param deploymentName deployment的名称     * @param namespace namespace名称     * @param jsonStr patch的json内容     * @param fieldManager server side apply用到     * @param force server side apply要设置为true     * @return patch结果对象转成的字符串     * @throws Exception     */    private String patch(String patchFormat, String deploymentName, String namespace, String jsonStr, String fieldManager, Boolean force) throws Exception {        // 创建api对象,指定格式是patchFormat        ApiClient patchClient = ClientBuilder                .standard()                .setOverridePatchFormat(patchFormat)                .build();
log.info("start deploy : " + patchFormat);
// 开启debug便于调试,生产环境慎用!!! patchClient.setDebugging(true);
// 创建deployment ExtensionsV1beta1Deployment deployment = new ExtensionsV1beta1Api(patchClient) .patchNamespacedDeployment( deploymentName, namespace, new V1Patch(jsonStr), null, null, fieldManager, force );
log.info("end deploy : " + patchFormat);
return new GsonBuilder().setPrettyPrinting().create().toJson(deployment); }
复制代码
  • 上述代码中,有一行代码要格外重视,就是 patchClient.setDebugging(true)这段,执行了这一行,在 log 日志中就会将 http 的请求和响应全部打印出来,是我们调试的利器,但是日志内容过多,生产环境请慎用;

  • 上述 patch 方法有六个入参,其实除了 patch 类型和 patch 内容,其他参数都可以固定下来,于是再做个简化版的 patch 方法:

/**     * 通用patch方法,fieldManager和force都默认为空     * @param patchFormat patch类型,一共有四种     * @param jsonStr patch的json内容     * @return patch结果对象转成的字符串     * @throws Exception     */    private String patch(String patchFormat, String jsonStr) throws Exception {        return patch(patchFormat,  DEPLOYMENT_NAME, NAMESPACE, jsonStr, null, null);    }
复制代码
  • 入参 patchFormat 的值是四种 patch 类型的定义,在 V1Patch.java 中,其值如下所示:

  • 接下来可以轻松的开发各种类型 patch 的代码了;

执行 json patch

  1. 首先来看 json patch 要提交的内容,即 json.json 文件的内容,这些内容在应用启动时被保存到变量 jsonStr,如下所示,非常简单,修改了 terminationGracePeriodSeconds 属性的值,原来是 30,这个属性在停止 pod 的时候用到,是等待 pod 的主进程的最长时间:


[  {    "op":"replace",    "path":"/spec/template/spec/terminationGracePeriodSeconds",    "value":27  }]
复制代码


  1. 接下来就是 web 接口的代码,可见非常简单,仅需调用前面准备好的 patch 方法:


/**     * JSON patch格式的关系     *     * @return     * @throws Exception     */    @RequestMapping(value = "/patch/json", method = RequestMethod.GET)    public String json() throws Exception {        return patch(V1Patch.PATCH_FORMAT_JSON_PATCH, jsonStr);    }
复制代码

merge patch(全量)

  1. 先尝试全量的 merge patch,也就是准备好完整的 deployment 内容,修改其中一部分后进行提交,下图是 json 文件 merge.json 的内容,其内容前面的 deploy.json 相比,仅增加了红框处的内容,即增加了一个 label:

  2. 代码依然很简单:


@RequestMapping(value = "/patch/fullmerge", method = RequestMethod.GET)    public String fullmerge() throws Exception {        return patch(V1Patch.PATCH_FORMAT_JSON_MERGE_PATCH, mergeStr);    }
复制代码

merge patch(增量)

  1. 前面曾提到 merge patch 和 strategic merge patch 的区别:merge patch 提交一个 container 时做的是替换,而 strategic merge patch 提交一个 container 时做的是合并,为了展示这两种 patch 的不同,这里我们就用同一个 json 内容,分别执行 merge patch 和 strategic merge patch,看看结果有什么区别,这是最直观的学习方法;

  2. 这个 json 对应的文件是 strategic.json,内容如下:


{  "spec":{    "template":{      "spec":{        "containers":[          {            "name":"test456",            "image":"tomcat:7.0.105-jdk8"          }        ]      }    }  }}
复制代码


  1. 增量 merge 的代码如下:


@RequestMapping(value = "/patch/partmerge", method = RequestMethod.GET)    public String partmerge() throws Exception {        return patch(V1Patch.PATCH_FORMAT_JSON_MERGE_PATCH, strategicStr);    }
复制代码

strategic merge patch

  • strategic merge patch 用的 json 内容和前面的增量 merge patch 是同一个,代码如下:


@RequestMapping(value = "/patch/strategic", method = RequestMethod.GET)    public String strategic() throws Exception {        return patch(V1Patch.PATCH_FORMAT_STRATEGIC_MERGE_PATCH, strategicStr);    }
复制代码

apply yaml patch

  1. apply yaml patch 与其他 patch 略有不同,调用 ExtensionsV1beta1Api 的 patchNamespacedDeployment 方法发请求时,fieldManager 和 force 字段不能像之前那样为空了:


 @RequestMapping(value = "/patch/apply", method = RequestMethod.GET)    public String apply() throws Exception {        return patch(V1Patch.PATCH_FORMAT_APPLY_YAML,  DEPLOYMENT_NAME, NAMESPACE, applyYamlStr, "example-field-manager", true);    }
复制代码
  1. 上面的代码中,如果 force 字段不等于 true,可能会导致 patch 失败,在官方文档也有说明,如下图红框:

  2. 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

  3. 以上就是所有代码和 patch 的内容了,接下来部署到 kubernetes 环境实战吧

制作镜像并且部署

  1. 在 patch 工程目录下执行以下命令编译构建:


mvn clean package -U -DskipTests
复制代码


  1. 在 patch 工程目录下创建 Dockerfile 文件,内容如下:


# 指定基础镜像,这是分阶段构建的前期阶段FROM openjdk:8u212-jdk-stretch as builder# 执行工作目录WORKDIR application# 配置参数ARG JAR_FILE=target/*.jar# 将编译构建得到的jar文件复制到镜像空间中COPY ${JAR_FILE} application.jar# 通过工具spring-boot-jarmode-layertools从application.jar中提取拆分后的构建结果RUN java -Djarmode=layertools -jar application.jar extract
# 正式构建镜像FROM openjdk:8u212-jdk-stretchWORKDIR application# 前一阶段从jar中提取除了多个文件,这里分别执行COPY命令复制到镜像空间中,每次COPY都是一个layerCOPY --from=builder application/dependencies/ ./COPY --from=builder application/spring-boot-loader/ ./COPY --from=builder application/snapshot-dependencies/ ./COPY --from=builder application/application/ ./ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]
复制代码


  1. 在 patch 工程目录下执行以下命令创建 docker 镜像:


docker build -t 192.168.50.43:5888/common/patch:1.0-SNAPSHOT .
复制代码


  1. 如果您已经配置了 docker 镜像仓库私服,建议将此镜像推送到私服上去,以便 kubernetes 上可以使用该镜像,我这边的推送命令如下,仅供参考(涉及到身份验证的话还请执行 docker login 登录):


docker push 192.168.50.43:5888/common/patch:1.0-SNAPSHOT
复制代码


  1. SSH 登录 kubernetes 环境,新建 patch.yaml 文件,内容如下,image 那里请按您的镜像情况自行调整:


apiVersion: v1kind: Servicemetadata:  name: patch  namespace : kubernetesclientspec:  type: NodePort  ports:    - port: 8080      nodePort: 30102  selector:    name: patch---apiVersion: extensions/v1beta1kind: Deploymentmetadata:  namespace : kubernetesclient  name: patchspec:  replicas: 1  template:    metadata:      labels:        name: patch    spec:      serviceAccountName: kubernates-client-service-account      containers:        - name: patch          image: 192.168.50.43:5888/common/patch:1.0-SNAPSHOT          tty: true          livenessProbe:            httpGet:              path: /actuator/health/liveness              port: 8080            initialDelaySeconds: 5            failureThreshold: 10            timeoutSeconds: 10            periodSeconds: 5          readinessProbe:            httpGet:              path: /actuator/health/readiness              port: 8080            initialDelaySeconds: 5            timeoutSeconds: 10            periodSeconds: 5          ports:            - containerPort: 8080          resources:            requests:              memory: "512Mi"              cpu: "100m"            limits:              memory: "1Gi"              cpu: "1000m"
复制代码


  1. 执行以下命令即可完成部署:


kubectl apply -f patch.yaml
复制代码


  1. 用于验证 patch 的 deployment 名为 test123(浏览器访问/patch/deploy 就会创建),这个 deployment 里面是个 nginx 容器,咱们要给它准备一个 NodePort 类型的 service,以便验证的是否可以通过浏览器访问,该 service 对应的 yaml 文件是 nginx-service.yaml,内容如下:


apiVersion: v1kind: Servicemetadata:  name: test123  namespace : defaultspec:  type: NodePort  ports:    - port: 80      nodePort: 30101  selector:    run: test123
复制代码


  1. 执行以下命令即可完成部署:


kubectl apply -f nginx-service.yaml
复制代码


  1. 终于,部署工作全部完成,可以验证 patch 了!

验证的步骤

先说一下验证的步骤:


  1. 调用创建 deployment 的接口,创建名为 test123 的 deployment,里面是个 nginx-1.18 版本的 pod,可以通过浏览器访问到(前面的 nginx-service.yaml 已经部署了 service);

  2. 通过 web 请求执行各种 patch 操作;


创建 deployment

  1. 假设 kubernetes 宿主机 IP 地址是 192.168.50.135,所以通过浏览器访问:http://192.168.50.135:30102/patch/deploy,即可创建名为 test123 的 deployment,如下图,创建成功后 deployment 的详细信息会展示在浏览器上:

  2. 用 kubectl 命令验证 deployment 和 pod 都正常:


3. 浏览器访问 test123 这个 deployment 里面的 nginx 容器,地址是http://192.168.50.135:30101/,如下图红框所示,返回的 header 中显示,nginx 的版本是 1.18.0:


4. 接下来开始提交 patch;

验证 json patch

  1. json patch 修改的是原 deployment 的 terminationGracePeriodSeconds 属性,所以咱们先来看看修改前是啥样的,执行命令 kubectl get deployment test123 -o yaml,如下图红框,terminationGracePeriodSeconds 等于 30

  1. 浏览器访问 http://192.168.50.135:30102/patch/json,即可发起 json patch 请求,并将 deployment 的结果返回,如下图所示,terminationGracePeriodSeconds 属性值已经改变:

  1. 再次用命令 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

  1. 此时的 test123 这个 deployment,其 pod 已经被刚才的 merge patch 操作改成了 tomcat,不适合接下来的验证,因此要执行以下操作进行清理和重新部署:


  • 删除 test123 这个 deployment:kubectl delete deployment test123

  • 浏览器访问:http://192.168.50.135:30102/patch/deploy ,就会重新部署 test123


  1. 上述操作完成后,test123 就恢复到最初状态了,在浏览器执行 http://192.168.50.135:30102/patch/strategic ,即可提交 strategic merge patch

  2. 再去执行 kubectl get pod 命令查看,会发现 pod 会被删除,然后创建新 pod,这个新 pod 的容器数量是 2,如下图红框:


  1. 执行命令 kubectl describe pod test123-59db4854f5-bstz5 查看新 pod 的详情,下图两个红框,可见原有的 strategic merge patch 并没有替换 container,而是新增了一个:

  1. 此时您应该对 merge patch 和 strategic merge patch 的区别应该有了清晰的认识;

开启 kubernetes 的 Server-side Apply(低于 1.16 版本才需要执行)

  1. 最后一个实战是 apply yaml patch,此类型 patch 主要是用于 Server-side Apply 特性的,

  2. 该特性自 kubernetes 的 1.14 版本就有了,但是默认并未开启,直到 1.16 版本才默认开启,因此,如果您的 kubernetes 低于 1.16 版本,需要开启这个特性;

  3. java 客户端的官方 demo 代码中,有一些简单描述,如下图红框:

  1. kubernetes 的官方文档中,提到此特性在低版本可以通过开关来开启,文档地址:https://kubernetes.cn/docs/reference/command-line-tools-reference/feature-gates/

  1. 我这里 kubernetes 版本是 1.14,因此需要手动开启 Server-side Apply,首先 SSH 登录 kubernetes 环境;

  2. 打开文件/etc/kubernetes/manifests/kube-apiserver.yaml ,给 spec.containers.command 增加一个参数--feature-gates=ServerSideApply=true,如下图红框所示:

  1. 修改完毕后请保存,由于 kubernetes 一直在监听此文件,因此会立即自动生效;

验证 apply yaml patch

  1. 此时的 test123,由于刚刚验证过 strategic merge patch,现在的 pod 里面有 nginx 和 tomcat 两个 container;

  2. 浏览器访问 http://192.168.50.135:30102/patch/apply

  3. 此时 pod 会重建,如下图,可见最终 container 还是两个,也就是说,尽管 applyYaml.json 中的 container 只有一个 nginx,但由于是在服务端 merge 的,服务端只会升级 nginx 的版本,对于已有的 tomcat 这个 container 依旧会保留:

  1. 用浏览器访问 test123 提供的 nginx 服务,如下图红框所示,nginx 版本已经升级,证明本次 patch 已经生效:

  • 至此,通过 kubernetes 的 java 客户端执行 patch 操作的实战就全部完成了,从理论到环境准备,再到实际操作,涉及到太多内容,感谢您的耐心,希望本文能助您用好 java 客户端这个利器,更高效的操作 kubernetes 环境;

欢迎关注 InfoQ:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...

发布于: 2022 年 04 月 04 日阅读数: 29
用户头像

搜索"程序员欣宸",一起畅游Java宇宙 2018.04.19 加入

前腾讯、前阿里员工,从事Java后台工作,对Docker和Kubernetes充满热爱,所有文章均为作者原创,个人Github:https://github.com/zq2599/blog_demos

评论

发布
暂无评论
Kubernetes官方java客户端之七:patch操作_四月月更_程序员欣宸_InfoQ写作平台