1. Introduction
1.绪论
In this tutorial, we’ll cover CRUD operations on Kubernetes resources using its official Java API.
在本教程中,我们将介绍使用Kubernetes官方Java API对其资源进行CRUD操作。
We’ve already covered the basics of this API usage in previous articles, including basic project setup and various ways in which we can use it to get information about a running cluster.
我们已经在之前的文章中介绍了该API使用的基本情况,包括基本项目设置和我们可以使用它来获取运行中的集群的信息的各种方式。
In general, Kubernetes deployments are mostly static. We create some artifacts (e.g. YAML files) describing what we want to create and submit them to a DevOps pipeline. The pieces of our system then remain the same until we add a new component or upgrade an existing one.
一般来说,Kubernetes的部署大多是静态的。我们创建一些工件(例如YAML文件),描述我们想要创建的东西,并将它们提交给DevOps管道。然后,我们系统的各个部分保持不变,直到我们添加一个新的组件或升级一个现有的组件。
However, there are cases where we need to add resources on the fly. A common one is running Jobs in response to a user-initiated request. In response, the application would launch a background job to process the report and make it available for later retrieval.
然而,在有些情况下,我们需要即时添加资源。一个常见的情况是响应用户发起的请求而运行工作。作为响应,应用程序将启动一个后台作业来处理报告,并使其可供以后检索。
The key point here is that, by using those APIs, we can make better use of the available infrastructure, as we can consume resources only when they’re needed, releasing them afterward.
这里的关键点是,通过使用这些API,我们可以更好地利用现有的基础设施,因为我们可以只在需要的时候消耗资源,之后再释放它们。
2. Creating a New Resource
2.创建一个新的资源
In this example, we’ll create a Job resource in a Kubernetes cluster. A Job is a kind of Kubernetes workload that, differently from other kinds, runs to completion. That is, once the programs running in its pod terminate, the job itself terminates. Its YAML representation is not unlike other resources:
在这个例子中,我们将在一个Kubernetes集群中创建一个Job资源。Job是一种Kubernetes工作负载,与其他类型的工作负载不同的是,它可以运行到结束。也就是说,一旦在其pod中运行的程序终止,工作本身也就终止了。它的YAML表示法与其他资源没有什么不同。
apiVersion: batch/v1
kind: Job
metadata:
namespace: jobs
name: report-job
labels:
app: reports
spec:
template:
metadata:
name: payroll-report
spec:
containers:
- name: main
image: report-runner
command:
- payroll
args:
- --date
- 2021-05-01
restartPolicy: Never
The Kubernetes API offers two ways to create the equivalent Java object:
Kubernetes API提供了两种方法来创建同等的Java对象。
- Creating POJOS with new and populating all required properties via setters
- Using a fluent API to build the Java resource representation
Which approach to use is mostly a personal preference. Here, we’ll use the fluent approach to create the V1Job object, as the building process looks very similar to its YAML counterpart:
使用哪种方法主要是个人的偏好。在这里,我们将使用流畅的方法来创建V1Job对象,因为其构建过程看起来与YAML对应的对象非常相似。
ApiClient client = Config.defaultClient();
BatchV1Api api = new BatchV1Api(client);
V1Job body = new V1JobBuilder()
.withNewMetadata()
.withNamespace("report-jobs")
.withName("payroll-report-job")
.endMetadata()
.withNewSpec()
.withNewTemplate()
.withNewMetadata()
.addToLabels("name", "payroll-report")
.endMetadata()
.editOrNewSpec()
.addNewContainer()
.withName("main")
.withImage("report-runner")
.addNewCommand("payroll")
.addNewArg("--date")
.addNewArg("2021-05-01")
.endContainer()
.withRestartPolicy("Never")
.endSpec()
.endTemplate()
.endSpec()
.build();
V1Job createdJob = api.createNamespacedJob("report-jobs", body, null, null, null);
We start by creating the ApiClient and then API stub instance. Job resources are part of the Batch API, so we create a BatchV1Api instance, which we’ll use to invoke the cluster’s API server.
我们首先创建ApiClient,然后创建API存根实例。Job资源是Batch API的一部分,所以我们创建一个BatchV1Api实例,我们将用它来调用集群的API服务器。
Next, we instantiate a V1JobBuilder instance, which kind of leads us through the process of filling all the properties. Notice the use of nested builders: to “close” a nested builder, we must call its endXXX() method, which brings us back to its parent builder.
接下来,我们实例化一个V1JobBuilder实例,它引导我们完成填充所有属性的过程。 注意嵌套构建器的使用:要 “关闭 “一个嵌套构建器,我们必须调用它的endXXX()方法,这将使我们回到其父构建器。
Alternatively, it’s also possible to use a withXXX method to inject a nested object directly. This is useful when we want to reuse a common set of properties, such as metadata, labels, and annotations.
另外,也可以使用withXXX方法来直接注入一个嵌套对象。当我们想重用一组共同的属性,如元数据、标签和注释时,这很有用。
The final step is just a call to the API stub. This will serialize our resource object and POST the request to the server. As expected, there are synchronous (used above) and asynchronous versions of the API.
最后一步只是对API存根的调用。这将使我们的资源对象序列化,并将请求POST 到服务器上。正如预期的那样,该API有同步(上面使用的)和异步版本。
The returned object will contain metadata and status fields related to the created job. In the case of a Job, we can use its status field to check when it is finished. We can also one of the techniques presented in our article about monitoring resources to receive this notification.
返回的对象将包含与创建的作业有关的元数据和状态字段。如果是一个Job,我们可以使用它的状态字段来检查它何时完成。我们也可以使用我们关于监控资源的文章中介绍的技术之一来接收这个通知。
3. Updating an Existing Resource
3.更新一个现有的资源
Updating an existing resource consists of sending a PATCH request to the Kubernetes API server, containing which fields we want to modify. As of Kubernetes version 1.16, there are four ways to specify those fields:
更新现有资源包括向Kubernetes API服务器发送一个PATCH请求,其中包含我们想要修改的字段。从Kubernetes 1.16版本开始,有四种方法可以指定这些字段。
- JSON Patch (RFC 6092)
- JSON Merge Patch (RFC 7396)
- Strategic Merge Patch
- Apply YAML
Of those, the last one is the easiest one to use as it leaves all merging and conflict resolution to the server: all we have to do is send a YAML document with the fields we want to modify.
其中,最后一个是最容易使用的,因为它把所有的合并和冲突解决留给了服务器:我们所要做的就是发送一个YAML文档,其中包含我们想要修改的字段。
Unfortunately, the Java API offers no easy way to build this partial YAML document. Instead, we must resort to the PatchUtil helper class to send a raw YAML or JSON string. However, we can use the built-in JSON serializer available through the ApiClient object to get it:
不幸的是,Java API没有提供简单的方法来构建这个部分YAML文档。相反,我们必须求助于PatchUtil助手类来发送一个原始YAML或JSON字符串。然而,我们可以使用通过ApiClient对象提供的内置JSON序列化器来获得它。
V1Job patchedJob = new V1JobBuilder(createdJob)
.withNewMetadata()
.withName(createdJob.getMetadata().getName())
.withNamespace(createdJob.getMetadata().getNamespace())
.endMetadata()
.editSpec()
.withParallelism(2)
.endSpec()
.build();
String patchedJobJSON = client.getJSON().serialize(patchedJob);
PatchUtils.patch(
V1Job.class,
() -> api.patchNamespacedJobCall(
createdJob.getMetadata().getName(),
createdJob.getMetadata().getNamespace(),
new V1Patch(patchedJobJSON),
null,
null,
"baeldung",
true,
null),
V1Patch.PATCH_FORMAT_APPLY_YAML,
api.getApiClient());
Here, we use the object returned from createNamespacedJob() as a template from which we’ll construct the patched version. In this case, we’re just increasing the parallelism value from one to two, leaving all other fields unchanged. An important point here is that as we build the modified resource, we must use the withNewMetadata(). This ensures that we don’t build an object containing managed fields, which are present in the returned object we got after creating the resource. For a full description of managed fields and how they’re used in Kubernetes, please refer to the documentation.
在这里,我们使用从createNamespacedJob()返回的对象作为模板,我们将从该模板中构建修补的版本。在这种情况下,我们只是将parallelism值从1增加到2,其他所有字段保持不变。这里重要的一点是,当我们构建修改后的资源时,我们必须使用withNewMetadata()。 这可以确保我们不会构建一个包含托管字段的对象,这些字段存在于我们创建资源后得到的返回对象中。有关托管字段的完整描述以及它们在Kubernetes中的使用方式,请参考文档。
Once we’ve built an object with the modified fields, we then convert it to its JSON representation using the serialize method. We then use this serialized version to construct a V1Patch object used as the payload for the PATCH call. The patch method also takes an additional argument where we inform the kind of data present in the request. In our case, this is PATCH_FORMAT_APPLY_YAML, which the library uses as the Content-Type header included in the HTTP request.
一旦我们建立了一个带有修改过的字段的对象,我们就会使用serialize方法将其转换为JSON表示。然后我们使用这个序列化的版本来构建一个V1Patch对象,作为PATCH调用的有效载荷。patch方法还需要一个额外的参数,在这里我们告知请求中存在的数据类型。在我们的例子中,这是PATCH_FORMAT_APPLY_YAML,该库将其作为HTTP请求中的Content-Type头。
The “baeldung” value passed to the fieldManager parameter defines the actor name who is manipulating the resource’s fields. Kubernetes uses this value internally to resolve an eventual conflict when two or more clients try to modify the same resource. We also pass true in the force parameter, meaning that we’ll take ownership of any modified field.
传递给fieldManager参数的“baeldung”值定义了正在操纵资源字段的行为者名称。Kubernetes在内部使用这个值来解决两个或多个客户端试图修改同一资源时的最终冲突。我们还在true参数中传递了force,意味着我们将获得任何修改过的字段的所有权。
4. Deleting a Resource
4.删除一个资源
Compared to the previous operations, deleting a resource is quite straightforward:
与前面的操作相比,删除一个资源是非常直接的。
V1Status response = api.deleteNamespacedJob(
createdJob.getMetadata().getName(),
createdJob.getMetadata().getNamespace(),
null,
null,
null,
null,
null,
null ) ;
Here, we’re just using the deleteNamespacedJob method to remove the job using default options for this specific kind of resource. If required, we can use the last parameter to control the details of the deletion process. This takes the form of a V1DeleteOptions object, which we can use to specify a grace period and cascading behavior for any dependent resources.
在这里,我们只是使用deleteNamespacedJob方法,使用默认选项来删除这种特定资源的作业。如果需要,我们可以使用最后一个参数来控制删除过程的细节。它采用V1DeleteOptions对象的形式,我们可以用它来指定宽限期和任何依赖资源的级联行为。
5. Conclusion
5.总结
In this article, we’ve covered how to manipulate Kubernetes resources using the Java Kubernetes API library. As usual, the full source code of the examples can be found over on GitHub.
在这篇文章中,我们已经介绍了如何使用Java Kubernetes API库来操作Kubernetes资源。像往常一样,可以在GitHub上找到这些示例的完整源代码。