【问题标题】:Java Testcontainers - Cannot connect to exposed portJava Testcontainers - 无法连接到暴露的端口
【发布时间】:2020-01-31 19:16:43
【问题描述】:

我使用 javax.mail 实现了一个 POP3 服务器和客户端,只是为了尝试使用 Docker 进行集成测试。所以我基于 openjdk:8-jre 镜像创建了两个 Docker 镜像,并将我的 jar 复制到它们并启动它。根据我的配置(见下文),它正在工作。他们正在互相交谈。

但是由于想要进行多个集成测试,为每个测试构建一个映像并启动它们将会很乏味。我也不知道如何自动化结果。 但后来我偶然发现了 TestContainers,这似乎对实现这些测试很有帮助。

所以我开始使用我的 POP3 服务器映像作为 GenericContainer 将这些测试移植到 TestContainers,并在 JUnit 测试方法中启动我的 POP3 客户端类。我暴露了我的 POP3 服务器正在监听的端口 24999。但是当我尝试连接到服务器时,出现以下错误:

com.sun.mail.util.MailConnectException: Couldn't connect to host, port: localhost, 32782; timeout -1;
  nested exception is:
    java.net.ConnectException: Connection refused
...

TestContainers 中可能缺少一些设置。你能帮帮我吗?

这是我正在使用的代码:

public class DockerPop3AutocryptKeyProvidingAndReceivingTest {
    @Test
    public void test() throws InterruptedException {
        GenericContainer container = new GenericContainer<>("immerfroehlich/emailfilter:latest")
                .withExposedPorts(24999);

        container.start();

        String host = container.getContainerIpAddress();
        String port = container.getFirstMappedPort().toString();

        //The following is simplified, but copied from the working jar used in the Docker Client image/container
        MyPOP3Client client = new MyPOP3Client(host, port);
        client.connect();

        container.stop();
    }
}

这就是我创建 Docker 映像的方式:

FROM openjdk:8-jre

ADD build/distributions/MyPOP3Server.tar . #This is where I have packeded all the needed files to. It gets unpackeded by Docker.
#EXPOSE 24999 #I tried both with and without this expose
WORKDIR /MyPOP3Server/bin
ENTRYPOINT ["sh","MyPOP3Server"] #Executes the shell script wich runs java with my jar

这是在服务器 Jar 中运行的代码的简化版本:

MyPOP3Server server = new MyPOP3Server();
server.listenToPort(24999);

请告诉我我错过了什么。这里有什么问题?

感谢和亲切的问候。

【问题讨论】:

  • 能否在容器启动后下断点并检查它以查看映射的端口。无论如何,如果您执行类似 port = container.getMappedPort(24999) 的操作
  • 好的,我改成 port = container.getMappedPort(24999) 但它还是不行。我做了调试,但我不知道在哪里看。 container.exposedPorts 是 [24999]; container.portBindings 是 []。

标签: java docker junit dockerfile testcontainers


【解决方案1】:

尝试添加 http 检查。

 new GenericContainer<>("immerfroehlich/emailfilter:latest")
 .withExposedPorts(24999)
 .waitingFor(new HttpWaitStrategy().forPort(24999)
 .withStartupTimeout(Duration.ofMinutes(5)));

您的容器可能已启动,但您在服务器初始化之前尝试连接。

另外,注册一个日志附加器以查看容器内的服务器发生了什么。

 .withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger(
              DockerPop3AutocryptKeyProvidingAndReceivingTest.class)))

【讨论】:

  • 我肯定会第二次使用 LogConsumer,但鉴于这是一个 POP3 服务器,我怀疑HttpWaitStrategy 不会有帮助,很遗憾。
  • 嗨,我使用了 LogMessageWaitStrategy 并在服务器中添加了 System.out.println 而不是 HttpWaitStrategy。与 switch container.getMappedPort(24999) 一起使用它是解决方案。谢谢。
【解决方案2】:

在其他答案中有一些很好的建议;我将通过其他一些技巧来补充这些:

如前所述:

  • 绝对添加LogConsumer,这样您就可以看到容器的日志输出——也许现在或将来会出现有用的东西。拥有它总是好的。

  • 在容器启动后设置断点,就在您启动客户端之前。

此外,我希望以下事情有所作为。在断点处暂停时:

  • 在终端运行docker ps -a
  • 首先,检查您的容器是否正在运行并且尚未退出。如果它已退出,请从终端查看容器的日志。
  • 其次,检查docker ps输出中的端口映射。您应该会看到类似 0.0.0.0:32768-&gt;24999/tcp 的内容(不过,第一个端口号是随机的)。
  • 在您的 IDE 中评估 container.getFirstMappedPort() 并检查您返回的端口号是否与随机公开的端口相同。除非您在本地计算机上安装了非常不寻常的 Docker,否则应该可以通过 localhost: + 此端口访问此容器。
  • 如果您已经走到这一步,那么很可能是容器或客户端代码有问题。您可以尝试将不同的客户端连接到正在运行的容器 - 如果您手边没有另一个 POP3 客户端,即使像 nc 这样的东西也会有所帮助。

要尝试的另一件事是手动运行容器,以减少发生的间接次数。您给出的 Testcontainers 代码 sn-p 相当于:

docker run -p 24999 immerfroehlich/emailfilter:latest

您可能会发现这有助于您将问题空间分成更小的部分。

【讨论】:

  • 感谢您的建议。它对找到正确的解决方案有很大帮助。我将其发布为新答案。谢谢。
【解决方案3】:

由于您的邮件服务器和客户端都在容器中运行,我认为您应该连接到端口 24999 而不是映射端口

【讨论】:

  • 不,这是我最初所做的。我只是想提一下,这样没人会问你的服务器是否正常工作。目前服务器在容器内运行,客户端在 JUnit 测试中本地运行。
【解决方案4】:

尝试container.getMappedPort(24999) 而不是getFirstMappedPort。可能你的 docker 镜像暴露了几个端口。

【讨论】:

  • 正如我已经向 MC Ninjava 提到的。我试过了,但它不起作用。
  • 如果在客户端连接前下断点,是否可以使用telnet或其他邮件客户端连接到映射的端口?
  • 不,它也没有工作。但后来我在命令行上用 Docker 启动了容器,它也没有工作。然后我在 docker 手册中阅读了有关网络的信息,我有了一个想法。所以两个容器可以互相通信,但是主机不能和容器通信。在手册中它说桥接网络是默认网络。它有什么作用?它允许两个容器相互通信(甚至更多)。因此,我阅读了有关主机网络的信息。它基本上说在 Linux 上它与主机在同一个网络上。 ...
  • ...我直接使用“docker run -i -t --network host immerfroehlich/emailfilter:latest”启动了容器,并且可以使用 JUnit Test 连接到容器。所以我现在唯一需要知道的是,如何在 TestContainers 中设置主机网络。我尝试使用“Network network = Network.builder().driver("host").build(); container.withNetwork(network)”。但后来我得到一个“ContainerLaunchException:容器启动失败”,在“DockerException:只允许一个主机网络实例”下方。那么有谁知道如何在 TestContainers 中做到这一点?
  • 实际上 TestContainers 被设计为在桥接模式下工作。它使用随机映射端口来确保您的 dockerized 测试可以同时运行。您可以尝试的另一件事是插入一些等待条件(如new GenericContainer("blah").waitingFor(Wait.forHttp("/some/url"))),甚至使用超时。
【解决方案5】:

感谢您的所有帮助。这使我找到了解决方案。这是缺少 WaitStrategy 和端口映射问题的结合。

这是我所做的: 1) 在 MyPop3Server.listenToPort(String port) 方法中,我添加了一个 System.out.println:

public class MyPop3Server {
  public void listenToPort(String port) {
     //simplified: do initialization and listenToPort
     System.out.println("Awaiting Connection...");
  }
}

在我的测试中,我添加了一个 LogMessageWaitStrategy 来监听“等待连接”

GenericContainer container = new GenericContainer<>("immerfroehlich/emailfilter:latest")
   .waitingFor(Wait.forLogMessage("Awaiting Connection.*", 1))
   .withExposedPorts(24999);

2) 我从 container.getFirstMappedPort() 切换到

container.getMappedPort(24999);

这里是整个改变和工作的测试代码:

public class DockerPop3AutocryptKeyProvidingAndReceivingTest {
    @Test
    public void test() throws InterruptedException {
        GenericContainer container = new GenericContainer<>("immerfroehlich/emailfilter:latest")
                .waitingFor(Wait.forLogMessage("Awaiting Connection.*", 1))
                .withExposedPorts(24999);

        container.start();

        String host = container.getContainerIpAddress();
        String port = container.getMappedPort(24999).toString();

        //The following is simplified, but copied from the working jar used in the Docker Client image/container
        MyPOP3Client client = new MyPOP3Client(host, port);
        client.connect();

        container.stop();
    }
}

谢谢大家。

【讨论】:

    猜你喜欢
    • 2021-09-07
    • 1970-01-01
    • 1970-01-01
    • 2021-11-28
    • 2021-08-03
    • 2018-02-01
    • 2018-09-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多