Как настроить приложение Spring Cloud Gateway, чтобы оно могло использовать Service Discovery в Spring Cloud Kubernetes?

Я создал два приложения Spring Boot, которые будут развернуты в кластере Kubernetes. Одно из этих приложений будет действовать как шлюз и, следовательно, использует Spring Cloud Gateway в качестве зависимости. Также я хочу интегрировать обнаружение служб с Spring Cloud Kubernetes и чтобы шлюз использовал обнаружение служб для автоматической генерации соответствующих маршрутов. Но когда я открываю приложение шлюза, работающее в локальном кластере Minikube, и вызываю второе приложение / службу, я получаю ошибку 503 со следующим сообщением: Unable to find instance for ...-service

В настоящее время я установил следующее:

  • Minikube
  • VirtualBox
  • Панель инструментов Docker

Я создал проект Gradle с двумя подпроектами (шлюз и другой сервис). Все проекты будут построены / развернуты локально. Учетная запись службы по умолчанию имеет разрешение на чтение API Kubernetes. После развертывания этих сервисов я выставляю службу шлюза извне. В службе шлюза у меня реализованы некоторые конечные точки, которые

  1. предоставить список всех служб в кластере с помощью DiscoveryClient.
  2. на прикладном уровне вызовите другую службу на основе URI, предоставленного DiscoveryClient.

Кажется, все работает, но когда я вызываю другой сервис с URI/serviceId Я получаю ошибку 503...

Используются следующие версии Spring Cloud: spring-cloud-starter-kubernetes 1.0.1.RELEASE spring-cloud-starter-gateway 2.1.1.RELEASE

Мое демонстрационное приложение доступно по адресу https://github.com/nmaoez/spring-cloud-gateway-kubernetes а файл README.md содержит инструкции по развертыванию обеих служб в локальном кластере Minikube. Также показаны все доступные конечные точки. Но интересной частью являются application.yaml и класс приложения шлюза.

application.yaml:

spring:
  application:
    name: gateway-service
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
management:
  endpoints:
    web:
      exposure:
        include: '*'

GatewayApplication.java

@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class GatewayApplication {

  @Autowired
  RestTemplate restTemplate;

  @Autowired
  private DiscoveryClient discoveryClient;

  @GetMapping("/")
  @ResponseBody
  public String hello() {
    return "GatewayApplication says hello!";
  }

  @GetMapping("/test")
  @ResponseBody
  public String invokeTestService() {
    List<ServiceInstance> testServiceInstances = this.discoveryClient.getInstances("test-service");
    return restTemplate.getForObject(testServiceInstances.get(0).getUri(), String.class);
  }

  @GetMapping("/services")
  public List<String> services() {
    return this.discoveryClient.getServices();
  }

  @GetMapping("/services/{serviceId}")
  public List<ServiceInstance> servicesById(@PathVariable("serviceId") String serviceId) {
    return this.discoveryClient.getInstances(serviceId);
  }

  @Bean
  public RestTemplate restTemplate() {
    return new RestTemplate();
  }

  public static void main(String[] args) {
    SpringApplication.run(GatewayApplication.class, args);
  }
}

Я понял, как работает после того, как я переписал поле url-expression в gateway-service / application.yaml, чтобы

url-expression: "uri+'/'"

После этого я получил правильный ответ после того, как вызвал gateway-uri/another-service/, Но я хочу, чтобы не заменить явно lb://serviceid, Как я могу это сделать?

Я ожидаю, что если я вызову другую службу в кластере через шлюз, я получу ответ 200 и правильный ответ, основанный на контроллере остальной части приложения.

3 ответа

Вы должны добавить зависимость к spring-cloud-starter-kubernetes-ribbon также.

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-kubernetes-ribbon</artifactId>
            <version>1.0.1.RELEASE</version>
        </dependency>

Тогда он будет работать без перезаписи, просто с spring.cloud.gateway.discovery.locator.enabled: true

Я только что реализовал пример приложения с использованием Spring Cloud Gateway и Kubernetes, которое отлично работает в Docker Desktop. И никаких лишних или забавных настроек не понадобилось.

Если это может помочь, это был мой build.gradle:

      plugins {
    id 'org.springframework.boot' version '2.4.2'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.1.0-SNAPSHOT'

repositories {
    mavenCentral()
    maven { url 'https://repo.spring.io/milestone' }
}

ext {
    set('springCloudVersion', "2020.0.0")
    set('springCloudKubernetesVersion', '1.1.7.RELEASE')
    set('springCloudVersion', '2020.0.0')
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
    implementation 'org.springframework.cloud:spring-cloud-starter-sleuth'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

    implementation "org.springframework.cloud:spring-cloud-starter-kubernetes:$springCloudKubernetesVersion"
    implementation "org.springframework.cloud:spring-cloud-starter-kubernetes-config:$springCloudKubernetesVersion"
    implementation "org.springframework.cloud:spring-cloud-starter-kubernetes-ribbon:$springCloudKubernetesVersion"
    implementation "org.springframework.cloud:spring-cloud-starter-kubernetes-loadbalancer:$springCloudKubernetesVersion"
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:$springCloudVersion"
    }
}

test {
    useJUnitPlatform()
}

Это конфигурация из application.yaml:

      spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true

И, наконец, DiscoveryClientвключено в приложении:

      @SpringBootApplication
@EnableDiscoveryClient // So services can be discovered
public class GatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }

}

Обратите внимание, как сказал Якуб Кубрински, вы должны включить зависимость ленты.

Также будьте осторожны, маршрутизация работает только тогда, когда шлюз развернут в кластере K8s. За его пределами он может видеть службы K8s, но не может маршрутизировать к ним, поскольку они используют IP-адреса в сети K8s.

Я могу настроить весенний облачный шлюз с обнаружением зависимости spring-cloud-kubernetes версии 1.1.10.RELASE и Spring-boot: 2.5.7 Spring облачный шлюз: 3.0.4

Файл Pom выглядит следующим образом:

      <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>


    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.7</version>
        <relativePath/>
    </parent>

    <groupId>com.csg.cro.rccs</groupId>
    <artifactId>api-gateway</artifactId>
    <version>${revision}</version>
    <name>RCC-APIGateway</name>
    <description>Enable Proxy and verify user token project for Risk 360</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>11</java.version>
        <spring-cloud.version>2020.0.3</spring-cloud.version>
        <revision>21.7.0-SNAPSHOT</revision>
    </properties>


    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-kubernetes</artifactId>
            <version>1.1.10.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-kubernetes-config</artifactId>
            <version>1.1.10.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-kubernetes-ribbon</artifactId>
            <version>1.1.10.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-kubernetes-loadbalancer</artifactId>
            <version>1.1.10.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
             <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>flatten-maven-plugin</artifactId>
            <version>1.1.0</version>
            <configuration>
                <updatePomFile>true</updatePomFile>
                <flattenMode>resolveCiFriendliesOnly</flattenMode>
            </configuration>
            <executions>
                <execution>
                    <id>flatten</id>
                    <phase>process-resources</phase>
                    <goals>
                        <goal>flatten</goal>
                    </goals>
                </execution>
                <execution>
                    <id>flatten.clean</id>
                    <phase>clean</phase>
                    <goals>
                        <goal>clean</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        </plugins>
    </build>
    
</project>

Моя конфигурация обнаружения выглядит так:

      spring:
  application.name: gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          url-expression: "'http://'+serviceId+':'+getPort()"
          lower-case-service-id: true