Spring Cloud Kubernetes 和 Spring Boot 3

在这篇文章中,我们将学习如何用 Spring Cloud Kubernetes 和 Spring Boot 3 创建、测试和运行应用程序。将会学习如何在Kubernetes环境中使用Skaffold、Testcontainers、Spring Boot Admin 和 Fabric8 client 等工具。这篇文章的主要目的是介绍Spring Cloud Kubernetes项目的最新版本。

源代码

如果你想自己尝试,你可以 clone 我的 GitHub repository。然后按如下指示进行。

首先,看一下仓库的结构。它包含五个应用程序。有三个微服务(employee-servicedepartment-serviceorganization-service)通过REST客户端相互通信并连接到Mongo数据库。还有用Spring Cloud Gateway项目创建的API网关(gateway-service)。最后,admin-service目录包含用于监控所有其他应用程序的Spring Boot Admin应用程序。你可以使用一个Skaffold命令轻松地从源代码中部署所有的应用程序。如果你从 repository 根目录运行以下命令,它将用Jib Maven插件构建镜像,并将所有应用部署到Kubernetes集群上:

$ skaffold run

另一方面,你可以进入特定的应用程序目录,只使用完全相同的命令来部署它。每个应用所需的所有Kubernetes YAML清单都放在k8s目录中。在项目根k8s目录下还有一个全局配置,例如Mongo部署。下面是我们的示例 repo 的结构:

它是如何工作的

在我们的示例架构中,我们将使用Spring Cloud Kubernetes Config来通过ConfigMap和Secret注入配置,使用Spring Cloud Kubernetes Discovery与OpenFeign客户端进行服务间通信。我们所有的应用程序都在同一个命名空间内运行,但我们也可以将它们部署在几个不同的命名空间内,并通过OpenFeign处理它们之间的通信。在这种情况下,我们唯一要做的就是将 spring.cloud.kubernetes.discovery.all-namespaces 属性设置为 true。

在我们的服务前面,有一个API网关。这是一个独立的应用,但我们也可以使用本地的CRD集成将其安装在Kubernetes上。在我们的案例中,这是一个标准的Spring Boot 3应用,只是包括并使用了Spring Cloud Gateway模块。它还使用Spring Cloud Kubernetes Discovery和Spring Cloud OpenFeign来定位和调用下游服务。以下是我们的架构图。

使用 Spring Cloud Kubernetes Config

我将通过 department-service 的例子来描述实现细节。它暴露了一些REST端点,但也调用了employee-service所暴露的端点。除了标准模块,我们还需要将Spring Cloud Kubernetes纳入Maven的依赖项。这里,我们必须决定是使用Fabric8客户端还是Kubernetes Java Client。就我个人而言,我有使用Fabric8的经验,所以我将使用spring-cloud-starter-kubernetes-fabric8-all starter来包含配置和发现模块。

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-kubernetes-fabric8-all</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

如你所见,我们的应用程序正在连接到Mongo数据库。我们需要提供应用程序所需的连接细节和凭证。在k8s目录中,你会发现 configmap.yaml 文件。它包含了Mongo的地址和数据库的名称。这些属性被作为 application.properties 文件注入到pod中。现在是最重要的事情。ConfigMap 的名称必须与我们应用程序的名称相同。Spring Boot的名称由 spring.application.name 属性表示。

configmap.yaml

kind: ConfigMap
apiVersion: v1
metadata:
  name: department
data:
  application.properties: |-
    spring.data.mongodb.host: mongodb
    spring.data.mongodb.database: admin
    spring.data.mongodb.authentication-database: admin

在目前的情况下,应用程序的名称是 department.。这里是应用程序里面的 application.yml 文件:

application.yml

spring:
  application:
    name: department

同样的命名规则也适用于 Secret。我们在下面的 Secret 里面保存敏感数据,比如Mongo数据库的用户名和密码。你也可以在 k8s 目录下的 secret.yaml 文件内找到这些内容。

secret.yaml

kind: Secret
apiVersion: v1
metadata:
  name: department
data:
  spring.data.mongodb.password: UGlvdF8xMjM=
  spring.data.mongodb.username: cGlvdHI=
type: Opaque

现在,让我们继续讨论 Deployment 清单。我们稍后将在这里澄清前两点。Spring Cloud Kubernetes需要在Kubernetes上有特殊的权限,以便与 master API互动 (1)。我们不必为镜像提供一个标签–Skaffold会处理它 (2)。为了启用从 ConfigMap 加载属性,我们需要设置spring.config.import=kubernetes: 属性(一种新方法)或将spring.cloud.bootstrap.enabled属性设置为true(旧方法)。我们将不直接使用属性,而是在Deployment (3) 上设置相应的环境变量。默认情况下,由于安全原因,通过API消费secrets的功能没有被启用。为了启用它,我们将把SPRING_CLOUD_KUBERNETES_SECRETS_ENABLEAPI环境变量设置为true (4)

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: department
  labels:
    app: department
spec:
  replicas: 1
  selector:
    matchLabels:
      app: department
  template:
    metadata:
      labels:
        app: department
    spec:
      serviceAccountName: spring-cloud-kubernetes # (1)
      containers:
      - name: department
        image: piomin/department # (2)
        ports:
        - containerPort: 8080
        env:
          - name: SPRING_CLOUD_BOOTSTRAP_ENABLED # (3)
            value: "true"
          - name: SPRING_CLOUD_KUBERNETES_SECRETS_ENABLEAPI # (4)
            value: "true"

使用 Spring Cloud Kubernetes Discovery

我们已经在上一节使用 spring-cloud-starter-kubernetes-fabric8-all starter包含了Spring Cloud Kubernetes发现模块。为了提供一个声明式REST客户端,我们还将包括Spring Cloud OpenFeign模块:

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

现在,我们可以声明 @FeignClient 接口。这里重要的是一个被发现的服务的名称。它应该与为employee-service应用程序定义的Kubernetes Service 的名称相同。

@FeignClient(name = "employee")
public interface EmployeeClient {

    @GetMapping("/department/{departmentId}")
    List<Employee> findByDepartment(@PathVariable("departmentId") String departmentId);

    @GetMapping("/department-with-delay/{departmentId}")
    List<Employee> findByDepartmentWithDelay(@PathVariable("departmentId") String departmentId);
}

下面是 employee-service 应用的Kubernetes服务清单。该服务的名称是 employee (1)。标签spring-boot 是为Spring Boot Admin发现目的而设置的 (2)。你可以在 employee-service/k8s 目录中找到以下YAML。

employee-service/k8s/service.yaml

apiVersion: v1
kind: Service
metadata:
  name: employee # (1)
  labels:
    app: employee
    spring-boot: "true" # (2)
spec:
  ports:
    - port: 8080
      protocol: TCP
  selector:
    app: employee
  type: ClusterIP

澄清一下–这里是由 OpenFeign 客户端在 department-service 中调用的employee-service API方法的实现。

@RestController
public class EmployeeController {

    private static final Logger LOGGER = LoggerFactory
        .getLogger(EmployeeController.class);
	
    @Autowired
    EmployeeRepository repository;

    // ... other endpoints implementation 

    @GetMapping("/department/{departmentId}")
    public List<Employee> findByDepartment(@PathVariable("departmentId") String departmentId) {
        LOGGER.info("Employee find: departmentId={}", departmentId);
        return repository.findByDepartmentId(departmentId);
    }

    @GetMapping("/department-with-delay/{departmentId}")
    public List<Employee> findByDepartmentWithDelay(@PathVariable("departmentId") String departmentId) throws InterruptedException {
        LOGGER.info("Employee find: departmentId={}", departmentId);
        Thread.sleep(2000);
        return repository.findByDepartmentId(departmentId);
    }
	
}

这就是我们要做的一切。现在,我们可以使用department-service中的OpenFeign客户端调用端点。例如,在 “delayed” 端点上,我们可以使用Spring Cloud Circuit Breaker与Resilience4J。

@RestController
public class DepartmentController {

    private static final Logger LOGGER = LoggerFactory
        .getLogger(DepartmentController.class);

    DepartmentRepository repository;
    EmployeeClient employeeClient;
    Resilience4JCircuitBreakerFactory circuitBreakerFactory;

    public DepartmentController(
        DepartmentRepository repository, 
        EmployeeClient employeeClient,
        Resilience4JCircuitBreakerFactory circuitBreakerFactory) {
            this.repository = repository;
            this.employeeClient = employeeClient;
            this.circuitBreakerFactory = circuitBreakerFactory;
    }

    @GetMapping("/{id}/with-employees-and-delay")
    public Department findByIdWithEmployeesAndDelay(@PathVariable("id") String id) {
        LOGGER.info("Department findByIdWithEmployees: id={}", id);
        Department department = repository.findById(id).orElseThrow();
        CircuitBreaker circuitBreaker = circuitBreakerFactory.create("delayed-circuit");
        List<Employee> employees = circuitBreaker.run(() ->
                employeeClient.findByDepartmentWithDelay(department.getId()));
        department.setEmployees(employees);
        return department;
    }

    @GetMapping("/organization/{organizationId}/with-employees")
    public List<Department> findByOrganizationWithEmployees(@PathVariable("organizationId") String organizationId) {
        LOGGER.info("Department find: organizationId={}", organizationId);
        List<Department> departments = repository.findByOrganizationId(organizationId);
        departments.forEach(d -> d.setEmployees(employeeClient.findByDepartment(d.getId())));
        return departments;
    }

}

使用Fabric8 Kubernetes进行测试

我们已经完成了服务的实施。所有的Kubernetes YAML清单都准备好了,可以部署。现在,问题是–在我们继续在真正的集群上进行部署之前,我们是否可以轻松地测试一切工作正常?答案是–可以。此外,我们可以在几种工具中进行选择。让我们从最简单的选项开始 - Kubernetes mock server。为了使用它,我们需要加入一个额外的Maven依赖项:

<dependency>
  <groupId>io.fabric8</groupId>
  <artifactId>kubernetes-server-mock</artifactId>
  <version>6.7.1</version>
  <scope>test</scope>
</dependency>

然后,我们可以继续进行测试。在第一步中,我们需要提供几个测试注解。在 @SpringBootTest 里面,我们应该模拟Kubernetes平台,将 spring.main.cloud-platform 属性设置为 KUBERNETES (1)。通常情况下,Spring Boot能够自动检测它是否在Kubernetes上运行。在这种情况下,我们需要 “欺骗他”,因为我们只是在模拟API,而不是在Kubernetes上运行测试。我们还需要用spring.cloud.bootstrap.enabled=true 属性启用 ConfigMap 注入的老方法。

一旦我们用 @EnableKubernetesMockClient (2) 来注解测试方法,我们就可以使用Fabric8 KubernetesClient (3) 的一个自动配置的静态实例。在测试过程中,Fabric8库运行一个web服务器,模拟客户端发送的所有API请求。顺便说一下,我们正在使用Testcontainers来运行Mongo (4)。在下一步,我们将创建 ConfigMap,将Mongo连接设置注入到Spring Boot应用中 (5)。由于Spring Cloud Kubernetes配置,它被应用自动加载,应用能够在动态生成的端口上连接Mongo数据库。

Spring Cloud Kubernetes自带自动配置的Fabric8 KubernetesClient。我们需要强制它连接到模拟的API服务器。因此,我们应该将Fabric8 KubernetesClient 使用的 kubernetes.master 属性覆盖到从测试的 "模拟 "实例 (6) 中获取的master URL。最后,我们可以用标准方式实现测试方法。

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
        properties = {
                "spring.main.cloud-platform=KUBERNETES",
                "spring.cloud.bootstrap.enabled=true"}) // (1)
@EnableKubernetesMockClient(crud = true) // (2)
@Testcontainers
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class EmployeeKubernetesMockTest {

    private static final Logger LOG = LoggerFactory
        .getLogger(EmployeeKubernetesMockTest.class);

    static KubernetesClient client; // (3)

    @Container // (4)
    static MongoDBContainer mongodb = new MongoDBContainer("mongo:5.0");

    @BeforeAll
    static void setup() {

        ConfigMap cm = client.configMaps()
                .create(buildConfigMap(mongodb.getMappedPort(27017)));
        LOG.info("!!! {}", cm); // (5)

        // (6)
        System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, 
            client.getConfiguration().getMasterUrl());
        System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
        System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "default");
    }

    private static ConfigMap buildConfigMap(int port) {
        return new ConfigMapBuilder().withNewMetadata()
                .withName("employee").withNamespace("default")
                .endMetadata()
                .addToData("application.properties",
                        """
                        spring.data.mongodb.host=localhost
                        spring.data.mongodb.port=%d
                        spring.data.mongodb.database=test
                        spring.data.mongodb.authentication-database=test
                        """.formatted(port))
                .build();
    }

    @Autowired
    TestRestTemplate restTemplate;

    @Test
    @Order(1)
    void addEmployeeTest() {
        Employee employee = new Employee("1", "1", "Test", 30, "test");
        employee = restTemplate.postForObject("/", employee, Employee.class);
        assertNotNull(employee);
        assertNotNull(employee.getId());
    }

    @Test
    @Order(2)
    void addAndThenFindEmployeeByIdTest() {
        Employee employee = new Employee("1", "2", "Test2", 20, "test2");
        employee = restTemplate.postForObject("/", employee, Employee.class);
        assertNotNull(employee);
        assertNotNull(employee.getId());
        employee = restTemplate
                .getForObject("/{id}", Employee.class, employee.getId());
        assertNotNull(employee);
        assertNotNull(employee.getId());
    }

    @Test
    @Order(3)
    void findAllEmployeesTest() {
        Employee[] employees =
                restTemplate.getForObject("/", Employee[].class);
        assertEquals(2, employees.length);
    }

    @Test
    @Order(3)
    void findEmployeesByDepartmentTest() {
        Employee[] employees =
                restTemplate.getForObject("/department/1", Employee[].class);
        assertEquals(1, employees.length);
    }

    @Test
    @Order(3)
    void findEmployeesByOrganizationTest() {
        Employee[] employees =
                restTemplate.getForObject("/organization/1", Employee[].class);
        assertEquals(2, employees.length);
    }

}

现在,在运行测试后,我们可以看一下日志。如你所见,我们的测试正在从 employee ConfigMap中加载属性。

最后,它能够成功连接动态端口上的Mongo,并针对该实例运行所有测试。

在k3s上用测试容器进行测试

正如我之前提到的,有几个工具我们可以用于Kubernetes的测试。这次我们将看到如何用Testcomntainers来做。我们已经在上一节中使用它来运行Mongo数据库。但也有用于Rancher的k3s Kubernetes发布的Testcontainers模块。目前,它处于孵化状态,但并不妨碍我们去尝试它。为了在项目中使用它,我们需要包含以下Maven依赖:

<dependency>
  <groupId>org.testcontainers</groupId>
  <artifactId>k3s</artifactId>
  <scope>test</scope>
</dependency>

下面是与上一节相同的测试的实现,但这次是用 k3s 容器。我们不需要创建任何mock。相反,我们将创建 K3sContainer对象 (1)。在运行测试之前,我们需要创建并初始化 KubernetesClient。测试容器 K3sContainer 提供了getKubeConfigYaml()方法来获取kubeconfig数据。有了Fabric8 Config 对象,我们可以从该 kubeconfig (2) (3) 初始化客户端。之后,我们将用Mongo连接细节创建 ConfigMap (4)。最后,我们要为Spring Cloud Kubernetes自动配置的Fabric8客户端重写master URL。与上一节相比,我们还需要设置Kubernetes客户端证书和密钥 (5)

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
        properties = {
                "spring.main.cloud-platform=KUBERNETES",
                "spring.cloud.bootstrap.enabled=true"})
@Testcontainers
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class EmployeeKubernetesTest {

   private static final Logger LOG = LoggerFactory
      .getLogger(EmployeeKubernetesTest.class);

   @Container
   static MongoDBContainer mongodb = new MongoDBContainer("mongo:5.0");
   @Container
   static K3sContainer k3s = new K3sContainer(DockerImageName
      .parse("rancher/k3s:v1.21.3-k3s1")); // (1)

   @BeforeAll
   static void setup() {
      Config config = Config
         .fromKubeconfig(k3s.getKubeConfigYaml()); // (2)
      DefaultKubernetesClient client = new 
         DefaultKubernetesClient(config); // (3)

      ConfigMap cm = client.configMaps().inNamespace("default")
         .create(buildConfigMap(mongodb.getMappedPort(27017)));
      LOG.info("!!! {}", cm); // (4)

      System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, 
         client.getConfiguration().getMasterUrl());
      
      // (5) 
      System.setProperty(Config.KUBERNETES_CLIENT_CERTIFICATE_DATA_SYSTEM_PROPERTY,
         client.getConfiguration().getClientCertData());
      System.setProperty(Config.KUBERNETES_CA_CERTIFICATE_DATA_SYSTEM_PROPERTY,
         client.getConfiguration().getCaCertData());
       System.setProperty(Config.KUBERNETES_CLIENT_KEY_DATA_SYSTEM_PROPERTY,
         client.getConfiguration().getClientKeyData());
      System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, 
         "true");
      System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, 
         "default");
    }

    private static ConfigMap buildConfigMap(int port) {
        return new ConfigMapBuilder().withNewMetadata()
                .withName("employee").withNamespace("default")
                .endMetadata()
                .addToData("application.properties",
                        """
                        spring.data.mongodb.host=localhost
                        spring.data.mongodb.port=%d
                        spring.data.mongodb.database=test
                        spring.data.mongodb.authentication-database=test
                        """.formatted(port))
                .build();
    }

    @Autowired
    TestRestTemplate restTemplate;

    @Test
    @Order(1)
    void addEmployeeTest() {
        Employee employee = new Employee("1", "1", "Test", 30, "test");
        employee = restTemplate.postForObject("/", employee, Employee.class);
        assertNotNull(employee);
        assertNotNull(employee.getId());
    }

    @Test
    @Order(2)
    void addAndThenFindEmployeeByIdTest() {
        Employee employee = new Employee("1", "2", "Test2", 20, "test2");
        employee = restTemplate
           .postForObject("/", employee, Employee.class);
        assertNotNull(employee);
        assertNotNull(employee.getId());
        employee = restTemplate
                .getForObject("/{id}", Employee.class, employee.getId());
        assertNotNull(employee);
        assertNotNull(employee.getId());
    }

    @Test
    @Order(3)
    void findAllEmployeesTest() {
        Employee[] employees =
                restTemplate.getForObject("/", Employee[].class);
        assertEquals(2, employees.length);
    }

    @Test
    @Order(3)
    void findEmployeesByDepartmentTest() {
        Employee[] employees =
                restTemplate.getForObject("/department/1", Employee[].class);
        assertEquals(1, employees.length);
    }

    @Test
    @Order(3)
    void findEmployeesByOrganizationTest() {
        Employee[] employees =
                restTemplate.getForObject("/organization/1", Employee[].class);
        assertEquals(2, employees.length);
    }

}

在Minikube上运行Spring Kubernetes应用程序

在这个练习中,我使用Minikube,但你也可以使用任何其他的发行版,如Kind或k3s。Spring Cloud Kubernetes需要在Kubernetes上有额外的权限,以便能够与master API互动。因此,在运行应用程序之前,我们将创建具有所需权限的 spring-cloud-kubernetes ServiceAccount。我们的角色需要拥有对configmapspodsservicesendpointssecrets 的访问权。如果我们没有启用跨所有命名空间的发现(spring.cloud.kubernetes.discovery.all-namespaces 属性),可以在命名空间内进行 Role。否则,我们应该创建一个 ClusterRole

k8s/privileges.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: spring-cloud-kubernetes
  namespace: default
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: spring-cloud-kubernetes
  namespace: default
rules:
  - apiGroups: [""]
    resources: ["configmaps", "pods", "services", "endpoints", "secrets"]
    verbs: ["get", "list", "watch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: spring-cloud-kubernetes
  namespace: default
subjects:
  - kind: ServiceAccount
    name: spring-cloud-kubernetes
    namespace: default
roleRef:
  kind: ClusterRole
  name: spring-cloud-kubernetes

当然,你不需要自己去应用上面所示的清单。正如我在文章开头提到的,在 repository 根目录文件中有一个 skaffold.yaml 文件,包含了整个配置。它与所有服务一起运行带有 Mongo 部署 (1) 和带有权限 (2) 的清单。

apiVersion: skaffold/v4beta5
kind: Config
metadata:
  name: sample-spring-microservices-kubernetes
build:
  artifacts:
    - image: piomin/admin
      jib:
        project: admin-service
    - image: piomin/department
      jib:
        project: department-service
        args:
          - -DskipTests
    - image: piomin/employee
      jib:
        project: employee-service
        args:
          - -DskipTests
    - image: piomin/gateway
      jib:
        project: gateway-service
    - image: piomin/organization
      jib:
        project: organization-service
        args:
          - -DskipTests
  tagPolicy:
    gitCommit: {}
manifests:
  rawYaml:
    - k8s/mongodb-*.yaml # (1)
    - k8s/privileges.yaml # (2)
    - admin-service/k8s/*.yaml
    - department-service/k8s/*.yaml
    - employee-service/k8s/*.yaml
    - gateway-service/k8s/*.yaml
    - organization-service/k8s/*.yaml

我们需要做的就是通过执行以下skaffold命令来部署所有的应用程序:

$ skaffold dev

完成后,我们就可以显示一个正在运行的 pods 的列表:

kubectl get pod
NAME                            READY   STATUS    RESTARTS   AGE
admin-5f8c8498f-vtstx           1/1     Running   0          2m38s
department-746774879b-llrdn     1/1     Running   0          2m38s
employee-5bbf6b765f-7hsv7       1/1     Running   0          2m37s
gateway-578cb64558-m9n7f        1/1     Running   0          2m37s
mongodb-7f68b8b674-dbfnb        1/1     Running   0          2m38s
organization-5688c58656-bv8n6   1/1     Running   0          2m37s

我们还可以显示一个服务列表。其中一些服务,如 admingateway,以 NodePort 的形式暴露。得益于此,我们可以在Kubernetes集群之外轻松访问它们。

kubectl get svc
NAME           TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
admin          NodePort    10.101.220.141   <none>        8080:31368/TCP   3m53s
department     ClusterIP   10.108.144.90    <none>        8080/TCP         3m52s
employee       ClusterIP   10.99.75.2       <none>        8080/TCP         3m52s
gateway        NodePort    10.96.7.237      <none>        8080:31518/TCP   3m52s
kubernetes     ClusterIP   10.96.0.1        <none>        443/TCP          38h
mongodb        ClusterIP   10.108.198.233   <none>        27017/TCP        3m53s
organization   ClusterIP   10.107.102.26    <none>        8080/TCP         3m52s

让我们在我们的本地机器上获得Minikube的IP地址:

$ minikube ip

现在,我们可以使用该IP地址来访问目标端口上的 Spring Boot Admin 服务器等。对我来说是 31368。Spring Boot Admin 应该能成功地发现所有三个微服务,并连接到这些应用所暴露的 /actuator 端点。

我们可以去了解每个Spring Boot应用程序的细节。如你所见,depatment-service 正在我的本地Minikube上运行。

一旦你停止 skaffold dev 命令,所有的应用程序和配置将从你的Kubernetes集群中删除。

思考

如果你在Kubernetes集群上只运行Spring Boot应用,Spring Cloud Kubernetes是一个有趣的选择。它允许我们轻松地与Kubernetes discovery、config map和secrets集成。正因为如此,我们可以利用其他Spring Cloud组件,如负载均衡器、断路器等。然而,如果你正在运行用不同语言和框架编写的应用程序,并使用service mesh(Istio、Linkerd)等语言无关的工具,Spring Cloud Kubernetes可能不是最佳选择。

原文:Spring Cloud Kubernetes with Spring Boot 3 - Piotr's TechBlog