likes
comments
collection
share

Spring Cloud Kubernetes实战

作者站长头像
站长
· 阅读数 13

介绍

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

使用ConfigMap的数据

添加依赖

<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属性:

spring:
  application:
    name: demo-resource-server
  config:
    import: "kubernetes:"

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

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

比如:

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)

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。如下:

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的名字

    • 比如:

      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显式地指定一个前缀

    • 比如:

      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。

比如,你有如下配置:

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

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数据

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一样:

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:

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

转载自:https://juejin.cn/post/7306337248621772827
评论
请登录