困境:部署多个应用程序和数据库容器对,具有相同的 docker 映像和代码,但配置不同(使用子域的不同客户端(。
有哪些合乎逻辑的方法可以解决这个问题,因为 kubernetes 似乎没有支持这种设置的集成?
可能的办法
对所有应用部署使用- 单个应用服务,对所有数据库部署使用单个数据库服务。运行单个 Nginx 静态文件服务和部署,该服务和部署将提供来自在应用部署之间共享的静态卷中的静态文件(所有静态文件都使用同一组静态文件(。每当需要新的部署时,让 bash 脚本复制应用程序和数据库 .yaml 部署文件,并将 sed 文本替换为客户端的名称,并指向正确的配置映射(当然是手动编写的(和 kubectl 应用它们。主 nginx 入口将处理传入流量,并通过应用部署服务指向正确的 pod
- 与上述类似,除了使用 StatefulSet 而不是单独的部署,以及使用 init 容器将不同的配置复制到挂载的卷(唯一的缺点是您无法删除 statefulset 中间的项目,如果您不再需要客户端的特定容器,就会出现这种情况,这似乎是一种非常黑客的方法(。
理想情况下,如果 StatefulSet 可以使用向下的 API 根据有状态集的索引动态选择配置映射名称,这将解决问题(您基本上会使用名称中的索引手动制作配置文件,并且会适当地选择它(。像这样:
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
envFrom:
- configMapRef:
name: $(POD_NAME)-config
但是,该功能在 kubernetes 中不可用。
像 Helm 这样的模板引擎可以帮助解决这个问题。 (我相信当前 Kubernetes 附带的 Kustomize 也可以做到这一点,但我对 Helm 更熟悉。 基本思想是,你有一个包含 Kubernetes YAML 文件的图表,但可以使用模板语言(Gotext/template
库(来动态填充内容。
在此设置中,通常你会让 Helm 同时创建 ConfigMap 和匹配的部署;在你描述的设置中,你将为每个租户单独安装它(Helm版本(。 假设 Nginx 配置足够不同,以至于您想将它们存储在外部文件中;图表的核心部分将包括
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 用作环境变量而不是文件,等等。
虽然动态结构替换是不可能的(正负,请参阅下面的整个故事(,但我相信你的想法是正确的initContainer:
; 您可以使用serviceAccount
从initContainer:
中的 API 获取configMap
,然后在启动时通过主容器获取该环境:
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: {}
在这里,我们有与PodSpec
内联获取ConfigMap
,但是如果您有一个专门用于获取ConfigMap
并将它们序列化为主容器可以使用的格式的docker容器,我不希望实际的解决方案几乎那么冗长
一个单独的,更复杂(但也许是优雅的(方法是变异入场Webhook,看起来他们最近甚至已经使用Pod Presets正式化了您的用例,但从文档中并不清楚该功能最初出现在哪个版本中,也不清楚是否有任何apiserver标志必须调整才能利用它。
PodPresets自 v1.20 起已被删除,基于突变入场 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"
}
]
}
]
}
现在,状态集的每个 Pod 中的nginx
容器将尝试以my-secret-{pod ordinal}
的模式挂载自己的专用密钥。
您只需要确保my-secret-0
、my-secret-1
等等都存在于 StatefulSet 的同一命名空间中。
在项目文档中,注释有更高级的用法。