Kubernetes如何使用不同配置部署多个相同的应用程序和数据库?

8
困境: 部署多个应用和数据库容器对,使用相同的Docker映像和代码,但具有不同的配置(不同的客户使用子域)。 有哪些逻辑方法可以解决这个问题,因为Kubernetes似乎没有集成支持这种设置的方法? 可能的方法:
  1. 为所有应用部署使用单个应用程序服务,为所有数据库部署使用单个数据库服务。运行一个单一的Nginx静态文件服务和部署,它将从共享的静态卷中提供静态文件(所有部署都使用相同的静态文件集)。每当需要新部署时,使用bash脚本复制app和database .yaml部署文件并sed文本替换客户端名称,并指向正确的configmap(手动编写),然后kubectl apply它们。主要的nginx入口将处理传入的流量并通过应用程序部署服务指向正确的pod。
  2. 与上述方法类似,只是使用StatefulSet而不是单独的部署,并使用init容器将不同的配置复制到挂载的卷中(唯一的缺点是如果您不再需要特定客户端的容器,则无法删除状态集中的项目,因为它会是一个案例,这似乎是一种非常欺骗性的方法)。

理想情况下,如果StatefulSet可以使用downward API根据状态集的索引动态选择configmap名称,那么将解决此问题(您基本上会手动制作带有索引名称的配置文件,然后会被适当地选择)。例如:

env:
- name: POD_NAME
  valueFrom:
    fieldRef:
      fieldPath: metadata.name

envFrom:
- configMapRef:
  name: $(POD_NAME)-config

然而在kubernetes中,该功能不可用。

1
你最终选择了什么?我卡在和你当时一模一样的地方了。 - Samuel Thompson
采用了第一种方法,将配置文件存储在持久卷中。每当有新的部署站点时(为了添加新的子域路由),nginx配置文件也会进行文本替换/添加。 - Mick
3个回答

2
一个像Helm这样的模板引擎可以帮助解决这个问题。(我认为随着当前Kubernetes的发展,也可以使用Kustomize来实现,但我更熟悉Helm。)基本思想是你有一个包含Kubernetes YAML文件的“chart”,但可以使用模板语言(如Go text/template库)动态填充内容。
在这种设置中,通常你会让Helm创建ConfigMap和匹配的Deployment;在你描述的设置中,你需要为每个租户单独安装它(一个Helm“release”)。假设Nginx配置不同到足以使你想将它们存储在外部文件中;你的chart的核心部分将包括
values.yaml(可重写配置,helm install --set nginxConfig=bar.conf):
# nginxConfig specifies the name of the Nginx configuration
# file to embed.
nginxConfig: foo.conf

templates/configmap.yaml:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-{{ .Chart.Name }}-config
data:
  nginx.conf: |-
{{ .Files.Get .Values.nginxConfig | indent 4 }}

deployment.yaml:

apiVersion: v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-{{ .Chart.Name }}-nginx
spec:
  ...
    volumes:
      - name: nginx-config
        configMap:
          name: {{ .Release.Name }}-{{ .Chart.Name }}-config

{{ .Release.Name }}-{{ .Chart.Name }} 是一种常见的约定,允许在同一命名空间中安装多个图表的副本;第一部分是您为 helm install 命令指定的名称,第二部分是图表本身的名称。您还可以直接指定 ConfigMap 的内容,从 values.yaml 文件中引用其他 .Values... 设置,将 ConfigMap 用作环境变量而不是文件等等。


1

虽然动态的结构替换不可能,但我认为你的initContainer:思路是正确的;你可以使用serviceAccount从API中获取configMap,并在initContainer:中启动时将其作为环境变量传递给主容器。

initContainers:
- command:
  - /bin/bash
  - -ec
  - |
       curl -o /whatever/env.sh \
       -H "Authorization: Bearer $(cat /var/run/secret/etc/etc)" \
       https://${KUBERNETES_SERVICE_HOST}/api/v1/namespaces/${POD_NS}/configmaps/${POD_NAME}-config
  volumeMounts:
  - name: cfg  # etc etc
containers:
- command:
  - /bin/bash
  - -ec
  - "source /whatever/env.sh; exec /usr/bin/my-program"
  volumeMounts:
  - name: cfg  # etc etc
volumes:
- name: cfg
  emptyDir: {}

在这里,我们将ConfigMapPodSpec进行内联获取,但是如果您有一个专门用于获取ConfigMap并将其序列化为主容器可以使用的格式的Docker容器,我不希望实际解决方案如此冗长。
一种更为复杂(但也许更优雅)的方法是使用变异准入 Webhook,并且现在看起来他们已经利用Pod Presets正式规范化了您的使用情况,但从文档中不太清楚该功能首次出现在哪个版本中,也不清楚是否有任何必须调整的 api 服务器标志以利用它。

0

从v1.20开始,PodPresets已被移除。现在有一种更优雅的解决方案,基于Mutating Admission Webhook,可以解决这个问题。https://github.com/spoditor/spoditor

本质上,它使用了一个自定义注释在PodSpec模板上,例如:

      annotations:
        spoditor.io/mount-volume: |
          {
            "volumes": [
              {
                "name": "my-volume",
                "secret": {
                  "secretName": "my-secret"
                }
              }
            ],
            "containers": [
              {
                "name": "nginx",
                "volumeMounts": [
                  {
                    "name": "my-volume",
                    "mountPath": "/etc/secrets/my-volume"
                  }
                ]
              }
            ]
          }

现在,StatefulSet中每个Pod中的nginx容器将尝试以my-secret-{pod ordinal}的模式挂载自己专用的密钥。

您只需要确保my-secret-0my-secret-1等等在StatefulSet的相同命名空间中存在即可。

在项目文档中还有更高级的注释用法。


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