【问题标题】:How to solve liquibase waiting for changelog lock problem in several pods in OpenShift cluster?如何解决 OpenShift 集群中多个 pod 中的 liquibase 等待更改日志锁定问题?
【发布时间】:2020-08-06 19:05:09
【问题描述】:

我们支持使用 Spring Boot 以 Java 编写并部署在 OpenShift 中的多个微服务。一些微服务与数据库通信。我们经常在单个部署中的多个 pod 中运行单个微服务。当每个微服务启动时,它会启动 liquibase,它会尝试更新数据库。问题是有时一个 pod 在等待更新日志锁定时失败。 当在我们的生产 OpenShift 集群中发生这种情况时,我们预计其他 pod 在重新启动时会失败,因为与更改日志锁定问题相同。因此,在最坏的情况下,所有 pod 都将等待解除锁定。

我们希望 Liquidbase 在每个 pod 启动时自动准备我们的数据库模式。

把这个逻辑存储在每个微服务中好不好?当出现liquidbase changelog lock问题时,我们如何自动解决问题?我们是否需要将数据库准备逻辑放在单独的部署中?

所以也许我应该解释一下我的问题。就微服务架构而言,运行数据库迁移的最佳方式是什么?也许我们不应该在每个 pod 中使用 db 迁移?也许最好通过单独的部署来完成,或者通过一些根本不在 OpenShift 中的额外 Jenkins 工作来完成?

【问题讨论】:

  • 怎么知道liquibase已经成功更新了db?
  • @ChrisBolton 在使用新版本的应用程序启动 pod 后,我们通过 Liquidbase 脚本更改了数据库结构中的某些内容,我们只需查看 postgres 并比较 liquibase 脚本中编写的内容和 postgres 中的内容。这是完全手动的过程。
  • 自动化流程。
  • @ChrisBolton,我们真的不知道该怎么做
  • 您好,您找到解决方案了吗?我们遇到了同样的问题,这真的很烦人......

标签: database spring-boot kubernetes openshift liquibase


【解决方案1】:

我们将 liquibase 迁移作为 Kubernetes 中的初始化容器运行。在微服务中运行 Liquibase 的问题是,如果在配置的超时之前准备就绪探测不成功,Kubernetes 将终止 pod。在我们的案例中,这有时会发生在大型数据库迁移期间,这可能需要几分钟才能完成。 Kubernetes 将终止 pod,使 DATABASECHANGELOGLOCK 处于锁定状态。使用 init-containers 不会有这个问题。详细解释见https://www.liquibase.org/blog/using-liquibase-in-kubernetes

更新 请看一下这个 Liquibase 扩展,它使用数据库锁替换了 StandardLockService:https://github.com/blagerweij/liquibase-sessionlock

此扩展使用 MySQL 或 Postgres 用户锁定语句,这些语句在数据库连接关闭时自动释放(例如,当容器意外停止时)。使用扩展唯一需要做的就是向库中添加一个依赖项。 Liquibase 将自动检测改进的 LockService。

我不是该库的作者,但我在寻找解决方案时偶然发现了该库。我通过将库发布到 Maven 中心来帮助作者。目前支持 MySQL 和 PostgreSQL,但应该很容易支持其他 RDBMS。

【讨论】:

【解决方案2】:

当 Liquibase 在 spring-boot 应用程序部署期间启动时,它会执行(在非常高的级别上)以下步骤:

  1. 锁定数据库(在databasechangeloglock创建记录)
  2. 执行更改日志;
  3. 解除数据库锁;

因此,如果您在 Liquibase 处于第 1 步和第 3 步之间时中断应用程序部署,那么您的数据库将保持锁定状态。因此,当您尝试重新部署您的应用时,Liquibase 将失败,因为它会将您的数据库视为已锁定。

因此,您必须在再次部署应用程序之前解锁数据库。

我知道有两个选项:

  1. 清除databasechangeloglock 表或将locked 设置为false。这是DELETE FROM databasechangeloglockUPDATE databasechangeloglock SET locked=0
  2. 执行liquibase releaseLocks 命令。您可以在 herehere 找到有关它的文档。

【讨论】:

  • 是的,谢谢,您描述的问题正确。但问题是我可以在我的应用程序的逻辑中释放锁吗?在我的情况下,每个 pod 都会在启动时尝试释放这个锁?我认为这会导致并发问题。我对吗?如何以及在何处自动释放此锁,是否可以在我的 SpringBoot 应用程序代码中执行此操作?请注意,应用程序在 OpenShift 的一个部署中运行在多个 pod 中,因此我的应用程序有多个实例同时运行
  • 我们现在手动完成,但是如何以及在哪里为此编写代码?我们希望这个问题能够自动解决
  • 您可以在部署管道中执行liquibase releaseLocks 命令吗?没错,Liquibase 锁定数据库是为了解决并发问题。也许您可以覆盖一些 Liquibase 类并向其添加“解锁逻辑”,以便它在 Liquibase 检查日志或类似的东西之前执行。
  • 所以也许我应该解释一下我的问题。就微服务架构而言,运行数据库迁移的最佳方式是什么?也许我们不应该在每个 pod 中使用 db 迁移?也许最好通过单独部署来完成,或者通过一些额外的 Jenkins 工作来完成,而不是在 OpenShift 中?
  • 哦,我明白了。以我的经验,如果多个微服务连接到同一个数据库,那么创建部署顺序是合理的,所以微服务一个接一个地部署。此外,最好的微服务架构应该让每个微服务都连接到它自己的数据库。否则它看起来更像是分布式单体。
【解决方案3】:

我们设法在我的公司通过同样的方法 Liquibase suggests with Init Containers 解决了这个问题,但是我们没有使用新容器并通过 Liquibase CLI 运行 Liquibase 迁移,而是重用现有的 Spring Boot 服务设置,但只是执行Liquibase 逻辑。我们创建了一个替代主类,可以在入口点中使用它来使用 Liquibase 填充数据库。

InitContainerApplication 类提供启动应用程序和设置 Liquibase 所需的最低配置。

典型用法:

entrypoint: "java -cp /app/extras/*:/app/WEB-INF/classes:/app/WEB-INF/lib/* com.backbase.buildingblocks.auxiliaryconfig.InitContainerApplication"

这里是类

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.context.ApplicationContext;

@SpringBootConfiguration
@ImportAutoConfiguration(InitContainerAutoConfigurationSelector.class)
public class InitContainerApplication implements ApplicationRunner {

    @Autowired
    private ApplicationContext appContext;

    public static void main(String[] args) {
        SpringApplication.run(InitContainerApplication.class, args);
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        SpringApplication.exit(appContext, () -> 0);
    }

}

这里是作为初始化容器的用途:

spec:
  initContainers:
    - name: init-liquibase
      command: ['java']
      args: ['-cp', '/app/extras/*:/app/WEB-INF/classes:/app/WEB-INF/lib/*',
                 'com.backbase.buildingblocks.auxiliaryconfig.InitContainerApplication']

【讨论】:

  • 这如何防止数据库处于锁定状态? init-container 与否,如果 pod 在 liquibase 释放锁定之前被鞭打(例如应用新部署时),谁将解锁它?我是否缺少 init-container 的某些内容?
  • 这只是 Liquibase 提议的另一种方法,以避免长期迁移被 k8s 就绪/活跃度探针杀死(liquibase.org/blog/using-liquibase-in-kubernetes 以获得详细说明),而不是使用带有 Liquibase CLI 的新容器我们将同一个容器与我们的服务重用,但使用不同的入口点。如果您在迁移过程中杀死 pod,您将获得锁定状态,并且您需要手动解决该问题,除非您更改 LockService,如先前答案中所示的示例:github.com/blagerweij/liquibase-sessionlock
【解决方案4】:

最后,我们在另一个项目中通过在微服务启动时移除 liquibase 迁移解决了这个问题。现在单独的 Jenkins 作业应用迁移,单独的 Jenkins 作业部署并在迁移应用后启动微服务。所以现在微服务本身并没有应用数据库更新

【讨论】:

    猜你喜欢
    • 2022-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-02
    • 1970-01-01
    • 2021-11-30
    • 2020-04-07
    • 1970-01-01
    相关资源
    最近更新 更多