编辑此页面

将 Stork 与 Kubernetes 一起使用

本指南将介绍如何在 Kubernetes 中使用 Stork 进行服务发现和负载均衡。

如果您是 Stork 的新手,请阅读 Stork 入门指南

此技术被认为是预览版。

预览版本中,不保证向后兼容性和在生态系统中的存在性。特定的改进可能需要更改配置或 API,并且我们正在计划使其成为稳定版本。欢迎在我们的 邮件列表 或我们的 GitHub 问题跟踪器 中提出问题并提供反馈。

有关可能的完整状态列表,请查看我们的常见问题解答条目

先决条件

要完成本指南,您需要

  • 大约 15 分钟

  • 一个 IDE

  • 已安装 JDK 17+ 并正确配置了 JAVA_HOME

  • Apache Maven 3.9.9

  • 一个正常工作的容器运行时(Docker 或 Podman

  • 如果您想使用它,可以选择 Quarkus CLI

  • 如果您想构建本机可执行文件(或者如果您使用本机容器构建,则为 Docker),可以选择安装 Mandrel 或 GraalVM 并进行适当的配置

  • 可以访问 Kubernetes 集群(Minikube 是一个可行的选项)

架构

在本指南中,我们将使用部署在 Kubernetes 集群中的几个组件

  • 一个简单的蓝色服务。

  • 一个简单的红色服务。

  • color-service 是 Kubernetes 服务,它是 Blue 和 Red 实例的入口点。

  • 一个使用 REST 客户端调用蓝色或红色服务的客户端服务。服务发现和选择委托给 Stork。

Architecture of the application

为了简单起见,所有内容都将在 Kubernetes 集群的同一命名空间中部署。

解决方案

我们建议您遵循后续章节中的说明,一步一步地创建应用程序。但是,您可以直接查看完整的示例。

克隆 Git 仓库:git clone https://github.com/quarkusio/quarkus-quickstarts.git,或下载 存档

解决方案位于 stork-kubernetes-quickstart 目录中。

发现与选择

在深入之前,我们需要讨论发现与选择。

  • 服务发现是指定位服务实例的过程。它会生成一个可能为空(如果没有服务匹配请求)或包含多个服务实例的服务实例列表。

  • 服务选择,也称为负载均衡,从发现过程返回的列表中选择最佳实例。结果是单个服务实例,或者在找不到合适的实例时抛出异常。

Stork 同时处理发现和选择。但是,它不处理与服务的通信,只提供一个服务实例。Quarkus 中的各种集成从该服务实例中提取服务的位置。

引导项目

使用您喜欢的方式,创建一个导入 quarkus-rest-client、quarkus-rest 和 quarkus-smallrye-stork 扩展的 Quarkus 项目

CLI
quarkus create app org.acme:stork-kubernetes-quickstart \
    --extension='quarkus-rest-client,quarkus-rest,quarkus-smallrye-stork' \
    --no-code
cd stork-kubernetes-quickstart

要创建 Gradle 项目,请添加 --gradle--gradle-kotlin-dsl 选项。

有关如何安装和使用 Quarkus CLI 的更多信息,请参阅 Quarkus CLI 指南。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.24.4:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=stork-kubernetes-quickstart \
    -Dextensions='quarkus-rest-client,quarkus-rest,quarkus-smallrye-stork' \
    -DnoCode
cd stork-kubernetes-quickstart

要创建 Gradle 项目,请添加 -DbuildTool=gradle-DbuildTool=gradle-kotlin-dsl 选项。

对于 Windows 用户

  • 如果使用 cmd,(不要使用反斜杠 \ 并将所有内容放在同一行上)

  • 如果您使用 Powershell,请将 -D 参数括在双引号中,例如 "-DprojectArtifactId=stork-kubernetes-quickstart"

在生成的项目中,还要添加以下依赖项

pom.xml
<dependency>
    <groupId>io.smallrye.stork</groupId>
    <artifactId>stork-service-discovery-kubernetes</artifactId>
</dependency>
<dependency>
      <groupId>io.smallrye.stork</groupId>
      <artifactId>stork-load-balancer-random</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-kubernetes</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-kubernetes-client</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-container-image-jib</artifactId>
</dependency>
build.gradle
implementation("io.smallrye.stork:stork-service-discovery-kubernetes")
implementation("io.smallrye.stork:stork-load-balancer-random")
implementation("io.quarkus:quarkus-kubernetes")
implementation("io.quarkus:quarkus-kubernetes-client")
implementation("io.quarkus:quarkus-container-image-jib")

stork-service-discovery-kubernetes 提供了 Kubernetes 的服务发现实现。stork-load-balancer-random 提供了随机负载均衡器的实现。quarkus-kubernetes 允许我们在每次构建时生成 Kubernetes 清单。quarkus-kubernetes-client 扩展允许在原生模式下使用 Fabric8 Kubernetes 客户端。而 quarkus-container-image-jib 允许使用 Jib 构建容器镜像。

蓝色和红色服务

让我们从最基本的部分开始:我们将要发现、选择和调用的服务。

Red 和 Blue 是两个简单的 REST 服务,它们提供一个端点,分别响应 Hello from Red!Hello from Blue!。两个应用程序的代码都是按照 入门指南 开发的。

由于本指南的目的是展示如何使用 Stork Kubernetes 服务发现,我们将不提供 Red 和 Blue 服务的具体步骤。它们的容器镜像已经构建好,并在公共注册表中提供。

在 Kubernetes 中部署蓝色和红色服务

现在我们已经有了公共注册表中提供的服务容器镜像,我们需要将它们部署到 Kubernetes 集群中。

以下文件包含在集群中部署 Blue 和 Red 服务并使其可访问所需的所有 Kubernetes 资源

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: development
  name: endpoints-reader
rules:
  - apiGroups: [""] # "" indicates the core API group
    resources: ["endpoints", "pods"]
    verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: stork-rb
  namespace: development
subjects:
  - kind: ServiceAccount
    # Reference to upper's `metadata.name`
    name: default
    # Reference to upper's `metadata.namespace`
    namespace: development
roleRef:
  kind: Role
  name: endpoints-reader
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: Service
metadata:
  annotations:
    app.quarkus.io/commit-id: f747f359406bedfb1a39c57392a5b5a9eaefec56
    app.quarkus.io/build-timestamp: 2022-03-31 - 10:36:56 +0000
  labels:
    app.kubernetes.io/name: color-service
    app.kubernetes.io/version: "1.0"
  name: color-service (1)
spec:
  ports:
    - name: http
      port: 80
      targetPort: 8080
  selector:
    app.kubernetes.io/version: "1.0"
    type: color-service
  type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    app.quarkus.io/commit-id: f747f359406bedfb1a39c57392a5b5a9eaefec56
    app.quarkus.io/build-timestamp: 2022-03-31 - 10:36:56 +0000
  labels:
    color: blue
    type: color-service
    app.kubernetes.io/name: blue-service
    app.kubernetes.io/version: "1.0"
  name: blue-service (2)
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: blue-service
      app.kubernetes.io/version: "1.0"
  template:
    metadata:
      annotations:
        app.quarkus.io/commit-id: f747f359406bedfb1a39c57392a5b5a9eaefec56
        app.quarkus.io/build-timestamp: 2022-03-31 - 10:36:56 +0000
      labels:
        color: blue
        type: color-service
        app.kubernetes.io/name: blue-service
        app.kubernetes.io/version: "1.0"
    spec:
      containers:
        - env:
            - name: KUBERNETES_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          image: quay.io/quarkus/blue-service:1.0
          imagePullPolicy: Always
          name: blue-service
          ports:
            - containerPort: 8080
              name: http
              protocol: TCP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    app.quarkus.io/commit-id: 27be03414510f776ca70d70d859b33e134570443
    app.quarkus.io/build-timestamp: 2022-03-31 - 10:38:54 +0000
  labels:
    color: red
    type: color-service
    app.kubernetes.io/version: "1.0"
    app.kubernetes.io/name: red-service
  name: red-service (2)
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/version: "1.0"
      app.kubernetes.io/name: red-service
  template:
    metadata:
      annotations:
        app.quarkus.io/commit-id: 27be03414510f776ca70d70d859b33e134570443
        app.quarkus.io/build-timestamp: 2022-03-31 - 10:38:54 +0000
      labels:
        color: red
        type: color-service
        app.kubernetes.io/version: "1.0"
        app.kubernetes.io/name: red-service
    spec:
      containers:
        - env:
            - name: KUBERNETES_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          image: quay.io/quarkus/red-service:1.0
          imagePullPolicy: Always
          name: red-service
          ports:
            - containerPort: 8080
              name: http
              protocol: TCP
---
apiVersion: networking.k8s.io/v1
kind: Ingress (3)
metadata:
  annotations:
    app.quarkus.io/commit-id: f747f359406bedfb1a39c57392a5b5a9eaefec56
    app.quarkus.io/build-timestamp: 2022-03-31 - 10:46:19 +0000
  labels:
    app.kubernetes.io/name: color-service
    app.kubernetes.io/version: "1.0"
    color: blue
    type: color-service
  name: color-service
spec:
  rules:
    - host: color-service.127.0.0.1.nip.io
      http:
        paths:
          - backend:
              service:
                name: color-service
                port:
                  name: http
            path: /
            pathType: Prefix

此列表中有几个有趣的部分

1 Kubernetes Service 资源 color-service,Stork 将会发现它。
2 color-service Kubernetes 服务背后的 Red 和 Blue 服务实例。
3 一个 Kubernetes Ingress 资源,使 color-service 可以从集群外部通过 color-service.127.0.0.1.nip.io URL 访问。请注意,Stork 不需要 Ingress,但它有助于检查架构是否已就位。

在项目根目录创建一个名为 kubernetes-setup.yml 的文件,并包含上述内容,然后运行以下命令将所有资源部署到 Kubernetes 集群。别忘了创建一个专用的命名空间

kubectl create namespace development
kubectl apply -f kubernetes-setup.yml -n=development

如果一切顺利,Color 服务将在 http://color-service.127.0.0.1.nip.io 上可用。您应该会随机收到 Hello from Red!Hello from Blue! 的响应。

Stork 不限于 Kubernetes,并与其他服务发现机制集成。

REST 客户端接口和前端 API

到目前为止,我们还没有使用 Stork;我们只是部署了我们将要发现、选择和调用的服务。

我们将使用 REST 客户端调用服务。创建 src/main/java/org/acme/MyService.java 文件,内容如下

package org.acme;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

/**
 * The REST Client interface.
 *
 * Notice the `baseUri`. It uses `stork://` as URL scheme indicating that the called service uses Stork to locate and
 * select the service instance. The `my-service` part is the service name. This is used to configure Stork discovery
 * and selection in the `application.properties` file.
 */
@RegisterRestClient(baseUri = "stork://my-service")
public interface MyService {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    String get();
}

这是一个直接的 REST 客户端接口,只包含一个方法。但是,请注意 baseUri 属性:* stork:// 后缀指示 REST 客户端将服务实例的发现和选择委托给 Stork,* URI 的 my-service 部分是我们将在应用程序配置中使用的服务名称。

它不改变 REST 客户端的使用方式。创建 src/main/java/org/acme/FrontendApi.java 文件,内容如下

package org.acme;

import org.eclipse.microprofile.rest.client.inject.RestClient;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

/**
 * A frontend API using our REST Client (which uses Stork to locate and select the service instance on each call).
 */
@Path("/api")
public class FrontendApi {

    @RestClient MyService service;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String invoke() {
        return service.get();
    }

}

它像往常一样注入并使用 REST 客户端。

Stork 配置

现在我们需要配置 Stork 以使用 Kubernetes 来发现服务的 red 和 blue 实例。

src/main/resources/application.properties 中,添加

quarkus.stork.my-service.service-discovery.type=kubernetes
quarkus.stork.my-service.service-discovery.k8s-namespace=development
quarkus.stork.my-service.service-discovery.application=color-service
quarkus.stork.my-service.load-balancer.type=random

stork.my-service.service-discovery 指示我们将使用哪种类型的服务发现来定位 my-service 服务。在我们的例子中,它是 kubernetes。如果您的 Kubernetes 集群访问是通过 Kube 配置文件配置的,则无需配置对它的访问。否则,请使用 quarkus.stork.my-service.service-discovery.k8s-host 属性设置正确的 Kubernetes URL。quarkus.stork.my-service.service-discovery.application 包含 Stork 将要请求的 Kubernetes 服务的名称。在我们的例子中,这是 color-service,对应于由 Red 和 Blue 实例支持的 kubernetes 服务。最后,quarkus.stork.my-service.load-balancer.type 配置服务选择。在我们的例子中,我们使用 random 负载均衡器。

在 Kubernetes 集群中部署 REST 客户端接口和前端 API

系统已基本完成。我们只需要将 REST 客户端接口和客户端服务部署到集群。在 src/main/resources/application.properties 中,添加

quarkus.container-image.registry=<public registry>
quarkus.kubernetes-client.trust-certs=true
quarkus.kubernetes.ingress.expose=true
quarkus.kubernetes.ingress.host=my-service.127.0.0.1.nip.io

quarkus.container-image.registry 包含要使用的容器注册表。quarkus.kubernetes.ingress.expose 指示服务将可以从集群外部访问。quarkus.kubernetes.ingress.host 包含访问服务的 URL。我们正在使用 nip.io 通配符进行 IP 地址映射。

有关更自定义的配置,您可以查看 部署到 Kubernetes 指南

构建和推送容器镜像

得益于我们使用的扩展,我们可以使用 Jib 构建容器镜像,同时还可以启用在构建应用程序时生成 Kubernetes 清单。例如,以下命令将在 target/kubernetes/ 目录中生成 Kubernetes 清单,并为项目构建和推送容器镜像

./mvnw package -Dquarkus.container-image.build=true -Dquarkus.container-image.push=true

将客户端服务部署到 Kubernetes 集群

生成的清单可以从项目根目录使用 kubectl 应用到集群

kubectl apply -f target/kubernetes/kubernetes.yml -n=development

请注意,如果您使用 Stork 的椭圆曲线密钥并遇到类似 java.lang.ClassNotFoundException: org.bouncycastle.jce.provider.BouncyCastleProvider 的异常,则需要添加 BouncyCastle PKIX 依赖项(org.bouncycastle:bcpkix-jdk18on)。

请注意,内部将注册 org.bouncycastle.jce.provider.BouncyCastleProvider 提供程序,如果尚未注册的话。

您可以在 BouncyCastleBouncyCastle FIPS 部分的描述中注册此提供程序。

我们完成了!那么,让我们看看它是否有效。

打开浏览器,导航到 http://my-service.127.0.0.1.nip.io/api

或者,如果您愿意,可以在另一个终端中运行

> curl http://my-service.127.0.0.1.nip.io/api
...
> curl http://my-service.127.0.0.1.nip.io/api
...
> curl http://my-service.127.0.0.1.nip.io/api
...

响应应该在 Hello from Red!Hello from Blue! 之间随机交替。

您可以将此应用程序编译为原生可执行文件

CLI
quarkus build --native
Maven
./mvnw install -Dnative
Gradle
./gradlew build -Dquarkus.native.enabled=true

然后,您需要构建一个基于原生可执行文件的容器镜像。为此,请使用相应的 Dockerfile

> docker build -f src/main/docker/Dockerfile.native -t quarkus/stork-kubernetes-quickstart .

发布新镜像到容器注册表后。您可以将 Kubernetes 清单重新部署到集群。

更进一步

本指南介绍了如何使用 SmallRye Stork 来发现和选择您的服务。您可以在以下位置找到更多关于 Stork 的信息:

相关内容