Testcontainers, Docker in Docker with custom network, 容器不属于网络
Posted
技术标签:
【中文标题】Testcontainers, Docker in Docker with custom network, 容器不属于网络【英文标题】:Testcontainers, Docker in Docker with custom network, Containers don't belong to network 【发布时间】:2020-08-13 12:17:48 【问题描述】:我正在尝试使用 Docker 构建器映像让 Testcontainers 在 TeamCity 上运行。
测试在本地运行良好(不在构建器映像内)。并且仅部分位于 TeamCity 的构建器图像中。我关注了guide on DinD,但没有关于 docker 网络如何发挥作用的示例。
我们在 TeamCity 中开始构建的方式(注意 --network 参数,ryuk 因连接问题而被禁用):
docker network create --driver bridge custom_network
docker run --rm -it -v $PWD:$PWD -w $PWD \
--privileged \
--network=custom_network \
-e TESTCONTAINERS_RYUK_DISABLED=true \
-e _JAVA_OPTIONS="" \
-e DOCKER_HOST="unix:///var/run/docker.sock" \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /home/teamcity/.docker:/home/java/.docker
-v /local/maven/cache/repository:/opt/m2/repository \
registry.ch/java:11-builder \
mvn verify
构建运行非常正常:junit 测试开始,我们使用的自定义 oracle-xe 映像已下载,日志提示它已启动。但是在本地我可以看到 testcontainers 正在轮询以创建连接,在 TeamCity 上构建只是继续并遇到错误:
[14:01:07] : [Step 3/3] 14:01:07.006 [tc-okhttp-stream-276714561] DEBUG com.github.dockerjava.core.command.PullImageResultCallback - ResponseItem(stream=null, status=Extracting, progressDetail=ResponseItem.ProgressDetail(current=625569807, total=625569807, start=null), progress=[==================================================>] 625.6MB/625.6MB, id=2538d1d7e815, from=null, time=null, errorDetail=null, error=null, aux=null)
[14:01:07] : [Step 3/3] 14:01:07.211 [tc-okhttp-stream-276714561] DEBUG com.github.dockerjava.core.command.PullImageResultCallback - ResponseItem(stream=null, status=Pull complete, progressDetail=ResponseItem.ProgressDetail(current=null, total=null, start=null), progress=null, id=2538d1d7e815, from=null, time=null, errorDetail=null, error=null, aux=null)
...
[14:01:07] : [Step 3/3] 14:01:07.228 [tc-okhttp-stream-276714561] INFO [registry/private/oracle/database:18c_xe] - Pull complete. 2 layers, pulled in 46s (downloaded 637 MB at 13 MB/s)
[14:01:07] : [Step 3/3] 14:01:07.228 [main] DEBUG com.github.dockerjava.core.command.AbstrDockerCmd - Cmd: registry/private/oracle/database:18c_xe
...
[14:01:07]i: [Step 3/3] Docker event: "status":"pull","id":"registry/private/oracle/database:18c_xe","Type":"image","Action":"pull","Actor":"ID":"registry/private/oracle/database:18c_xe","Attributes":"name":"registry/private/oracle/database","scope":"local","time":1588075267,"timeNano":1588075267227817791
...
[14:01:08] : [Step 3/3] :: Spring Boot :: (v2.2.6.RELEASE)
[14:01:08] : [Step 3/3]
[14:01:08] : [Step 3/3] 2020-04-28 14:01:08.502 ERROR 47 --- [ main] o.s.boot.SpringApplication : Application run failed
[14:01:08] : [Step 3/3]
[14:01:08] : [Step 3/3] java.lang.IllegalStateException: Mapped port can only be obtained after the container is started
[14:01:08] : [Step 3/3] at org.testcontainers.shaded.com.google.common.base.Preconditions.checkState(Preconditions.java:174) ~[testcontainers-1.14.1.jar:na]
[14:01:08] : [Step 3/3] at org.testcontainers.containers.ContainerState.getMappedPort(ContainerState.java:129) ~[testcontainers-1.14.1.jar:na]
[14:01:08] : [Step 3/3] at org.testcontainers.containers.OracleContainer.getOraclePort(OracleContainer.java:95) ~[oracle-xe-1.14.1.jar:na]
[14:01:08] : [Step 3/3] at org.testcontainers.containers.OracleContainer.getJdbcUrl(OracleContainer.java:64) ~[oracle-xe-1.14.1.jar:na]
[14:01:08] : [Step 3/3] at ch.package.OracleFlywayDatabaseTest$Initializer.initialize(OracleFlywayDatabaseTest.java:35) ~[test-classes/:na]
[14:01:08] : [Step 3/3] at org.springframework.boot.SpringApplication.applyInitializers(SpringApplication.java:626) ~[spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
...
[14:01:08] : [Step 3/3] at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128) ~[junit-platform-launcher-1.3.1.jar:1.3.1]
...
[14:01:08] : [Step 3/3] at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:418) ~[surefire-booter-2.22.2.jar:2.22.2]
[14:01:08] : [Step 3/3]
...
[14:01:08] : [Step 3/3] org.testcontainers.containers.ContainerLaunchException: Container startup failed
[14:01:08] : [Step 3/3] Caused by: org.testcontainers.containers.ContainerFetchException: Can't get Docker image: RemoteDockerImage(imageName=registry/private/oracle/database:18c_xe, imagePullPolicy=DefaultPullPolicy())
[14:01:08] : [Step 3/3] Caused by: java.time.format.DateTimeParseException: Text '2020-03-04T15:17:25.025952651+01:00' could not be parsed at index 29
[14:01:08] : [Step 3/3]
我不确定最后一个异常,它似乎并不坏,问题似乎是我们启动的oracle容器'不可见'。 DateTimeParseException 中的日期是我们注册表中 oracle-xe 映像的创建日期。
我也尝试使用构建器上的withNetwork
选项创建容器:
@Testcontainers
public abstract class OracleFlywayDatabaseTest
@Container
private static final OracleContainer oracle =
new OracleContainer("registry/private/oracle/database:18c_xe")
// .withNetwork(Network.builder().id("custom_network").build())
.withUsername("TESTUSR")
.withPassword("TESTPWD");
如果我使用 docker network inspect custom_network
在本地调查此问题,则由 Testcontainers 启动的数据库容器不在该网络中。
将容器放入该网络的正确方法是什么?意味着构建器图像最初开始的同一网络? id
真的是 docker 创建时分配给网络的 id 吗? (我试过了,但也许我做错了什么)。
【问题讨论】:
【参考方案1】:我们找到了一种方法来完成这项工作。这不是人们所说的美丽......但这是目前有效的:
使用 docker 命令创建自定义网络(“custom_nework”是本示例中的网络名称):
docker network ls|grep custom_network > /dev/null || docker network create --driver bridge custom_network
然后确定那个网络的id:
network_id=`docker network inspect custom_network --format ".ID"`
并且设置是和环境变量。
在 Testcontainers 测试中,您现在可以通过以下方式引用此网络: (在IDE本地环境中只运行一次测试,我们要区分是否有自定义网络(CI服务器),或者没有(IDE))
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.BeforeAll;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.testcontainers.containers.OracleContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
@Testcontainers
@ContextConfiguration(initializers = OracleFlywayDatabaseTest.Initializer.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
public abstract class OracleFlywayDatabaseTest
private static final Logger LOGGER = LoggerFactory.getLogger(OracleFlywayDatabaseTest.class);
private static final String NETWORK_ID = "NETWORK_ID";
@Container
protected static final OracleContainer oracle;
static
String networkId = System.getenv(NETWORK_ID);
if (StringUtils.isBlank(networkId))
oracle = new OracleContainer("diemobiliar/minimized-oraclexe-image:18.4.0-xe");
else
oracle = new NetworkOracleContainer("diemobiliar/minimized-oraclexe-image:18.4.0-xe", networkId);
oracle.withUsername("AOO_TESTS").withPassword("AOO_TESTS");
@BeforeAll
public static void setupOracle()
LOGGER.info("ORACLE 18 JDBC URL: " + oracle.getJdbcUrl());
public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext>
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext)
TestPropertyValues.of("spring.datasource.platform=" + "ORACLE", //
"spring.datasource.url=" + oracle.getJdbcUrl(), //
"spring.datasource.username=" + oracle.getUsername(), //
"spring.datasource.password=" + oracle.getPassword()) //
.applyTo(configurableApplicationContext.getEnvironment());
LOGGER.info("spring.datasource. Properties set.");
以及帮助 NetworkOracleContainer 类:
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.testcontainers.containers.Network;
public class NetworkOracleContainer extends LocalOracleContainer
private static final String CONTAINER_NAME = "oracle";
public NetworkOracleContainer(String dockerImageName, String networkId)
super(dockerImageName);
this.withNetwork(new ExistingNetwork(networkId))
.withCreateContainerCmdModifier(cmd -> cmd.withName(CONTAINER_NAME));
@Override
public String getHost()
return CONTAINER_NAME;
@Override
public Integer getOraclePort()
return 1521;
private static class ExistingNetwork implements Network
private final String networkId;
ExistingNetwork(String networkId)
this.networkId = networkId;
@Override
public String getId()
return networkId;
@Override
public void close()
// noop
@Override
public Statement apply(Statement base, Description description)
return base;
我们还没有在 Testcontainers API 中找到更好的方法来执行此操作。也许在更新的版本中(目前为 1.14.3)
【讨论】:
以上是关于Testcontainers, Docker in Docker with custom network, 容器不属于网络的主要内容,如果未能解决你的问题,请参考以下文章
如何强制 Testcontainers 使用特定的 docker 镜像?
如何使用 Testcontainers 将可执行文件复制到 Docker 容器
如果不存在,Testcontainers 可以为我创建 docker 网络吗?
使用 testcontainers 使用自定义端口运行 ES docker 映像