启动时使用静态数据进行 Docker 集成测试

Posted

技术标签:

【中文标题】启动时使用静态数据进行 Docker 集成测试【英文标题】:Docker integration test with static data at launch 【发布时间】:2017-03-07 13:57:05 【问题描述】:

我已经为这个问题苦苦挣扎了一段时间。我想使用一组特定的数据进行性能测试。 为此,我使用了 Docker-compose,并导出了几个 .sql 文件。

当我第一次使用docker-compose builddocker-compose up 构建和运行容器时,数据集很好。然后我运行我的测试(也插入)数据。运行测试后,我想再次执行它,所以我使用docker-compose up 重新启动容器。但是这一次,由于某种我不明白的原因,上次插入的数据(通过我的测试)仍然存在。我现在有不同的行为。

目前我有以下 Dockerfile:

FROM mysql:5.7

ENV MYSQL_DATABASE dev_munisense1\
    MYSQL_ROOT_PASSWORD pass

EXPOSE 3306

ADD docker/data/ /docker-entrypoint-initdb.d/

因为我读到 mysql Docker 映像运行 /docker-entrypoint-initdb.d/ 中的所有内容。第一次正常运行。

我也尝试过这些帖子的建议:

How do i migrate mysql data directory in docker container?

http://txt.fliglio.com/2013/11/creating-a-mysql-docker-container/

How to create populated MySQL Docker Image on build time

How to make a docker image with a populated database for automated tests?

还有一些相同的其他帖子。

它们目前似乎都不起作用。

如何确保每次启动容器时数据集都完全相同?无需每次都重建图像(由于数据集很大,这需要很长时间)。

提前致谢

编辑:

我还尝试使用不同的参数运行容器,例如: docker-compose up --force-recreate --build mysql 但这没有成功。容器被重建并重新启动,但数据库仍然受到我的测试的影响。目前解决我的问题的唯一方法是删除整个容器和图像。

【问题讨论】:

这个答案可能对你有用:***.com/a/27572665/1856086 @StasMakarov 这与我想要的完全相反。我不想持久化数据库。我只是想从某个点开始一个容器,对它做一些事情,然后把它全部扔掉,这样我就可以从同一个起点重新开始...... 你可以尝试使用official Dockerfile创建你自己的mysql镜像,但没有VOLUME /var/lib/mysql这一行(目前是#68)。这样它不应该在删除容器后保存任何数据。它可能远不是这个问题的最佳解决方案,但它可能会奏效。 您也可以尝试使用docker volume rm $volume_hash 删除包含mysql 数据的卷。或者使用docker volume rm $(docker volume ls -q) 删除所有卷。最后一个命令可以删除有用的东西。 @StasMakarov 当使用没有该行#68的确切Dockerfile时,我如何填充图像,以便每次启动容器时都预先填充?我不介意拥有更大的图像,只要创建的容器很短 【参考方案1】:

我通过执行以下操作设法解决了这个问题(使用 mysql 映像):

    更改 sql 存储的挂载点(这实际上是导致问题的原因)我使用了此处建议的解决方案:How to create populated MySQL Docker Image on build time,但我通过运行 sed 命令做到了:RUN sed -i 's|/var/lib/mysql|/var/lib/mysql2|g' /etc/mysql/my.cnf

    将我的脚本添加到容器内的文件夹中

    运行使用守护进程插入数据的 import.sh 脚本(使用 wait-for-it.sh 脚本) 删除 sql 脚本 像普通一样暴露端口

docker文件长这样(变量用来选择不同的SQL文件,我想要多个版本的镜像):

FROM mysql:5.5.54

ADD https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh /utils/wait-for-it.sh
COPY docker/import.sh /usr/local/bin/

RUN sed -i 's|/var/lib/mysql|/var/lib/mysql2|g' /etc/mysql/my.cnf

ARG MYSQL_DATABASE
ARG MYSQL_USER
ARG MYSQL_PASSWORD
ARG MYSQL_ROOT_PASSWORD
ARG MYSQL_ALLOW_EMPTY_PASSWORD
ARG DEVICE_INFORMATION
ARG LAST_NODE_STATUS
ARG V_NODE
ARG NETWORKSTATUS_EVENTS

ENV MYSQL_DATABASE=$MYSQL_DATABASE \
    MYSQL_USER=$MYSQL_USER \
    MYSQL_PASSWORD=$MYSQL_PASSWORD \
    MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD \
    DEVICE_INFORMATION=$DEVICE_INFORMATION \
    LAST_NODE_STATUS=$LAST_NODE_STATUS \
    V_NODE=$V_NODE \
    MYSQL_ALLOW_EMPTY_PASSWORD=$MYSQL_ALLOW_EMPTY_PASSWORD

#Set up tables
COPY docker/data/$DEVICE_INFORMATION.sql /usr/local/bin/device_information.sql
COPY docker/data/$NETWORKSTATUS_EVENTS.sql /usr/local/bin/networkstatus_events.sql
COPY docker/data/$LAST_NODE_STATUS.sql /usr/local/bin/last_node_status.sql
COPY docker/data/$V_NODE.sql /usr/local/bin/v_node.sql


RUN chmod 777 /usr/local/bin/import.sh && chmod 777 /utils/wait-for-it.sh && \
    /bin/bash /entrypoint.sh mysqld --user='root' & /bin/bash /utils/wait-for-it.sh -t 0 localhost:3306 -- /usr/local/bin/import.sh; exit
RUN rm -f /usr/local/bin/*.sql

ENTRYPOINT ["docker-entrypoint.sh"]

EXPOSE 3306
CMD ["mysqld"]

脚本如下所示:

#!/bin/bash
echo "Going to insert the device information"
mysql  --user=$MYSQL_USER --password=$MYSQL_PASSWORD --database=$MYSQL_DATABASE < /usr/local/bin/device_information.sql
echo "Going to insert the last_node_status"
mysql  --user=$MYSQL_USER --password=$MYSQL_PASSWORD --database=$MYSQL_DATABASE < /usr/local/bin/last_node_status.sql
echo "Going to insert the v_node"
mysql  --user=$MYSQL_USER --password=$MYSQL_PASSWORD --database=$MYSQL_DATABASE < /usr/local/bin/v_node.sql
echo "Going to insert the networkstatus_events"
mysql  --user=$MYSQL_USER --password=$MYSQL_PASSWORD --database=$MYSQL_DATABASE < /usr/local/bin/networkstatus_events.sql
echo "Database now has the following tables"
mysql --user=$MYSQL_USER --password=$MYSQL_PASSWORD --database=$MYSQL_DATABASE --execute="SHOW TABLES;"

所以现在我要做的就是开始我的性能测试了

#!/usr/bin/env bash
echo "Shutting down previous containers"
docker-compose -f docker-compose.yml down
docker-compose -f docker-compose-test-10k-half.yml down
docker-compose -f docker-compose-test-100k-half.yml down
docker-compose -f docker-compose-test-500k-half.yml down
docker-compose -f docker-compose-test-1m-half.yml down

echo "Launching rabbitmq container"
docker-compose up -d rabbitmq & sh wait-for-it.sh -t 0 -h localhost -p 5672 -- sleep 5;

echo "Going to execute 10k test"
docker-compose -f docker-compose-test-10k-half.yml up -d mysql_10k & sh wait-for-it.sh -t 0 -h localhost -p 3306 -- sleep 5 && ./networkstatus-event-service --env=performance-test --run-once=true;
docker-compose -f docker-compose-test-10k-half.yml stop mysql_10k

更多这些行(略有不同,导致不同的容器名称)

【讨论】:

【参考方案2】:

在您的测试之后运行docker-compose down 将破坏与您的docker-compose.yml 相关的所有内容

Docker Compose 是一个容器生命周期管理器,默认情况下它会尝试在多次运行中保留所有内容。正如Stas Makarov 提到的,mysql 映像中定义了一个VOLUME,它将数据保存在容器之外。

【讨论】:

这个“down”命令似乎确实破坏了容器,但现在我每次运行docker-compose up它都必须重新插入所有测试数据。 @antonsteenvoorden 您可以将初始数据添加到图像中,而不是在启动后添加,以便下一次运行包含一些初始状态。 我通过执行以下操作设法解决了这个问题(使用 mysql 映像): 1. 更改 sql 存储的挂载点(这实际上是导致问题的原因)我使用了这里建议的解决方案: ***.com/questions/32482780/… 但我是通过运行 sed 命令完成的:RUN sed -i 's|/var/lib/mysql|/var/lib/mysql2|g' /etc/mysql/my.cnf 2. 将我的脚本添加到容器内的文件夹 3. 运行 import.sh 脚本,使用守护程序插入数据 4. 删除我刚刚制作的多个 sql 脚本具有不同数据的图像

以上是关于启动时使用静态数据进行 Docker 集成测试的主要内容,如果未能解决你的问题,请参考以下文章

Spring启动在集成测试中使用PowerMock模拟静态方法

使用远程Docker进行集成测试

解决Docker容器时区及时间不同步问题

使用 Testcontainer 进行 Spring 集成测试 - 数据库在应用程序之后启动

使用 Redis 进行集成测试

SpringBoot 集成测试 Sybase 和 Testcontainers