Gradle Docker镜像无法连接到MySQL TestContainer。

huangapple go评论68阅读模式
英文:

Gradle docker image can't connect to mysql testcontainer

问题

我有一个使用MySQL测试容器的小型集成测试。

当我在本地构建时,运行简单的gradlew build命令,它会运行测试并构建项目。但是当我尝试使用官方的gradle docker镜像时,测试会在等待mysql数据库可用时停滞不前。

当我的构建运行时,它创建了3个容器:

  • Gradle容器
  • quay.io/testcontainers/ryuk:0.2.3
  • mysql:latest

我的gradle镜像与官方的不同,因为它已经安装了Docker:

FROM gradle
USER root
RUN	apt-get update && \
    	apt-get install -y \
        apt-transport-https \
        ca-certificates \
        curl \
        gnupg2 \
        software-properties-common && \
    	curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - && \
    	apt-key fingerprint 0EBFCD88 && \
        add-apt-repository \
    		"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
    		$(lsb_release -cs) \
    		stable" && \
        apt-get update && \
        apt-get install -y docker-ce docker-ce-cli containerd.io

当连接被阻塞时,我连接到gradle镜像,并安装了mysql客户端。我尝试连接到与测试连接的相同镜像,并没有遇到问题。

有没有人有任何关于我做错了什么的想法,因为只有在容器内运行构建时才会出现这个问题?

我使用以下命令运行:

docker container run -v /var/run/docker.sock:/var/run/docker.sock -v <本地存储库>:/home/gradle/project -w /home/gradle/project --rm mygradle gradle build -i

编辑:
这不是因为gradle docker镜像,而是任何镜像。如果我在Jenkins中运行,并且它是一个容器,这也不起作用。尝试连接到172.x.x.1(主机地址)和暴露的端口,但测试不连接,但访问容器并尝试使用mysql客户端进行相同的连接时,我可以连接。

英文:

I have a small integration test that uses MySQL Test container.

When I build locally, running the simple gradlew build command, it runs, test and builds the project. But when I try to use the official gradle docker image, the test stalls waiting for the availability of the mysql database.

When my build runs, it creates 3 containers:

  • Gradle Container
  • quay.io/testcontainers/ryuk:0.2.3
  • mysql:latest

My gradle image is different from the official one, because it has docker installed:

FROM gradle
USER root
RUN	apt-get update &amp;&amp; \
	apt-get install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg2 \
    software-properties-common &amp;&amp; \
	curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - &amp;&amp; \
	apt-key fingerprint 0EBFCD88 &amp;&amp; \
    add-apt-repository \
		&quot;deb [arch=amd64] https://download.docker.com/linux/ubuntu \
		$(lsb_release -cs) \
		stable&quot; &amp;&amp; \
    apt-get update &amp;&amp; \
    apt-get install -y docker-ce docker-ce-cli containerd.io 

When the connection was stalled, I connected to the gradle image, and I installed the mysql client. I tried to connect to the same image the tests are being connected and I had no problem to connect.
Does anyone has any idea on what I'm doing wrong, since only running the build inside the container I have this problem?

I run with this:

docker container run -v /var/run/docker.sock:/var/run/docker.sock -v &lt;local repo&gt;:/home/gradle/project -w /home/gradle/project --rm mygradle gradle build -i

Edit:
This is not because of the gradle docker image, but any image. If I run using the Jenkins, and it being a container, this doesn't work also. Tries to connect to the 172.x.x.1 (host address) and the port exposed and the Test doesn't connect, but accessing the container, and trying the same exact connection using the mysql client, I can connect.

答案1

得分: 1

我找到了一个解决方案。

而不是使用MysqlContainer类,我定义了一个新的类扩展了这个类。它会检测应用程序是否运行在容器内,如果是的话,它会尝试在内部连接到数据库容器,而不是使用localhost和生成的端口。

public static class IntegrationTestsDatabase<SELF extends MySQLContainer<SELF>> extends MySQLContainer<SELF> {
    public IntegrationTestsDatabase() {
    }

    public IntegrationTestsDatabase(String dockerImageName) {
        super(dockerImageName);
    }

    public String getJdbcUrl() {
        String containerIp = ((ContainerNetwork)this.getContainerInfo().getNetworkSettings().getNetworks().values().stream().findFirst().get()).getIpAddress();
        return this.isRunningInsideDocker() ? "jdbc:mysql://" + containerIp + ":" + MySQLContainer.MYSQL_PORT + "/" + this.getDatabaseName() : super.getJdbcUrl();
    }

    public Boolean isRunningInsideDocker() {
        try {
            Stream<String> stream = Files.lines(Paths.get("/proc/1/cgroup"));

            Boolean var2;
            try {
                var2 = stream.anyMatch((line) -> {
                    return line.contains("/docker");
                });
            } catch (Throwable var5) {
                if (stream != null) {
                    try {
                        stream.close();
                    } catch (Throwable var4) {
                        var5.addSuppressed(var4);
                    }
                }

                throw var5;
            }

            if (stream != null) {
                stream.close();
            }

            return var2;
        } catch (IOException var6) {
            return false;
        }
    }
}

这将适用于其他容器类型。

英文:

I found a solution for this.

Instead of using the MysqlContainer class, I defined a new class extending this one. It will detect if the application is running inside a container, and if so, it tries to connect to the database container internally, instead of using the localhost and the generated port.

public static class IntegrationTestsDatabase&lt;SELF extends MySQLContainer&lt;SELF&gt;&gt; extends MySQLContainer&lt;SELF&gt; {
    public IntegrationTestsDatabase() {
    }

    public IntegrationTestsDatabase(String dockerImageName) {
        super(dockerImageName);
    }

    public String getJdbcUrl() {
        String containerIp = ((ContainerNetwork)this.getContainerInfo().getNetworkSettings().getNetworks().values().stream().findFirst().get()).getIpAddress();
        return this.isRunningInsideDocker() ? &quot;jdbc:mysql://&quot; + containerIp + &quot;:&quot; + MySQLContainer.MYSQL_PORT + &quot;/&quot; + this.getDatabaseName() : super.getJdbcUrl();
    }

    public Boolean isRunningInsideDocker() {
        try {
            Stream stream = Files.lines(Paths.get(&quot;/proc/1/cgroup&quot;));

            Boolean var2;
            try {
                var2 = stream.anyMatch((line) -&gt; {
                    return line.contains(&quot;/docker&quot;);
                });
            } catch (Throwable var5) {
                if (stream != null) {
                    try {
                        stream.close();
                    } catch (Throwable var4) {
                        var5.addSuppressed(var4);
                    }
                }

                throw var5;
            }

            if (stream != null) {
                stream.close();
            }

            return var2;
        } catch (IOException var6) {
            return false;
        }
    }
}

This will work with other container types.

huangapple
  • 本文由 发表于 2020年1月3日 22:40:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/59580477.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定