Spring Cloud Kubernetes 配置观察器

Kubernetes 提供了将 ConfigMap 或 Secret 挂载为应用程序容器中的卷的能力。当 ConfigMap 或 Secret 的内容发生变化时,挂载的卷将随之更新。

然而,除非重启应用程序,否则 Spring Boot 不会自动更新这些变化。Spring Cloud 提供了无需重启应用即可刷新应用上下文的能力,可以通过访问 /refresh actuator 端点或使用 Spring Cloud Bus 发布 RefreshRemoteApplicationEvent 来实现。

为了在 Kubernetes 上运行的 Spring Cloud 应用中实现此配置刷新,您可以将 Spring Cloud Kubernetes Configuration Watcher 控制器部署到您的 Kubernetes 集群中。

该应用作为容器发布,可在 Docker Hub 上获取。但是,如果您需要自定义配置观察器行为或更喜欢自己构建镜像,您可以轻松地从 GitHub 上的源代码构建自己的镜像并使用。

配置它的另一种选择是在用于部署配置观察器的 deployment.yaml 中提供一些环境变量。以下是一些重要的变量:

env:
  - name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CONFIGURATION_WATCHER
    value: DEBUG
  - name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_CONFIG_RELOAD
    value: DEBUG
  - name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_COMMONS_CONFIG_RELOAD
    value: DEBUG

这些变量用于启用配置观察器的调试日志记录,在初始设置时特别有用,以便诊断潜在的配置错误。

env:
  - name: SPRING_CLOUD_KUBERNETES_RELOAD_NAMESPACES_0
    value: "namespace-a"

此变量让观察器知道在哪里搜索 secrets 和 configmaps。这里有两种选择:选择性命名空间(上面的设置)和通过命名空间解析(Namespace Resolution)选择的命名空间(这是默认选项)。请记住,所有这些选项都需要适当的 RBAC 规则。

configmaps/secrets 的更改只有在其来源于带有以下标签的源时,才会触发配置观察器发出事件:spring.cloud.kubernetes.config=truespring.cloud.kubernetes.secret=true

简单来说,如果您更改了一个没有上述标签的 configmap(或 secret),配置观察器将跳过为此更改触发事件(如果您启用了调试日志记录,这将在日志中可见)。

默认情况下,配置观察器将监控配置的命名空间中的所有 configmaps/secrets。如果您想过滤只监控特定的源,可以通过设置以下变量实现:

SPRING_CLOUD_KUBERNETES_CONFIG_INFORMER_ENABLED=TRUE

这将告诉观察器只监控带有标签:spring.cloud.kubernetes.config.informer.enabled=true 的源。

另一个重要的配置,特别是对于作为卷挂载的 configmaps 和 secrets(通过 spring.cloud.kubernetes.config.paths/spring.cloud.kubernetes.secrets.paths 或使用 spring.config.import),是

- name: SPRING_CLOUD_KUBERNETES_CONFIGURATION_WATCHER_REFRESHDELAY
  value: "10000"

这指定了在配置观察器触发事件之前应该等待多少毫秒。这很重要,因为 Kubernetes 文档提到

当卷中当前使用的 ConfigMap 更新时,投影的键也会最终更新。

您需要将这个“最终”的部分与集群中该值的毫秒数“匹配”。

Spring Cloud Kubernetes Configuration Watcher 可以通过两种方式向应用发送刷新通知。

  1. 通过 HTTP,在这种情况下,被通知的应用必须暴露 /refresh actuator 端点,并且可以从集群内部访问。

  2. 使用 Spring Cloud Bus,在这种情况下,您需要在集群中部署一个消息代理供应用使用。

部署 YAML

下面是一个您可以用来将 Kubernetes Configuration Watcher 部署到 Kubernetes 的示例部署 YAML。

---
apiVersion: v1
kind: List
items:
  - apiVersion: v1
    kind: Service
    metadata:
      labels:
        app: spring-cloud-kubernetes-configuration-watcher
      name: spring-cloud-kubernetes-configuration-watcher
    spec:
      ports:
        - name: http
          port: 8888
          targetPort: 8888
      selector:
        app: spring-cloud-kubernetes-configuration-watcher
      type: ClusterIP
  - apiVersion: v1
    kind: ServiceAccount
    metadata:
      labels:
        app: spring-cloud-kubernetes-configuration-watcher
      name: spring-cloud-kubernetes-configuration-watcher
  - apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      labels:
        app: spring-cloud-kubernetes-configuration-watcher
      name: spring-cloud-kubernetes-configuration-watcher:view
    roleRef:
      kind: Role
      apiGroup: rbac.authorization.k8s.io
      name: namespace-reader
    subjects:
      - kind: ServiceAccount
        name: spring-cloud-kubernetes-configuration-watcher
  - apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      namespace: default
      name: namespace-reader
    rules:
      - apiGroups: ["", "extensions", "apps"]
        resources: ["configmaps", "pods", "services", "endpoints", "secrets"]
        verbs: ["get", "list", "watch"]
  - apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: spring-cloud-kubernetes-configuration-watcher-deployment
    spec:
      selector:
        matchLabels:
          app: spring-cloud-kubernetes-configuration-watcher
      template:
        metadata:
          labels:
            app: spring-cloud-kubernetes-configuration-watcher
        spec:
          serviceAccount: spring-cloud-kubernetes-configuration-watcher
          containers:
          - name: spring-cloud-kubernetes-configuration-watcher
            image: springcloud/spring-cloud-kubernetes-configuration-watcher:3.2.1
            imagePullPolicy: IfNotPresent
            readinessProbe:
              httpGet:
                port: 8888
                path: /actuator/health/readiness
            livenessProbe:
              httpGet:
                port: 8888
                path: /actuator/health/liveness
            ports:
            - containerPort: 8888

Service Account 和相关的 Role Binding 对于 Spring Cloud Kubernetes Configuration 的正常工作很重要。控制器需要访问权限来读取 Kubernetes 集群中的 ConfigMaps、Pods、Services、Endpoints 和 Secrets 的数据。

监控 ConfigMaps 和 Secrets

如果对带有有效标签(如上所述)的 ConfigMap 或 Secret 进行了更改,则 Spring Cloud Kubernetes Configuration Watcher 将获取该 ConfigMap 或 Secret 的名称,并向同名应用发送通知。但这可能不足以满足您的用例,例如,您可能希望

  • 将一个 config-map 绑定到多个应用,以便单个 configmap 内的更改能触发多个服务的刷新

  • 让基于配置文件的源为您的应用触发事件

因此,您可以指定一个额外的注解

spring.cloud.kubernetes.configmap.appsspring.cloud.kubernetes.secret.apps。它接受一个逗号分隔的应用名称字符串,指定当此 secret/configmap 发生更改时将收到通知的应用名称。

例如

kind: ConfigMap
apiVersion: v1
metadata:
  name: example-configmap
  labels:
    spring.cloud.kubernetes.config: "true"
  annotations:
    spring.cloud.kubernetes.configmap.apps: "app-a, app-b"

HTTP 实现

HTTP 实现是默认使用的实现。当使用此实现时,如果 ConfigMap 或 Secret 发生变化,Spring Cloud Kubernetes Configuration Watcher 的 HTTP 实现将使用 Spring Cloud Kubernetes Discovery Client 获取与 ConfigMap 或 Secret 名称匹配的所有应用实例,并向应用的 /refresh actuator 端点发送一个 HTTP POST 请求。默认情况下,它将使用在 Discovery Client 中注册的端口向 /actuator/refresh 发送 POST 请求。

您还可以配置配置观察器调用实例的 shutdown actuator 端点。为此,您可以设置 spring.cloud.kubernetes.configuration.watcher.refresh-strategy=shutdown

非默认管理端口和 Actuator 路径

如果应用使用非默认的 actuator 路径和/或使用不同的端口作为管理端点,应用的 Kubernetes service 可以添加一个名为 boot.spring.io/actuator 的注解,并将其值设置为应用使用的路径和端口。例如

apiVersion: v1
kind: Service
metadata:
  labels:
    app: config-map-demo
  name: config-map-demo
  annotations:
    boot.spring.io/actuator: http://:9090/myactuator/home
spec:
  ports:
    - name: http
      port: 8080
      targetPort: 8080
  selector:
    app: config-map-demo

另一种配置 actuator 路径和/或管理端口的方式是设置 spring.cloud.kubernetes.configuration.watcher.actuatorPathspring.cloud.kubernetes.configuration.watcher.actuatorPort

消息实现

将 Spring Cloud Kubernetes Configuration Watcher 应用部署到 Kubernetes 时,可以通过将 profile 设置为 bus-amqp (RabbitMQ) 或 bus-kafka (Kafka) 来启用消息实现。默认情况下,使用消息实现时,配置观察器将使用 Spring Cloud Bus 向所有应用实例发送 RefreshRemoteApplicationEvent。这将导致应用实例在不重启的情况下刷新应用的配置属性。

您还可以配置以便关闭应用实例来刷新应用的配置属性。当应用关闭时,Kubernetes 将重启应用实例并加载新的配置属性。要使用此策略,请设置 spring.cloud.kubernetes.configuration.watcher.refresh-strategy=shutdown

配置 RabbitMQ

启用 bus-amqp profile 后,您需要配置 Spring RabbitMQ,使其指向您希望使用的 RabbitMQ 实例位置以及进行身份验证所需的任何凭据。这可以通过设置标准的 Spring RabbitMQ 属性来实现,例如

spring:
  rabbitmq:
    username: user
    password: password
    host: rabbitmq

配置 Kafka

启用 bus-kafka profile 后,您需要配置 Spring Kafka,使其指向您希望使用的 Kafka Broker 实例位置。这可以通过设置标准的 Spring Kafka 属性来实现,例如

spring:
  kafka:
    producer:
      bootstrap-servers: localhost:9092