<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.vertx.howtos</groupId>
<artifactId>k8s-client-slide-lb-microservice</artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<vertx.version>5.0.0.CR2</vertx.version>
<main.verticle>io.vertx.howtos.clientsidelb.MicroServiceVerticle</main.verticle>
<launcher.class>io.vertx.launcher.application.VertxApplication</launcher.class>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-stack-depchain</artifactId>
<version>${vertx.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-launcher-application</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-service-resolver</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<release>11</release>
</configuration>
</plugin>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>3.4.4</version>
<configuration>
<to>
<image>client-side-lb/microservice</image>
</to>
<container>
<mainClass>${launcher.class}</mainClass>
<args>
<arg>${main.verticle}</arg>
</args>
<ports>
<port>8080</port>
</ports>
</container>
</configuration>
</plugin>
</plugins>
</build>
</project>
Kubernetes 上的客户端负载均衡
本文档将向您展示如何在 Kubernetes 上使用微服务执行客户端负载均衡。
您将构建什么
您将构建一个 Vert.x 微服务,它
-
监听针对
/
URI 的 HTTP 请求 -
使用负载均衡器向后端服务发起 HTTP 请求
-
发送后端服务的 HTTP 响应内容
它包含一个名为 microservice 的单一组件,该组件与部署在 Kubernetes 中的另一个 Pod 进行通信。
创建项目
microservice 项目的代码包含功能等效的 Maven 和 Gradle 构建文件。
容器化
为了创建容器,我们将使用 Jib,因为它
-
它为依赖项、资源和类创建了不同的层,从而节省了构建时间和部署时间
-
它同时支持 Maven 和 Gradle
-
它既不需要 Docker 也不需要 Podman
使用 Maven
以下是您应该用于 microservice 的 pom.xml
文件的内容
pom.xml
使用 Gradle
假设您使用带有 Kotlin DSL 的 Gradle,以下是您的 microservice 的 build.gradle.kts
文件应有的样子
build.gradle.kts
plugins {
java
application
id("com.google.cloud.tools.jib") version "3.4.4"
}
repositories {
mavenCentral()
}
val vertxVersion = "5.0.0.CR2"
val verticle = "io.vertx.howtos.clientsidelb.MicroServiceVerticle"
dependencies {
implementation("io.vertx:vertx-core:${vertxVersion}")
implementation("io.vertx:vertx-service-resolver:${vertxVersion}")
implementation("io.vertx:vertx-launcher-application:${vertxVersion}")
}
jib {
to {
image = "client-side-lb/microservice"
}
container {
mainClass = "io.vertx.launcher.application.VertxApplication"
args = listOf(verticle)
ports = listOf("8080")
}
}
tasks.wrapper {
gradleVersion = "8.11.1"
}
实现服务
让我们实现 microservice,然后在开发机器上进行测试。
前端服务被封装在 MicroServiceVerticle
类中。
该服务将使用服务地址请求 Kubernetes 集群中的另一个 Pod。microservice Verticle 创建一个配置了负载均衡器和解析器的 HttpClient
。
src/main/java/io/vertx/howtos/clientsidelb/MicroServiceVerticle.java
client = vertx
.httpClientBuilder()
.withLoadBalancer(loadBalancer)
.withAddressResolver(resolver)
.build();
为此,我们创建一个地址解析器,它将逻辑 ServiceAddress
作为输入,并返回 HTTP 客户端在实践中可以使用的地址列表。
KubeResolver
是在 Kubernetes 中部署时首选的解析器。请注意,该解析器是使用 new KubeResolverOptions()
创建的,并根据 Kubernetes 设置的 Pod 环境变量进行配置。
src/main/java/io/vertx/howtos/clientsidelb/MicroServiceVerticle.java
AddressResolver resolver = KubeResolver.create(new KubeResolverOptions());
负载均衡器部分采用轮询(round-robin)策略,非常简单。
src/main/java/io/vertx/howtos/clientsidelb/MicroServiceVerticle.java
LoadBalancer loadBalancer = LoadBalancer.ROUND_ROBIN;
还有其他可用的策略。
我们还需要为我们的服务创建并绑定一个 Web 服务器,这非常简单
src/main/java/io/vertx/howtos/clientsidelb/MicroServiceVerticle.java
return vertx.createHttpServer()
.requestHandler(request -> handleRequest(request))
.listen(8080);
最后,让我们看看服务请求处理。
首先,我们向后端服务器创建一个 HTTP 服务器请求。我们不传递后端服务器的套接字地址,而是使用逻辑服务地址,即 Kubernetes 中服务的名称 (hello-node
)。
src/main/java/io/vertx/howtos/clientsidelb/MicroServiceVerticle.java
ServiceAddress serviceAddress = ServiceAddress.of("hello-node");
Future<HttpClientRequest> fut = client.request(new RequestOptions()
.setMethod(HttpMethod.GET)
.setServer(serviceAddress)
.setURI("/"));
然后我们实现后端服务器的响应处理。我们将原始响应作为我们响应的一部分发送回去,并附加响应的套接字地址,以便我们可以确定服务与哪个服务器进行了交互。
src/main/java/io/vertx/howtos/clientsidelb/MicroServiceVerticle.java
fut.compose(r -> r.send()
.expecting(HttpResponseExpectation.SC_OK)
.compose(resp -> resp.body())
.map(body -> "Response of pod " + r.connection().remoteAddress() + ": " + body + "\n"))
.onSuccess(res -> {
request.response()
.putHeader("content-type", "text/plain")
.end(res);
})
.onFailure(cause -> {
request.response()
.setStatusCode(500)
.putHeader("content-type", "text/plain")
.end("Error: " + cause.getMessage());
});
部署到 Kubernetes
首先,确保 Minikube 已使用 minikube status
启动。
如果您不使用 Minikube,请验证 kubectl 已连接到您的集群。 |
推送容器镜像
有多种方法可以将容器镜像推送到 Minikube。
在本文档中,我们将直接推送到集群内的 Docker 守护进程。
为此,我们必须将 shell 指向 Minikube 的 docker 守护进程
eval $(minikube -p minikube docker-env)
然后,在同一个 shell 中,我们可以使用 Jib 构建镜像
-
使用 Maven:
mvn compile jib:dockerBuild
,或者 -
使用 Gradle:
./gradlew jibDockerBuild
(Linux, macOS) 或gradlew jibDockerBuild
(Windows)。
Jib 不会使用 Docker 守护进程来构建镜像,而只会用来推送镜像。 |
如果您不使用 Minikube,请参阅 Jib Maven 或 Jib Gradle 插件文档,了解在推送到注册表时如何配置它们。 |
后端服务部署
为了简单起见,我们将重用 Minikube 教程中的 HTTP 服务器。
出于本操作指南的目的,我们只需创建一个包含 3 个 Pod 的部署。
kubectl create deployment hello-node --image=registry.k8s.io/e2e-test-images/agnhost:2.39 -- /agnhost netexec --http-port=8080
验证 Pod 已成功启动
kubectl get pods --selector=app=hello-node
您应该看到类似如下内容
NAME READY STATUS RESTARTS AGE hello-node-66d457cb86-vndgc 1/1 Running 0 45s
出于本操作指南的目的,我们将副本数量增加到 3。
kubectl scale deployment hello-node --replicas=3
并验证新的 Pod 已成功启动
NAME READY STATUS RESTARTS AGE hello-node-66d457cb86-m9nsr 1/1 Running 0 11s hello-node-66d457cb86-vndgc 1/1 Running 0 2m51s hello-node-66d457cb86-z6x26 1/1 Running 0 11s
最后,我们需要将 Pod 作为服务暴露
kubectl expose deployment hello-node --type=LoadBalancer --port=8080
再次验证服务已成功创建
kubectl get services hello-node
您应该看到类似如下内容
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE hello-node LoadBalancer 10.101.56.23 <pending> 8080:32159/TCP 2m31s
微服务部署
现在我们可以在 Kubernetes 中部署我们的微服务了。
deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: microservice-deployment
labels:
app: microservice
spec:
replicas: 1
selector:
matchLabels:
app: microservice
template:
metadata:
labels:
app: microservice
spec:
containers:
- name: microservice
image: client-side-lb/microservice:latest
imagePullPolicy: IfNotPresent
env:
- name: HTTP_PORT
value: "8080"
ports:
- containerPort: 8080
应用此配置
kubectl apply -f deployment.yml
验证 Pod 已成功启动
kubectl get pods --selector=app=microservice
您应该看到类似如下内容
NAME READY STATUS RESTARTS AGE microservice-deployment-69dfcbc79c-kk85f 1/1 Running 0 117s
我们还需要一个服务来负载均衡 HTTP 流量。
Pod 将通过部署中定义的标签 app:microservice
进行选择
service.yml
apiVersion: v1
kind: Service
metadata:
name: microservice
spec:
selector:
app: microservice
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
应用此配置
kubectl apply -f service.yml
验证服务已成功创建
kubectl get services microservice
您应该看到类似如下内容
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE microservice LoadBalancer 10.109.6.50 <pending> 80:30336/TCP 2m3s
最后,我们需要配置 *default* 服务账户,以授予 Vert.x 服务解析器观察端点的权限。
roles.yml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: observe-endpoints
namespace: default
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "watch", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: observe-endpoints
namespace: default
roleRef:
kind: Role
name: observe-endpoints
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: default
namespace: default
应用此配置
kubectl apply -f roles.yml
测试微服务
现在是时候测试我们的微服务并观察客户端负载均衡的实际运行情况了。
如果您使用 Minikube,请打开另一个终端窗口并运行
minikube service microservice
这将打开一个网页浏览器并展示我们的微服务正在运行,您应该会看到类似以下内容:
Hello from: 10.244.0.48:8080 with: NOW: 2024-11-27 17:18:37.179191424 +0000 UTC m=+1267.971197286
您可以刷新页面,以显示我们的微服务所交互的后端服务的 IP 地址已更改。
总结
本文档涵盖了
-
在 Kubernetes Pod 之间部署微服务负载均衡所需的依赖项
-
使用 Jib 对 Vert.x 服务进行容器化