Quarkus Kubernetes 客户端

Quarkus 提供 kubernetes-client 扩展,使 Fabric8 Kubernetes Client 能在原生模式下运行,同时简化使用。 Fabric8 Kubernetes Client

这个扩展有助于发挥 Kubernetes Operator 的能力。Operator 正在成为一类新的云原生应用:它们监听 Kubernetes API,对资源变化作出响应,管理数据库、消息系统等复杂系统的生命周期。用 Java 编写这类 Operator,再利用原生镜像较低的资源占用,是很合适的组合。

配置 相关文档

配置好 Quarkus 项目后,在项目根目录运行以下命令,添加 kubernetes-client 扩展。

CLI
quarkus extension add kubernetes-client
Maven
./mvnw quarkus:add-extension -Dextensions='kubernetes-client'
Gradle
./gradlew addExtension --extensions='kubernetes-client'

这会在构建文件中加入:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-kubernetes-client</artifactId>
</dependency>
build.gradle
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 依赖,例如:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-test-kubernetes-client</artifactId>
    <scope>test</scope>
</dependency>
build.gradle
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 依赖:

pom.xml
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpkix-jdk18on</artifactId>
</dependency>
build.gradle
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 前缀。

用以下命令添加扩展:

CLI
quarkus extension add openshift-client
Maven
./mvnw quarkus:add-extension -Dextensions='openshift-client'
Gradle
./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 依赖:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-test-kubernetes-client</artifactId>
    <scope>test</scope>
</dependency>
build.gradle
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 模型依赖。

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-kubernetes-client</artifactId>
</dependency>
<dependency>
    <groupId>io.fabric8</groupId>
    <artifactId>openshift-model</artifactId>
</dependency>
build.gradle
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 模式下很方便,无需调整项目配置。

但原生模式下,这意味着为很可能不会使用的模型注册反射。可以更细致地配置项目,只依赖应用实际使用的模型。

pom.xml
<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>
<!-- ... -->
build.gradle
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: QUARKUS_KUBERNETES_CLIENT_GENERATE_RBAC

详细说明

布尔值

true

客户端是否信任 API 服务器提供的自签名证书。

Environment variable: QUARKUS_KUBERNETES_CLIENT_TRUST_CERTS

详细说明

布尔值

Kubernetes API 服务器 URL。

Environment variable: QUARKUS_KUBERNETES_CLIENT_API_SERVER_URL

详细说明

字符串

默认使用的命名空间。

Environment variable: QUARKUS_KUBERNETES_CLIENT_NAMESPACE

详细说明

字符串

CA 证书文件。

Environment variable: QUARKUS_KUBERNETES_CLIENT_CA_CERT_FILE

详细说明

字符串

CA 证书数据。

Environment variable: QUARKUS_KUBERNETES_CLIENT_CA_CERT_DATA

详细说明

字符串

用于配置客户端的 kubeconfig 文件路径。设置后读取该文件,并用作基础配置。

Environment variable: QUARKUS_KUBERNETES_CLIENT_KUBECONFIG_FILE

详细说明

字符串

客户端证书文件。

Environment variable: QUARKUS_KUBERNETES_CLIENT_CLIENT_CERT_FILE

详细说明

字符串

客户端证书数据。

Environment variable: QUARKUS_KUBERNETES_CLIENT_CLIENT_CERT_DATA

详细说明

字符串

客户端密钥文件。

Environment variable: QUARKUS_KUBERNETES_CLIENT_CLIENT_KEY_FILE

详细说明

字符串

客户端密钥数据。

Environment variable: QUARKUS_KUBERNETES_CLIENT_CLIENT_KEY_DATA

详细说明

字符串

客户端密钥算法。

Environment variable: QUARKUS_KUBERNETES_CLIENT_CLIENT_KEY_ALGO

详细说明

字符串

客户端密钥口令。

Environment variable: QUARKUS_KUBERNETES_CLIENT_CLIENT_KEY_PASSPHRASE

详细说明

字符串

Kubernetes 身份验证用户名。

Environment variable: QUARKUS_KUBERNETES_CLIENT_USERNAME

详细说明

字符串

Kubernetes 身份验证密码。

Environment variable: QUARKUS_KUBERNETES_CLIENT_PASSWORD

详细说明

字符串

Kubernetes OAuth 令牌。

Environment variable: QUARKUS_KUBERNETES_CLIENT_TOKEN

详细说明

字符串

监听重连间隔。

Environment variable: QUARKUS_KUBERNETES_CLIENT_WATCH_RECONNECT_INTERVAL

详细说明

时长 

监听失败时的最大重连次数。默认不限制次数。

Environment variable: QUARKUS_KUBERNETES_CLIENT_WATCH_RECONNECT_LIMIT

详细说明

整数

等待与 API 服务器建立连接的最长时间。

Environment variable: QUARKUS_KUBERNETES_CLIENT_CONNECTION_TIMEOUT

详细说明

时长 

等待 API 请求完成的最长时间。

Environment variable: QUARKUS_KUBERNETES_CLIENT_REQUEST_TIMEOUT

详细说明

时长 

API 请求因 HTTP 状态码大于或等于500失败时,最大重试次数。

Environment variable: QUARKUS_KUBERNETES_CLIENT_REQUEST_RETRY_BACKOFF_LIMIT

详细说明

整数

API 请求因 HTTP 状态码大于或等于500失败时,两次重试之间的时间间隔。

Environment variable: QUARKUS_KUBERNETES_CLIENT_REQUEST_RETRY_BACKOFF_INTERVAL

详细说明

时长 

访问 Kubernetes API 服务器所用 HTTP 代理。

Environment variable: QUARKUS_KUBERNETES_CLIENT_HTTP_PROXY

详细说明

字符串

访问 Kubernetes API 服务器所用 HTTPS 代理。

Environment variable: QUARKUS_KUBERNETES_CLIENT_HTTPS_PROXY

详细说明

字符串

代理用户名。

Environment variable: QUARKUS_KUBERNETES_CLIENT_PROXY_USERNAME

详细说明

字符串

代理密码。

Environment variable: QUARKUS_KUBERNETES_CLIENT_PROXY_PASSWORD

详细说明

字符串

不经过代理的 IP 地址或主机。

Environment variable: QUARKUS_KUBERNETES_CLIENT_NO_PROXY

详细说明

字符串列表

开发服务(Dev Services) Dev Services

类型

默认值

是否使用 Dev Services for Kubernetes,默认为 true。

为 true 且未配置 Kubernetes 客户端时,会启动并使用 Kubernetes 集群。

Environment variable: QUARKUS_KUBERNETES_CLIENT_DEVSERVICES_ENABLED

详细说明

布尔值

true

使用的 Kubernetes API 服务器版本。

未设置时,使用该 flavor 支持的最新版本。 latest supported version

Environment variable: QUARKUS_KUBERNETES_CLIENT_DEVSERVICES_API_VERSION

详细说明

字符串

使用的 Kubernetes 镜像。

未设置时,使用指定 flavor() 与 api-version() 对应的默认镜像。

Environment variable: QUARKUS_KUBERNETES_CLIENT_DEVSERVICES_IMAGE_NAME

详细说明

字符串

使用的集群类型:kind、k3s 或 api-only。

未设置时,默认为 api-only。

Environment variable: QUARKUS_KUBERNETES_CLIENT_DEVSERVICES_FLAVOR

详细说明

kind:需要特权 Docker;k3s:需要特权 Docker;api-only:仅 API。

默认情况下,发现 kubeconfig 后不会启动 Dev Services for Kubernetes。设为 true 可覆盖 kubeconfig 配置。

Environment variable: QUARKUS_KUBERNETES_CLIENT_DEVSERVICES_OVERRIDE_KUBECONFIG

详细说明

布尔值

false

启动 Kubernetes 集群开发服务时要应用的清单文件路径列表。未设置时不应用任何清单。

Environment variable: QUARKUS_KUBERNETES_CLIENT_DEVSERVICES_MANIFESTS

详细说明

字符串列表

是否共享 Quarkus Dev Services 管理的 Kubernetes 集群。共享时,Quarkus 通过基于标签的服务发现查找运行中的容器;找到匹配容器就复用,不再启动第二个,否则启动新容器。

发现机制使用 quarkus-dev-service-kubernetes 标签,其值通过 service-name 属性配置。

仅开发模式使用容器共享。

Environment variable: QUARKUS_KUBERNETES_CLIENT_DEVSERVICES_SHARED

详细说明

布尔值

true

启动的容器上 quarkus-dev-service-kubernetes 标签的值。shared 为 true 时使用此属性。启动容器前,Dev Services 会查找标签值匹配的容器;找到则复用,否则启动带有指定标签值的新容器。

需要多个共享 Kubernetes 集群时,使用此属性。

Environment variable: QUARKUS_KUBERNETES_CLIENT_DEVSERVICES_SERVICE_NAME

详细说明

字符串

kubernetes

传给容器的环境变量。

Environment variable: QUARKUS_KUBERNETES_CLIENT_DEVSERVICES_CONTAINER_ENV__ENVIRONMENT_VARIABLE_NAME_

详细说明

Map<String,String>

About the Duration format

时长使用标准 java.time.Duration 格式,详情见 Duration#parse() Java API 文档。 Duration#parse() Java API documentation

也可以使用以数字开头的简化格式:

  • 只有数字时,表示秒。

  • 数字后跟 ms 时,表示毫秒。

其他简化格式会先转换为 java.time.Duration 格式再解析:

  • 数字后跟 h、m 或 s 时,添加 PT 前缀。

  • 数字后跟 d 时,添加 P 前缀。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容