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

TestContainers启动容器遇ContainerLaunchException问题求助

问题分析与修复方案

问题现象

  • 执行./gradlew clean build批量运行测试时,控制台打印ContainerLaunchException但测试最终通过
  • 单独运行部分功能测试时,测试直接因该异常失败
  • 异常核心:TestContainers尝试链接容器k5bii89amzd5_app7_1时,发现该容器未处于运行状态

核心原因

当前容器初始化逻辑存在两处致命问题:

  1. 在构造函数中初始化静态容器实例,JUnit 5并行执行测试时会触发多线程竞态,导致容器状态不一致
  2. 手动控制容器启动流程,未利用TestContainers官方提供的生命周期管理机制,容器就绪状态检查不严谨

修复代码

重构后的ContainerBaseTest.java

@Testcontainers
@Tag("integration")
@Tag("docker")
@Slf4j
public abstract class ContainerBaseTest {

    @Container
    static DockerComposeContainer CONTAINER;

    static {
        final WaitStrategy waitStrategy = Wait.forListeningPort().withStartupTimeout(Duration.ofMinutes(2));
        Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(log);

        log.info("Starting testcontainers...");

        CONTAINER = new DockerComposeContainer(new File("docker-compose.yml"))
                .withExposedService("app1_1", 2929, waitStrategy).withLogConsumer("app1_1", logConsumer)
                .withExposedService("app2_1", 8065, waitStrategy).withLogConsumer("app2_1", logConsumer)
                .withExposedService("app3_1", 8090, waitStrategy).withLogConsumer("app3_1", logConsumer)
                .withExposedService("app4_1", 9339, waitStrategy).withLogConsumer("app4_1", logConsumer)
                .withExposedService("app5_1", 2222, waitStrategy).withLogConsumer("app5_1", logConsumer)
                .withExposedService("app6_1", 18080, waitStrategy).withLogConsumer("app6_1", logConsumer)
                .withExposedService("app7_1", 9090, waitStrategy).withLogConsumer("app7_1", logConsumer)
                .withLocalCompose(true);

        log.info("testcontainers started.");
    }
}

关键修改说明

  • 新增@Container注解标记静态容器实例,TestContainers会自动处理容器的启动、停止、状态检查等生命周期
  • 将容器初始化逻辑移到静态代码块,确保所有测试类加载前完成容器初始化,避免多线程竞态
  • 移除手动调用的CONTAINER.start(),交由@Testcontainers自动触发启动流程

额外优化建议

  • 升级TestContainers到最新稳定版(当前使用的1.15.2版本较旧,新版本修复了大量Docker Compose兼容性问题)
  • 单独执行docker-compose up验证app7服务的启动稳定性,排除服务自身启动后立即退出的情况

原参考代码与日志

SampleE2ETest.java

@MicronautTest(transactional = false)
@ExtendWith(MockitoExtension.class)
public class SampleE2ETest extends ContainerBaseTest {
    @Test
    void sampleTest() {
        assertThat("sadas").isEqualTo("asa");
    }
}

原报错日志

18:01:38.913 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app4-1  Creating  
18:01:38.914 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app6-1  Creating  
18:01:38.914 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app1-1  Creating  
18:01:38.914 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app3-1  Creating  
18:01:38.914 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app7-1  Creating  
18:01:38.914 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app5-1  Creating  
18:01:38.915 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-cron-1  Creating  
18:01:38.915 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app2-1  Creating  
18:01:39.002 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-cron-1  Created  
18:01:39.006 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app6-1  Created  
18:01:39.007 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app7-1  Created  
18:01:39.009 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app2-1  Created  
18:01:39.011 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app4-1  Created  
18:01:39.014 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app1-1  Created  
18:01:39.017 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app5-1  Created  
18:01:39.017 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app3-1  Created  
18:01:39.022 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app1-1  Starting  
18:01:39.023 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app7-1  Starting  
18:01:39.024 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-cron-1  Starting  
18:01:39.024 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app2-1  Starting  
18:01:39.025 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app5-1  Starting  
18:01:39.027 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app6-1  Starting  
18:01:39.028 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app4-1  Starting  
18:01:39.029 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app3-1  Starting  
18:01:39.546 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-cron-1  Started  
18:01:39.637 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app1-1  Started  
18:01:39.746 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app3-1  Started  
18:01:39.908 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app2-1  Started  
18:01:39.914 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app6-1  Started  
18:01:39.955 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app7-1  Started  
18:01:40.010 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app5-1  Started  
18:01:40.030 [Thread-9] INFO  🐳 [docker-compose] - Container k5bii89amzd5-app4-1  Started  
18:01:40.044 [Test worker] DEBUG o.t.s.o.z.exec.WaitForProcess - Process[pid=45511, exitValue=0] stopped with exit code 0  
18:01:40.046 [Test worker] INFO  🐳 [docker-compose] - Docker Compose has finished running  
18:01:40.050 [Test worker] DEBUG o.t.u.PrefixingImageNameSubstitutor - No prefix is configured  
18:01:40.051 [Test worker] DEBUG o.t.utility.ImageNameSubstitutor - Did not find a substitute image for alpine/socat:1.7.3.4-r0 (using image substitutor: DefaultImageNameSubstitutor (composite of 'ConfigurationFileImageNameSubstitutor' and 'PrefixingImageNameSubstitutor'))  
18:01:40.054 [Test worker] DEBUG o.t.s.c.g.d.c.command.AbstrDockerCmd - Cmd: ListImagesCmdImpl[imageNameFilter=<null>,showAll=false,filters=org.testcontainers.shaded.com.github.dockerjava.core.util.FiltersBuilder@0]  
18:01:40.203 [Test worker] DEBUG o.t.images.AbstractImagePullPolicy - Using locally available and not pulling image: alpine/socat:1.7.3.4-r0  
18:01:40.204 [Test worker] DEBUG 🐳 [alpine/socat:1.7.3.4-r0] - Starting container: alpine/socat:1.7.3.4-r0  
18:01:40.204 [Test worker] DEBUG 🐳 [alpine/socat:1.7.3.4-r0] - Trying to start container: alpine/socat:1.7.3.4-r0 (attempt 1/1)  
18:01:40.205 [Test worker] DEBUG 🐳 [alpine/socat:1.7.3.4-r0] - Starting container: alpine/socat:1.7.3.4-r0  
18:01:40.205 [Test worker] INFO  🐳 [alpine/socat:1.7.3.4-r0] - Creating container for image: alpine/socat:1.7.3.4-r0  
18:01:40.205 [Test worker] DEBUG o.t.utility.RegistryAuthLocator - Looking up auth config for image: alpine/socat:1.7.3.4-r0 at registry: index.docker.io  
18:01:40.206 [Test worker] DEBUG o.t.utility.RegistryAuthLocator - No matching Auth Configs - falling back to defaultAuthConfig [null]  
18:01:40.206 [Test worker] DEBUG o.t.d.AuthDelegatingDockerClientConfig - Effective auth config [null]  
18:01:40.211 [Test worker] DEBUG o.t.s.c.g.d.c.command.AbstrDockerCmd - Cmd: -1,<null>,false,<null>,<null>,org.testcontainers.shaded.com.github.dockerjava.core.util.FiltersBuilder@96a2da2c  
18:01:40.244 [Test worker] ERROR 🐳 [alpine/socat:1.7.3.4-r0] - Could not start container  
org.testcontainers.containers.ContainerLaunchException: Aborting attempt to link to container k5bii89amzd5_app7_1 as it is not running
    at org.testcontainers.containers.GenericContainer.applyConfiguration(GenericContainer.java:779)
    at org.testcontainers.containers.GenericContainer.tryStart(GenericContainer.java:359)
    at org.testcontainers.containers.GenericContainer.lambda$doStart$0(GenericContainer.java:325)
    at org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess(Unreliables.java:81)
    at org.testcontainers.containers.GenericContainer.doStart(GenericContainer.java:323)

Container startup failed
org.testcontainers.containers.ContainerLaunchException: Container startup failed
    at org.testcontainers.containers.GenericContainer.doStart(GenericContainer.java:330)
    at org.testcontainers.containers.GenericContainer.start(GenericContainer.java:311)
    at org.testcontainers.containers.DockerComposeContainer.startAmbassadorContainers(DockerComposeContainer.java:331)
    at org.testcontainers.containers.DockerComposeContainer.start(DockerComposeContainer.java:178)
Caused by: org.rnorth.ducttape.RetryCountExceededException: Retry limit hit with exception
    at org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess(Unreliables.java:88)
    at org.testcontainers.containers.GenericContainer.doStart(GenericContainer.java:323)
    ... 96 more
Caused by: org.testcontainers.containers.ContainerLaunchException: Could not create/start container
    at org.testcontainers.containers.GenericContainer.tryStart(GenericContainer.java:497)
    at org.testcontainers.containers.GenericContainer.lambda$doStart$0(GenericContainer.java:325)
    at org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess(Unreliables.java:81)
    ... 97 more
Caused by: org.testcontainers.containers.ContainerLaunchException: Aborting attempt to link to container k5bii89amzd5_app7_1 as it is not running
    at org.testcontainers.containers.GenericContainer.applyConfiguration(GenericContainer.java:779)
    at org.testcontainers.containers.GenericContainer.tryStart(GenericContainer.java:359)

原Gradle依赖

testImplementation("io.micronaut.test:micronaut-test-junit5")
testImplementation("org.junit.jupiter:junit-jupiter-api")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
testImplementation("org.testcontainers:testcontainers:1.15.2")
testImplementation("org.testcontainers:junit-jupiter:1.15.2")
testImplementation("org.junit.jupiter:junit-jupiter-params")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 15:55:54