'Testcontainers: communication between containers + mapped outside port
I have such test setup:
MyServiceconnects toPostgtreSQLMyServiceendpoint is being called from test suite
Both MyService and PostgreSQL are being run with Testcontainers.
Here is the network schema I want to achieve.
At first I tried to arrange communication by exposing ports.
static final PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>(DockerImageName.parse(POSTGRES_VERSION));
static final GenericContainer<?> myService = new GenericContainer<>(DockerImageName.parse(MY_SERVICE_IMAGE))
.withEnv(
Map.of(
"SPRING_DATASOURCE_URL", postgres.getJdbcUrl(),
"SPRING_DATASOURCE_USERNAME", postgres.getUsername(),
"SPRING_DATASOURCE_PASSWORD", postgres.getPassword()
)
)
.withExposedPorts(8080)
.withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("MyService")))
According to logs MyService couldn't establish connection to PostgreSQL.
Caused by: java.net.ConnectException: Connection refused
Then I configured both services to share the same network.
static final Network SHARED_NETWORK = Network.newNetwork();
static final PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>(DockerImageName.parse(POSTGRES_VERSION))
.withNetwork(SHARED_NETWORK)
.withNetworkAliases("postgres");
static final GenericContainer<?> myService = new GenericContainer<>(DockerImageName.parse(MY_SERVICE_IMAGE))
.withEnv(
Map.of(
"SPRING_DATASOURCE_URL", "jdbc:postgresql://postgres:5432/" + postgres.getDatabaseName(),
"SPRING_DATASOURCE_USERNAME", postgres.getUsername(),
"SPRING_DATASOURCE_PASSWORD", postgres.getPassword()
)
)
.withExposedPorts(8080)
.withNetwork(SHARED_NETWORK)
.withNetworkAliases("MyService")
.withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("MyService")))
Now MyService has established connection with PostgreSQL successfully. But when I perform HTTP request to MyService from the test suite, I get the same error.
restTemplate.getForObject("http://" + myService.getHost() + ":" + myService.getMappedPort(8080) +"/api/endpoint", Void.class)
Caused by: java.net.ConnectException: Connection refused
My question is how can I setup the containers network to make this architecture work?
Solution 1:[1]
You need to specify port bindings to expose a port to the "outside world".
Example similar to what you want:
Network network = Network.newNetwork();
GenericContainer mariaDbServer = getMariaDbContainer(network);
GenericContainer flywayRunner = getFlywayContainer(network);
...
@SuppressWarnings("rawtypes")
private GenericContainer getMariaDbContainer(Network network) {
return new GenericContainer<>("mariadb:10.4.21-focal")
.withEnv(Map.of("MYSQL_ROOT_PASSWORD", "password", "MYSQL_DATABASE", "somedatabase"))
.withCommand(
"mysqld", "--default-authentication-plugin=mysql_native_password", "--character-set-server=utf8mb4",
"--collation-server=utf8mb4_unicode_ci").withNetwork(network).withNetworkAliases("somedatabasedb")
.withNetworkMode(network.getId())
.withExposedPorts(3306).withCreateContainerCmdModifier(
cmd -> cmd.withNetworkMode(network.getId()).withHostConfig(
new HostConfig()
.withPortBindings(new PortBinding(Ports.Binding.bindPort(20306), new ExposedPort(3306))))
.withNetworkMode(network.getId())).withStartupTimeout(Duration.ofMinutes(2L));
}
@SuppressWarnings("rawtypes")
private GenericContainer getFlywayContainer(Network network) {
return new GenericContainer<>("flyway/flyway:7.15.0-alpine")
.withEnv(Map.of("MYSQL_ROOT_PASSWORD", "password", "MYSQL_DATABASE", "somedatabase"))
.withCommand(
"-url=jdbc:mariadb://somedatabasedb -schemas=somedatabase-user=root -password=password -connectRetries=300 migrate")
.withFileSystemBind(Paths.get(".", "infrastructure/database/schema").toAbsolutePath().toString(),
"/flyway/sql", BindMode.READ_ONLY).withNetwork(network).waitingFor(
Wait.forLogMessage(".*Successfully applied.*", 1)
).withStartupTimeout(Duration.of(60, ChronoUnit.SECONDS));
}
Container two communicates with container one using "internal" port.
Container one exposes 20306 (that redirects to 3306) port to the "outside world".
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | pringi |

