许多数据库 schema 管理工具创建了一个命令式接口,要求开发人员了解 schema 的当前状态以及将当前 schema(和相关数据)迁移到新的 schema 的相关命令,SchemaHero 提出了一个声明式接口的方式来替换穿透的命令式接口。
SchemaHero是一个开源的声明式数据库 schema 迁移的云原生工具,可将 schema 定义转换为可在任何环境中应用的迁移脚本。作为 CLI 工具和 Kubernetes Operator 编写的 SchemaHero 消除了创建和管理与运行应用程序的所有环境兼容的顺序迁移脚本的任务。
许多数据库 schema 管理工具创建了一个命令式接口,要求开发人员了解 schema 的当前状态以及将当前 schema(和相关数据)迁移到新的 schema 的相关命令,SchemaHero提出了一个声明式接口的方式来替换穿透的命令式接口。
声明式模型
以声明式方式管理数据库 schema 有很多好处,包括:
- 遵守变更管理流程的能力。
- 可重复部署到新环境。
- 兼容新运行时。
在声明式模型中,仅定义 schema 的当前状态,声明式 schema 管理程序会负责将 schema 从之前的任何状态迁移到所需状态所需的命令,只存储当前状态的好处是,在创建新的环境或者实例时,将不需要以前使用的数据库扩展、表等数据。
传统的数据库引擎通过被称为DDL(数据定义语言)的 SQL 语句子集来接收 schema 变更。开发人员不需要为他们所针对的每个数据库引擎了解 DDL 之间的差别,根据数据库的能力和状态,从一个统一的声明式模型转换为适当的 DDL 命令可以通过编程方式进行处理。
当采用声明式模式来定义数据库 schema 管理时,就可以在应用之前根据一组策略来对 schema 进行验证。这对于只存储迁移脚本的命令式工具来说是不容易实现的。有了所需要的全部可用状态,就可以根据一组规则评估数据库 schema,以确保策略和最佳实践得以执行。
数据迁移
有两种类型的迁移需要管理和部署。
- Schema 迁移。
- 数据迁移。
Schema 迁移可以使用用 SQL 语法来表示,并改变数据库的结构。这些往往是一些新的表、列变更、更改索引数据等等。这些都是常用的写法,并且总是可以用幂等的语法来表达。不同的数据库引擎对如何应用这些规则执行不同的规则。例如,MySQL 不允许在事务中执行 schema 迁移,而 Postgres 是可以的。Schema 管理往往是数据库所独有的方式,而SchemaHero
则专注于处理 schema 迁移。
不太常见的是,开发人员必须将一些数据迁移到数据库中的新格式。这可能涉及到计算一个新列并将其写入,或者在代码中创建新的值并将其插入它们。许多传统的数据库管理工具将 shema 迁移和数据迁移的任务融合到一个工具中去了。
SchemaHero目前专注于 schema 迁移,并计划在未来支持数据迁移。
使用
接下来我们将来部署SchemaHero和一个示例数据库,将为一个虚拟的航空公司预订系统设计一个非常基本的数据库,并将其部署到 PostgreSQL 上去。
一开始我们将部署一个空数据库,创建一个初始 schema,然后修改该 schema,使其最终得到以下表结构:
安装 SchemaHero
安装 kubectl 插件
SchemaHero客户端组件被打包成了一个 kubectl 插件,并通过krew包管理器发布,所以首先我们需要先安装 krew,安装了 krew 过后,使用下面的命令即可安装SchemaHero客户端。
$ kubectl krew install schemahero
安装后可以通过下面的命令来校验是否安装成功。
$ kubectl schemahero version
正常你会看到安装的 SchemaHero 版本(0.12.1或类似版本)。
安装集群内组件
一旦安装了 kubectl 插件,我们就可以安装SchemaHero在 Kubernetes 集群中的组件了,只需要使用下面的命令即可一键安装:
$ kubectl schemahero install
该命令将创建一个名为schemahero-system的命名空间,并部署一个SchemaHero operator,可以通过执行以下命令来确认是否已经安装SchemaHero:
$ kubectl get pods -n schemahero-system
正常情况下应该会看到 1 个 pod 正在运行,输出将类似于以下内容:
NAME READY STATUS RESTARTS AGE
schemahero-0 1/1 Running 0 38s
客户端和集群组件都安装好过后,接下来我们可以去连接数据库。
连接数据库
接下来我们需要部署一个 PostgreSQL 实例,然后配置SchemaHero来管理该数据库实例。
为了方便,我这里直接将 PostgreSQL 的 Helm Chart 模板渲染后的 YAML 文件添加到 SchemaHero 代码仓库中去,这里我们部署到一个名为schemahero-tutorial的命名空间中去:
$ kubectl create ns schemahero-tutorial
$ kubectl apply -n schemahero-tutorial -f https://raw.githubusercontent.com/schemahero/schemahero/main/examples/tutorial/postgresql/postgresql-11.8.0.yaml
PostgreSQL 部署完成后,我们可以使用下面的命令连接到该数据库实例:
$ kubectl exec -it -n schemahero-tutorial \
postgresql-0 -- psql -U airlinedb-user -d airlinedb
其中airlinedb-user的密码为password。
这里我们使用前面介绍过的 Beekeeper Studio 这个数据库管理工具来管理该数据库实例。
默认情况下上面我们部署的 PostgreSQL 实例无法在集群外访问,所以我们可以使用 kubectl 创建一个端口转发来暴露该数据库实例。
$ kubectl port-forward -n schemahero-tutorial svc/postgresql 5432:5432
然后我们就可以使用用户airlinedb-user、数据库airlinedb和密码password连接到数据库127.0.0.01:5432。
连接后可以看到现在数据库还是空的。
现在我们已经在集群中运行了SchemaHero和 PostgreSQL 实例,接下来我们可以向SchemaHero提供数据库信息,以便我们可以管理该数据库。我们可以通过使用连接信息将自定义资源部署到集群来实现。
database对象可以允许SchemaHero管理数据库的 schema,database中定义包括名称类型和连接参数。我们这里创建一个名为airline-db.yaml的文件,内容如下所示:
apiVersion: databases.schemahero.io/v1alpha4
kind: Database
metadata:
name: airlinedb
namespace: schemahero-tutorial
spec:
connection:
postgres:
uri:
valueFrom:
secretKeyRef:
name: postgresql
key: uri
上面定义的Database就是Schemahero定义的一个 CRD 对象,这里我们是通过一个secretKeyRef引用前面 PostgreSQL 部署的一个身份验证数据,SchemaHero 支持从 inline、secrets 或 HashiCorp Vault 读取凭据。
直接应用上面的资源对象即可。
$ kubectl apply -f airline-db.yaml
$ kubectl get databases -n schemahero-tutorial
NAME AGE
airlinedb 47m
现在我们就可以通过SchemaHero来管理我们的 PostgreSQL 实例了,接下来我们使用SchemaHero将一个新表部署到这个实例中。
新建表
在这一步中,我们将把几个表部署到前面创建的airlinesdb数据库中去。在执行该操作时,我们将在SchemaHero中执行批准和拒绝工作流程,以了解如何在执行这些变更之前对其进行验证。
首先我们先定义一个简单的airports表,然后在我们的数据模型在定义路由时使用它。该表我们只定义了 2 列来表示机场代码和名称。
在使用SchemaHero之前我们可能会编写如下所示的 SQL 语句。
CREATE TABLE airport
( code char(4) not null primary key,
name varchar(255)
)
而现在我们有了SchemaHero之后,就不需要编写 SQL 语句了,我们只需要创建为一个SchemaHero的Table自定义对象。
创建一个名为airport-table.yaml的资源清单文件,如下所示:
apiVersion: schemas.schemahero.io/v1alpha4
kind: Table
metadata:
name: airport
namespace: schemahero-tutorial
spec:
database: airlinedb
name: airport
schema:
postgres:
primaryKey: [code]
columns:
- name: code
type: char(4)
- name: name
type: varchar(255)
constraints:
notNull: true
其中spec.database用来关联前面定义的database对象,spec.name是 Postgres 数据库中真实的数据表名称,然后在spec.schema中定义表的结构,primaryKey可以用来指定主键,columns定义数据表的列定义。
同样直接应用该资源对象即可。
$ kubectl apply -f airport-table.yaml
需要注意的是尽管我们部署了table对象,但表结构并不会自动更改。相反,一个新的(或编辑的)table对象将生成一个migration对象,可以检查然后批准或拒绝该变更。默认情况下,SchemaHero需要审批流程,因为某些数据库结构迁移可能具有破坏性,可以通过向database对象添加属性来启用立即部署(未经批准)。
我们可以通过下面的命令查看到处于pending状态的迁移对象。
$ kubectl schemahero get migrations -n schemahero-tutorial
ID DATABASE TABLE PLANNED EXECUTED APPROVED REJECTED
eaa36ef airlinedb airport 11s
如果没有找到资源,请等待几秒钟,然后重试,SchemaHero Operator 必须在migration可用之前完成plan阶段。
在批准该迁移之前,我们可以查看附加到migration对象上面生成的 SQL 语句。从上一个命令的输出中获取 ID,然后运行describe migration即可:
$ kubectl schemahero describe migration eaa36ef -n schemahero-tutorial
Migration Name: eaa36ef
Generated DDL Statement (generated at 2020-06-06T10:41:04-07:00):
create table "airport" ("code" character (4), "name" character varying (255) not null, primary key ("code"));
To apply this migration:
kubectl schemahero -n schemahero-tutorial approve migration eaa36ef
To recalculate this migration against the current schema:
kubectl schemahero -n schemahero-tutorial recalculate migration eaa36ef
To deny and cancel this migration:
kubectl schemahero -n schemahero-tutorial reject migration eaa36ef
在输出的顶部,生成的 DDL 语句是计划的迁移,这是SchemaHero将运行以应用此迁移的确切 SQL 语句。
在后面,SchemaHero为后续步骤提供了 3 个命令:
- apply:运行此命令将接受 SQL 语句,SchemaHero将对数据库执行它。
- recalculate:运行此命令将指示SchemaHero丢弃生成的 SQL 语句并再次生成它们。如果数据库结构已更改并且你希望SchemaHero重新执行计划,这将很有用。
- reject:运行此命令将拒绝迁移并且不执行它。
比如我们这里可以看到当前的迁移是安全的符合预期的,接下来我们可以批准迁移,以便让SchemaHero执行该计划,执行下面的命令即可:
$ kubectl schemahero -n schemahero-tutorial approve migration eaa36ef
Migration eaa36ef approved
我们可以再次运行get migrations来查看迁移的状态:
$ kubectl schemahero get migrations -n schemahero-tutorial
ID DATABASE TABLE PLANNED EXECUTED APPROVED REJECTED
eaa36ef airlinedb airport 9m38s 38s 52s
上面的信息表明迁移计划在 9 分 38 秒前,在 52 秒前批准,在 38 秒前执行。
现在我们在 Beekeeper Studio 工具中单击左侧导航中Tables & Views标题上的刷新按钮,正常我们现在可以在public下看到airport表,点开该表,可以看到表中的列数据。
修改表
接下来我们来部署一个table对象然后来修改表结构。
和前面一样,首先我们创建一个schedule表,结构如下所示:
定义一个对应的Table表对象,内容如下所示:
# schedule-table.yaml
apiVersion: schemas.schemahero.io/v1alpha4
kind: Table
metadata:
name: schedule
namespace: schemahero-tutorial
spec:
database: airlinedb
name: schedule
schema:
postgres:
primaryKey: [flight_num]
columns:
- name: flight_num
type: int
- name: origin
type: char(4)
constraints:
notNull: true
- name: destination
type: char(4)
constraints:
notNull: true
- name: departure_time
type: time
constraints:
notNull: true
- name: arrival_time
type: time
constraints:
notNull: true
直接应用上面的资源清单即可:
$ kubectl apply -f schedule-table.yaml -n schemahero-tutorial
$ kubectl schemahero get migrations -n schemahero-tutorial
当schedule迁移准备好时,它将显示在输出中:
ID DATABASE TABLE PLANNED EXECUTED APPROVED REJECTED
a9626a8 airlinedb schedule 21s
eaa36ef airlinedb airport 4h 3h 3h
同样可以通过describe查看对应的 SQL 语句过后,如果是安全的,则可以审批该变更。
$ kubectl schemahero -n schemahero-tutorial approve migration a9626a8
批准后可以在 Beekeeper Studio 中查看是否有schedule表了。
现在让我们对这个表结构做一些更改:
- 使departure_time和arrival_time列可以为空
- 添加一个名为duration的新列
修改上面的schedule-table.yaml文件,将departure_time和arrival_time列中的constraints属性移出掉,然后增加一个名为duration,类型为int且没有constraints属性。修改后的文件如下所示:
apiVersion: schemas.schemahero.io/v1alpha4
kind: Table
metadata:
name: schedule
namespace: schemahero-tutorial
spec:
database: airlinedb
name: schedule
schema:
postgres:
primaryKey: [flight_num]
columns:
- name: flight_num
type: int
- name: origin
type: char(4)
constraints:
notNull: true
- name: destination
type: char(4)
constraints:
notNull: true
- name: departure_time
type: time
- name: arrival_time
type: time
- name: duration
type: int
修改后重新应用该文件:
$ kubectl apply -f schedule-table.yaml
应用后,同样查看下该migration对象:
$ kubectl schemahero get migrations -n schemahero-tutorial
ID DATABASE TABLE PLANNED EXECUTED APPROVED REJECTED
a9626a8 airlinedb schedule 9m30s 7m58s 8m0s
eaa36ef airlinedb airport 4h 4h 4h
fa32022 airlinedb schedule 5s
可以看到多了一个新的migration对象处于pending状态。同样使用describe命令查看该migration的详细信息。
$ kubectl schemahero -n schemahero-tutorial describe migration fa32022
Migration Name: fa32022
Generated DDL Statement (generated at 2020-06-06T14:56:04-07:00):
alter table "schedule" alter column "departure_time" type time, alter column "departure_time" drop not null;
alter table "schedule" alter column "arrival_time" type time, alter column "arrival_time" drop not null;
alter table "schedule" add column "duration" integer;
To apply this migration:
kubectl schemahero -n schemahero-tutorial approve migration fa32022
To recalculate this migration against the current schema:
kubectl schemahero -n schemahero-tutorial recalculate migration fa32022
To deny and cancel this migration:
kubectl schemahero -n schemahero-tutorial reject migration fa32022
上面的信息中可以看到该次迁移生成的 DDL 语句包括 3 个不同的语句,SchemaHero
将我们刚刚部署的 YAML 与实际的数据库结构进行了比较,并生成了上述命令。
同样校验下迁移语句没问题过后我们就可以直接批准该次迁移。
$ kubectl schemahero -n schemahero-tutorial approve migration fa32022
Migration fa32022 approved
正常批准后表结构就变更成功了。
到这里我们就基本上了解了SchemaHero的基础知识,当然SchemaHero还有需要功能没有介绍,可以查看官方文档 https://schemahero.io/docs/ 了解更多相关信息。
Git 仓库:https://github.com/schemahero/schemahero。
©本文为清一色官方代发,观点仅代表作者本人,与清一色无关。清一色对文中陈述、观点判断保持中立,不对所包含内容的准确性、可靠性或完整性提供任何明示或暗示的保证。本文不作为投资理财建议,请读者仅作参考,并请自行承担全部责任。文中部分文字/图片/视频/音频等来源于网络,如侵犯到著作权人的权利,请与我们联系(微信/QQ:1074760229)。转载请注明出处:清一色财经