Kubernetes配置映射中的自动子目录?

18

大约两年前也有类似的问题被问到,虽然那个问题是关于机密的,但我认为配置映射的情况也不会有什么不同,但至少我可以介绍一下使用案例以及现有的解决方法为何对我们来说不可行。

假设有一个简单的、精简的deployment.yaml

apiVersion: apps/v1beta1
kind: Deployment
metadata: 
  name: example
spec:
  template: 
    spec:
      containers:
      - name: example
        volumeMounts:
        - name: vol
          mountPath: /app/Configuration
      volumes:
        - name: vol
          configMap:
            name: configs

还有匹配的configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: configs
  labels:
    k8s-app: example
data:
    example1.json: |-
        {
            "key1": "value1"
        }

    example2.json: |-
        {
            "key2": "value2"
        }
< p > 无论configmap.yaml中的密钥是什么,它们都会被简单地创建为文件,而无需修改deployment.yaml或具有任何特定要求,只需使用即可。

问题在于实际的结构具有子文件夹来处理特定区域的值以覆盖根目录:

Configuration \ example1.json
Configuration \ example2.json
Configuration \ us \ example1.json
Configuration \ us \ ca \ example2.json

这些配置的数量和性质显然会因为不同国家、地区和每个单独配置的模块而有所不同。我们的目标是为终端用户提供一个工具,使他们能够设置和管理这些配置。在幕后自动生成configmap.yaml并在kubernetes中更新它。
然而,除非我还没有发现的技巧,否则这似乎超出了kubernetes的能力,有几个方面。
首先,没有语法允许指定作为目录的configmap键,也不能在键中包含子目录路径。
data:
    # one possible approach (currently complains that it doesn't validate '[-._a-zA-Z0-9]+')
    /us/example1.json: |-
        {
            "key1": "value1"
        }

    # another idea; this obviously results in 'invalid type for io.k8s.api.core.v1.ConfigMap.data: got "map", expected "string"'
    us:
        example2.json: |-
            {
                "key2": "value2"
            }

那么我们有哪些选项来实现这个目标呢?

嗯,我们可以使用deployment.yamlvolumes: -configMap:节点中的items: -key: path:方法将键映射到特定位置,

或者在deployment.yamlvolumeMounts:节点中生成多个节点,

使用subPath:(基本上与在volumes: configMap:中使用items: -key: -path:相同),

或者为每个子目录单独生成配置映射,并将它们全部作为不同的volumes挂载到deployment.yaml中。

所有这些方法都需要对deployment.yaml进行大量且非常冗长的更改,泄漏出它不应该知道的知识,使其变得可变并不断重新生成而不是静态的,使滚动设置更新到已部署的pod变得复杂等等等等。这样做并不好。而所有这些只是为了映射一个目录,仅仅因为它包含子目录...

肯定不是这个工作的方式吧?我错过了什么?我该怎么办?

4个回答

5
使用Helm解决OP的问题非常简单,只需从`.Files.Glob`构建一个配置映射{ filename_sha: file_contents },然后挂载每个文件。它适用于二进制数据、有趣的文件名和深层嵌套的目录,至少目前为止都有效。
如果文件很多,这种方法效率可能不高,但如果文件数量不太多,这种简洁性可能会弥补其缺点。
以下是我代码的摘录,它将Helm图表中的所有内容从`files/**`复制,并在容器中将其挂载到`/mnt/**`下:
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-files
data:
# map all files under the files/ to a shasummed key pointing at the contents
{{ range $path, $bytes := .Files.Glob "files/**" }}
{{ sha256sum $path | nindent 2 }}: {{ $.Files.Get $path | quote }}
{{ end }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-container
spec:
  # ... (omitted for brevity)
  template:
    spec:
      containers:
        - name: amp-server
          # ... (omitted for brevity)
          volumeMounts:

            # now mount each such file, trimming the 'files/' prefix here
            {{ range $path, $bytes := .Files.Glob "files/**" }}
            - name: config-files-multipart
              mountPath: {{ printf "/mnt/%s" (trimPrefix "files/" $path) | quote }}
              subPath: {{ sha256sum $path | quote }}
            {{ end }}

      volumes:
        - name: config-files-multipart
          configMap:
            name: my-files

太棒了!谢谢 - 正是我需要的。 - Eric B.

4
从“容器本地”角度来看,一个包含大量配置文件的文件系统树,在应用程序启动时需要处理这些文件以达到其规范配置是一种反模式。最好采用工作流程生成单个文件,该文件可以存储在ConfigMap中,并且在最终形式下易于检查。例如,参见nginx ingress。
但显然,并非每个人都在重新编写其应用程序以更好地与kubernetes方法相匹配。因此,在部署时将完整目录树的配置文件置于容器中的最简单方法是使用initContainers和emptyDir挂载。
将配置文件树打包到容器中(有时称为“仅数据”容器),并使容器启动脚本将配置树复制到emptyDir挂载中。然后,应用程序可以按其预期方式使用该树。

0
根据您的配置树的规模,另一个可行的选项可能是使用下划线模拟子树,而不是在configmap内的文件“路径”中使用斜杠。这将使您失去一般文件系统性能(如果您只需要读取配置,则不应该成为问题),并强制您重写一些应用程序代码(在访问配置时进行文件模式遍历,而不是目录遍历),但应该以相当便宜的价格解决您的用例。

0
一些解决方法:
  1. 使用仅包含数据的容器构建数据...
FROM scratch
... # copy data here

然后将其作为 Sidecar 添加,将卷挂载到另一个容器上...

  1. 从配置创建 tar 文件,转换为 ConfigMap,在容器中挂载,并在启动前更改容器命令以解压缩配置文件...

  2. 使用一些特殊字符而不是“/”重命名文件,例如“us@example.json”,并使用脚本将它们移回起始位置。

所有这些都非常 hacky... 最好的方案是对它们进行重构,以便可以在一个平面文件夹中使用,并使用类似 kustomize 的工具创建它们:

kustomize edit add configmap my-configmap --from-file='./*.json'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接