手工迁移数据库时代
在数据库版本管理概念还没有流行起来的时候,或者是系统规模还不大,只需要维护一个数据库时,数据库管理员常采用直接在数据库中执行sql语句,或者用脚本的方式来导入sql文件来迁移数据库,而在某个git仓库中保存初始化数据的全量脚本,或者是每次更新时再记录下增量脚本。
手工的方式在只需要维护单个数据库还是可以胜任的,但是现实场景中,往往需要维护多个环境的数据库且迁移较频繁,这就导致了如下问题:
- 如何确认各环节数据库的迁移变更是否一致
- 当前环境的数据库迁移到哪个步骤了
《持续交付》中的数据管理建议
由于手工方式迁移数据库存在以上的弊端,在《持续交付:发布可靠软件的系统方法》中数据管理方面有如下原则于实践:
- 像代码版本控制那样,管理初始化数据的版本
- 通过自动化脚本来应用数据库变更
- 每个修改都要可以执行回滚
- 新的数据版本最好保证向后兼容性
- 测试数据可以分不同环境来维护,尽量用程序API的方式导入测试数据,而不是直接写入数据库中
- 数据版本应与应用程序版本解耦
在 K8s 环境中应用 Flyway 的演示
Flyway是一种 Java 编写的数据库管理工具,可以通过命令行工具、Java API 、Maven插件等形式使用,支持 Mysql 、Postgres等关系型数据库。
Flyway基于原生的sql语句来实现数据库迁移,并在sql文件名的样式要求上体现版本管理,并且通过创建迁移历史记录表来描述某个数据库的迁移状态,通过sql文件和历史记录表的对比保证数据变更的幂等性,可以满足基本的数据管理要求。关于Flyway的更多信息可以参考官方文档。
在 K8s 环境中应用 Flyway 的演示主要参考这个博客:https://developers.redhat.com/blog/2018/01/10/flyway-containerized-db-changes/
步骤1:创建演示数据库,以 Postgres 为例
先创建个 namespace 用于演示
kubectl create namespace flyway
创建 Postgres 数据库,里面已经包含一些数据了
kubectl -n flyway run pgsql --image=jbossdevguidebook/beosbank_posgres_db_europa:latest
查看数据库信息
psql -U postgres # 进入数据库
\l # 列出数据库信息
\connect beosbank-europa # 切换到 beosbank-europa
\d # 列出数据表信息
select * from eu_customer; # 列出数据表 eu_customer 的内容
步骤2: 构建 Flyway 镜像
下载参考博客中的github仓库
git clone https://github.com/nelvadas/ocp-flyway-db-migration.git
ocp-flyway-db-migration
构建 Flyway 镜像,也可以直接用 Flyway 的官方镜像,但是要注意 sql 文件的存放路径
docker build -t jbossdevguidebook/flyway:v1.0.4-rhdblog .
步骤3:创建 ConfigMap 及 Job
Flyway镜像要封装到 K8s workload 中运行,参考博客中推荐的有 Deployment,Job,CronJob,InitContainer 还有边车容器的形式,由于 flyway 的迁移命令本身就是执行一次就推出的,而且也为了演示方便,这里选择 Job 来封装 Flyway。
# beosbank-flyway-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: beosbank-dbupdater-job
spec:
template:
metadata:
name: beosbank-dbupdater
spec:
containers:
- name: db-updater
image: jbossdevguidebook/flyway:v1.0.4-rhdblog
volumeMounts:
- name: sql-configmap-volume
mountPath: /var/flyway/data/
env:
- name: DB_USER
value: root
- name: DB_PASSWORD
value: Europa01#
- name: DB_URL
value: "jdbc:postgresql://beosbank-posgres-db-europa/beosbank-europa"
restartPolicy: Never
volumes:
- name: sql-configmap-volume
configMap:
name: sql-configmap
从yaml文件中可以看到,Flyway迁移使用的sql文件是放在 /var/flyway/data/ 这个目录下的,为了使后续可以更改,我们将sql文件内容保存在 ConfigMap 中,然后挂载到容器里。
创建包含迁移文件的 ConfigMap
kubectl -n flyway create cm sql-configmap --from-file=./sql
创建 Job
kubectl -n flyway create -f beosbank-flyway-job.yaml
步骤4:查看迁移结果
kubectl -n flyway logs job/beosbank-dbupdater
可以看到每个迁移文件对应的版本和描述,它们会按照版本大小顺序执行。
此时登录数据库也可以看到变更后的数据库以及迁移历史记录表
在实际应用中的优化设想
任何一项工具和技术在实际使用中,还要考虑多部门的分工协作以及融入现有流程等问题。
这里设想下以 Flyway 为工具的数据管理在实际使用中的流程,希望不久后能用上:
1.数据库初始化数据与应用程序息息相关,因此应该由开发人员主要维护 迁移sql文件,这样也比较好命名。
2.在本地开发环境,以及前期的非 K8s 测试环境中,数据迁移可以通过 Flyway 的 Java API来实现,在每次程序启动后正常连接数据库时执行,sql 文件应该以一个独立的 git 仓库存放,并通过 git submodule 同步到应用程序所在的仓库中,这样每次迁移时就可以从程序的某个路径下读取迁移文件;独立 git 仓库的形式,又有利于后续上生产环境后,数据版本可以独立于应用程序的版本升级,实现解耦。
3.在生产环境 K8s 集群中,可以关闭 Flyway Java API,把 Flyway 作为边车容器和应用程序部署到一个 Pod 中,在数据库实例启动后,应用容器启动前完成初始化操作。
此时 Flyway 应该被脚本包装成一个不停监听 sql 文件路径的后台进程,并在有新的sql文件加入时执行一次迁移命令。
为了实现用于迁移的 sql 文件的动态加载,以及版本管理的需求,可以给 Flyway 边车容器设置一个 InitContainer ,初始化容器的镜像就是之前的 sql 文件 git 仓库打包而来,并以最新的sql文件版本作为镜像 tag ,它的主要功能就是把最新的迁移文件拷贝的 Flyway 监听的 sql 文件路径下,这样每当初始化容器的版本变动时,Flyway都会重新执行一次迁移命令,优雅地实现数据版本迭代。