如何使用 Kubernetes 和 Skaffold 处理数据库迁移

How to handle database migrations with Kubernetes and Skaffold

问题:

在本地,我使用 Skaffold (Kubernetes) 热重载代码的客户端和服务器端。当我关闭它时,它会删除我的服务器 pod,包括我的 /migrations/ 文件夹,因此与我的数据库不同步 alembic_version。在生产环境中,我没有删除我的服务器 pod,但我在部署时重建了 docker 图像,这导致我的 /migrations/ 文件夹被替换。

问题

如何处理这些迁移以使我的数据库不同步?


应用程序设置

Flask/Python API 并使用 Flask Migrate。对于那些不熟悉的人,它所做的是创建一个 migrations 文件夹,其中包含 5a7b1a44a69a_.py 等版本文件。该文件内部是 def upgrade()downgrade() 来操作数据库。它还记录了我的 postgres pod 中 alembic_version table 的修订和 down_revision 引用。

Kubernetes 和 Docker 设置

我有一个服务器 pod 和 postgres pod。我登录到服务器 pod 的 shell 以 运行 迁移命令。它在 docker 容器内创建版本文件并更新数据库。

显示问题的分步示例:

  1. sh 进入服务器部署 pod 和 运行 db init.
  2. 迁移文件夹已创建。
  3. 在创建迁移文件并更新数据库的服务器部署上执行迁移。
  4. postgres pod db alembic_version 输入并更新。
  5. 使用 skaffold 删除或 ctrl-c skaffold。
  6. server-deployment pod 被删除,但 postgres 没有。迁移文件夹消失了。
  7. 开始备份 skaffold 和 sh 到服务器部署 pod 并尝试 运行 数据库迁移。要求您执行数据库初始化。
  8. 如果您尝试从这里降级,它不会执行任何操作。现在服务器 pod 和 postgres pod 在 alembic_version.
  9. 方面不同步

最后的笔记

我过去 pre-docker/kubernetes 是在本地 运行 做的,我会将该迁移版本文件提交到我的存储库。它在所有环境中同步,因此每个人的回购都是相同的 alembic_version。我考虑过创建一个单独的、永远在线的 "migrations-deployment" pod,它是 flask 的另一个实例,这样它就永远不会丢失 /migrations/ 文件夹。但是,这似乎是一个非常糟糕的解决方案。


希望获得最佳实践或想法!

我想出了一个办法来处理这个问题。在其他人确认这是否是一个好方法之前,我不会将其设置为正确答案。

基本上,我所做的是创建一个持久卷声明。在服务器部署中,我将 migrations/ 文件夹连接到该持久卷。这样,无论何时 pod 被删除,migrations/ 文件夹都会保留并在 pod 重新启动时保持不变。

在服务器部署中看起来像这样。

      containers:
      ..........
        volumeMounts:
          - name: migrationstuff
            mountPath: 'MyServerApplicationDirectory/migrations'
      volumes:
        - name: migrationstuff
          persistentVolumeClaim:
            claimName: migrate-pvc

PVC 看起来像这样:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: migrate-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

对于那些决定通过 flask migrate 采用这种方法的人来说,有一个棘手的 "chicken or the egg" 问题。当您 运行 flask db init 时,它会创建 migrations/ 文件夹,其中包含一些内容。但是,如果有一个 PVC 创建空的 migrations/ 文件夹,flask migrate 已经认为该文件夹存在。您不能使用 rmdir 删除该文件夹,因为它有一个工作进程。但是,你需要将flask migrate init命令的内容放到空的migrations/文件夹中.....

我发现的技巧是:

python flask db init --directory migration
mv migration/* migrations/

这会将您需要的所有文件初始化到一个新的 "migration" 文件夹中。然后,您将其全部复制到 migrations/ 文件夹中,以便从此保留。如果您省略 --directory 标志,Flask migrate 会自动查找该文件夹。

然后删除迁移文件夹rmdir migration(或者等到你的 pod 重新启动,在这种情况下它无论如何都会消失)。

你现在有一个正确的 migrations/ 文件夹,其中包含所有内容。当您关闭 Flask Pod 并重新启动时,PVC 会将填满 migrations/ 文件夹的内容注入到 Pod 中。我现在可以 upgrade/downgrade。只是要注意不要把pvc删了!