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,与本文的配置方式不一样。

相关推荐
小猿姐1 天前
从 0 到 1 构建领先 DBaaS 平台:移动云架构师丁顺的 KubeBlocks 实践之路
云原生·kubernetes·dba
VX:Fegn08951 天前
计算机毕业设计|基于springboot + vue图书管理系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
2201_761199041 天前
7.statefulset
开发语言·kubernetes·php
利刃大大1 天前
【SpringBoot】搭建Java部署环境 && 部署项目到Linux服务器
java·服务器·spring boot
椰果子1 天前
Nacos 2.x.x版本不适用JDK17的处理方式
java·spring boot·后端
骇客野人1 天前
基于springboot的Java快速定时任务
java·windows·spring boot
05大叔1 天前
Springboot
java·spring boot·spring
lpfasd1231 天前
Spring Boot 4.0.1 集成 Spring Boot AI 全攻略
人工智能·spring boot·后端
+VX:Fegn08951 天前
计算机毕业设计|基于springboot + vue在线教育学习系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·学习·课程设计
我爱娃哈哈1 天前
SpringBoot集成:5分钟实现HTML转PDF功能
spring boot·pdf·html