K8S - 运行 并建立一次数据库,无论何时在主图表中使用,在子图表中使用主处理

K8S - run and build database once, whenever used in main chart and with main handling in sub-chart

我正在使用 Kubernetes 和 Helm 3.3.4。

我有一个 MySQL 5.6 数据库,在调用 helm install ... 时声明为 Helm 图表。

对于删除,我使用 helm delete ...。发生了什么 - 所有数据库都被删除(由子图表处理,子图表也被删除)。

我试过为子图表使用依赖关系,但我真的不知道如何在我自己的集群中(在使用外部注册表之前)保留数据而不是删除它(也许使用 StatefulSet?)

我需要创建一次数据库并填充它(填充到数据库中的作业可以 运行 多次,因为它检查数据库的完整性,并且只在需要时填充)。这一切都应该用一个 helm install 命令来完成。

我用于数据库的图表类型是 Pod 和 Job(用于填充数据库的作业),还有 Role 和 RoleBinding,以及子域的服务。

当我将一些数据填充到数据库中时 - 有一个工作用于检查人口,所以我的人口工作可以 运行 很多次,因为该工作负责检查分贝

第一次创建数据库需要创建一次

对于数据库的创建和填充,我使用了 Helm 子图表,它也监听了一些作业。

Helm YAML 文件的基本代码概念如下所示:

在主项目中,首先创建MySQL数据库:

apiVersion: v1
kind: Pod
metadata:
  name: {{ .Release.Namespace }}-mysql
  namespace: {{ .Release.Namespace }}
  labels:
    name: {{ .Release.Namespace }}-mysql
    app: {{ .Release.Namespace }}-mysql
spec:
  hostname: {{ .Release.Name }}-mysql
  subdomain: {{ .Release.Name }}-subdomain # there must be a service
  containers:
    - name: {{ .Release.Name }}-mysql
      image: {{ .Values.global.registry}}/{{ .Values.global.mysql.image }}:{{ .Values.global.mysql.tag | default "latest" }}
      imagePullPolicy: IfNotPresent
      env:
      {{- include "k8s.db.env" . | nindent 8}}
      ports:
        - name: mysql
          protocol: TCP
          containerPort: {{ .Values.global.mysql.port }}
      resources: {{- toYaml .Values.global.mysql.resources | nindent 8 }}

主服务侦听外部 REST 请求并使用子项目。

子项目包含用于创建和填充数据库的迁移作业:

apiVersion: batch/v1
kind: Job
metadata:
  name: {{ include "mySubproject.fullname" . }}-migration-job
  namespace: {{ .Release.Namespace }}
  labels:
    name: {{ include "mySubproject.fullname" . }}-migration-job
    app: {{ include "mySubproject.fullname" . }}-migration-job
spec:
  template: #PodTemplateSpec (Core/V1)
    spec: #PodSpec (core/v1)
      initContainers:
        - name: wait-mysql-exist-pod
          image: {{ .Values.global.registry }}/{{ .Values.global.k8s.image }}:{{ .Values.global.k8s.tag | default "latest" }}
          imagePullPolicy: IfNotPresent
          ...
          ... # db wait for mysql pod first time to exists
        - name: wait-mysql-ready
          image: {{ .Values.global.registry }}/{{ .Values.global.k8s.image }}:{{ .Values.global.k8s.tag | default "latest" }}
          imagePullPolicy: IfNotPresent
          ...
          ... # wait for mysql to be ready state
        - name: wait-mysql-has-db
          image: {{ .Values.global.registry }}/{{ .Values.global.k8s.image }}:{{ .Values.global.k8s.tag | default "latest" }}
          imagePullPolicy: IfNotPresent
          ...
          ... # wait for mysql to have a database
      containers:
        - name: migrate-db
          securityContext:
            {{- toYaml .Values.securityContext | nindent 12 }}
          image: {{ .Values.global.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default "latest" }}
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ...
          ... # db migration ...
      restartPolicy: Never

启用迁移作业的角色:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: {{ include "mySubProject.fullname" . }}-mysql-role
rules:
- apiGroups: [""] # "" indicates the core API group
  resources: ["pods"]
  verbs: ["get", "watch", "list", "update"]
- apiGroups: [""] # "" indicates the core API group
  resources: ["pods/exec"]
  verbs: ["create", "delete", "deletecollection", "get", "list", "patch", "update", "watch"]
- apiGroups: ["", "app", "batch"] # "" indicates the core API group
  resources: ["jobs"]
  verbs: ["get", "watch", "list"]
---      
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: {{ include "mySubProject.fullname" . }}-mysql-rolebinding
subjects:
- kind: ServiceAccount
  name: default
roleRef:
  kind: Role
  name: {{ include "mySubProject.fullname" . }}-mysql-role
  apiGroup: rbac.authorization.k8s.io  

侦听器作业:

apiVersion: batch/v1
kind: Job
metadata:
  name: {{ include "mySubProject.fullname" . }}-listen-job
  namespace: {{ .Release.Namespace }}
  labels:
    name: {{ include "mySubProject.fullname" . }}-listen-job
    app: {{ include "mySubProject.fullname" . }}-listen-job
  annotations:
    "prometheus.io/scrape": {{ .Values.prometheus.scrape | quote }}
    "prometheus.io/path": {{ .Values.prometheus.path }}
    "prometheus.io/port": {{ .Values.ports.api.container | quote }}
spec:
  template: #PodTemplateSpec (Core/V1)
    spec: #PodSpec (core/v1)
      initContainers:
        - name: wait-migration-job-exists
          image: {{ .Values.global.registry }}/{{ .Values.global.k8s.image }}:{{ .Values.global.k8s.tag | default "latest" }}
          imagePullPolicy: IfNotPresent
          ... 
          ... # wait for job for existance
        - name: wait-migration-job-complete
          image: {{ .Values.global.registry }}/{{ .Values.global.k8s.image }}:{{ .Values.global.k8s.tag | default "latest" }}
          imagePullPolicy: IfNotPresent
          ...
          ... # wait for job to complete
      containers:
        - name: listen-db
          securityContext:
            {{- toYaml .Values.securityContext | nindent 12 }}
          image: {{ .Values.global.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default "latest" }}
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          # listen the external requests and database

      restartPolicy: Never

helm 一次创建一些 pods 而不是从头开始重新创建的方法是什么? helm install命令可以吗?

What is the methodology for helm to create some pods once

不要直接创建pods,使用deployment, or in your case StatefulSet等控制器会更好

您在此处显示的内容最明显的问题是 MySQL 数据库背后没有持久性。数据库本身存储在 Pod 的本地存储中。如果 Pod 因任何原因被删除(包括您无法控制的事情,例如节点故障),那么您的数据库将会丢失。

解决这个问题的最简单方法是不要自己安装 MySQL;相反,在这里声明 Helm dependency on a prebuilt MySQL chart. The Bitnami MySQL chart 可能是一个不错的选择。包含在您的 Chart.yaml 文件中:

dependencies:
  - name: mysql
    version: ^8
    repository: https://charts.bitnami.com/bitnami

这将为您提供一个名为 {{ .Release.Name }}-mysql 的服务,您可以将其用作数据库主机名。

如果您不想使用此图表,您应该将 MySQL 安装转换为 StatefulSet。这有一点可以为 PersistentVolumeClaim 创建一个模板,它可以分配持久存储。 PVC会有一个一致的名字,helm delete不会删除它。因此,如果您确实删除并重新安装图表,它将创建一个新的 StatefulSet,这将创建一个新的(主)Pod,它将重新附加到现有的 PVC。

(你几乎不应该创建一个裸 Pod。根据你具体要做什么,创建一个 Deployment、Job 或 StatefulSet,这将自动创建[并在需要时重新创建]实际的 Pods.)