Starting axon server using Testcontainers from a SpringBootTest -> port conflict

42 views
Skip to first unread message

Frank Mölder

unread,
Aug 9, 2019, 12:23:05 PM8/9/19
to Axon Framework Users
Hi all,

I'd like to use axon server in my integration tests (actually a cucumber steps definition). So I'am configuring a org.testcontainers.containers.GenericContainer to start the axon server docker image, which works fine as the log shows:

2019-08-09 17:52:55.981  INFO 7200 --- [           main] 🐳 [axoniq/axonserver:4.1.7]             : Creating container for image: axoniq/axonserver:4.1.7
2019-08-09 17:52:56.020  INFO 7200 --- [           main] o.t.utility.RegistryAuthLocator          : Credentials not found for host (index.docker.io) when using credential helper/store (docker-credential-desktop)
2019-08-09 17:52:56.138  INFO 7200 --- [           main] 🐳 [axoniq/axonserver:4.1.7]             : Starting container with ID: 7491a169dc40e54dfc8923e791c74c633846401dc679ddedddadc9f48e26344c
2019-08-09 17:52:56.607  INFO 7200 --- [           main] 🐳 [axoniq/axonserver:4.1.7]             : Container axoniq/axonserver:4.1.7 is starting: 7491a169dc40e54dfc8923e791c74c633846401dc679ddedddadc9f48e26344c
2019-08-09 17:52:57.105  INFO 7200 --- [ream-1170173753] docker.axonserver                        : STDOUT: 15:52:57.113 [main] INFO org.springframework.core.KotlinDetector - Kotlin reflection implementation not found at runtime, related features won't be available.
2019-08-09 17:52:57.560  INFO 7200 --- [ream-1170173753] docker.axonserver                        : STDOUT:      _                     ____
2019-08-09 17:52:57.560  INFO 7200 --- [ream-1170173753] docker.axonserver                        : STDOUT:     / \   __  _____  _ __ / ___|  ___ _ ____   _____ _ __
2019-08-09 17:52:57.560  INFO 7200 --- [ream-1170173753] docker.axonserver                        : STDOUT:    / _ \  \ \/ / _ \| '_ \\___ \ / _ \ '__\ \ / / _ \ '__|
2019-08-09 17:52:57.560  INFO 7200 --- [ream-1170173753] docker.axonserver                        : STDOUT:   / ___ \  >  < (_) | | | |___) |  __/ |   \ V /  __/ |
2019-08-09 17:52:57.561  INFO 7200 --- [ream-1170173753] docker.axonserver                        : STDOUT:  /_/   \_\/_/\_\___/|_| |_|____/ \___|_|    \_/ \___|_|
2019-08-09 17:52:57.561  INFO 7200 --- [ream-1170173753] docker.axonserver                        : STDOUT:  Standard Edition                        Powered by AxonIQ
2019-08-09 17:52:57.561  INFO 7200 --- [ream-1170173753] docker.axonserver                        : STDOUT: 
2019-08-09 17:52:57.561  INFO 7200 --- [ream-1170173753] docker.axonserver                        : STDOUT: version: 4.1.7
2019-08-09 17:52:57.668  INFO 7200 --- [ream-1170173753] docker.axonserver                        : STDOUT: 2019-08-09 15:52:57.678  INFO 7 --- [           main] io.axoniq.axonserver.AxonServer          : Starting AxonServer on 7491a169dc40 with PID 7 (/opt/axonserver/axonserver.jar started by root in /opt/axonserver)
2019-08-09 17:52:57.669  INFO 7200 --- [ream-1170173753] docker.axonserver                        : STDOUT: 2019-08-09 15:52:57.679  INFO 7 --- [           main] io.axoniq.axonserver.AxonServer          : No active profile set, falling back to default profiles: default
2019-08-09 17:52:57.956  INFO 7200 --- [ream-1170173753] docker.axonserver                        : STDOUT: 2019-08-09 15:52:57.966  WARN 7 --- [kground-preinit] org.springframework.web.HttpLogging      : For Jackson Kotlin classes support please add "com.fasterxml.jackson.module:jackson-module-kotlin" to the classpath
2019-08-09 17:53:00.451  INFO 7200 --- [ream-1170173753] docker.axonserver                        : STDOUT: 2019-08-09 15:53:00.463  INFO 7 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8024 (http)
2019-08-09 17:53:06.182  INFO 7200 --- [ream-1170173753] docker.axonserver                        : STDOUT: 2019-08-09 15:53:06.197  INFO 7 --- [           main] io.axoniq.axonserver.grpc.Gateway        : Axon Server Gateway started on port: 8124 - no SSL
2019-08-09 17:53:06.626  INFO 7200 --- [ream-1170173753] docker.axonserver                        : STDOUT: 2019-08-09 15:53:06.641  INFO 7 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8024 (http) with context path ''
2019-08-09 17:53:06.631  INFO 7200 --- [ream-1170173753] docker.axonserver                        : STDOUT: 2019-08-09 15:53:06.647  INFO 7 --- [           main] io.axoniq.axonserver.AxonServer          : Started AxonServer in 9.496 seconds (JVM running for 10.035)
2019-08-09 17:53:08.436  INFO 7200 --- [           main] 🐳 [axoniq/axonserver:4.1.7]             : Container axoniq/axonserver:4.1.7 started
2019-08-09 17:53:08.562  INFO 7200 --- [           main] c.w.e.ddd.glue.PointRequestStepsDef      : Starting PointRequestStepsDef on Mac-mini-van-Frank.local with PID 7200 

The configuration to achieve this:
public class ContainerStepsDef {
private static final String AXON_SERVER_NAME = "axonserver";
private static final String MY_AXON_SERVER = "my-axon-server";
private static Logger AXON_SERVER_LOGGER = LoggerFactory.getLogger("docker.axonserver");

private static final GenericContainer AXON_SERVER = new GenericContainer("axoniq/axonserver:4.1.7")
.withLabel("name", MY_AXON_SERVER)
.withLabel("hostname", AXON_SERVER_NAME)
.withEnv("AXONSERVER_HOSTNAME", AXON_SERVER_NAME)
.withExposedPorts(8024, 8124)
.withLogConsumer(new Slf4jLogConsumer(AXON_SERVER_LOGGER)
);

public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

public void initialize(@Nonnull ConfigurableApplicationContext configurableApplicationContext) {
AXON_SERVER.start();

Awaitility.waitAtMost(Duration.TEN_SECONDS)
.until(AXON_SERVER::isRunning);

TestPropertyValues.of(
"axoniq.axonserver.name="+ MY_AXON_SERVER,
"axoniq.axonserver.hostname=" + AXON_SERVER_NAME,
"server.port=" + AXON_SERVER.getMappedPort(8024),
"axoniq.axonserver.port=" + AXON_SERVER.getMappedPort(8124)
)
.applyTo(configurableApplicationContext.getEnvironment());
}
}
}

As shown above the server.port will be set to the mapped port and the application is able to find it. However the @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT also uses this property (server.port) for the embedded webserver. Since the axon server port is already bound to this port spring cannot bind the webserver and an exception occurs (Caused by: java.net.BindException: Address already in use). 

What is the best way to solve this? I really prefer using the axon server docker image for this purpose.

Regards,

Frank


Jakob Hatzl

unread,
Aug 12, 2019, 1:47:18 AM8/12/19
to Axon Framework Users
Hi,

are you sure that the conflict is related to your SprigBootTest WebEnvironment and the Axon container? 

As far as I understood it, new GenericContainer("...").withExposedPorts maps the 8024 and 8124 to random ports on your host, which you can read with AXON_SERVER.getMappedPort(...). The random port assignment should be done automatically by the underlying docker run command, which should check for available ports automatically (see https://www.testcontainers.org/features/networking/).
Same applies to the SpringBootTest RANDOM_PORT (see https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications), which should automatically choose a random, available port.

Maybe you could set a breakpoint somewhere before your SpringBootTest.WebEnvironment is started and check your netstat -a for the ports in question.

As far as I can see from your log, you are using MacOS, which adds an additional layer of complexity because docker is run in a virtual enviroment as far as I know. 

Hope some of this makes sense and helps to find an error.

Best Regards

Frank Mölder

unread,
Jan 5, 2020, 4:09:39 PM1/5/20
to Axon Framework Users
Hi Jakob,

Sorry for my late reply, but thanks for your reponse. It turned out I mapped the ports in the wrong direction. The setup underneath works fine:
public class ContainerStepsDef {

private static final Logger LOGGER = LoggerFactory.getLogger(ContainerStepsDef.class);
    private static final String AXON_SERVER_NAME = "axonserver";
    private static Logger AXON_SERVER_LOGGER = LoggerFactory.getLogger("docker.axonserver");

    private static final GenericContainer AXON_SERVER = new GenericContainer("axoniq/axonserver:4.2.4-jdk11") {

@Override
public void stop() {
AXON_SERVER.stop();

while (AXON_SERVER.isRunning()) {
LOGGER.info("Waiting for Axon shutdown");
}
}
}
.withLabel("name", AXON_SERVER_NAME)

.withEnv("AXONSERVER_HOSTNAME", AXON_SERVER_NAME)
.withExposedPorts(8024, 8124)
.withLogConsumer(new Slf4jLogConsumer(AXON_SERVER_LOGGER));

public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

public void initialize(@Nonnull ConfigurableApplicationContext configurableApplicationContext) {
AXON_SERVER.start();

Awaitility.waitAtMost(Duration.TEN_SECONDS).until(AXON_SERVER::isRunning);

            TestPropertyValues.of("axon.axonserver.servers=" + "localhost:" + AXON_SERVER.getMappedPort(8124))
.applyTo(configurableApplicationContext.getEnvironment());

LOGGER.info("Axon server rest endpoints available on: http://localhost:" + AXON_SERVER.getMappedPort(8024));
}
}
}



Op maandag 12 augustus 2019 07:47:18 UTC+2 schreef Jakob Hatzl:
Reply all
Reply to author
Forward
0 new messages