1. Introduction
1.绪论
In this tutorial, we’ll show how to use the Kubernetes API from Java applications using its official client library.
在本教程中,我们将展示如何使用Kubernetes API从Java应用程序中使用其官方客户端库。
2. Why Use the Kubernetes API?
2.为什么使用Kubernetes的API?
Nowadays, it is safe to say that Kubernetes became the de facto standard for managing containerized applications. It offers a rich API that allows us to deploy, scale and monitor applications and associated resources, such as storage, secrets, and environment variables. In fact, one way to think about this API is the distributed analog of the system calls available in a regular operating system.
如今,可以说Kubernetes成为了管理容器化应用程序的事实标准。它提供了丰富的API,使我们能够部署、扩展和监控应用程序和相关资源,如存储、秘密和环境变量。事实上,我们可以把这个API看成是普通操作系统中系统调用的分布式类似物。
Most of the time, our applications can ignore the fact that they’re running under Kubernetes. This is a good thing, as it allows us to develop them locally and, with a few commands and YAML incantations, quickly deploy them to multiple cloud providers with just minor changes.
大多数时候,我们的应用程序可以忽略它们在Kubernetes下运行的事实。这是一件好事,因为它允许我们在本地开发它们,并通过一些命令和YAML咒语,快速将它们部署到多个云供应商,只需稍作改动。
However, there are some interesting use cases where we need to talk to the Kubernetes API to achieve specific functionality:
然而,有一些有趣的用例,我们需要与Kubernetes API对话,以实现特定的功能。
- Start an external program to perform some task and, later on, retrieve its completion status
- Dynamically create/modify some service in response to some customer request
- Create a custom monitoring dashboard for a solution running across multiple Kubernetes clusters, even across cloud providers
Granted, those use-cases are not that common but, thanks to its API, we’ll see that they’re quite straightforward to achieve.
当然,这些用例并不常见,但由于它的API,我们将看到它们是很容易实现的。
Furthermore, since the Kubernetes API is an open specification, we can be quite confident that our code will run without any modifications on any certified implementation.
此外,由于Kubernetes API是一个开放的规范,我们可以很有信心,我们的代码将在没有任何修改的情况下运行在任何认证的实现上。
3. Local Development Environment
3.地方发展环境
The very first thing we need to do before we move on to create an application is to get access to a functioning Kubernetes cluster. While we can either use a public cloud provider for this, a local environment usually provides more control on all the aspects of its setup.
在我们继续创建一个应用程序之前,我们需要做的第一件事是获得一个正常运行的Kubernetes集群。虽然我们可以为此使用公共云提供商,但本地环境通常可以对其设置的所有方面提供更多控制。
There are a few lightweight distributions that are suitable for this task:
有几个轻量级的发行版适合这项任务。
The actual setup steps are beyond the scope of this article but, whatever option you choose, just make sure kubectl runs fine before starting any development.
实际的设置步骤超出了本文的范围,但是,无论你选择什么方案,只要确保kubectl在开始任何开发之前运行良好。
4. Maven Dependencies
4.Maven的依赖性
First, let’s add the Kubernetes Java API dependency to our project’s pom.xml:
首先,让我们把Kubernetes Java API的依赖关系添加到我们项目的pom.xml。
<dependency>
<groupId>io.kubernetes</groupId>
<artifactId>client-java</artifactId>
<version>11.0.0</version>
</dependency>
The latest version of client-java can be downloaded from Maven Central.
最新版本的client-java可以从Maven中心下载。
5. Hello, Kubernetes
5.你好,Kubernetes
Now, let’s create a very simple Kubernetes application that will list the available nodes, along with some information about them.
现在,让我们创建一个非常简单的Kubernetes应用程序,它将列出可用的节点,以及关于它们的一些信息。
Despite its simplicity, this application illustrates the necessary steps we must go through to connect to a running cluster and perform an API call. Regardless of which API we use in a real application, those steps will always be the same.
尽管该应用很简单,但它说明了我们必须经历的必要步骤,以连接到正在运行的集群并执行API调用。无论我们在实际应用中使用哪种API,这些步骤都将是相同的。
5.1. ApiClient Initialization
5.1.ApiClient初始化
The ApiClient class one of the most important classes in the API since it contains all the logic to perform a call to the Kubernetes API server. The recommended way to create an instance of this class is using one of the available static methods from the Config class. In particular, the easiest way of doing that is using the defaultClient() method:
ApiClient类是API中最重要的类之一,因为它包含对Kubernetes API服务器进行调用的所有逻辑。创建该类实例的推荐方法是使用Config类中的一个可用静态方法。特别是,最简单的方法是使用defaultClient()方法。
ApiClient client = Config.defaultClient();
Using this method ensures that our code will work both remotely and in-cluster scenarios. Also, it will automatically follow the same steps used by the kubectl utility to locate the configuration file
使用这种方法可以确保我们的代码在远程和集群场景中都能工作。此外,它将自动遵循kubectl工具所使用的相同步骤来定位配置文件
- Config file defined by KUBECONFIG environment variable
- $HOME/.kube/config file
- Service account token under /var/run/secrets/kubernetes.io/serviceaccount
- Direct access to http://localhost:8080
The third step is the one that makes it possible for our app to run inside the cluster as part of any pod, as long the appropriate service account is made available to it.
第三步是使我们的应用程序有可能作为任何pod的一部分在集群内运行,只要适当的服务账户对其可用。
Also, notice that if we have multiple contexts defined in the config file, this procedure will pick the “current” context, as defined using the kubectl config set-context command.
另外,请注意,如果我们在配置文件中定义了多个上下文,这个过程将选择 “当前 “的上下文,正如使用kubectl config set-context命令所定义的。
5.2. Creating an API Stub
5.2.创建一个API存根
Once we’ve got hold of an ApiClient instance, we can use it to create a stub for any of the available APIs. In our case, we’ll use the CoreV1Api class, which contains the method we need to list the available nodes:
一旦我们掌握了一个ApiClient实例,我们就可以用它来为任何可用的API创建一个存根。在我们的案例中,我们将使用CoreV1Api类,它包含我们需要的列出可用节点的方法。
CoreV1Api api = new CoreV1Api(client);
Here, we’re using the already existing ApiClient to create the API stub.
这里,我们使用已经存在的ApiClient来创建API存根。
Notice that there’s also a no-args constructor available, but in general, we should refrain from using it. The reasoning for not using it is the fact that, internally, it will use a global ApiClient that must be previously set through Configuration.setDefaultApiClient(). This creates an implicit dependency on someone calling this method before using the stub, which, in turn, may lead to runtime errors and maintenance issues.
请注意,还有一个无args构造函数,但一般来说,我们应该避免使用它。不使用它的理由是,在内部,它将使用一个全局的ApiClient,而这个全局的ApiClient必须事先通过Configuration.setDefaultApiClient()进行设置。这就造成了对某人在使用存根之前调用该方法的隐性依赖,而这又可能导致运行时错误和维护问题。
A better approach is to use any dependency injection framework to do this initial wiring, injecting the resulting stub wherever needed.
一个更好的方法是使用任何依赖性注入框架来做这个初始接线,在需要的地方注入所产生的存根。
5.3. Calling a Kubernetes API
5.3.调用Kubernetes API
Finally, let’s get into the actual API call that returns the available nodes. The CoreApiV1 stub has a method that does precisely this, so this becomes trivial:
最后,让我们进入返回可用节点的实际API调用。CoreApiV1存根有一个方法正是这样做的,所以这变得微不足道。
V1NodeList nodeList = api.listNode(null, null, null, null, null, null, null, null, 10, false);
nodeList.getItems()
.stream()
.forEach((node) -> System.out.println(node));
In our example, we pass null for most of the method’s parameters, as they’re optional. The last two parameters are relevant for all listXXX calls, as they specify the call timeout and whether this is a Watch call or not. Checking the method’s signature reveals the remaining arguments:
在我们的例子中,我们为该方法的大部分参数传递了null,因为它们是可选的。最后两个参数与所有listXXX调用有关,因为它们指定了调用超时以及这是否是一个Watch调用。检查该方法的签名可以发现其余的参数。
public V1NodeList listNode(
String pretty,
Boolean allowWatchBookmarks,
String _continue,
String fieldSelector,
String labelSelector,
Integer limit,
String resourceVersion,
String resourceVersionMatch,
Integer timeoutSeconds,
Boolean watch) {
// ... method implementation
}
For this quick intro, we’ll just ignore the paging, watch and filter arguments. The return value, in this case, is a POJO with a Java representation of the returned document. For this API call, the document contains a list of V1Node objects with several pieces of information about each node. Here’s a typical output produced on the console by this code:
在这个快速介绍中,我们将忽略分页、观察和过滤参数。在这种情况下,返回值是一个POJO,包含了返回文档的Java表示。对于这个API调用,文档包含一个V1Node对象的列表,其中有关于每个节点的一些信息。下面是这段代码在控制台产生的一个典型输出。
class V1Node {
metadata: class V1ObjectMeta {
labels: {
beta.kubernetes.io/arch=amd64,
beta.kubernetes.io/instance-type=k3s,
// ... other labels omitted
}
name: rancher-template
resourceVersion: 29218
selfLink: null
uid: ac21e09b-e3be-49c3-9e3a-a9567b5c2836
}
// ... many fields omitted
status: class V1NodeStatus {
addresses: [class V1NodeAddress {
address: 192.168.71.134
type: InternalIP
}, class V1NodeAddress {
address: rancher-template
type: Hostname
}]
allocatable: {
cpu=Quantity{number=1, format=DECIMAL_SI},
ephemeral-storage=Quantity{number=18945365592, format=DECIMAL_SI},
hugepages-1Gi=Quantity{number=0, format=DECIMAL_SI},
hugepages-2Mi=Quantity{number=0, format=DECIMAL_SI},
memory=Quantity{number=8340054016, format=BINARY_SI},
pods=Quantity{number=110, format=DECIMAL_SI}
}
capacity: {
cpu=Quantity{number=1, format=DECIMAL_SI},
ephemeral-storage=Quantity{number=19942490112, format=BINARY_SI},
hugepages-1Gi=Quantity{number=0, format=DECIMAL_SI},
hugepages-2Mi=Quantity{number=0, format=DECIMAL_SI},
memory=Quantity{number=8340054016, format=BINARY_SI},
pods=Quantity{number=110, format=DECIMAL_SI}}
conditions: [
// ... node conditions omitted
]
nodeInfo: class V1NodeSystemInfo {
architecture: amd64
kernelVersion: 4.15.0-135-generic
kubeProxyVersion: v1.20.2+k3s1
kubeletVersion: v1.20.2+k3s1
operatingSystem: linux
osImage: Ubuntu 18.04.5 LTS
// ... more fields omitted
}
}
}
As we can see, there’s quite a lot of information available. For comparison, this is the equivalent kubectl output with default settings:
我们可以看到,有相当多的信息可用。作为比较,这是默认设置下的同等kubectl输出。
root@rancher-template:~# kubectl get nodes
NAME STATUS ROLES AGE VERSION
rancher-template Ready control-plane,master 24h v1.20.2+k3s1
6. Conclusion
6.结语
In this article, we’ve presented a quick intro to the Kubernetes API for Java. In future articles, we’ll dig deeper into this API and explore some of its additional features:
在这篇文章中,我们对Kubernetes API for Java进行了快速介绍。在未来的文章中,我们将更深入地挖掘这个API,并探索它的一些额外功能。
- Explain the difference between the available API call variants
- Using Watch to monitor cluster events in realtime
- How to use paging to efficiently retrieve a large volume of data from a cluster
As usual, the full source code of the examples can be found over on GitHub.
像往常一样,这些例子的完整源代码可以在GitHub上找到。