Spring Cloud Kubernetes实战

介绍

使用Spring Cloud Kubernetes部署Spring应用程序到k8s集群时,主要的目的是动态读取ConfigMap/Secret里面的配置。本文主要介绍如何开发Spring应用。

使用ConfigMap的数据

添加依赖

xml 复制代码
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-kubernetes-fabric8-config</artifactId>
  <version>3.0.4</version>
</dependency>

加载单一ConfigMap

Spring Cloud k8s Config默认会在应用所在的namespace搜寻名字为spring.application.name属性值的ConfigMap,然后加载里面的属性到Spring Config中。

但是,前提是在Spring应用的配置文件(application.yml)中,指定了spring.config.import属性:

yaml 复制代码
spring:
  application:
    name: demo-resource-server
  config:
    import: "kubernetes:"

在没有bootstrap依赖,并且没有bootstrap.yml文件的情况下,如果没有上述属性,那么Spring Cloud k8s不会搜寻k8s来查找配置。

使用上述配置,只要在k8s集群中,与应用同一个namespace里面,有一个名字叫做demo-resource-server的ConfigMap,那么里面的配置就会被加载到Spring的Config中。

比如:

yaml 复制代码
apiVersion: v1
kind: ConfigMap
metadata:
  name: demo-resource-server
  namespace: default
data:
  application.properties: |-
    demo.name=demo-app-from-k8s

因为这个ConfigMap的名字(demo-resource-server)与应用的spring.application.name(demo-resource-server)相同,所以这个ConfigMap的内容(application.properties里面的内容)就会被加载到Spring的Config中。

加载多个ConfigMap

如果想从多个ConfigMap加载配置,甚至从不同的namespace加载配置,可以使用如下的配置(application.yml)

yaml 复制代码
spring:
  application:
    name: demo-resource-server
  config:
    import: "kubernetes:"
  cloud:
    kubernetes:
      config:
        sources:
          - name: demo-resource-server
          - name: demo-app

这样,部署到k8s的应用,就会在应用所在在namespace查找名字为demo-resource-server和demo-app的两个ConfigMap,来加载配置了。

甚至,你可以利用ConfigMap的标签(labels)来查找符合条件的ConfigMap。如下:

yaml 复制代码
spring:
  application:
    name: labeled-configmap-with-prefix
  cloud:
    kubernetes:
      config:
        sources:
          - labels:
              letter: a

这个配置,会导致Spring应用从当前namespace中查找所有具有label:({letter : a})的所有ConfigMap数据被加载成配置。

属性前缀

如果有多个ConfigMap被匹配到,那么对于重复的属性,到底哪个起作用,其实是未知的,不过大部分情况下,可以认为最后一个生效。为了区分不同的ConfigMap过来的属性,可以通过两个配置选项来给属性配置前缀。

  • use-name-as-prefix

    • 默认false,如果为true则给当前ConfigMap中的所有属性添加一个前缀,前缀的名字就是ConfigMap的名字

    • 比如:

      yaml 复制代码
      spring:
        application:
          name: with-prefix
        cloud:
          kubernetes:
            config:
              namespace: default-namespace
              sources:
                - name: config-map-one
                  use-name-as-prefix: false

      那么假设在config-map-one这个ConfigMap中有一个属性:demo.name,那么在加载到Spring的时候,就变成了:config-map-one.demo.name

  • explicit-prefix

    • 默认false,如果为true则给当前ConfigMap显式地指定一个前缀

    • 比如:

      yaml 复制代码
      spring:
        application:
          name: with-prefix
        cloud:
          kubernetes:
            config:
              useNameAsPrefix: true
              namespace: default-namespace
              sources:
                - name: config-map-one
                  useNameAsPrefix: false
                - name: config-map-two
                  explicit-prefix: two

      那么假设在config-map-two这个ConfigMap中有一个属性:demo.name,那么加载到Spring的时候,就变成了:tow.demo.name

Profile

在Spring Cloud k8s寻找匹配的ConfigMap时,默认会同时考虑生效的profile。

比如,你有如下配置:

yaml 复制代码
spring:
  application:
    name: demo-app
  config:
    import: "kubernetes:"

并且正在生效的profile是dev,那么Spring Cloud k8s在查找匹配的ConfigMap时,会考虑:demo-app和demo-app-dev两个ConfigMap。

如果想要禁止这个功能,可以关闭属性:spring.cloud.kubernetes.config.include-profile-specific-sources

yaml 复制代码
spring:
  application:
    name: demo-app
  config:
    import: "kubernetes:"
  cloud:
    kubernetes:
      config:
        include-profile-specific-sources: false

使用Secret的数据

默认行为

  • 默认Spring应用程序只能通过共享卷(volume)来访问secret,这也是官方推荐的做法
  • 默认spring.cloud.kubernetes.secrets.enable-api=false,意味着不能通过名字/labels来查找secret

通过Volume访问Secret数据

在部署(deployment)中,指定通过环境变量的方式来获取Secret数据

yaml 复制代码
apiVersion: v1
kind: Deployment
metadata:
  name: ${project.artifactId}
spec:
   template:
     spec:
       containers:
         - env:
            - name: DB_USERNAME
              valueFrom:
                 secretKeyRef:
                   name: db-secret
                   key: username
            - name: DB_PASSWORD
              valueFrom:
                 secretKeyRef:
                   name: db-secret
                   key: password

或者,也可以通过-Dspring.cloud.kubernetes.secrets.paths=/etc/secrets/db-secret,etc/secrets/postgresql这样的配置来指定secret挂在的目录

通过API访问Secret数据

这不是推荐的做法,但是这样却能够达到reload的效果。方式其实与ConfigMap一样:

yaml 复制代码
spring:
  application:
    name: cloud-k8s-app
  cloud:
    kubernetes:
      secrets:
        name: default-name
        namespace: default-namespace
        sources:
         # Spring Cloud Kubernetes looks up a Secret named s1 in namespace default-namespace
         - name: s1
         # Spring Cloud Kubernetes looks up a Secret named default-name in namespace n2
         - namespace: n2
         # Spring Cloud Kubernetes looks up a Secret named s3 in namespace n3
         - namespace: n3
           name: s3

自动刷新配置(reload)

spring.cloud.kubernetes.config.fail-fast=true时,reload不起作用

若想启用reload,只要打开这个配置开关:spring.cloud.kubernetes.reload.enabled=true

默认,只会reload ConfigMap的更新,如果也想要reload Secret的更新,可以设置属性:spring.cloud.kubernetes.reload.monitoring-secrets=true

reload级别

  • refresh(默认)

    • 只有那些标注了@ConfigurationProperties或者@RefreshScope的bean被reload
  • restart_context

    • 整个SpringContext会被优雅的重启,bean会根据新的配置被重建

    • 为了达到这个目的,必须暴露restart actuator:

      yaml 复制代码
      management:
        endpoint:
          restart:
            enabled: true
        endpoints:
          web:
            exposure:
              include: restart
  • shutdown

    • ApplicationContext会被关闭,从而导致容器重启(container restart)
    • 需要确保所有k8s的controller被配置成重启pod

reload的两种方式

  • Event(默认)

    • 使用websocket推送configmap/secret的变更事件
  • Polling

    • 定期主动查询configmap/secret,查看是否有变动,如果有则更新配置

结论

使用Spring Cloud Kubernetes开发的Spring应用,可以实现动态读取ConfigMap/Secret中的配置,适合配置需要动态调整的场景。

特别地,本文是基于3.0.4版本撰写,所以使用的是spring.config.import的方式来启用k8s配置读取。对于老版本,可能大部分使用的是bootstrap,与本文的配置方式不一样。

相关推荐
用户9083246027316 小时前
Spring AI 1.1.2 + Neo4j:用知识图谱增强 RAG 检索(上篇:图谱构建)
java·spring boot
可观测性用观测云1 天前
云原生网关 Ingress-Nginx 链路追踪实战:OpenTelemetry 采集与观测云集成方案
nginx·kubernetes
用户8307196840821 天前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解2 天前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解2 天前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记2 天前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者2 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840822 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
Java水解3 天前
SpringBoot3全栈开发实战:从入门到精通的完整指南
spring boot·后端
蝎子莱莱爱打怪3 天前
GitLab CI/CD + Docker Registry + K8s 部署完整实战指南
后端·docker·kubernetes