Spring Cloud Gateway使用Kubernetes Service服务发现和Kubernetes ConfigMap配置中心

Work May 31, 2023

由于历史原因, 系统网关组件过旧, 现在想要对其进行改造, 将使用全新的组件进行升级, 完全摒弃一些外部组件, 使用Kubernetes统一管理, 降低维护成本. 大概是这样:

现状:

  • API网关:Spring Cloud Zuul
  • 注册中心:Spring Cloud Eureka
  • 配置中心:无
  • 部署环境:Kubernetes
  • 构建工具:Gradle

目标:

  • API网关:Spring Cloud Gateway
  • 注册中心:Kubernetes Service Discovery
  • 配置中心:Kubernetes ConfigMap Configuration
  • 部署环境:Kubernetes
  • 构建工具:Gradle

特性列表

  • [x]  Kubernetes Service Discovery
  • [x]  Kubernetes ConfigMap Configuration
  • [x]  Kubernetes ConfigMap Configuration Hot-Reload
  • [x]  Kubernetes Health Check
  • [x]  Gateway URL Route to Kubernetes Service
  • [ ]  Gateway White/Black List

开发环境

  • Windows 11:22H2
  • OpenJDK:11
  • Jetbrains IDEA
  • Spring Boot:2.7.12
  • Spring Cloud:2021.0.7

快速开始

  1. 通过 https://start.spring.io/ 生成你的项目, 引入需要的依赖, 这是我的 build.gradle
dependencies {
    // 应用健康检查
    implementation 'org.springframework.boot:spring-boot-starter-actuator'

    // Logback日志与链路追踪 TraceID
    implementation 'org.springframework.boot:spring-boot-starter-logging'
    implementation 'org.apache.skywalking:apm-toolkit-logback-1.x:8.8.0'

    // 网关组件
    implementation 'org.springframework.cloud:spring-cloud-starter-gateway'

    // Kubernetes服务发现与配置中心
    implementation 'org.springframework.cloud:spring-cloud-starter-kubernetes-client-all'

    // 单元测试
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

2. 配置 bootstrap-dev.yml 开启必要的特性, 这是我的 bootstrap-dev.yml

spring:
  application:
    name: dyrs-cncf-gateway
  cloud:
    kubernetes:
      config:
        enabled: true
        name: my-config-map
        namespace: dev
      discovery:
        enabled: true
        namespaces:
          - dev
      reload:
        enabled: true
        monitoring-config-maps: true
        strategy: refresh
    gateway:
      enabled: true
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
          url-expression: "uri"

3. 预部署一个应用, 并为它配置 Kubernetes Service , 在后面会通过Gateway应用访问这个 Kubernetes Service. 在这里我的服务名为 py-http-example , 这是我的service.yaml

kind: Service
apiVersion: v1
metadata:
  name: py-http-example
  namespace: dev
  labels:
    app: py-http-example
spec:
  ports:
    - name: http-5000
      protocol: TCP
      port: 5000
      targetPort: 5000
      nodePort: 32214
  selector:
    app: py-http-example
  type: NodePort

4. 使用Gradle打包我的应用程序, 并通过docker build/push到镜像仓库

5. 将Gateway应用部署到Kubernetes集群, 并配置NodePort Service, 方便在开发过程中访问

kind: Service
apiVersion: v1
metadata:
  name: cncf-gateway
  namespace: dev
  labels:
    app: cncf-gateway
spec:
  ports:
    - name: http-8080
      protocol: TCP
      port: 8080
      targetPort: 8080
      nodePort: 30142
  selector:
    app: cncf-gateway
  type: NodePort

6. 现在通过curl测试Gateway应用程序NodePort能否被正确访问

$ curl <http://172.16.103.210:30142/actuator/health>

{"status":"UP","groups":["liveness","readiness"]}

7. 最后通过Gateway应用程序调用py-http-example 服务的接口, 来进行网关的验证

$ curl <http://172.16.103.210:30142/py-http-example/api/books>

{
  "books": [
    {
      "author": "Judy",
      "id": 1,
      "title": "Python"
    },
    {
      "author": "Cline",
      "id": 2,
      "title": "Java"
    }
  ]
}

常见问题

  • Kubernetes ConfigMap 改变, 但是应用获取不到变更后的值(@Value)

  需要启用Class注解@RefreshScope

  • 通过Gateway应用访问Service, 返回503错误: There was an unexpected error (type=Service Unavailable, status=503).

  需要修改 spring.cloud.gateway.discovery.locator.url-expression="url" , 默认值是 "'lb://'+serviceId"

  • 通过 restTemplate.getForObject 访问Service报错

  Service port必须是80

标签