helm - 运行 pods 和具有预定义流程顺序的依赖项
helm - run pods and dependencies with a predefined flow order
我正在使用 K8S 和 helm。
我需要 运行 pods 和具有预定义流程顺序的依赖项。
如何创建 helm 依赖项,运行 pod 仅一次(即 - 第一次填充数据库),并在第一次成功后退出?
此外,如果我有多个 pods,并且我想 运行 仅在特定条件下以及在创建 pod 之后出现 pod。
需要建造2个pods,如下所述:
我有一个数据库。
第一步是创建数据库。
第二步是填充数据库。
填充数据库后,此作业需要完成。
第三步 是另一个使用该数据库的 pod(不是 db pod),并且始终处于侦听模式(永不停止)。
我可以定义依赖关系的顺序 运行ning(并不总是平行的)。
我看到 helm create
命令有 deployment.yaml 和 service.yaml 的模板,也许 pod.yaml 是更好的选择?
这种情况下最好的图表类型是什么?
此外,需要知道什么是图表层次结构。
即:当有一个类型为监听器的图表,一个用于数据库创建的 pod,一个用于数据库填充的 pod(完成后删除)时,我可能有一个图表树层次结构来解释流程。
主图表使用填充的数据(在所有子图表和模板都正确 运行 之后 - 顺便说一句,同一个图表可以有多个模板吗?)。
什么是正确的树流
谢谢。
您可以使用 helm hooks 和 K8s Jobs 来实现这一点,下面是为 Rails 应用程序定义相同的设置。
第一步,定义一个k8s作业来创建和填充数据库
apiVersion: batch/v1
kind: Job
metadata:
name: {{ template "my-chart.name" . }}-db-prepare
annotations:
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "-1"
"helm.sh/hook-delete-policy": hook-succeeded
labels:
app: {{ template "my-chart.name" . }}
chart: {{ template "my-chart.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
backoffLimit: 4
template:
metadata:
labels:
app: {{ template "my-chart.name" . }}
release: {{ .Release.Name }}
spec:
containers:
- name: {{ template "my-chart.name" . }}-db-prepare
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
command: ["/docker-entrypoint.sh"]
args: ["rake", "db:extensions", "db:migrate", "db:seed"]
envFrom:
- configMapRef:
name: {{ template "my-chart.name" . }}-configmap
- secretRef:
name: {{ if .Values.existingSecret }}{{ .Values.existingSecret }}{{- else }}{{ template "my-chart.name" . }}-secrets{{- end }}
initContainers:
- name: init-wait-for-dependencies
image: wshihadeh/wait_for:v1.2
imagePullPolicy: {{ .Values.image.pullPolicy }}
command: ["/docker-entrypoint.sh"]
args: ["wait_for_tcp", "postgress:DATABASE_HOST:DATABASE_PORT"]
envFrom:
- configMapRef:
name: {{ template "my-chart.name" . }}-configmap
- secretRef:
name: {{ if .Values.existingSecret }}{{ .Values.existingSecret }}{{- else }}{{ template "my-chart.name" . }}-secrets{{- end }}
imagePullSecrets:
- name: {{ .Values.imagePullSecretName }}
restartPolicy: Never
注意以下几点:
1- 作业定义在每个部署上都有 运行 的 helm 挂钩,并且是第一个任务
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "-1"
"helm.sh/hook-delete-policy": hook-succeeded
2- 容器命令,将负责准备数据库
command: ["/docker-entrypoint.sh"]
args: ["rake", "db:extensions", "db:migrate", "db:seed"]
3- 在 db-connection 启动之前作业不会开始(这是通过 initContainers 实现的)
args: ["wait_for_tcp", "postgress:DATABASE_HOST:DATABASE_PORT"]
第二步是定义应用部署对象。这可以是常规部署对象(确保您不使用 helm hooks)示例:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ template "my-chart.name" . }}-web
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
checksum/secret: {{ include (print $.Template.BasePath "/secrets.yaml") . | sha256sum }}
labels:
app: {{ template "my-chart.name" . }}
chart: {{ template "my-chart.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.webReplicaCount }}
selector:
matchLabels:
app: {{ template "my-chart.name" . }}
release: {{ .Release.Name }}
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
checksum/secret: {{ include (print $.Template.BasePath "/secrets.yaml") . | sha256sum }}
labels:
app: {{ template "my-chart.name" . }}
release: {{ .Release.Name }}
service: web
spec:
imagePullSecrets:
- name: {{ .Values.imagePullSecretName }}
containers:
- name: {{ template "my-chart.name" . }}-web
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
command: ["/docker-entrypoint.sh"]
args: ["web"]
envFrom:
- configMapRef:
name: {{ template "my-chart.name" . }}-configmap
- secretRef:
name: {{ if .Values.existingSecret }}{{ .Values.existingSecret }}{{- else }}{{ template "my-chart.name" . }}-secrets{{- end }}
ports:
- name: http
containerPort: 8080
protocol: TCP
resources:
{{ toYaml .Values.resources | indent 12 }}
restartPolicy: {{ .Values.restartPolicy }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{ toYaml . | indent 8 }}
{{- end }}
如果我没理解错的话,您想在部署策略中构建一个依赖链,以确保在您的任何应用程序启动之前准备好某些事情。在您的情况下,您需要在应用程序启动之前部署 pre-populated 数据库。
我建议不要构建这样的依赖链,因为它会使部署管道中的事情变得复杂,并且如果您将来开始部署多个应用程序,则会阻止部署过程的适当扩展。在像 kubernetes 这样高度动态的环境中,每个部署都应该能够检查它自己启动所需的先决条件,而不依赖于部署顺序。
这可以通过 initContainers
和 probes
的组合来实现。如果不满足某些先决条件and/or 可以在每个部署中指定两者以防止它失败and/or 以在服务开始将流量路由到您的部署(在您的情况下是数据库)之前满足某些先决条件。
简而言之:
- 要在数据库启动前填充数据库卷,请使用
initContainer
- 要让数据库在初始化和预填充后为流量提供服务,请定义
probes
以检查这些条件。您的数据库只会在其 livenessProbe
和 readinessProbe
成功后开始为流量提供服务。如果它需要额外的时间,保护 pod 不被 startupProbe
. 终止
- 为确保您的应用程序部署不会在数据库准备就绪之前启动和失败,请使用
initContainer
检查数据库是否已准备好在您的应用程序启动之前为流量提供服务。
退房
- https://12factor.net/(动态系统的一般准则)
- https://kubernetes.io/docs/concepts/workloads/pods/init-containers/#init-containers
https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
了解更多信息。
helm 和创建资源有一个固定的顺序,除了 hooks 之外你无法影响它。
根据我的经验,Helm 挂钩可能导致的问题多于它们解决的问题。这是因为大多数情况下,它们实际上依赖于仅在挂钩完成后才可用的资源。例如,配置映射、机密和服务帐户/角色绑定。引导您将越来越多的东西移动到钩子生命周期中,这不是惯用的 IMO。在卸载一个版本时,它也会让它们悬空。
我倾向于使用作业和初始化容器,这些容器会阻塞直到作业完成。
---
apiVersion: v1
kind: Pod
metadata:
name: mysql
labels:
name: mysql
spec:
containers:
- name: mysql
image: mysql
---
apiVersion: batch/v1
kind: Job
metadata:
name: migration
spec:
ttlSecondsAfterFinished: 100
template:
spec:
initContainers:
- name: wait-for-db
image: bitnami/kubectl
args:
- wait
- pod/mysql
- --for=condition=ready
- --timeout=120s
containers:
- name: migration
image: myapp
args: [--migrate]
restartPolicy: Never
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
selector:
matchLabels:
app: myapp
replicas: 3
template:
metadata:
labels:
app: myapp
spec:
initContainers:
- name: wait-for-migration
image: bitnami/kubectl
args:
- wait
- job/migration
- --for=condition=complete
- --timeout=120s
containers:
- name: myapp
image: myapp
args: [--server]
如果您想水平扩展您的应用程序,将迁移移动到它自己的工作中是有益的。您的迁移只需要 运行 1 次。因此,对于每个已部署的副本,运行 它没有意义。
此外,如果 pod 崩溃并重新启动,迁移不需要再次 运行。所以把它放在一份单独的一次性工作中是有道理的。
主图表结构如下所示。
.
├── Chart.lock
├── charts
│ └── mysql-8.8.26.tgz
├── Chart.yaml
├── templates
│ ├── deployment.yaml # waits for db migration job
│ └── migration-job.yaml # waits for mysql statefulset master pod
└── values.yaml
我正在使用 K8S 和 helm。
我需要 运行 pods 和具有预定义流程顺序的依赖项。
如何创建 helm 依赖项,运行 pod 仅一次(即 - 第一次填充数据库),并在第一次成功后退出?
此外,如果我有多个 pods,并且我想 运行 仅在特定条件下以及在创建 pod 之后出现 pod。
需要建造2个pods,如下所述:
我有一个数据库。
第一步是创建数据库。
第二步是填充数据库。
填充数据库后,此作业需要完成。
第三步 是另一个使用该数据库的 pod(不是 db pod),并且始终处于侦听模式(永不停止)。
我可以定义依赖关系的顺序 运行ning(并不总是平行的)。
我看到 helm create
命令有 deployment.yaml 和 service.yaml 的模板,也许 pod.yaml 是更好的选择?
这种情况下最好的图表类型是什么?
此外,需要知道什么是图表层次结构。
即:当有一个类型为监听器的图表,一个用于数据库创建的 pod,一个用于数据库填充的 pod(完成后删除)时,我可能有一个图表树层次结构来解释流程。
主图表使用填充的数据(在所有子图表和模板都正确 运行 之后 - 顺便说一句,同一个图表可以有多个模板吗?)。
什么是正确的树流
谢谢。
您可以使用 helm hooks 和 K8s Jobs 来实现这一点,下面是为 Rails 应用程序定义相同的设置。
第一步,定义一个k8s作业来创建和填充数据库
apiVersion: batch/v1
kind: Job
metadata:
name: {{ template "my-chart.name" . }}-db-prepare
annotations:
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "-1"
"helm.sh/hook-delete-policy": hook-succeeded
labels:
app: {{ template "my-chart.name" . }}
chart: {{ template "my-chart.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
backoffLimit: 4
template:
metadata:
labels:
app: {{ template "my-chart.name" . }}
release: {{ .Release.Name }}
spec:
containers:
- name: {{ template "my-chart.name" . }}-db-prepare
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
command: ["/docker-entrypoint.sh"]
args: ["rake", "db:extensions", "db:migrate", "db:seed"]
envFrom:
- configMapRef:
name: {{ template "my-chart.name" . }}-configmap
- secretRef:
name: {{ if .Values.existingSecret }}{{ .Values.existingSecret }}{{- else }}{{ template "my-chart.name" . }}-secrets{{- end }}
initContainers:
- name: init-wait-for-dependencies
image: wshihadeh/wait_for:v1.2
imagePullPolicy: {{ .Values.image.pullPolicy }}
command: ["/docker-entrypoint.sh"]
args: ["wait_for_tcp", "postgress:DATABASE_HOST:DATABASE_PORT"]
envFrom:
- configMapRef:
name: {{ template "my-chart.name" . }}-configmap
- secretRef:
name: {{ if .Values.existingSecret }}{{ .Values.existingSecret }}{{- else }}{{ template "my-chart.name" . }}-secrets{{- end }}
imagePullSecrets:
- name: {{ .Values.imagePullSecretName }}
restartPolicy: Never
注意以下几点: 1- 作业定义在每个部署上都有 运行 的 helm 挂钩,并且是第一个任务
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "-1"
"helm.sh/hook-delete-policy": hook-succeeded
2- 容器命令,将负责准备数据库
command: ["/docker-entrypoint.sh"]
args: ["rake", "db:extensions", "db:migrate", "db:seed"]
3- 在 db-connection 启动之前作业不会开始(这是通过 initContainers 实现的)
args: ["wait_for_tcp", "postgress:DATABASE_HOST:DATABASE_PORT"]
第二步是定义应用部署对象。这可以是常规部署对象(确保您不使用 helm hooks)示例:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ template "my-chart.name" . }}-web
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
checksum/secret: {{ include (print $.Template.BasePath "/secrets.yaml") . | sha256sum }}
labels:
app: {{ template "my-chart.name" . }}
chart: {{ template "my-chart.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.webReplicaCount }}
selector:
matchLabels:
app: {{ template "my-chart.name" . }}
release: {{ .Release.Name }}
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
checksum/secret: {{ include (print $.Template.BasePath "/secrets.yaml") . | sha256sum }}
labels:
app: {{ template "my-chart.name" . }}
release: {{ .Release.Name }}
service: web
spec:
imagePullSecrets:
- name: {{ .Values.imagePullSecretName }}
containers:
- name: {{ template "my-chart.name" . }}-web
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
command: ["/docker-entrypoint.sh"]
args: ["web"]
envFrom:
- configMapRef:
name: {{ template "my-chart.name" . }}-configmap
- secretRef:
name: {{ if .Values.existingSecret }}{{ .Values.existingSecret }}{{- else }}{{ template "my-chart.name" . }}-secrets{{- end }}
ports:
- name: http
containerPort: 8080
protocol: TCP
resources:
{{ toYaml .Values.resources | indent 12 }}
restartPolicy: {{ .Values.restartPolicy }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{ toYaml . | indent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{ toYaml . | indent 8 }}
{{- end }}
如果我没理解错的话,您想在部署策略中构建一个依赖链,以确保在您的任何应用程序启动之前准备好某些事情。在您的情况下,您需要在应用程序启动之前部署 pre-populated 数据库。
我建议不要构建这样的依赖链,因为它会使部署管道中的事情变得复杂,并且如果您将来开始部署多个应用程序,则会阻止部署过程的适当扩展。在像 kubernetes 这样高度动态的环境中,每个部署都应该能够检查它自己启动所需的先决条件,而不依赖于部署顺序。
这可以通过 initContainers
和 probes
的组合来实现。如果不满足某些先决条件and/or 可以在每个部署中指定两者以防止它失败and/or 以在服务开始将流量路由到您的部署(在您的情况下是数据库)之前满足某些先决条件。
简而言之:
- 要在数据库启动前填充数据库卷,请使用
initContainer
- 要让数据库在初始化和预填充后为流量提供服务,请定义
probes
以检查这些条件。您的数据库只会在其livenessProbe
和readinessProbe
成功后开始为流量提供服务。如果它需要额外的时间,保护 pod 不被startupProbe
. 终止
- 为确保您的应用程序部署不会在数据库准备就绪之前启动和失败,请使用
initContainer
检查数据库是否已准备好在您的应用程序启动之前为流量提供服务。
退房
- https://12factor.net/(动态系统的一般准则)
- https://kubernetes.io/docs/concepts/workloads/pods/init-containers/#init-containers https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
了解更多信息。
helm 和创建资源有一个固定的顺序,除了 hooks 之外你无法影响它。
根据我的经验,Helm 挂钩可能导致的问题多于它们解决的问题。这是因为大多数情况下,它们实际上依赖于仅在挂钩完成后才可用的资源。例如,配置映射、机密和服务帐户/角色绑定。引导您将越来越多的东西移动到钩子生命周期中,这不是惯用的 IMO。在卸载一个版本时,它也会让它们悬空。
我倾向于使用作业和初始化容器,这些容器会阻塞直到作业完成。
---
apiVersion: v1
kind: Pod
metadata:
name: mysql
labels:
name: mysql
spec:
containers:
- name: mysql
image: mysql
---
apiVersion: batch/v1
kind: Job
metadata:
name: migration
spec:
ttlSecondsAfterFinished: 100
template:
spec:
initContainers:
- name: wait-for-db
image: bitnami/kubectl
args:
- wait
- pod/mysql
- --for=condition=ready
- --timeout=120s
containers:
- name: migration
image: myapp
args: [--migrate]
restartPolicy: Never
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
selector:
matchLabels:
app: myapp
replicas: 3
template:
metadata:
labels:
app: myapp
spec:
initContainers:
- name: wait-for-migration
image: bitnami/kubectl
args:
- wait
- job/migration
- --for=condition=complete
- --timeout=120s
containers:
- name: myapp
image: myapp
args: [--server]
如果您想水平扩展您的应用程序,将迁移移动到它自己的工作中是有益的。您的迁移只需要 运行 1 次。因此,对于每个已部署的副本,运行 它没有意义。
此外,如果 pod 崩溃并重新启动,迁移不需要再次 运行。所以把它放在一份单独的一次性工作中是有道理的。
主图表结构如下所示。
.
├── Chart.lock
├── charts
│ └── mysql-8.8.26.tgz
├── Chart.yaml
├── templates
│ ├── deployment.yaml # waits for db migration job
│ └── migration-job.yaml # waits for mysql statefulset master pod
└── values.yaml