Kubernetes Client
Quarkus 包含 kubernetes-client
扩展,它支持在原生模式中使用 Fabric8 Kubernetes Client,同时也使用户更容易对其进行操作。
Quarkus includes the kubernetes-client
extension which enables the use of the Fabric8 Kubernetes Client
in native mode while also making it easier to work with.
Quarkus 中有 Kubernetes 客户端扩展对于解锁 Kubernetes Operators 非常有用。Kubernetes Operators 正迅速成为一类新的云原生应用程序。这些应用程序本质上会监视 Kubernetes API 并对各种资源上的更改做出反应,并且可用于管理所有类型复杂系统的生命周期,如数据库、消息系统等等。能够使用原生映像提供的极低开销用 Java 编写这样的操作员非常匹配。
Having a Kubernetes Client extension in Quarkus is very useful in order to unlock the power of Kubernetes Operators. Kubernetes Operators are quickly emerging as a new class of Cloud Native applications. These applications essentially watch the Kubernetes API and react to changes on various resources and can be used to manage the lifecycle of all kinds of complex systems like databases, messaging systems and much more. Being able to write such operators in Java with the very low footprint that native images provide is a great match.
Configuration
配置好 Quarkus 项目后,您可以通过在项目基目录中运行以下命令将 kubernetes-client
扩展添加到项目。
Once you have your Quarkus project configured you can add the kubernetes-client
extension
to your project by running the following command in your project base directory.
Unresolved directive in kubernetes-client.adoc - include::{includes}/devtools/extension-add.adoc[]
这会将以下内容添加到构建文件中:
This will add the following to your build file:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kubernetes-client</artifactId>
</dependency>
implementation("io.quarkus:quarkus-kubernetes-client")
Usage
Quarkus 配置了 KubernetesClient
类型的 Bean,可以使用常见的 CDI 方法将其注入到应用程序代码中。此客户机可以通过各种属性进行配置,如下例所示:
Quarkus configures a Bean of type KubernetesClient
which can be injected into application code using the well known CDI methods.
This client can be configured using various properties as can be seen in the following example:
quarkus.kubernetes-client.trust-certs=false
quarkus.kubernetes-client.namespace=default
请注意,完整属性列表在 Dev Services section of the configuration reference 中可用。
Note that the full list of properties is available in the quarkus-kubernetes-client_section_quarkus-kubernetes-client-devservices.
在开发模式和运行测试时,Dev Services for Kubernetes 会自动启动 Kubernetes API 服务器。
In dev mode and when running tests, Dev Services for Kubernetes automatically starts a Kubernetes API server.
Customizing and overriding
Quarkus 提供多个集成点,以影响作为 CDI bean 提供的 Kubernetes Client。
Quarkus provides multiple integration points for influencing the Kubernetes Client provided as a CDI bean.
Kubernetes Client Config customization
第一个集成点是使用 io.quarkus.kubernetes.client.KubernetesConfigCustomizer
接口。当存在这样的 Bean 时,它允许对 Quarkus 创建的 io.fabric8.kubernetes.client.Config
进行任意自定义(考虑了 quarkus.kubernetes-client.*
属性)。
The first integration point is the use of the io.quarkus.kubernetes.client.KubernetesConfigCustomizer
interface. When such a bean exists,
it allows for arbitrary customizations of the io.fabric8.kubernetes.client.Config
created by Quarkus (which takes into account the quarkus.kubernetes-client.*
properties).
或者,应用程序代码可以通过简单地声明这些 bean 的自定义版本来覆盖 io.fabric8.kubernetes.client.Config
甚至 io.fabric8.kubernetes.client.KubernetesClient
bean(这些 bean 通常由扩展提供)。
Alternatively, application code can override the io.fabric8.kubernetes.client.Config
or even the io.fabric8.kubernetes.client.KubernetesClient
bean (which are
normally provided by the extension) by simply declaring custom versions of those beans.
以下代码段中可以找到一个示例:
An example of this can be seen in the following snippet:
@Singleton
public class KubernetesClientProducer {
@Produces
public KubernetesClient kubernetesClient() {
// here you would create a custom client
return new DefaultKubernetesClient();
}
}
Kubernetes Client ObjectMapper customization
Fabric8 Kubernetes Client 使用自己的 ObjectMapper
实例来序列化和反序列化 Kubernetes 资源。此映射器通过注入到 KubernetesClient
bean 中的 KubernetesSerialization
实例提供给客户端。
The Fabric8 Kubernetes Client uses its own ObjectMapper
instance for serialization and deserialization of Kubernetes resources.
This mapper is provided to the client through a KubernetesSerialization
instance that’s injected into
the KubernetesClient
bean.
如果出于某种原因必须自定义此扩展提供的并由 Kubernetes Client 使用的默认 ObjectMapper
bean,则可以通过声明实现 KubernetesClientObjectMapperCustomizer
接口的 bean 来执行此操作。
If for some reason you must customize the default ObjectMapper
bean provided by this extension and used by the Kubernetes Client, you can do so by declaring a bean that implements the KubernetesClientObjectMapperCustomizer
interface.
以下代码段包含 KubernetesClientObjectMapperCustomizer
的示例,用于设置 ObjectMapper
本地语言:
The following code snippet contains an example of a KubernetesClientObjectMapperCustomizer
to set the ObjectMapper
locale:
@Singleton
public static class Customizer implements KubernetesClientObjectMapperCustomizer {
@Override
public void customize(ObjectMapper objectMapper) {
objectMapper.setLocale(Locale.ROOT);
}
}
此外,如果必须替换扩展自动创建的 Kubernetes Client 使用的默认 ObjectMapper
bean,则可以通过声明 @KubernetesClientObjectMapper
类型 bean 来执行此操作。以下代码段展示了如何声明此 bean:
Furthermore, if you must replace the default ObjectMapper
bean used by the Kubernetes Client that the extension creates automatically, you can do so by declaring a bean of type @KubernetesClientObjectMapper
.
The following code snippet shows how you can declare this bean:
@Singleton
public class KubernetesObjectMapperProducer {
@KubernetesClientObjectMapper
@Singleton
@Produces
public ObjectMapper kubernetesClientObjectMapper() {
return new ObjectMapper();
}
}
静态 io.fabric8.kubernetes.client.utils.Serialization
实用程序类已弃用,不应使用。对 Serialization.jsonMapper()
的访问应替换为使用声明的 @KubernetesClientObjectMapperCustomizer`。
The static io.fabric8.kubernetes.client.utils.Serialization
utils class is deprecated and should not be used.
Access to Serialization.jsonMapper()
should be replaced by the usage of @KubernetesClientObjectMapperCustomizer` declared beans.
Testing
为了让针对模拟 Kubernetes API 服务器的测试非常简单,Quarkus 提供了 WithKubernetesTestServer
注释,该注释会自动启动 Kubernetes API 服务器 simulatio,并设置 Kubernetes Client 配置自身以使用该模拟所需的环境变量。测试可以使用 @KubernetesTestServer
注释注入模拟服务器,并以特定测试所需的任何方式设置它。
To make testing against a mock Kubernetes API extremely simple, Quarkus provides the WithKubernetesTestServer
annotation which automatically launches
a mock of the Kubernetes API server and sets the proper environment variables needed so that the Kubernetes Client configures itself to use said mock.
Tests can inject the mock server and set it up in any way necessary for the particular testing using the @KubernetesTestServer
annotation.
假设我们有一个这样定义的 REST 端点:
Let’s assume we have a REST endpoint defined like so:
@Path("/pod")
public class Pods {
private final KubernetesClient kubernetesClient;
public Pods(KubernetesClient kubernetesClient) {
this.kubernetesClient = kubernetesClient;
}
@GET
@Path("/{namespace}")
public List<Pod> pods(String namespace) {
return kubernetesClient.pods().inNamespace(namespace).list().getItems();
}
}
我们可以像这样非常轻松地为这个端点编写一个测试:
We could write a test for this endpoint very easily like so:
// you can even configure aspects like crud, https and port on this annotation
@WithKubernetesTestServer
@QuarkusTest
public class KubernetesClientTest {
@KubernetesTestServer
KubernetesServer mockServer;
@Inject
KubernetesClient client;
@BeforeEach
public void before() {
final Pod pod1 = new PodBuilder().withNewMetadata().withName("pod1").withNamespace("test").and().build();
final Pod pod2 = new PodBuilder().withNewMetadata().withName("pod2").withNamespace("test").and().build();
// Set up Kubernetes so that our "pretend" pods are created
client.pods().resource(pod1).create();
client.pods().resource(pod2).create();
}
@Test
public void testInteractionWithAPIServer() {
RestAssured.when().get("/pod/test").then()
.body("size()", is(2));
}
}
请注意,要利用这些功能,需要添加 quarkus-test-kubernetes-client
依赖项,例如如下所示:
Note that to take advantage of these features, the quarkus-test-kubernetes-client
dependency needs to be added, for example like so:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-kubernetes-client</artifactId>
<scope>test</scope>
</dependency>
testImplementation("io.quarkus:quarkus-test-kubernetes-client")
默认情况下,模拟服务器将处于 CRUD 模式,因此在应用程序可以检索该状态之前,必须使用客户端构建该状态,但你也可以以非 CRUD 模式对其进行设置,并模拟对 Kubernetes 进行的所有 HTTP 请求:
By default, the mock server will be in CRUD mode, so you have to use the client to build your state before your application can retrieve it, but you can also set it up in non-CRUD mode and mock all HTTP requests made to Kubernetes:
// you can even configure aspects like crud, https and port on this annotation
@WithKubernetesTestServer(crud = false)
@QuarkusTest
public class KubernetesClientTest {
@KubernetesTestServer
KubernetesServer mockServer;
@BeforeEach
public void before() {
final Pod pod1 = new PodBuilder().withNewMetadata().withName("pod1").withNamespace("test").and().build();
final Pod pod2 = new PodBuilder().withNewMetadata().withName("pod2").withNamespace("test").and().build();
// Mock any HTTP request to Kubernetes pods so that our pods are returned
mockServer.expect().get().withPath("/api/v1/namespaces/test/pods")
.andReturn(200,
new PodListBuilder().withNewMetadata().withResourceVersion("1").endMetadata().withItems(pod1, pod2)
.build())
.always();
}
@Test
public void testInteractionWithAPIServer() {
RestAssured.when().get("/pod/test").then()
.body("size()", is(2));
}
}
你还可以对 @WithKubernetesTestServer
注释使用 setup
属性,以提供一个类来配置 KubernetesServer
实例:
You can also use the setup
attribute on the @WithKubernetesTestServer
annotation to provide a class that will configure the KubernetesServer
instance:
@WithKubernetesTestServer(setup = MyTest.Setup.class)
@QuarkusTest
public class MyTest {
public static class Setup implements Consumer<KubernetesServer> {
@Override
public void accept(KubernetesServer server) {
server.expect().get().withPath("/api/v1/namespaces/test/pods")
.andReturn(200, new PodList()).always();
}
}
// tests
}
或者,你可以创建 KubernetesServerTestResource
类的扩展,以确保所有启用了 @QuarkusTest
的测试类通过 WithTestResource
注释共享相同的模拟服务器设置:
Alternately, you can create an extension of the KubernetesServerTestResource
class to ensure all your @QuarkusTest
enabled test classes share the same mock server setup via the WithTestResource
annotation:
public class CustomKubernetesMockServerTestResource extends KubernetesServerTestResource {
@Override
protected void configureServer() {
super.configureServer();
server.expect().get().withPath("/api/v1/namespaces/test/pods")
.andReturn(200, new PodList()).always();
}
}
并在其他测试类中按如下方式使用它:
and use this in your other test classes as follows:
@WithTestResource(CustomKubernetesMockServerTestResource.class)
@QuarkusTest
public class KubernetesClientTest {
//tests will now use the configured server...
}
Note on implementing or extending generic types
由于 GraalVM 施加的限制,如果应用程序要在原生模式下运行,在实现或扩展客户端提供的泛型类型时需要格外小心。本质上,Watcher
、ResourceHandler
或 CustomResource
等泛型类的每个实现或扩展都需要在类定义时间指定其关联的 Kubernetes 模型类(或在 CustomResource
的情况下是常规 Java 类型)。为了更好地理解这一点,假设我们要监视 Kubernetes Pod
资源的更改。有几种编写此类 Watcher
的方法可以确保原生模式下工作:
Due to the restrictions imposed by GraalVM, extra care needs to be taken when implementing or extending generic types provided by the client if the application is intended to work in native mode.
Essentially every implementation or extension of generic classes such as Watcher
, ResourceHandler
or CustomResource
needs to specify their associated Kubernetes model class (or, in the case of CustomResource
, regular Java types) at class definition time.
To better understand this, suppose we want to watch for changes to Kubernetes Pod
resources.
There are a couple ways to write such a Watcher
that are guaranteed to work in native:
client.pods().watch(new Watcher<Pod>() {
@Override
public void eventReceived(Action action, Pod pod) {
// do something
}
@Override
public void onClose(KubernetesClientException e) {
// do something
}
});
或
or
public class PodResourceWatcher implements Watcher<Pod> {
@Override
public void eventReceived(Action action, Pod pod) {
// do something
}
@Override
public void onClose(KubernetesClientException e) {
// do something
}
}
...
client.pods().watch(new PodResourceWatcher());
请注意,通过类似于以下示例的类层次结构定义泛型类型也能正常工作:
Note that defining the generic type via a class hierarchy similar to the following example will also work correctly:
public abstract class MyWatcher<S> implements Watcher<S> {
}
...
client.pods().watch(new MyWatcher<Pod>() {
@Override
public void eventReceived(Action action, Pod pod) {
// do something
}
});
以下示例将在原生模式下 not 工作,因为无法通过查看类和方法定义来确定观察者的泛型类型,因此让 Quarkus 无法正确确定需要进行反射注册的 Kubernetes 模型类:
The following example will not work in native mode because the generic type of watcher cannot be determined by looking at the class and method definitions thus making Quarkus unable to properly determine the Kubernetes model class for which reflection registration is needed:
public class ResourceWatcher<T extends HasMetadata> implements Watcher<T> {
@Override
public void eventReceived(Action action, T resource) {
// do something
}
@Override
public void onClose(KubernetesClientException e) {
// do something
}
}
client.pods().watch(new ResourceWatcher<Pod>());
Note on using Elliptic Curve keys
请注意,如果您想在 Kubernetes 客户端中使用椭圆曲线密钥,则需要添加 BouncyCastle PKIX 依赖项:
Please note that if you would like to use Elliptic Curve keys with Kubernetes Client then adding a BouncyCastle PKIX dependency is required:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
</dependency>
implementation("org.bouncycastle:bcpkix-jdk18on")
请注意,如果 org.bouncycastle.jce.provider.BouncyCastleProvider
提供程序尚未注册,则内部将会自动注册此提供程序。
Note that internally an org.bouncycastle.jce.provider.BouncyCastleProvider
provider will be registered if it has not already been registered.
您可以按照 BouncyCastle 或 BouncyCastle FIPS 部分中的说明将此提供程序注册。
You can have this provider registered as described in the BouncyCastle or BouncyCastle FIPS sections.
Access to the Kubernetes API
在很多情况下,为了访问 Kubernetes API 服务器,需要 ServiceAccount
、Role
和 RoleBinding
。下面是一个列出所有 pod 的示例:
In many cases in order to access the Kubernetes API server a ServiceAccount
, Role
and RoleBinding
will be necessary.
An example that allows listing all pods could look something like this:
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: <applicationName>
namespace: <namespace>
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: <applicationName>
namespace: <namespace>
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: <applicationName>
namespace: <namespace>
roleRef:
kind: Role
name: <applicationName>
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: <applicationName>
namespace: <namespace>
将 <applicationName>
和 <namespace>
替换为您自己的值。有关更多信息,请参考 Configure Service Accounts for Pods。
Replace <applicationName>
and <namespace>
with your values.
Have a look at Configure Service Accounts for Pods to get further information.
OpenShift Client
如果目标 Kubernetes 集群是 OpenShift 集群,则可以通过以下 openshift-client
扩展以类似方式访问它。这利用了专用的 fabric8openshift 客户端,并提供访问 OpenShift
专有对象(例如 Route
、ProjectRequest
、BuildConfig
等)的权限。
If the targeted Kubernetes cluster is an OpenShift cluster, it is possible to access it through
the openshift-client
extension, in a similar way. This leverages the dedicated fabric8
openshift client, and provides access to OpenShift
proprietary objects (e.g. Route
, ProjectRequest
, BuildConfig
…)
请注意,配置属性与 kubernetes-client
扩展共享。特别是,它们具有相同的 quarkus.kubernetes-client
前缀。
Note that the configuration properties are shared with the kubernetes-client
extension. In
particular, they have the same quarkus.kubernetes-client
prefix.
使用以下命令添加扩展:
Add the extension with:
Unresolved directive in kubernetes-client.adoc - include::{includes}/devtools/extension-add.adoc[]
请注意,openshift-client
扩展依赖于 kubernetes-client
扩展。
Note that openshift-client
extension has a dependency on the kubernetes-client
extension.
要使用客户端,请注入 OpenShiftClient
,而不是 KubernetesClient
:
To use the client, inject an OpenShiftClient
instead of the KubernetesClient
:
@Inject
private OpenShiftClient openshiftClient;
如果您需要覆盖默认 OpenShiftClient
,请提供一个生产者,例如:
If you need to override the default OpenShiftClient
, provide a producer such as:
@Singleton
public class OpenShiftClientProducer {
@Produces
public OpenShiftClient openshiftClient() {
// here you would create a custom client
return new DefaultOpenShiftClient();
}
}
还以类似的方式提供模拟支持:
Mock support is also provided in a similar fashion:
@WithTestResource(OpenShiftMockServerTestResource.class)
@QuarkusTest
public class OpenShiftClientTest {
@MockServer
private OpenShiftMockServer mockServer;
...
或者通过使用 @WithOpenShiftTestServer
,类似于前一部分中解释的 @WithKubernetesTestServer
:
Or by using the @WithOpenShiftTestServer
similar to the @WithKubernetesTestServer
explained in the
previous section:
@WithOpenShiftTestServer
@QuarkusTest
public class OpenShiftClientTest {
@OpenShiftTestServer
private OpenShiftServer mockOpenShiftServer;
...
要使用此功能,您必须添加对 quarkus-test-openshift-client
的依赖:
To use this feature, you have to add a dependency on quarkus-test-openshift-client
:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-openshift-client</artifactId>
<scope>test</scope>
</dependency>
testImplementation("io.quarkus:quarkus-test-openshift-client")