Quarkus 提供 kubernetes-client 扩展,使 Fabric8 Kubernetes Client 能在原生模式下运行,同时简化使用。 Fabric8 Kubernetes Client
这个扩展有助于发挥 Kubernetes Operator 的能力。Operator 正在成为一类新的云原生应用:它们监听 Kubernetes API,对资源变化作出响应,管理数据库、消息系统等复杂系统的生命周期。用 Java 编写这类 Operator,再利用原生镜像较低的资源占用,是很合适的组合。
配置 相关文档
配置好 Quarkus 项目后,在项目根目录运行以下命令,添加 kubernetes-client 扩展。
quarkus extension add kubernetes-client
./mvnw quarkus:add-extension -Dextensions='kubernetes-client'
./gradlew addExtension --extensions='kubernetes-client'
这会在构建文件中加入:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kubernetes-client</artifactId>
</dependency>
implementation("io.quarkus:quarkus-kubernetes-client")
用法 相关文档
Quarkus 配置一个 KubernetesClient 类型的 bean,可以通过标准 CDI 方法注入应用。客户端支持多种属性配置,如下例:
quarkus.kubernetes-client.trust-certs=false
quarkus.kubernetes-client.namespace=default
完整属性列表见配置参考中的 Dev Services 一节。 Dev Services section of the configuration reference
开发模式和运行测试时,Dev Services for Kubernetes 会自动启动 Kubernetes API 服务器。 Dev Services for Kubernetes
定制与覆盖 相关文档
Quarkus 提供多个集成点,允许调整以 CDI bean 提供的 Kubernetes Client。
定制 Kubernetes Client Config 相关文档
第一个集成点是 io.quarkus.kubernetes.client.KubernetesConfigCustomizer 接口。存在该接口的 bean 时,可以任意调整 Quarkus 创建的 io.fabric8.kubernetes.client.Config;该配置已考虑 quarkus.kubernetes-client.* 属性。
也可以声明自己的 bean,覆盖扩展通常提供的 io.fabric8.kubernetes.client.Config,甚至 io.fabric8.kubernetes.client.KubernetesClient。
例如:
@Singleton
public class KubernetesClientProducer {
@Produces
public KubernetesClient kubernetesClient() {
// here you would create a custom client
return new DefaultKubernetesClient();
}
}
定制 Kubernetes Client ObjectMapper 相关文档
Fabric8 Kubernetes Client 使用自己的 ObjectMapper 实例序列化和反序列化 Kubernetes 资源。该映射器通过 KubernetesSerialization 实例提供,后者注入 KubernetesClient bean。
需要定制扩展提供、供客户端使用的默认 ObjectMapper 时,可以声明实现 KubernetesClientObjectMapperCustomizer 接口的 bean。
下面用 KubernetesClientObjectMapperCustomizer 设置 ObjectMapper 的 locale:
@Singleton
public static class Customizer implements KubernetesClientObjectMapperCustomizer {
@Override
public void customize(ObjectMapper objectMapper) {
objectMapper.setLocale(Locale.ROOT);
}
}
如果需要替换扩展自动创建的默认 ObjectMapper,可以声明带 @KubernetesClientObjectMapper 的 bean,如下所示:
@Singleton
public class KubernetesObjectMapperProducer {
@KubernetesClientObjectMapper
@Singleton
@Produces
public ObjectMapper kubernetesClientObjectMapper() {
return new ObjectMapper();
}
}
| 静态工具类 io.fabric8.kubernetes.client.utils.Serialization 已弃用,不应继续使用。对 Serialization.jsonMapper() 的访问应改为使用声明的 KubernetesClientObjectMapperCustomizer bean。 |
测试 相关文档
Quarkus 提供 WithKubernetesTestServer 注解,自动启动模拟 Kubernetes API 服务器并设置相应环境变量,使客户端自动连接到该模拟服务器。测试可通过 @KubernetesTestServer 注入服务器,按具体测试需求配置它。
假设有以下 REST 端点:
@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();
}
}
可以很容易地为它编写测试:
// 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 依赖,例如:
<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 请求:
// 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 实例:
@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,通过 QuarkusTestResource 注解,让所有启用 @QuarkusTest 的测试类共享同样的模拟服务器设置:
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();
}
}
在其他测试类中这样使用:
@QuarkusTestResource(CustomKubernetesMockServerTestResource.class)
@QuarkusTest
public class KubernetesClientTest {
//tests will now use the configured server...
}
实现或继承泛型类型的注意事项 相关文档
受 GraalVM 限制,如果应用要在原生模式运行,实现或继承客户端提供的泛型类型时必须格外注意。Watcher、ResourceHandler、CustomResource 等泛型类的每个实现或子类,都必须在类定义时指定相应的 Kubernetes 模型类型;CustomResource 对应普通 Java 类型。例如监听 Pod 资源变化时,以下 Watcher 写法可以保证原生模式正常工作:
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());
通过如下类继承层次指定泛型类型,也能正常工作:
public abstract class MyWatcher<S> implements Watcher<S> {
}
...
client.pods().watch(new MyWatcher<Pod>() {
@Override
public void eventReceived(Action action, Pod pod) {
// do something
}
});
| 下面的例子无法在原生模式运行,因为仅通过类和方法定义无法确定 watcher 的泛型类型,Quarkus 因而不能正确判断哪些 Kubernetes 模型类型需要注册反射: |
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>());
使用椭圆曲线密钥的注意事项 相关文档
Kubernetes Client 使用椭圆曲线密钥时,需要添加 BouncyCastle PKIX 依赖:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
</dependency>
implementation("org.bouncycastle:bcpkix-jdk18on")
如果尚未注册,内部会注册 org.bouncycastle.jce.provider.BouncyCastleProvider 提供者。
也可以按照 BouncyCastle 或 BouncyCastle FIPS 章节的说明注册该提供者。 BouncyCastle BouncyCastle FIPS
访问 Kubernetes API 相关文档
很多情况下,访问 Kubernetes API 需要 ServiceAccount、Role 和 RoleBinding。允许列出所有 Pod 的例子如下:
---
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> 替换成实际值。更多信息见为 Pod 配置 ServiceAccount 的文档。 Configure Service Accounts for Pods
OpenShift 客户端 相关文档
目标集群为 OpenShift 时,可以用类似方式通过 openshift-client 扩展访问。它使用专门的 Fabric8 OpenShift 客户端,可访问 Route、ProjectRequest、BuildConfig 等 OpenShift 专有对象。
配置属性与 kubernetes-client 扩展共享,尤其是相同的 quarkus.kubernetes-client 前缀。
用以下命令添加扩展:
quarkus extension add openshift-client
./mvnw quarkus:add-extension -Dextensions='openshift-client'
./gradlew addExtension --extensions='openshift-client'
openshift-client 扩展依赖 kubernetes-client。
使用时注入 OpenShiftClient,而不是 KubernetesClient:
@Inject
private OpenShiftClient openshiftClient;
需要覆盖默认 OpenShiftClient 时,提供如下生产者:
@Singleton
public class OpenShiftClientProducer {
@Produces
public OpenShiftClient openshiftClient() {
// here you would create a custom client
return new DefaultOpenShiftClient();
}
}
模拟测试同样使用前面介绍的 @WithKubernetesTestServer:
@WithKubernetesTestServer
@QuarkusTest
public class OpenShiftClientTest {
@KubernetesTestServer
KubernetesServer mockServer;
@Inject
OpenShiftClient client;
@Test
public void testInteractionWithAPIServer() {
RestAssured.when().get("/route/test").then()
.body("size()", is(2));
}
}
使用该功能需添加 quarkus-test-kubernetes-client 依赖:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-kubernetes-client</artifactId>
<scope>test</scope>
</dependency>
testImplementation("io.quarkus:quarkus-test-kubernetes-client")
优化原生镜像 相关文档
Kubernetes 和 OpenShift 客户端扩展旨在提供良好开发体验,并支持原生模式。构建原生镜像时,Kubernetes Client 扩展会为所有可访问的 Kubernetes 模型类注册反射,但这可能增大镜像并延长构建时间。
完成应用实现后,如果要以原生镜像分发和部署,可以按以下建议缩小体积。
使用 Kubernetes Client 扩展 相关文档
OpenShift Client 为常见 OpenShift 资源提供领域专用语言(DSL)访问器,并自动引入 OpenShift 模型类型模块所需的项目配置。 OpenShift Client
JVM 模式下这样很方便,无需操心配置;但原生模式中依赖 OpenShift 扩展会引入许多可能不需要的资源,不必要地增大镜像。
此时最好只依赖实际需要的内容:添加 Kubernetes Client 扩展以及最少的 OpenShift 模型依赖。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kubernetes-client</artifactId>
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>openshift-model</artifactId>
</dependency>
implementation("io.quarkus:quarkus-kubernetes-client")
implementation("io.fabric8:openshift-model")
因为 bean 类型现在是 KubernetesClient 而不是 OpenShiftClient,OpenShift 专用 DSL 访问器不可用。不过 Fabric8 Kubernetes Client 提供了操作任意资源的通用入口:
// List OpenShift Routes in any namespace
kubernetesClient
.resources(io.fabric8.openshift.api.model.Route.class)
.inAnyNamespace().list();
// Delete an OpenShift Route
kubernetesClient
.resources(io.fabric8.openshift.api.model.Route.class)
.inNamespace("default").withName("the-route").delete();
// Create or replace a new OpenShift Route
kubernetesClient
.resource(new RouteBuilder()/* ... */.build())
.inNamespace("default").createOr(NonDeletingOperation::update);
只依赖需要的模块 相关文档
Kubernetes Client 扩展传递依赖所有标准 Kubernetes API 模型类型。JVM 模式下很方便,无需调整项目配置。
但原生模式下,这意味着为很可能不会使用的模型注册反射。可以更细致地配置项目,只依赖应用实际使用的模型。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kubernetes-client</artifactId>
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-client-api</artifactId>
<!-- Exclude all transitive dependencies -->
<exclusions>
<exclusion>
<groupId>io.fabric8</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Include only those that make sense for your application -->
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-client</artifactId>
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-model-core</artifactId>
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-model-admissionregistration</artifactId>
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-model-apps</artifactId>
</dependency>
<!-- ... -->
implementation("quarkus-kubernetes-client")
implementation("io.fabric8:kubernetes-client-api") {
// Exclude all transitive dependencies
exclude group: "io.fabric8"
}
// Include only those that make sense for your application
implementation("io.fabric8:kubernetes-client")
implementation("io.fabric8:kubernetes-model-core")
implementation("io.fabric8:kubernetes-model-admissionregistration")
implementation("io.fabric8:kubernetes-model-apps")
// ...
配置参考 相关文档
带标记的配置属性在构建时固定,其他属性可在运行时覆盖。
|
配置属性 |
类型 |
默认值 |
|---|---|---|
|
是否生成 RBAC 清单。启用后,如果没有通过 quarkus.kubernetes.rbac. 属性提供其他角色绑定,会使用 view 角色和应用 ServiceAccount 生成默认角色绑定。 Environment variable: 详细说明 |
布尔值 |
|
|
客户端是否信任 API 服务器提供的自签名证书。 Environment variable: 详细说明 |
布尔值 |
|
|
Kubernetes API 服务器 URL。 Environment variable: 详细说明 |
字符串 |
|
|
默认使用的命名空间。 Environment variable: 详细说明 |
字符串 |
|
|
CA 证书文件。 Environment variable: 详细说明 |
字符串 |
|
|
CA 证书数据。 Environment variable: 详细说明 |
字符串 |
|
|
用于配置客户端的 kubeconfig 文件路径。设置后读取该文件,并用作基础配置。 Environment variable: 详细说明 |
字符串 |
|
|
客户端证书文件。 Environment variable: 详细说明 |
字符串 |
|
|
客户端证书数据。 Environment variable: 详细说明 |
字符串 |
|
|
客户端密钥文件。 Environment variable: 详细说明 |
字符串 |
|
|
客户端密钥数据。 Environment variable: 详细说明 |
字符串 |
|
|
客户端密钥算法。 Environment variable: 详细说明 |
字符串 |
|
|
客户端密钥口令。 Environment variable: 详细说明 |
字符串 |
|
|
Kubernetes 身份验证用户名。 Environment variable: 详细说明 |
字符串 |
|
|
Kubernetes 身份验证密码。 Environment variable: 详细说明 |
字符串 |
|
|
Kubernetes OAuth 令牌。 Environment variable: 详细说明 |
字符串 |
|
|
监听重连间隔。 Environment variable: 详细说明 |
||
|
监听失败时的最大重连次数。默认不限制次数。 Environment variable: 详细说明 |
整数 |
|
|
等待与 API 服务器建立连接的最长时间。 Environment variable: 详细说明 |
||
|
等待 API 请求完成的最长时间。 Environment variable: 详细说明 |
||
|
API 请求因 HTTP 状态码大于或等于500失败时,最大重试次数。 Environment variable: 详细说明 |
整数 |
|
|
API 请求因 HTTP 状态码大于或等于500失败时,两次重试之间的时间间隔。 Environment variable: 详细说明 |
||
|
访问 Kubernetes API 服务器所用 HTTP 代理。 Environment variable: 详细说明 |
字符串 |
|
|
访问 Kubernetes API 服务器所用 HTTPS 代理。 Environment variable: 详细说明 |
字符串 |
|
|
代理用户名。 Environment variable: 详细说明 |
字符串 |
|
|
代理密码。 Environment variable: 详细说明 |
字符串 |
|
|
不经过代理的 IP 地址或主机。 Environment variable: 详细说明 |
字符串列表 |
|
|
开发服务(Dev Services) Dev Services |
类型 |
默认值 |
|
是否使用 Dev Services for Kubernetes,默认为 true。 为 true 且未配置 Kubernetes 客户端时,会启动并使用 Kubernetes 集群。 Environment variable: 详细说明 |
布尔值 |
|
|
使用的 Kubernetes API 服务器版本。 未设置时,使用该 flavor 支持的最新版本。 latest supported version Environment variable: 详细说明 |
字符串 |
|
|
使用的 Kubernetes 镜像。 未设置时,使用指定 flavor() 与 api-version() 对应的默认镜像。 Environment variable: 详细说明 |
字符串 |
|
|
使用的集群类型:kind、k3s 或 api-only。 未设置时,默认为 api-only。 Environment variable: 详细说明 |
kind:需要特权 Docker;k3s:需要特权 Docker;api-only:仅 API。 |
|
|
默认情况下,发现 kubeconfig 后不会启动 Dev Services for Kubernetes。设为 true 可覆盖 kubeconfig 配置。 Environment variable: 详细说明 |
布尔值 |
|
|
启动 Kubernetes 集群开发服务时要应用的清单文件路径列表。未设置时不应用任何清单。 Environment variable: 详细说明 |
字符串列表 |
|
|
是否共享 Quarkus Dev Services 管理的 Kubernetes 集群。共享时,Quarkus 通过基于标签的服务发现查找运行中的容器;找到匹配容器就复用,不再启动第二个,否则启动新容器。 发现机制使用 quarkus-dev-service-kubernetes 标签,其值通过 service-name 属性配置。 仅开发模式使用容器共享。 Environment variable: 详细说明 |
布尔值 |
|
|
启动的容器上 quarkus-dev-service-kubernetes 标签的值。shared 为 true 时使用此属性。启动容器前,Dev Services 会查找标签值匹配的容器;找到则复用,否则启动带有指定标签值的新容器。 需要多个共享 Kubernetes 集群时,使用此属性。 Environment variable: 详细说明 |
字符串 |
|
|
传给容器的环境变量。 Environment variable: 详细说明 |
Map<String,String> |
|
About the Duration format
时长使用标准 java.time.Duration 格式,详情见 Duration#parse() Java API 文档。 Duration#parse() Java API documentation 也可以使用以数字开头的简化格式:
其他简化格式会先转换为 java.time.Duration 格式再解析:
|











暂无评论内容