如何在不同的(依赖的)容器启动后对 docker 容器运行 .sql 脚本?

Posted

技术标签:

【中文标题】如何在不同的(依赖的)容器启动后对 docker 容器运行 .sql 脚本?【英文标题】:How to run .sql script against docker container after a different (dependent) container starts? 【发布时间】:2022-01-02 01:31:56 【问题描述】:

我有一个 SpringBoot 应用程序容器myApi,它依赖于另一个 SpringBoot 应用程序容器configApi,它们都使用 flyway。它们也都依赖于postgres 容器。 configApi 公开了一个端点,myApi 使用它来获取所有相关配置(数据库详细信息等)。

目前发生的情况是:

    postgres 容器启动并初始化相应的数据库和用户 configApi 容器启动 a) 它连接到postgres b) 它运行一个 flyway 迁移(创建所需的模式和表) c) api 启动并准备就绪 myApi 容器启动 a) 它到达configApi 公开的配置端点 b) 请求失败,因为configApipostgres 中找不到任何有用的数据,因为没有插入任何数据

我的限制是:

我无法修改 configApi 代码以包含任何特定于 myApi 或环境的内容 configApi 启动期间的 Flyway 迁移是创建包含任何所需数据的表的原因 当postgres 启动(使用init.sql)时,我无法创建表并填充它们,因为那样configApi flyway 迁移将失败 myApi 不能包含有关 postgres 的任何硬编码或环境信息,因为它们都应该从 configApi 端点获取

问题总结 TLDR:

如何在configApi 启动之后但在myApi 启动之前对postgres 容器执行一个sql 脚本,而不修改configApimyApi 以包含任何特定于彼此环境的内容?

我有以下 docker-compose 文件:

version: "3"

volumes:
  db_data:

services:
  postgres:
    image: postgres:10.14
    volumes:
      - ./init-local.sql:/docker-entrypoint-initdb.d/init.sql
      - db_data:/var/lib/postgresql
    ports:
      - 5432:5432

  configApi:
    image: org/config-api:latest
    ports:
      - 8234:8234
    environment:
      - DB_PORT=5432
      - DB_HOST=postgres
    depends_on:
      - postgres

  myApi:
    build: ./my-api
    image: org/my-api:latest
    container_name: my-api
    ports:
     - 9080:9080
    environment:
     - CONFIG_MANAGER_API_URL=http://configApi:8234/
    depends_on:
     - postgres
     - configApi

注释(我会在有问题时添加更多内容):

我正在使用单个 postgres 容器,因为这是用于本地/测试的,两个 api 都使用该 postgres 实例中的唯一数据库

【问题讨论】:

我过去看到的一个解决方案是创建一个轻量级、自终止的应用程序,它将在configApimyApi 之间启动,仅用于数据加载,我不喜欢这个解决方案。维护开销太大。 该应用程序可以是 Flyway 本身,它可以作为 docker 镜像使用:hub.docker.com/r/flyway/flyway。我用它来建立一个完整的本地开发数据库。您可以使用它仅插入数据。不知道这是否对你有用,我有点迷失在你的限制中。老实说,我宁愿打破限制也不愿绕过它们,最有意义的是将数据作为配置 API 设置的一部分插入。 @Gimby 谢谢,我去看看。限制来自 configApi 作为通用应用程序,我无法修改它以包含特定于产品的配置。这会破坏它的通用性。 【参考方案1】:

所以这是我的解决方案。

我修改了我的 flyway 代码以动态包含额外的脚本(如果它们存在),如下所示。

configApi 的我的数据库java 配置中,我读取了一个环境变量,该变量指定了任何带有额外/应用程序外部脚本的目录:

// on class level
@Value("$FLYWAY_FOLDER")
private String externalFlywayFolder;

//when creating DataSource bean
List<String> flywayFolders = new ArrayList<>();
flywayFolders.add("classpath:db/migrations");
if (externalFlywayFolder != null && !externalFlywayFolder.isEmpty()) 
    flywayFolders.add("filesystem:"+externalFlywayFolder);

String[] flywayFoldersArray = new String[flywayFolders.size()];
flywayFolders.toArray(flywayFoldersArray);

Flyway flyway = Flyway
        .configure()
        .dataSource(dataSource)
        .baselineOnMigrate(true)
        .schemas("flyway")
        .mixed(true)
        .locations(flywayFoldersArray)
        .load();
flyway.migrate();

然后我修改了 docker compose 以将额外的文件附加到容器并设置 FLYWAY_FOLDER env 变量:

  configApi:
    image: org/config-api:latest
    volumes:
      - ./scripts/flyway/configApi:/flyway #attach scripts to container
    ports:
      - 8234:8234
    environment:
      - DB_PORT=5432
      - DB_HOST=postgres
      - FLYWAY_FOLDER=flyway #specify script dir for api
    depends_on:
      - postgres

然后只是添加文件的情​​况,但诀窍是将它们设为repeatable migrations,这样它们就不会干扰可能为 configApi 本身完成的任何版本化迁移。

在版本化迁移之后应用可重复迁移,如果它们的校验和发生变化,它们也会重新应用。

【讨论】:

以上是关于如何在不同的(依赖的)容器启动后对 docker 容器运行 .sql 脚本?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 docker-compose 在 docker 容器之间建立依赖关系 [重复]

如何使用不同的命令启动停止的 Docker 容器?

如何在容器中运行docker命令

Docker容器依赖link连接按顺序启动

Docker容器依赖link连接按顺序启动

Docker容器依赖link连接按顺序启动