【问题标题】:Run Postgres migration with Docker/Docker compose使用 Docker/Docker compose 运行 Postgres 迁移
【发布时间】:2020-07-05 03:36:55
【问题描述】:

我对这整个主题很陌生,如果这些问题很愚蠢,请原谅。我想像这样在 Docker 容器中运行单元测试:

  • 启动没有表/数据的 Postgres 容器
  • 运行 DB 迁移(我使用的是 node-pg-migrate)来创建所有表
  • 用测试数据填充数据库
  • 使用我的服务启动 Node 容器
  • 运行单元测试
  • 删除数据库
  • 关闭/删除所有容器(出现错误除外)

我目前正在努力运行迁移。我创建了服务图像FROM 我的产品图像和RUN npm install --only=dev。我的docker-compose.yml 看起来像这样:

version: "3.7"
services:
    db:
        image: postgres
        environment: 
            POSTGRES_PASSWORD: test_pw
            POSTGRES_USER: test_user
            POSTGRES_DB: test_db

        ports:
            - '5432:5432'

    web:
        image: docker_tests
        environment:
            DATABASE_URL: postgres://test_user:test_pw@db:5432/test_db?ssl=false
            DATABASE_SSL: "false"
            DATABASE_PW: test_pw
            DATABASE_USER: test_user
            DATABASE_NAME: test_db
            DATABASE_HOST: db
        depends_on: 
            - db
        ports:
            - '1337:1337'

还有我的 Dockerfile:

FROM docker_service
ENV NODE_ENV TEST
WORKDIR /usr/src/app
RUN npm install --only=dev
RUN ["./node_modules/.bin/node-pg-migrate", "up"]
EXPOSE 1337
CMD ["npm", "run", "test"]

运行组合时,两个容器都启动,我什至得到 ​​p>

No migrations to run!
Migration complete!

但是,如果我运行测试,则没有可用的表。迁移未应用于我的 Postgres 容器,并且在迁移后添加 RUN ["printenv"] 时,原因变得很清楚:必要的 DATABASE_URL 不存在。我搜索了一下,发现docker-compose.yml 中指定的环境变量仅在运行时可用,而不是在构建时可用。但是,当我将ENV DATABASE_URL ... 添加到我的 Dockerfile 时,它​​当然无法连接到数据库,因为 Postgres 容器尚未启动。

我该如何解决这个问题?一种可能的解决方案是在两个容器都启动后立即在 web 中运行 ./node_modules/.bin/node-pg-migrate up,但 Docker 只能有一个 CMD,对吗?我用它来运行我的单元测试。

TL;DR:如何在运行单元测试之前使用 Docker 服务容器中的 node-pg-migrate 在 Docker Postgres 容器中运行迁移?

非常感谢!

【问题讨论】:

    标签: postgresql docker docker-compose migration node-pg-migrate


    【解决方案1】:

    我无法对此进行测试,但想法如下:

    • 在与 Dockerfile 相同的文件夹中有一个 docker-entrypoint.sh 脚本
    #!/usr/bin/env bash
    
    ./node_modules/.bin/node-pg-migrate up
    exec "$@"
    
    • 更改 Dockerfile 以使用该脚本
    FROM docker_service
    ENV NODE_ENV TEST
    WORKDIR /usr/src/app
    RUN npm install --only=dev
    
    COPY docker-entrypoint.sh /usr/local/bin/
    RUN chmod 777 /usr/local/bin/docker-entrypoint.sh && \
        ln -s usr/local/bin/docker-entrypoint.sh / # backwards compat
    
    ENTRYPOINT ["docker-entrypoint.sh"]
    EXPOSE 1337
    CMD ["npm", "run", "test"]
    

    这样docker-entrypoint.sh 脚本将在您每次创建容器时执行,之后将因为exec "$@" 执行npm run test。每个数据库镜像都有这种设置,我建议你看看 PostgreSQL Dockerfileentrypoint script


    在执行迁移脚本之前,您可能需要wait-for-it 之类的东西来确保数据库已启动。 depends_on 帮助您启动订单,但不等待服务健康

    资源Control startup and shutdown order in Compose

    【讨论】:

    • 感谢您的回复!如果我这样做,迁移根本不会运行。我在 .sh 文件中添加了一个 echo test 并且在运行它时没有得到回显。但是,测试会运行。另外,我在我的数据库连接中添加了一个重试逻辑,它应该首先考虑数据库不可用。明天我会查看你发布的链接。不幸的是,我的 Linux/Bash-know-how 也不是很好,所以我不知道 exec $@ 做了什么。谷歌了,但不明白我发现了什么,呵呵。
    • 是的,你是对的,我犯了一些错误。我在答案中更正了其中一些,但我也创建了MWE on GitHub。至于exec "$@"exec 在 bash 中用于执行传递给它的命令,$@give me all pass arguments 的缩写。在这个例子中,我们有 docker-entrypoint.sh 脚本,它像 bash docker-entrypoint.sh npm run test 一样执行,所以脚本完成后,npm run test 将被执行。
    • 非常感谢,这似乎奏效了!之前的问题究竟是什么?我看到您添加了 COPY 声明 - 这真的有必要吗?每次我开始测试时都会构建我的服务映像,复制 .到.,因此,该文件应该可用,不是吗?所以我猜问题是缺少chmod?我用谷歌搜索了一下,发现这是为了授予权限?为什么在那之前我没有收到缺少权限的错误?我只是试图了解我做错了什么以及是什么使它现在起作用,以便我可以从中吸取教训。再次感谢! :)
    • 就我而言,我忘记将脚本复制到容器中,因此无法执行任何脚本。如果您COPY . .,那么您的脚本在/usr/src/app 中,我猜该路径不在您的$PATH 中,但您可以使用ENV PATH="/usr/src/app:${PATH}" 更新您的PATH。这就是它被复制到/usr/local/bin 的原因。当然,如果你想运行一些脚本/文件,它需要是可执行的,所以你需要添加执行权限(x)。我从 PostgreSQL Dockerfile 复制了命令,所以它添加了所有权限 - 777 意味着为每个人添加所有权限(读、写、执行)。
    • 另外两个问题: 1. 为什么我们将 docker-entrypoint.sh 复制到 /usr/local/bin 而不是 /usr/src/app? 2. ln 命令只是为了让它在旧版本的 Linux 上也能工作吗?
    猜你喜欢
    • 2020-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多