You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Java TestContainers配置Docker容器间通信端口转发

问题描述

我正在使用Java TestContainers运行两个容器:一个简单的OpenLiberty服务器和MySQL数据库,通过network将它们置于同一网络中,创建代码如下:

@ClassRule
public static Network network = Network.newNetwork();

@Container
public static MySQLContainer<?> mysqlDb = new MySQLContainer<>()
            .withDatabaseName("DBNAME")
            .withUsername("user")
            .withPassword("pswd")
            .withExposedPorts(3306)
            .withCreateContainerCmdModifier(cmd -> cmd.withPortBindings(new PortBinding(Ports.Binding.bindPort(443), new ExposedPort(3306))))
            .withNetwork(network)
            .withNetworkAliases("testDb");

@Container
public static ApplicationContainer app = new ApplicationContainer("test")
            .withNetwork(network)
            .dependsOn(mysqlDb)
            .withAppContextRoot("/backend")
            .withReadinessPath("/")
            .withHttpPort(9080)
            .withStartupTimeout(Duration.ofMinutes(5))
            .withFileSystemBind("/root/.m2", "/root/.m2")
            .withEnv(envVariables);

创建完成后,执行docker container ls确认配置正确,也通过docker container inspect 'id'确认它们处于同一网络且别名配置正确:

sudo docker container ls
CONTAINER ID   IMAGE                       COMMAND                  CREATED          STATUS          PORTS                                                    NAMES
a540e22c9543   test:latest                 "/usr/local/bin/mvn-…"   25 minutes ago   Up 25 minutes   0.0.0.0:32772->9080/tcp, :::32772->9080/tcp              sweet_goldstine
73a2ff3460c4   mysql:5.7.34                "docker-entrypoint.s…"   25 minutes ago   Up 25 minutes   33060/tcp, 0.0.0.0:443->3306/tcp, :::443->3306/tcp   flamboyant_tu
89993b13a2c5   testcontainers/ryuk:0.4.0   "/bin/ryuk"              25 minutes ago   Up 25 minutes   0.0.0.0:32770->8080/tcp, :::32770->8080/tcp              testcontainers-ryuk-c7ccfb4f-cc3b-42d2-b602-fe7f62472a9d

我原本以为容器间通信使用与主机相同的端口,所以将MySQL容器的内部端口3306绑定到主机端口443,但实际测试发现容器间通信直接使用暴露的内部端口(9080和3306)而非绑定到主机的端口(443和32772):

~$ sudo docker container exec -it 73a2ff3460c4 telnet a540e22c9543 9080
Trying 172.19.0.3...
Connected to a540e22c9543.

~$ sudo docker container exec -it 73a2ff3460c4 telnet a540e22c9543 32772
Trying 172.19.0.3...
telnet: Unable to connect to remote host: Connection refused

~$ sudo docker container exec -it a540e22c9543 telnet 73a2ff3460c4 443
telnet: Unable to connect to remote host: Connection refused

~$ sudo docker container exec -it a540e22c9543 telnet 73a2ff3460c4 3306
Connected to 73a2ff3460c4

我的需求是:让app容器能通过mysqlDb:443访问MySQL服务,请求自动转发到容器内部的3306端口,能否通过TestContainers实现这个需求?

解决方案

可以实现,核心是给MySQL容器在内部网络中配置端口映射,让容器本身监听443端口并转发到3306,而不是只做主机端口绑定。具体有两种可行方式:

方式一:通过TestContainers内置配置添加内部端口转发

修改MySQL容器的配置,同时暴露443端口,并添加内部网络的端口映射规则:

@Container
public static MySQLContainer<?> mysqlDb = new MySQLContainer<>()
            .withDatabaseName("DBNAME")
            .withUsername("user")
            .withPassword("pswd")
            .withExposedPorts(3306, 443) // 同时暴露内部的3306和443端口
            .withCreateContainerCmdModifier(cmd -> {
                // 保留原有的主机端口绑定(如果需要从主机访问MySQL可保留,不需要则可删除)
                cmd.withPortBindings(
                    new PortBinding(Ports.Binding.bindPort(443), new ExposedPort(3306)),
                    // 关键:配置内部网络的端口转发,容器的443端口映射到自身的3306端口
                    new PortBinding(Ports.Binding.empty(), new ExposedPort(443, 3306))
                );
            })
            .withNetwork(network)
            .withNetworkAliases("testDb");

这里的new PortBinding(Ports.Binding.empty(), new ExposedPort(443, 3306))是核心,它告诉Docker在容器所在的内部网络中,把443端口的流量转发到容器自身的3306端口。

方式二:自定义MySQL镜像实现端口转发

如果上述方式不生效,可以预先构建一个自定义MySQL镜像,在镜像内添加端口转发逻辑:

FROM mysql:5.7.34
EXPOSE 443
# 安装socat工具实现端口转发
RUN apt-get update && apt-get install -y socat && rm -rf /var/lib/apt/lists/*
# 启动时先启动socat转发443到3306,再启动MySQL服务
CMD ["sh", "-c", "socat TCP-LISTEN:443,fork TCP:localhost:3306 & exec docker-entrypoint.sh mysqld"]

构建镜像后,在TestContainers中使用这个自定义镜像:

@Container
public static MySQLContainer<?> mysqlDb = new MySQLContainer<>("your-custom-mysql-image:tag")
            .withDatabaseName("DBNAME")
            .withUsername("user")
            .withPassword("pswd")
            .withNetwork(network)
            .withNetworkAliases("testDb");

验证配置

修改完成后重新启动容器,进入app容器执行telnet testDb 443,应该能成功连接到MySQL服务。

内容的提问来源于stack exchange,提问作者Some_Person

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 19:45:22