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 这样高度动态的环境中,每个部署都应该能够检查它自己启动所需的先决条件,而不依赖于部署顺序。

这可以通过 initContainersprobes 的组合来实现。如果不满足某些先决条件and/or 可以在每个部署中指定两者以防止它失败and/or 以在服务开始将流量路由到您的部署(在您的情况下是数据库)之前满足某些先决条件。

简而言之:

  • 要在数据库启动前填充数据库卷,请使用 initContainer
  • 要让数据库在初始化和预填充后为流量提供服务,请定义 probes 以检查这些条件。您的数据库只会在其 livenessProbereadinessProbe 成功后开始为流量提供服务。如果它需要额外的时间,保护 pod 不被 startupProbe.
  • 终止
  • 为确保您的应用程序部署不会在数据库准备就绪之前启动和失败,请使用 initContainer 检查数据库是否已准备好在您的应用程序启动之前为流量提供服务。

退房

了解更多信息。

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