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

Docker容器内通过docker.sock运行Spring集成测试连接MySQLContainer失败

解决Docker容器内运行Testcontainers连接Ryuk失败的问题

问题背景

本地执行gradle test时,使用Testcontainers的MySQLContainer运行Spring集成测试完全正常,但在Docker容器内执行测试时抛出异常:

Caused by: java.lang.IllegalStateException: Could not connect to Ryuk at 172.17.0.1:49157

已挂载/var/run/docker.sock卷,尝试过设置TESTCONTAINERS_HOST_OVERRIDE=host.docker.internal、创建网络关联容器等操作,问题仍未解决。

相关配置

build.gradle

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.0.5'
    id 'io.spring.dependency-management' version '1.1.0'
}

group = 'org.autovill'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation group: 'org.springframework.boot', name: 'spring-boot-starter-webflux', version: '3.0.5'
    implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-r2dbc', version: '3.0.5'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation group: 'org.flywaydb', name: 'flyway-core', version: '9.16.3'
    implementation group: 'org.flywaydb', name: 'flyway-mysql', version: '9.16.3'
    implementation group: 'io.asyncer', name: 'r2dbc-mysql', version: '1.0.0'
    implementation group: 'com.mysql', name: 'mysql-connector-j', version: '8.0.32'

    implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.5'
    runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5'
    runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5'

    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'

    testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: '3.0.5'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.springframework.security:spring-security-test'
    testImplementation group: 'io.projectreactor', name: 'reactor-test', version: '3.5.5'

    testImplementation "org.testcontainers:testcontainers:1.18.0"
    implementation platform('org.testcontainers:testcontainers-bom:1.18.0') //import bom
    testImplementation('org.testcontainers:mysql') //no version specified
}

tasks.named('test') {
    useJUnitPlatform()
}

test {
    systemProperty 'spring.profiles.active', 'test'
}

Dockerfile

FROM gradle:7.6.1-jdk17-jammy as base
WORKDIR /app
COPY ./ ./
RUN gradle dependencies

FROM base as test
CMD ["gradle", "test"]

测试代码

@SpringBootTest
class AutovillApplicationStarterTest {
    @ClassRule
    public static MySQLContainer mysql = new MySQLContainer<>("mysql:8.0")
        .withExposedPorts(3306, 3306)
        .withEnv("MYSQL_ROOT_HOST", "%")
        .withUsername("azamat")
        .withPassword("123456")
        .withDatabaseName("autovill")
        .withEnv("MYSQL_HOST", "%");

    static {
        mysql.start();

        System.out.println("MYSQL_HOST: " + mysql.getHost());
        System.out.println("MYSQL JDBC URL: " + mysql.getJdbcUrl());

        System.setProperty("spring.r2dbc.url", mysql.getJdbcUrl().replace("jdbc:", "r2dbc:"));
        System.setProperty("spring.r2dbc.username", mysql.getUsername());
        System.setProperty("spring.r2dbc.password", mysql.getPassword());

        System.setProperty("spring.flyway.url", mysql.getJdbcUrl());
        System.setProperty("spring.flyway.username", mysql.getUsername());
        System.setProperty("spring.flyway.password", mysql.getPassword());
    }

    @Test
    void contextLoads() {
    }
}

构建与运行命令

docker build -t spring-test --target=test . 
docker run \
--volume /var/run/docker.sock:/var/run/docker.sock \
-v $PWD:$PWD \
-w $PWD \
spring-test   

解决方案

1. 固定Ryuk端口并映射

Ryuk默认使用随机端口,容器内测试进程无法直接访问宿主机的随机端口。可以强制指定固定端口,并在运行测试容器时映射该端口:

docker run \
--volume /var/run/docker.sock:/var/run/docker.sock \
-v $PWD:$PWD \
-w $PWD \
-e TESTCONTAINERS_HOST_OVERRIDE=host.docker.internal \
-e TESTCONTAINERS_RYUK_PORT=49157 \
-p 49157:49157 \
spring-test

2. 临时禁用Ryuk

如果不需要Ryuk的资源自动清理功能,可以直接禁用它绕过连接问题:

docker run \
--volume /var/run/docker.sock:/var/run/docker.sock \
-v $PWD:$PWD \
-w $PWD \
-e TESTCONTAINERS_RYUK_DISABLED=true \
spring-test

3. 修正测试代码与版本

  • 移除测试代码中手动调用的mysql.start():@ClassRule会自动管理容器的启动与停止,手动调用可能导致冲突
  • 升级Testcontainers版本到1.19.x及以上,新版本对Docker-in-Docker场景的支持更完善

修改后的测试代码片段:

@SpringBootTest
class AutovillApplicationStarterTest {
    @ClassRule
    public static MySQLContainer mysql = new MySQLContainer<>("mysql:8.0")
        .withEnv("MYSQL_ROOT_HOST", "%")
        .withUsername("azamat")
        .withPassword("123456")
        .withDatabaseName("autovill");

    static {
        // 移除mysql.start()

        System.out.println("MYSQL_HOST: " + mysql.getHost());
        System.out.println("MYSQL JDBC URL: " + mysql.getJdbcUrl());

        System.setProperty("spring.r2dbc.url", mysql.getJdbcUrl().replace("jdbc:", "r2dbc:"));
        System.setProperty("spring.r2dbc.username", mysql.getUsername());
        System.setProperty("spring.r2dbc.password", mysql.getPassword());

        System.setProperty("spring.flyway.url", mysql.getJdbcUrl());
        System.setProperty("spring.flyway.username", mysql.getUsername());
        System.setProperty("spring.flyway.password", mysql.getPassword());
    }

    @Test
    void contextLoads() {
    }
}

4. 使用宿主机网络模式

让测试容器直接使用宿主机网络,这样可以直接访问宿主机上的Ryuk容器:

docker run \
--volume /var/run/docker.sock:/var/run/docker.sock \
-v $PWD:$PWD \
-w $PWD \
--network host \
spring-test

注意:该模式在Windows和Mac的Docker Desktop下可能需要额外配置,且会失去容器网络隔离性。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 03:07:01