有些应用需要自我意识,需要关于自己的信息。自我意识(Self Awareness)模式描述了 Kubernetes Downward API,它为应用程序提供了一个简单的自省和元数据注入机制。

问题描述

对于大多数用例来说,云原生应用是无状态的、可处置的,没有与其他应用相关的身份。然而,有时,即使是这些类型的应用程序也需要有关于它们自己和它们运行的环境的信息。这可能包括只有在运行时才知道的信息,如 Pod 名称,Pod IP 地址,以及应用程序所在的主机名。或者,在 Pod 级别定义的其他静态信息,如特定的资源请求和限制,或者一些动态信息,如注释和标签,可以由用户在运行时改变。

例如,根据容器可用的资源,您可能想调整应用程序线程池的大小,或改变垃圾收集算法或内存分配。您可能想在记录信息时使用 Pod 名称和主机名,或在向中央服务器发送指标时使用。你可能想在同一命名空间中发现其他具有特定标签的 Pod,并将它们加入到一个集群应用中。对于这些和其他用例,Kubernetes 提供了 Downward API。

解决方案

我们所描述的需求和下面的解决方案不仅仅是针对容器的,而是存在于任何资源元数据变化的动态环境中。例如,AWS 提供的实例元数据和用户数据服务,可以从任何 EC2 实例中查询,以检索有关 EC2 实例本身的元数据。同样,AWS ECS 也提供了可以被容器查询的 API,并检索有关容器集群的信息。

Kubernetes 的方法更优雅,也更容易使用。Downward API 允许通过环境变量和文件将 Pod 的元数据传递给容器和集群。这些机制与我们用于从 ConfigMap 和 Secret 传递应用相关数据的机制相同。但在这种情况下,数据不是由我们创建的。相反,我们指定我们感兴趣的键,Kubernetes 会动态地填充值。图 13-1 给出了 Downward API 如何向感兴趣的 Pod 注入资源和运行时信息的概述。
image.png
图 13-1 应用自省机制

这里的要点是,通过 Downward API,元数据被注入到你的 Pod 中并在本地提供。应用程序不需要使用客户端并与 Kubernetes API 交互,可以保持 Kubernetes 无关性。让我们看看在例 13-1 中,通过环境变量请求元数据是多么容易。

  1. # 例 13-1 来自 Downward API 的环境变量
  2. ---
  3. apiVersion: v1
  4. kind: Pod
  5. metadata:
  6. name: random-generator
  7. spec:
  8. containers:
  9. - image: k8spatterns/random-generator:1.0
  10. name: random-generator
  11. env:
  12. - name: POD_IP
  13. valueFrom:
  14. # 环境变量 POD_IP 是从这个 Pod 的属性中设置的,并且来自于在 Pod 启动时进入存在
  15. fieldRef:
  16. fieldPath: status.podIP
  17. - name: MEMORY_LIMIT
  18. valueFrom:
  19. resourceFieldRef:
  20. # 环境变量 MEMORY_LIMIT 被设置为这个容器的内存资源限制值,这里不显示实际的限制声明
  21. container: random-generator
  22. resource: limits.memory

在本例中,我们使用 fieldRef 来访问 Pod 级元数据。表 13-1 中所示的键既可用于 fieldRef.fieldPath,也可作为环境变量和 downwardAPI 存储卷。
image.png
表 13-1 可用于 fieldRef.fieldPath 的 Download API 信息

fieldRef 类似,我们可以使用 resourceFieldRef 来访问属于 Pod 的容器的特定元数据。这个元数据是特定于一个容器的,可以用 resourceFieldRef.container 来指定。当作为环境变量使用时,默认使用当前容器。resourceFieldRef.resource 的可能键如表 13-2 所示。
image.png
表 13-2 可用于 resourceFieldRef.resource 的 Download API 信息

用户可以在 Pod 运行时更改某些元数据,如标签和注释。除非重新启动 Pod,否则环境变量不会反映这种变化。但是 Downward API 卷可以反映标签和注释的更新。除了前面描述的单个字段,Downward API 卷可以将所有的 Pod 标签和注释捕获到带有 metadata.labelmetadata.annotations 引用的文件中。例 13-2 展示了如何使用这种卷。

# 例 13-2 Downward API 的存储卷

---
apiVersion: v1 
kind: Pod 
metadata:
    name: random-generator 
spec:
    containers:
    - image: k8spatterns/random-generator:1.0
        name: random-generator 
       volumeMounts:
    # Downward API 的值可以作为文件挂载到 Pod 中
        - name: pod-info
            mountPath: /pod-info 
  volumes:
    - name: pod-info 
      downwardAPI:
            items:
      # 文件标签包含所有标签,一行一行的,格式为 name=value
      # 当标签发生变化时,这个文件会被更新
            - path: labels
                fieldRef:
                    fieldPath: metadata.labels
      # 注释文件保存了与标签相同格式的所有注释
            - path: annotations 
          fieldRef:
                    fieldPath: metadata.annotations

:::tips 对于卷,如果 Pod 运行时元数据发生变化,就会反映在卷文件中。但还是要由消费应用程序来检测文件变化并相应地读取更新的数据。如果应用中没有实现这样的功能,仍然可能需要重新启动 Pod。 :::

一些讨论

在很多场合,一个应用需要自知,需要掌握自身和运行环境的信息。Kubernetes 为自省和元数据注入提供了非侵入式机制。Downward API 的一个缺点是它提供了一个固定数量的键,可以被引用。如果你的应用程序需要更多的数据,特别是关于其他资源或集群相关的元数据,它必须在 API Server 上进行查询。

这种技术被许多应用程序使用,它们查询 API Server 以发现同一命名空间中具有某些标签或注释的其他 Pod。然后应用程序可能会与发现的 Pod 组成一个集群,并同步状态,例如。它也被监控应用程序用来发现感兴趣的 Pod,然后开始对它们进行仪表化。

许多不同语言的客户端库可以与 Kubernetes API Server 交互,以获得更多的自引用信息,而这些信息超出了 Downward API 提供的范围。

参考资料