在 Kubernetes 中使用 Helm Hooks 迁移数据库
如果你应用程序中使用的是关系型数据库,随着时间的推移你的数据库结构必然或多或少会有一些变化。在部署你新版本的应用之前,必须确保数据库的结构是最新的,本文不是关于如何生成和管理 schema 迁移的,而是如何将其作为 Kubernetes 上应用部署过程的一部分来完成迁移。
在应用中执行迁移
我们可以将自动迁移程序作为服务启动的一部分而存在,这看上去是可行的,可以保证服务不会在迁移之前就启动,并消除了在过时的 schema 结构上运行应用的风险。
但是当我们将应用程序跑在 Kubernetes 集群上的时候,这会带来一些其他问题。
- 每次创建或重启新的 Pod 时候,它都会尝试再次执行迁移操作,如果你的迁移脚本写得足够优秀,是可以规避这些问题,但是这并不是一个很好的设计。
- 如果迁移需要一段比较长的时间(比如在一个大表上添加一列),你的 Pod 可能会错过就绪状态的检查,在迁移完成之前会杀掉容器重启。我们当然可以增加 Pod 的初始延迟时间,但是这个时间是没办法控制的,而且也会导致正常运行的时候等待大量的时间。
使用 init 容器
Init 容器[1]是指在你的 Pod 中的常规容器启动之前将运行完成的容器。这对于在你的应用程序启动之前执行任何需要的设置都是非常有用的(例如下载一些配置文件)。使用 init 容器来运行数据库迁移似乎是一个更好的方式,但我们将面临与在应用程序中启动的方式相同的问题。
- 如果同时创建多个 Pods,则可能会同时运行多个 init 容器。
- 每次创建新的 Pod 时,init 容器都会运行。
使用 Helm Hooks 执行任务
Kubernetes jobs
首先,我们来看看 Kubernetes 中的 job 资源对象。Jobs 允许我们运行1个或多个 Pod 来完成任务。和 Deployment 中的 Pod 不同,Job 中的 Pod 在退出时不会重新创建(除非它们失败,并且 Job 被配置为在失败时重新启动)。
这对于运行一个只需要运行一次就能完成的任务来说是非常有用的,而运行数据库迁移显然就是一个一次性的任务。
现在要做的是在部署应用程序的新版本之前自动运行一个 Job 来执行迁移任务。
Helm release 生命周期
Helm[2] 允许你将你的应用程序定义的所有 K8S 资源清单打包在一个Chart 中一次性部署,并使用模板来定制每个部署(例如允许在多个环境中用不同的参数部署同一个 Chart)。
Helm 还提供了 Hooks[3] 钩子来决定部署过程中何时创建资源,我们可以利用这一点,在创建或更新任何资源之前执行迁移任务。下面是我们的迁移任务的示例:
apiVersion: batch/v1
kind: Job
metadata:
name: "{{ .Release.Name }}"
labels:
annotations:
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "-1"
"helm.sh/hook-delete-policy": hook-succeeded
spec:
template:
metadata:
name: "{{ .Release.Name }}"
spec:
restartPolicy: Never
containers:
- name: db-migrations # 我们需要构建一个专门用于数据库迁移的docker镜像
image: "database-migrations"
Hooks 是通过 annotations 来进行配置的:
-
"helm.sh/hook":pre-install,pre-upgrade
是告诉 helm 在安装之前和升级应用程序之前执行这个 Job 任务 -
"helm.sh/hook-weight": "-1"
是用于定义 helm 应该以何种顺序创建实现相同钩子的资源 -
helm.sh/hook-delete-policy: hook-succeeded
是告诉 helm 在 Job 执行成功后删除该 Job 资源对象。
其余部分就是一个普通的 Job 资源对象模板。
问题
Helm hooks 的缺点
你的迁移任务很可能需要一些配置和或 Secret 才能运行(至少需要db 服务器地址和凭证),但是这个 Job 资源将在 Chart 中所有其他资源之前创建。这意味着我们的 Job 将无法挂载 Chart 创建的ConfigMap 资源。要解决这个问题我们可以创建一个实现相同钩子的 ConfigMap,如下所示:
apiVersion: v1
kind: ConfigMap
metadata:
name: db-migrations
annotations:
"helm.sh/hook": pre-install,pre-upgrade
"helm.sh/hook-weight": "-10" # 使用一个比迁移任务更小的权重
"helm.sh/hook-delete-policy": hook-succeeded
data:
DB_ADDR: {{ .Values.db.addr }}
DB_NAME: {{ .Values.db.name }}
我们可以配置这个 hook 的权重比迁移任务的权重更小,这样就可以在迁移任务执行之前创建这个 ConfigMap 资源,这样就可以在 Job 中挂载这个 ConfigMap 来获取配置信息了。
部署策略和回滚
默认情况下,Kubernetes Deployment 默认更新策略是滚动更新。这意味着在部署过程中,将有 Pod 同时运行应用程序的上一个和新版本。这将要求所有的迁移至少要向后兼容以前的版本。
如果你需要使用 helm rollback
命令回滚到应用程序的以前版本,你重新部署的版本的迁移任务也会再次运行。在回滚期间试图向下迁移到以前版本的数据库结构,很可能会导致现有的 Pods 运行失败。最后,如果你必须回滚到一个更老的版本,你需要确保当前的数据库结构与你计划回滚到的版本向后兼容。
原文链接:https://itnext.io/database-migrations-on-kubernetes-using-helm-hooks-fb80c0d97805
参考资料
[1]
Init 容器: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/
[2]
Helm: https://helm.sh/
[3]
Hooks: https://helm.sh/docs/topics/charts_hooks/
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【DB笔试面试851】在Oracle中,造成“ORA-28040: No matching ...”错误的原因是什么?
- 在 Silverlight 5 项目中使用 async/await
- 开源一个 Sliverlight 导航框架
- 【DB笔试面试852】在Oracle中,什么是静默建库?
- 从 SVN 迁移到 Git
- 在 Windows 系统上配置 Apache Git 服务器
- WMCTF2020 部分Writeup&招新帖
- 【DB宝15】生产环境中,如何利用DG的备库来异机还原一个新库?
- Java命令执行学习笔记
- SAP UI5应用DatePicker控件的设计明细
- 如何根据自己的实际需求开发属于自己的sublime text插件
- Sony Z13 系列笔记本安装 NVIDIA 官方最新版显卡驱动程序
- 通过网页进行 iOS 应用内部分发
- 【DB笔试面试853】在Oracle中,什么是手动建库?手动建库有哪些步骤?
- 使用 Intel HAXM 为 Android 模拟器加速,媲美真机